Skip to content

feat(codex): add subscription auth via CODEX_AUTH_JSON credential#801

Merged
zbigniewsobiecki merged 275 commits intomainfrom
dev
Mar 14, 2026
Merged

feat(codex): add subscription auth via CODEX_AUTH_JSON credential#801
zbigniewsobiecki merged 275 commits intomainfrom
dev

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

Summary

  • Enables ChatGPT Plus/Pro users to authenticate the Codex engine via a stored CODEX_AUTH_JSON credential instead of an API key
  • Before each run, writeCodexAuthFile() validates and writes ~/.codex/auth.json (mode 0600); after the run, captureRefreshedToken() detects any token refresh the Codex CLI performed and updates the DB credential automatically — keeping it current in ephemeral worker environments
  • CODEX_AUTH_JSON is stripped from the subprocess environment so the JSON blob never leaks into the spawned codex process
  • Added findCredentialIdByEnvVarKey() to credentialsRepository with isDefault = true filter to correctly target the default credential
  • 5 new unit tests covering auth write, env stripping, token refresh capture, no-op on unchanged file, and WARN on missing credential
  • CLAUDE.md: new Codex Backend section documenting subscription auth setup and automatic token refresh behaviour

Test plan

  • npm run typecheck — clean
  • npm run lint — clean
  • npm test — 4683 tests, 270 files, all pass
  • Unit tests cover: auth.json written with correct mode, CODEX_AUTH_JSON absent from subprocess env, DB credential updated on token refresh, no-op when file unchanged, WARN logged when credential row not found

🤖 Generated with Claude Code

Cascade Bot and others added 30 commits March 6, 2026 22:27
Add per-agent-type concurrency limits to prevent multiple instances of the
same agent type running simultaneously for a project.

Core implementation:
- New `agent_configs.max_concurrency` column with CHECK constraint
- Two-layer lock in `src/router/agent-type-lock.ts`:
  1. In-memory concurrency map (fast path, TTL safety net)
  2. DB count of running `agent_runs` (authoritative, survives restarts)
- Trigger-level dedup (60s TTL) suppresses batch webhook re-triggers
- Shared `checkAgentTypeConcurrency()` consolidates logic from 3 handlers

Key design decisions:
- Use `Math.max(dbCount, inMemoryCount)` instead of sum to avoid
  double-counting during the enqueued→running transition
- 5-second TTL cache on `getMaxConcurrency()` DB queries
- Graceful fallback (no limit) when DB is unreachable
- Periodic cleanup of dedup map when it exceeds 100 entries

Dashboard & CLI:
- Agent config forms expose Max Concurrency field
- `cascade agents create/update --max-concurrency N`
- Agent configs table shows concurrency column

Container manager:
- `extractAgentType()` handles both trigger-based and dashboard jobs
- Worker cleanup releases agent-type locks on exit
- `detachAll()` clears all agent-type locks on shutdown

Tests: 24 unit tests for agent-type-lock, concurrency-blocked tests added
to all 3 webhook handler test suites (3868 total tests passing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ency-limiting

feat(router): add agent-type concurrency limiting
* refactor: clean up trigger config migration — deduplicate, validate, and harden

- Add `checkTriggerEnabledWithParams()` to combine enabled check + parameter
  fetch in a single DB call (eliminates double query in check-suite-success
  and pr-opened handlers)
- Extract `evaluateAuthorMode()` into `src/triggers/github/utils.ts` to
  deduplicate ~25 lines of authorMode gating logic across two handlers,
  with proper validation against ['own', 'external', 'all'] and fallback
- Switch pr-ready-to-merge and pr-merged from direct `isTriggerEnabled()`
  to shared `checkTriggerEnabled()` helper for consistent logging
- Add debug log for no-@mention path in pr-comment-mention
- Remove ~1900 lines of legacy trigger config schemas and tests
- Add disabled-trigger + argument verification tests to all 13 handler
  test files for complete coverage of the DB-driven trigger config path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: include evaluateAuthorMode in utils.ts (missed in prior commit)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update integration tests to use DB-driven trigger config

Config toggle tests were using legacy `project_integrations.triggers` JSON
which is no longer consulted. Updated to:
- Seed `agent_trigger_configs` rows with `enabled: false` for disable tests
- Test via `handle()` (which checks DB config) instead of `matches()`
- Add `seedTriggerConfig` helper and include table in truncateAll

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The backlog manager posted its selection comment after moving the card,
which meant downstream automation (pm:status-changed triggers) could
fire before the rationale was visible. Swap steps 5 and 6 so the
comment is posted first, then the card is moved.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…OMMENT_ID env var (#638)

* feat(progress): replace file-based comment ID with CASCADE_PROGRESS_COMMENT_ID env var

* chore: remove .cascade-progress-comment-id state file from repo

The file-based progress comment ID mechanism has been replaced with the
CASCADE_PROGRESS_COMMENT_ID env var. Remove the previously tracked state
file and its .gitignore entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(progress): use ENV_VAR_NAME constant and add injection tests

- Import ENV_VAR_NAME from progressState.ts in secretBuilder.ts and env.ts
  instead of hardcoding 'CASCADE_PROGRESS_COMMENT_ID' string literal
- Add 5 unit tests for injectProgressCommentId covering: string ackCommentId
  with cardId injects, numeric ackCommentId skips, missing cardId skips,
  undefined ackCommentId skips, empty string ackCommentId skips

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): update progressMonitor.test.ts for env-var-based progress state

Tests from dev branch (PR #637) used old file-based signatures for
writeProgressCommentId and clearProgressCommentId. Updated to match
the new env-var-based API (no repoDir parameter).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ci): restore .cascade-progress-comment-id in .gitignore

The file may still be created by running cascade processes (e.g. the
orchestrator session itself). Keeping the gitignore entry prevents it
from appearing as an uncommitted change and triggering the stop hook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Three bugs caused the review agent to silently not run on PR#425:

1. Check polling window too short — `waitForChecks` polled 5×10s = 50s
   max, but CI took ~60s. Increase MAX_RETRIES from 5 to 12 (120s window).

2. Orphaned ack comment — when `pollWaitForChecks` returns false the
   handler returned without cleaning up the "👀 Reviewing" ack comment,
   leaving a misleading message on the PR. Now deletes the ack comment
   before returning.

3. Ack posted before job enqueued — the router posted the ack comment
   (step 8) before calling `addJob()` (step 9). A router crash between
   them orphans the ack with no corresponding job. Swap the ordering:
   enqueue first (durable in Redis), then post ack and patch the
   ackCommentId onto the job via `updateData()`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lience

fix(router): review agent trigger resilience and ack ordering
…tions

Replace the tautological assertion in the "provides suggestions when similar
content exists" test that would pass regardless of gadget behavior. The test
now verifies that the error message contains both "NOT FOUND", "SIMILAR
CONTENT FOUND", and the actual similar content from the file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-replace-tests

test(gadgets): add unit tests for FileSearchAndReplace
Removes the unused `fireAndForget` and `checkCapacity` properties from
`WebhookHandlerConfig` interface along with their associated JSDoc.
Also simplifies the `processWebhook` JSDoc to reflect router-only
(always-await) semantics.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…bhook processor

Step 2 (event type filter) now returns "Event type not processable: <eventType>"
instead of reusing the Step 1 (parse failure) reason string, making the two
distinct decision points distinguishable in logs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the binary work-item lock with a two-dimensional concurrency
model: up to 2 agents per work item but only 1 of the same type. This
allows implementation + review to overlap on the same card while still
preventing duplicate agents.

Key changes:
- work-item-lock: track per (projectId, workItemId, agentType) with
  count-based slots, in-memory short-circuits before DB queries
- container-manager: separate clearWorkItemEnqueued guard (needs
  agentType) from failOrphanedRun guard (only needs projectId +
  workItemId) — fixes silent regression for jobs lacking agentType
- webhook-processor: pass agentType through to lock functions, call
  onBlocked() when job cannot be enqueued
- check-suite-success: add onBlocked callback to clear dedup entry
  when router blocks the job
- TriggerResult: add optional onBlocked callback for trigger handlers
- runsRepository: add countActiveRunsForWorkItem/AndType queries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
aaight and others added 29 commits March 13, 2026 17:39
#774)

* feat(cancel-button): improve UX with loading spinner and success state

- Add loading spinner (Loader2 with animate-spin) during mutation
- Show brief success indicator with auto-dismiss after 2 seconds
- Replace window.confirm() with shadcn AlertDialog
- Update dialog text: 'This will terminate the worker container?'
- Improve error display to show full error message
- Prevent double-cancellation by disabling button during success

* fix(cancel-button): remove dead code and use AlertDialogFooter

- Remove dead `disabled={cancelMutation.isPending}` props from AlertDialogCancel
  and AlertDialogAction — Radix UI auto-closes the dialog on action click, so
  isPending is never true while the dialog is visible
- Remove the unreachable Loader2 spinner inside AlertDialogAction for the same reason
- Replace raw `<div>` footer with `AlertDialogFooter` for consistent responsive
  layout matching credentials-table, projects-table, and agent-configs-table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ancellation (#777)

* feat(router): implement cancel listener and API integration for run cancellation

- New src/router/cancel-listener.ts: subscribes to Redis cancel channel and kills workers
- Fallback Docker label scanning when jobId not found in database (race condition handling)
- Updated router startup in src/router/index.ts to start/stop cancel listener with lifecycle
- Updated runs.ts cancel mutation to publish cancel command after cancelRunById succeeds
- Fire-and-forget publish with error logging to prevent API failures
- Comprehensive unit tests for cancel-listener with Docker and DB mocking
- Updated runs.test.ts to verify publishCancelCommand is called with correct parameters

All tests pass, lint and typecheck clean.

* fix(cancel-listener): address review feedback on correctness and cleanup

- Remove unsafe Docker fallback that could kill wrong container in
  multi-run environments; containers have no run ID label so matching
  is impossible — replaced with a warning log
- Add unsubscribeFromCancelCommands() to queue/cancel.ts so the Redis
  subscriber connection is properly closed on graceful shutdown
- Replace misleading type hack (cancelSubscriber = true as unknown as ...)
  with a clear boolean flag (cancelSubscriberActive)
- Remove redundant cascadeContainers.length > 0 check that was always
  true after the early return guard
- Update tests to cover new warning-log behavior and verify shutdown
  properly calls unsubscribeFromCancelCommands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(scm): fix pre-existing sidecar test assertion for ackCommentDeleted field

The sidecar now includes ackCommentDeleted in its output; update the
assertion to use toMatchObject to allow for extra fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(opencode): hardcode agent to 'build' and remove selection

* test(opencode): remove stale agent field references from schema tests

Remove `agent` field from the two opencode engine settings test cases
in schema.test.ts, matching the cleanup done in the rest of this PR.
The tests now only assert on `webSearch`, which is the only remaining
field in OpenCodeSettingsSchema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Cascade Bot <bot@cascade.dev>
…irect navigation (#780)

Co-authored-by: Cascade Bot <bot@cascade.dev>
* feat(trello): add createBoardCustomField support

- Add createBoardCustomField method to trelloClient making POST to /customFields
- Method accepts boardId, name, type and returns { id, name, type }
- Add createTrelloCustomField tRPC mutation in integrationsDiscoveryRouter
- Validate boardId (alphanumeric, max 32), name (min 1, max 100), type (enum)
- Add unit tests for client method and tRPC endpoint
- Tests cover success, validation, auth, credentials, and error handling

* test(trello): move createTrelloCustomField describe inside parent block

Move the describe('createTrelloCustomField') block inside the parent
describe('integrationsDiscoveryRouter') so the beforeEach DB mock setup
runs before these tests, matching the pattern of all other endpoint suites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…oint (#782)

* feat(jira): add createCustomField method to JIRA client and tRPC endpoint

* fix(jira): use neutral error prefix in createCustomField error handler

Replace misleading "requires admin permissions" prefix with neutral
"failed (admin permissions may be required)" to avoid confusion when
the root cause is a network error, duplicate name, or validation issue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(jira): add searcherKey to createCustomField for JQL searchability

Pass the exactnumber searcherKey when creating a float custom field so
the field is queryable via JQL (e.g. `"Cost" > 100`). The method
signature accepts an optional searcherKey param; the tRPC endpoint
always passes exactnumber for the hardcoded float type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Cascade Bot <bot@cascade.dev>
…on (#784)

* feat(pm-wizard): add Trello custom field creation UI with Create button

- Add ADD_TRELLO_BOARD_CUSTOM_FIELD action to WizardAction type
- Handle ADD_TRELLO_BOARD_CUSTOM_FIELD in wizardReducer to append custom field
- Create useTrelloCustomFieldCreation hook following useTrelloLabelCreation pattern
- Hook calls createTrelloCustomField tRPC endpoint and dispatches actions on success
- Add Create button next to cost field in TrelloFieldMappingStep
- Button shows Loader2 spinner during creation and is disabled while creating
- New field auto-selects in dropdown on successful creation
- Display clear 403 error about Custom Fields power-up requirement on failure
- Add comprehensive test cases for ADD_TRELLO_BOARD_CUSTOM_FIELD action
- All tests passing (4618 tests)

Closes #309

* fix(pm-wizard): hide cost field Create button when field already selected or exists

Guard the Create button with checks for an already-selected cost field
(trelloCostFieldId) and an already-existing Cost number custom field in
customFields, matching the label creation pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
… pages (#786)

* feat(dashboard): add recharts visualizations to Work Item and PR Runs pages

* docs(chart-colors): correct misleading comment about dark mode support

The JSDoc claimed the palette covered both light and dark themes, but
only light-mode oklch approximations were defined. Update the comment to
accurately state the palette is static and visible in both modes but not
theme-adaptive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(wizard): add Create button for JIRA cost custom field

* fix(wizard): add duplicate-field guard to JIRA cost field Create button

Check whether a "Cost" field already exists in jiraProjectDetails.fields
before showing the Create button, mirroring the Trello equivalent guard.
This prevents creating duplicate global JIRA custom fields when a Cost
field already exists but hasn't been selected yet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…nly agent configs

The agent config tests were still using the old API signatures (orgId param,
projectId: null, no-arg listAgentConfigs). Updated to match the new
project-only schema after migration 0036.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ent-configs

feat(agent-configs): remove global/org-level agent configs — project-only migration
* feat(run-links): add per-project run links in agent comments

* fix(run-links): include run link in GitHub PR ack comment and deduplicate buildRunLinkFooter

- Fix correctness bug where GitHub PR ack comment was posted without the
  run link footer. Previously the link was appended to the internal
  AckResult.message *after* the comment was already on GitHub, so users
  never saw it. Now the message is built with the run link before calling
  postGitHubPRAck, matching the Trello/JIRA/PM-focused paths.

- Extract shared buildRunLinkFooterFromEnv(workItemId?) to src/utils/runLink.ts
  and replace the 4 identical copies in createPR.ts, createPRReview.ts,
  postPRComment.ts, and postComment.ts, preventing future drift.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ository functions (#791)

Co-authored-by: Cascade Bot <bot@cascade.dev>
…to workers

The Claude Code backend now surfaces a human-readable error when a run
hits the maxBudgetUsd limit instead of the raw SDK subtype string
`error_max_budget_usd`. The error formatting is extracted into a
standalone `formatErrorMessage` function, resolving a pre-existing
biome cognitive-complexity lint warning on `buildResult`.

Worker containers spawned by the router now receive the
`CASCADE_DASHBOARD_URL` env var so the progress monitor can include
run links in Trello/JIRA comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e-and-worker-dashboard-url

fix: improve budget-exceeded error message and forward dashboard URL to workers
…s control (#792)

* feat(api): add users tRPC router with CRUD operations and admin access control

* fix(users): enforce superadmin privilege hierarchy for update and delete

- Prevent regular admins from revoking superadmin role (update procedure)
- Prevent regular admins from deleting superadmin users (delete procedure)
- Add tests covering both new security guards
- Replace misleading passwordHash assertion with explanatory comment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…lete) (#794)

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Cascade Bot <bot@cascade.dev>
)

* feat(dashboard): merge agent config modal into expandable sections

* fix(dashboard): move Saved indicator after confirmed save and clean up timers

- setSaved(true) now fires only after the mutation's onSuccess callback,
  preventing "Saved" text showing when the mutation fails
- Save success is signalled via a per-agent saveSuccessNonce counter that
  increments on each confirmed onSuccess, so repeated saves always trigger
  the useEffect in DefinitionAgentSection
- setTimeout IDs are stored in useRef and cleared in useEffect cleanup to
  avoid state updates on unmounted components (fixes both agent section and
  lifecycle triggers section)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(dashboard): fix saved indicator race condition and add delete error handling

- Add `justSavedRef` to prevent the config sync effect from clearing the
  "Saved" indicator when `invalidateQueries` triggers a refetch after save
- Set `justSavedRef.current = true` in the nonce effect before showing the
  indicator, cleared by the next config sync cycle
- Add `onError` toast to `deleteMutation` for consistent error feedback
  alongside create/update mutations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…dence module (#798)

Introduces a continuation loop in both Claude Code and OpenCode backends so agents
that fail a post-completion check (e.g. no authoritative PR sidecar written) can
resume the session and retry rather than immediately failing the run.

## Core changes

### Shared completion module (`src/backends/completion.ts`)
- Extract `applyCompletionEvidence()` from OpenCode's local scope to the shared
  `completion.ts` module so both backends share identical evidence-upgrade logic.

### Claude Code backend (`src/backends/claude-code/index.ts`)
- Add `consumeStream()` helper: processes the SDK stream, returns `turnCount` and
  `toolCallCount` (replaces the `StreamConsumptionContext` mutable-ref pattern).
- Add `countToolCalls()` helper to count `tool_use` blocks without deep nesting.
- Add `decideContinuation()` helper: encapsulates the completion-failure check,
  max-turns guard, and continuation-warning log; keeps `execute()` complexity low.
- Add `cleanupPersistedSession()`: removes `~/.claude/projects/<encoded-cwd>` after
  each worker run. Encodes cwd with `replaceAll(path.sep, '-')` to match the SDK's
  actual directory naming (fix: plain `path.join` silently produced a wrong path).
- Continuation loop in `execute()`: on completion failure, re-prompt the existing
  session with `{ continue: true }` up to `maxContinuationTurns` times.
- Fix log message: "execution completed" → "turn completed" (accurate for both
  initial and continuation turns).
- Fix spread pattern: `...(bool && obj)` → `...(bool ? obj : {})`.
- Log `toolCallCount` in the continuation warning (parity with OpenCode).

### OpenCode backend (`src/backends/opencode/index.ts`)
- Remove local `applyCompletionEvidence()` in favour of the shared import.
- Fix `maxContinuationTurns` fallback: `1` → `0` (match Claude Code; if
  `completionRequirements` is absent there is nothing to check).

### Adapter (`src/backends/adapter.ts`)
- Bump `maxContinuationTurns` from 1 → 2.

## Tests

- `tests/unit/backends/completion.test.ts` (new): covers `applyCompletionEvidence`
  for all cases (no sidecar, sidecar upgrades text evidence, adds missing prUrl,
  default command fallback).
- `tests/unit/backends/claude-code.test.ts`: five new continuation-loop scenarios
  (success after retry, exhausted turns, non-success stops immediately, cost
  accumulation, no-op when completionRequirements absent). Adds `beforeEach` reset
  and renames inner helper to `queueStream` to avoid shadowing the outer `mockStream`.
- `tests/unit/backends/adapter.test.ts`: update `maxContinuationTurns` expectation.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Bumps [undici](https://github.com/nodejs/undici) from 7.22.0 to 7.24.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](nodejs/undici@v7.22.0...v7.24.1)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 7.24.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…lls, not describe them (#799)

Gemini Flash (and similar models) sometimes output bash commands inside
markdown code blocks instead of actually invoking them as tool calls,
then stop with reason:stop — causing silent no-ops (card never moved).

Adds an engine-agnostic CRITICAL rule to the Rules section of the
backlog-manager prompt instructing the model to always invoke tool calls
rather than narrate them as text.

Also adds a regression test asserting the rule appears in the rendered
system prompt.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cascade Bot <bot@cascade.dev>
Enables ChatGPT Plus/Pro users to run the Codex engine without an API
key by storing the `~/.codex/auth.json` OAuth token pair as an
org-scoped credential.

- `writeCodexAuthFile`: validates and writes `auth.json` (mode 0600)
  before each run; returns the original content for change detection
- `captureRefreshedToken`: reads `auth.json` after the run; if the
  Codex CLI refreshed the access token, updates the DB credential so
  it stays current in ephemeral worker environments
- Strips `CODEX_AUTH_JSON` from the subprocess env so the JSON blob
  never leaks into the spawned `codex` process
- `findCredentialIdByEnvVarKey` added to `credentialsRepository` with
  `isDefault = true` filter to target the right credential row
- Module-level path constants (`CODEX_AUTH_DIR`, `CODEX_AUTH_FILE`)
  shared by both helpers; JSON validated before writing
- 5 new unit tests covering auth write, env stripping, token refresh
  capture, no-op on unchanged file, and WARN on missing credential
- CLAUDE.md: Codex Backend section documenting subscription auth setup
  and automatic token refresh behaviour

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zbigniewsobiecki zbigniewsobiecki merged commit 65d41c1 into main Mar 14, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants