From ae7e5a967f7a68ad02fd0c1526f1356a4bc6407d Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Fri, 16 Oct 2026 17:21:22 +0000 Subject: [PATCH 01/10] Fix Cloud policy loading and harden environment setup validation --- .../references/repository-workflow.md | 5 +- .../scripts/verify-repository-isolation.mjs | 26 +- .agents/skills/rag-cloud-sdd/SKILL.md | 28 +- .claude/agents/rag-retrieval-reviewer.md | 49 +- .claude/cloud-profile/CLAUDE.md | 11 + .claude/cloud-profile/memory/MEMORY.md | 23 +- .../memory/claude-hook-exec-bit-trap.md | 14 +- .../memory/claude-plugin-native-deps-trap.md | 16 +- .../communication-style-plain-and-brief.md | 12 +- .../memory/never-print-env-values.md | 14 +- .../cloud-profile/memory/seed-manifest.json | 12 + .../memory/token-usage-hygiene.md | 29 +- .../skills/choose-model/SKILL.md | 77 +- .../cloud-profile/skills/code-review/SKILL.md | 19 +- .claude/cloud-profile/skills/prompt/SKILL.md | 4 +- .../prompt/references/coding-workflow.md | 15 +- .claude/hooks/testing-policy.sh | 32 +- .claude/settings.json | 4 +- .claude/skills/newtask/SKILL.md | 178 ++--- .claude/skills/prompt/SKILL.md | 4 +- .../prompt/references/coding-workflow.md | 15 +- AGENTS.md | 5 +- README.md | 18 +- docs/README.md | 34 +- docs/agents-guide.md | 2 +- docs/agents/codex-cloud-environment.md | 218 +++--- docs/agents/codex-reasoning-effort.md | 74 +- docs/agents/shared-context-and-memory.md | 2 +- docs/agents/smart-agent-allocation.md | 36 +- docs/agents/verification-gates.md | 7 +- .../cloud-session-state-2026-10-16/README.md | 19 + .../codex-chat-import-tooling.md | 0 .../db-remediation-coordination-state.md | 0 .../dev-drive-project-location.md | 0 .../ingestion-review-remediation-state.md | 0 .../lighthouse-mobile-root-cls-flake.md | 0 .../local-test-failures-windows.md | 0 .../rag-programme-coordination-state.md | 0 ...loud-design-status-semantics-2026-10-16.md | 292 ++++++++ .../codex-cloud-legacy-profile-2026-10-16.md | 672 ++++++++++++++++++ ...rag-coverage-gate-extraction-2026-10-16.md | 163 +++++ docs/claude-cloud.md | 175 +++-- docs/codex-cloud.md | 621 +--------------- .../2026-10-16-cloud-legacy-profile-scope.md | 11 + ...6-10-16-cloud-operating-policy-overhaul.md | 29 + ...2026-10-16-cloud-policy-repair-boundary.md | 11 + ...ove-obsolete-cloud-startup-instructions.md | 11 + ...-10-16-user-controlled-reasoning-effort.md | 11 + docs/organisation/retired-rules.json | 32 + docs/process-hardening.md | 3 +- ...chitecture-maintainability-ultra-review.md | 13 +- .../codex-cloud-design-status-semantics.md | 291 +------- ...codex-data-database-safety-ultra-review.md | 13 +- ...ex-documentation-ownership-ultra-review.md | 13 +- ...dex-functional-correctness-ultra-review.md | 17 +- ...ex-performance-reliability-ultra-review.md | 17 +- .../codex-tests-quality-gates-ultra-review.md | 17 +- ...aware-clinical-ask-codex-cloud-handover.md | 51 +- docs/prompts/rag-coverage-gate-extraction.md | 159 +---- docs/scripts-index.md | 52 +- ...-mode-aware-clinical-ask-implementation.md | 63 +- .../rag-upgrade/CLOUD-EXECUTION-PROMPT.md | 10 +- .../rag-upgrade/canonical/START-HERE.cloud.md | 42 +- .../rag-upgrade/canonical/execution-order.md | 2 +- .../canonical/programme-manifest.json | 2 +- .../rag-upgrade/canonical/sdd-execution.md | 4 +- .../rag-upgrade/cloud/START-HERE.md | 42 +- .../rag-upgrade/cloud/execution-order.md | 2 +- .../rag-upgrade/cloud/programme-manifest.json | 2 +- .../rag-upgrade/cloud/sdd-execution.md | 4 +- .../rag-upgrade/local/execution-order.md | 2 +- .../rag-upgrade/local/programme-manifest.json | 2 +- .../rag-upgrade/local/sdd-execution.md | 4 +- plugins/clinical-kb/README.md | 28 +- .../skills/clinical-kb-workflow/SKILL.md | 16 +- scripts/apply-claude-cloud-profile.mjs | 109 ++- scripts/build-rag-plan-packages.mjs | 4 +- scripts/check-codex-cloud-setup.mjs | 83 ++- scripts/diagnose-codex-cloud.mjs | 115 ++- scripts/ensure-codex-cloud-git-remote.mjs | 12 +- scripts/install-codex-cloud-command-shims.sh | 12 + scripts/maintain-codex-cloud.sh | 53 +- scripts/organisation/check-retired-rules.mjs | 34 +- scripts/rag-phase-launch-check.mjs | 9 +- scripts/run-codex-cloud-github.sh | 23 +- scripts/setup-claude-cloud.sh | 162 +++-- scripts/setup-codex-cloud.sh | 79 +- tests/claude-cloud-profile.test.ts | 361 +++++++++- tests/cloud-installed-guidance.test.ts | 83 +++ tests/codex-cloud-diagnose.test.ts | 142 +++- tests/codex-cloud-github-lifecycle.test.ts | 73 +- tests/codex-cloud-policy-harness.test.ts | 314 +++++++- tests/codex-cloud-remote.test.ts | 55 ++ tests/codex-cloud-setup.test.ts | 260 ++++++- tests/database-skills.test.ts | 6 +- tests/organisation-instruction-guards.test.ts | 97 +++ tests/rag-plan-package-parity.test.ts | 22 + 97 files changed, 4032 insertions(+), 1977 deletions(-) create mode 100644 .claude/cloud-profile/memory/seed-manifest.json create mode 100644 docs/archive/cloud-session-state-2026-10-16/README.md rename {.claude/cloud-profile/memory => docs/archive/cloud-session-state-2026-10-16}/codex-chat-import-tooling.md (100%) rename {.claude/cloud-profile/memory => docs/archive/cloud-session-state-2026-10-16}/db-remediation-coordination-state.md (100%) rename {.claude/cloud-profile/memory => docs/archive/cloud-session-state-2026-10-16}/dev-drive-project-location.md (100%) rename {.claude/cloud-profile/memory => docs/archive/cloud-session-state-2026-10-16}/ingestion-review-remediation-state.md (100%) rename {.claude/cloud-profile/memory => docs/archive/cloud-session-state-2026-10-16}/lighthouse-mobile-root-cls-flake.md (100%) rename {.claude/cloud-profile/memory => docs/archive/cloud-session-state-2026-10-16}/local-test-failures-windows.md (100%) rename {.claude/cloud-profile/memory => docs/archive/cloud-session-state-2026-10-16}/rag-programme-coordination-state.md (100%) create mode 100644 docs/archive/codex-cloud-design-status-semantics-2026-10-16.md create mode 100644 docs/archive/codex-cloud-legacy-profile-2026-10-16.md create mode 100644 docs/archive/rag-coverage-gate-extraction-2026-10-16.md create mode 100644 docs/decisions/2026-10-16-cloud-legacy-profile-scope.md create mode 100644 docs/decisions/2026-10-16-cloud-operating-policy-overhaul.md create mode 100644 docs/decisions/2026-10-16-cloud-policy-repair-boundary.md create mode 100644 docs/decisions/2026-10-16-remove-obsolete-cloud-startup-instructions.md create mode 100644 docs/decisions/2026-10-16-user-controlled-reasoning-effort.md create mode 100644 tests/cloud-installed-guidance.test.ts create mode 100644 tests/codex-cloud-remote.test.ts diff --git a/.agents/skills/prompt-perfector/references/repository-workflow.md b/.agents/skills/prompt-perfector/references/repository-workflow.md index 03d2b7d99c..3352053a13 100644 --- a/.agents/skills/prompt-perfector/references/repository-workflow.md +++ b/.agents/skills/prompt-perfector/references/repository-workflow.md @@ -61,10 +61,9 @@ if ($LASTEXITCODE -ne 0) { throw 'Repository isolation verification failed.' } ### Codex Cloud checkout -Codex Cloud does not have the Windows bootstrap. It may use its single disposable checkout as the primary Git worktree only when `CODEX_CLOUD=1`, the checkout is clean, and it is on a task-specific non-protected branch. Create a branch before verification if the supplied checkout is detached or protected. +Use this mode when the current host or task configuration establishes a disposable Cloud task checkout. `--cloud` explicitly attests that workflow choice; the legacy `CODEX_CLOUD=1` marker is neither required nor sufficient. The verifier binds the choice to the expected repository, branch and `HEAD`, and permits a primary checkout only when it is the sole registered worktree, clean, and on a task-specific non-protected branch. Create a branch before verification if the supplied checkout is detached or protected. If current host evidence does not establish a disposable Cloud checkout, use the applicable local worktree workflow above. ```bash -test "${CODEX_CLOUD:-}" = "1" || { echo 'CODEX_CLOUD=1 is required.' >&2; exit 1; } test -z "$(git status --porcelain --untracked-files=all)" || { echo 'Cloud checkout is dirty.' >&2; exit 1; } branch="$(git branch --show-current)" case "$branch" in @@ -83,7 +82,7 @@ node .agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs \ 5. Re-run the verifier immediately before editing. If any condition is unproved or changes unexpectedly, stop and request direction. 6. For a same-task dirty continuation, first run the verifier without `--allow-dirty`, inventory every changed path, and record the emitted `SAFE_STATUS_HASH`. Re-run with `--allow-dirty --expected-status-hash ` plus the same expected repo, branch, and `HEAD` values. Include `--cloud` in Cloud. -The verifier is read-only and establishes workflow isolation; it does not provide an OS-level sandbox or protection from unrelated processes. State that limit honestly. +The verifier is read-only and establishes workflow isolation; it does not provide an OS-level sandbox or protection from unrelated processes. `--cloud` does not detect or prove platform isolation, provider authorization or readiness of the legacy bootstrap. State those limits honestly. ## Execution and verification diff --git a/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs b/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs index 4970e8444f..be695316af 100644 --- a/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs +++ b/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs @@ -65,7 +65,6 @@ export function evaluateRepositoryState(state, options = {}) { const currentIndex = state.worktrees.findIndex( (worktree) => normalizePath(String(worktree.worktree)) === currentPath, ); - const cloudEnvironment = options.cloudEnvironment ?? false; if (!path.isAbsolute(state.root)) reasons.push("repository_path_not_absolute"); if (!state.branch) reasons.push("detached_head"); @@ -74,7 +73,6 @@ export function evaluateRepositoryState(state, options = {}) { else if (currentIndex === 0 && !options.cloud) reasons.push("primary_worktree"); else if (currentIndex === 0 && options.cloud && state.worktrees.length !== 1) reasons.push("cloud_primary_requires_single_worktree"); - if (options.cloud && !cloudEnvironment) reasons.push("cloud_environment_required"); if (state.operations.length) reasons.push("git_operation_in_progress"); if (options.expectedRepo && normalizePath(options.expectedRepo) !== currentPath) reasons.push("repository_drift"); if (options.expectedBranch && options.expectedBranch !== state.branch) reasons.push("branch_drift"); @@ -192,17 +190,30 @@ function runSelfTest() { expectedBranch: cloudBase.branch, expectedHead: head, cloud: true, - cloudEnvironment: true, }; const cases = [ ["safe secondary worktree", base, expected, true, ""], ["safe Cloud primary", cloudBase, cloudExpected, true, ""], - [ - "Cloud flag without environment", + ["Cloud primary without explicit mode", cloudBase, { ...cloudExpected, cloud: false }, false, "primary_worktree"], + ...["expectedRepo", "expectedBranch", "expectedHead"].map((key) => [ + `Cloud missing ${key}`, cloudBase, - { ...cloudExpected, cloudEnvironment: false }, + { ...cloudExpected, [key]: undefined }, + false, + "expected_state_required", + ]), + ["Cloud repository drift", cloudBase, { ...cloudExpected, expectedRepo: "/other" }, false, "repository_drift"], + ["Cloud branch drift", cloudBase, { ...cloudExpected, expectedBranch: "codex/other" }, false, "branch_drift"], + ["Cloud HEAD drift", cloudBase, { ...cloudExpected, expectedHead: "b".repeat(40) }, false, "head_drift"], + ["Cloud protected branch", { ...cloudBase, branch: "main" }, cloudExpected, false, "protected_branch"], + ["Cloud detached HEAD", { ...cloudBase, branch: "" }, cloudExpected, false, "detached_head"], + ["Cloud dirty default", { ...cloudBase, status: dirtyStatus }, cloudExpected, false, "dirty_worktree"], + [ + "Cloud dirty content drift", + { ...cloudBase, status: dirtyStatus, statusHash: changedContentHash }, + { ...cloudExpected, allowDirty: true, expectedStatusHash: dirtyHash }, false, - "cloud_environment_required", + "status_drift", ], [ "Cloud primary with sibling worktree", @@ -267,7 +278,6 @@ function main() { const options = parseArguments(process.argv.slice(2)); if (options.selfTest) runSelfTest(); else { - options.cloudEnvironment = process.env.CODEX_CLOUD === "1"; const state = inspectRepository(process.cwd()); const result = evaluateRepositoryState(state, options); emit(result, state, options); diff --git a/.agents/skills/rag-cloud-sdd/SKILL.md b/.agents/skills/rag-cloud-sdd/SKILL.md index 526a699bf6..8fc1e4c006 100644 --- a/.agents/skills/rag-cloud-sdd/SKILL.md +++ b/.agents/skills/rag-cloud-sdd/SKILL.md @@ -1,18 +1,26 @@ --- name: rag-cloud-sdd -description: Execute one RAG Cloud phase with fresh agents, adaptive effort, immutable review ranges, and fail-closed routing. +description: Execute one RAG Cloud phase with fresh agents, adaptive effort, immutable reviews, and fail-closed routing. --- # RAG Cloud SDD -Read the generated package and root `AGENTS.md`. +Read root `AGENTS.md`, canonical Cloud policy and the generated package. Keep execution provider-free; +legacy setup applies only when explicitly selected. -1. Require `TARGET_PHASE` before inspection. Run either `node scripts/rag-phase-launch-check.mjs --target "$TARGET_PHASE" --effort high` or `node scripts/rag-phase-launch-check.mjs --target "$TARGET_PHASE" --effort xhigh --xhigh-confirmed`. -2. Prove controller routing with the route-evidence schema. Hash this skill, helpers and phase skills. Dispatch the manifest-routed read-only probe; bind its ID and direct dispatch record. Stop on unverified route, runtime or capability. -3. Scan the plan, receipts, relevant code, and worktrees for conflicts. Preserve unrelated or quarantined work. -4. Record `TASK_BASE`; generate the brief; dispatch a fresh implementer; require proof, self-review and defined completion status. -5. Commit only owned implementation. Build the full `TASK_BASE..TASK_HEAD` package. Dispatch one distinct fresh reviewer for separate specification and quality verdicts. -6. Return Critical/Important findings to the implementer and repeat full-range review. Carry Minor findings in the ledger. Serialize writers. -7. Dispatch a distinct phase reviewer over `PHASE_START..PHASE_END`. Bind every agent ID to authoritative route metadata. Atomically commit the accepted receipt/evidence, validate, push only when authorized, and stop. +1. Require `TARGET_PHASE`. Confirm selected effort in ordinary language; no prompt token or + resubmission. Run `scripts/rag-phase-launch-check.mjs` with target/effort; `--xhigh-confirmed` + records user attestation, not runtime proof. +2. Require authoritative route evidence. Hash controller/helpers/skills; bind the fresh read-only + probe's source/receipt inspection and dispatch metadata. Stop on unverified routes/capabilities. +3. Check receipts, dependencies and worktree conflicts; preserve unrelated work. +4. Record `TASK_BASE`; generate the brief; dispatch one fresh implementer; require decisive proof, + self-review and completion status. +5. Commit only authorised task files. Review the entire `TASK_BASE..TASK_HEAD` range with a distinct + reviewer returning separate specification and quality verdicts. +6. Return Critical/Important findings to the implementer; re-review the full range. Record Minor findings. +7. Require distinct phase review across `PHASE_START..PHASE_END`, route evidence and atomic accepted + receipts. Validate; push only when authorised; stop. -P17 adds a fresh Sol/xhigh whole-programme review and a separately authorized add-only atomic `PROGRAMME.json` commit. Local connected work uses L00–L10 and never rewrites Cloud receipts. Repository authority always overrides branch-finishing defaults. +P17 retains independent whole-programme review and its authorised add-only `PROGRAMME.json` commit. +Local L00–L10 keep separate approvals and never rewrite accepted Cloud receipts. diff --git a/.claude/agents/rag-retrieval-reviewer.md b/.claude/agents/rag-retrieval-reviewer.md index 7c0aafef55..d96be20102 100644 --- a/.claude/agents/rag-retrieval-reviewer.md +++ b/.claude/agents/rag-retrieval-reviewer.md @@ -27,20 +27,49 @@ Follow `AGENTS.md` review throttling and `docs/codex-review-protocol.md` before ## Review Checklist -### 1. Local-eval trap — diagnose config before touching code +### 1. Diagnose the actual environment and harness -- **Degraded local output is usually not a code bug.** Offline / source-only answers (`grounded=false`) or a 0/N golden retrieval result are almost always an owner/corpus/keys artifact: the live corpus is entirely `owner_id = NULL`, owner-scoped retrieval **fail-closes** on a null owner, and evals default to the public sentinel `00000000-0000-0000-0000-000000000000`. Diagnose owner/corpus/keys first. -- **Prove regression against pristine `origin/main`** before changing retrieval/answer code to make a local eval pass. If pristine `main` reproduces byte-identical output, it is an environment block, not a regression. -- **Source-only degradation is expected, not a failure** — `RAG_PROVIDER_MODE=auto` degrades to a deterministic Source-only answer that still cites real docs when generation misses the quality gate. +- Separate intended source-only/offline degradation from a regression using the current + `src/lib/env.ts`, provider route, eval harness, fixtures and applicable RAG behaviour docs. + Earlier sessions reported null-owner corpora and public-sentinel eval defaults; those are + dated observations, not current corpus facts. Do not query live rows or inspect keys to + validate them. Use permitted offline evidence and report any remaining capability gap. +- Compare the affected behaviour with an appropriate unchanged baseline before attributing + a failure to this diff. Record the exact revision, inputs and environment. A failure that + reproduces on the baseline is pre-existing; it may still be a real bug requiring separate + work and must not automatically be dismissed as an environment problem. +- Confirm the current conservative fallback contract in source and tests. A source-only + answer can be expected degradation, but fabricated grounding, lost citations or unsupported + claims remain defects even when provider generation is unavailable. -### 2. Token budget & reasoning-effort starvation (gpt-5.5) +### 2. Provider effort, token budgets and failure evidence -- **Never raise strong reasoning effort to `high`.** On gpt-5.5 it overruns `OPENAI_ANSWER_TIMEOUT_MS` (→ provider_timeout) and exhausts `OPENAI_MAX_OUTPUT_TOKENS` (→ truncation) — the two dominant production answer-gen failure modes. `medium` is ample. -- **Effort is only lowered, never raised.** `strongReasoningEffortForQueryClass()` must never exceed the configured value; the safety-critical `medication_dose_risk` / `table_threshold` classes starve first under high effort. -- Watch the `truncationFallbackQueries` / `timeoutFallbackQueries` counters (`src/lib/observability/answer-slo.ts`) for the fallback signature. +- Derive the application model IDs, configured effort, supported provider controls, deadlines + and output budgets from the current runtime owners and tests. Historical gpt-5.5 timeout + and truncation findings are useful failure examples, not a universal model/effort rule or + proof that `medium` fits every current route. +- Preserve the query-class effort clamp: `strongReasoningEffortForQueryClass()` must not + exceed the configured value. Review safety-critical `medication_dose_risk` and + `table_threshold` cases for starvation, timeouts and truncation. A provider-effort change + needs the applicable evidence and approval; this review does not authorise it. +- Inspect current SLO counter definitions and existing authorised evidence for timeout and + truncation signatures. Do not infer current performance from an old counter name or case + count, or run provider-backed experiments without approval. The application provider model + is separate from the user's main-chat selection. -### 3. No re-index / retrieval change without a real golden miss +### 3. Retrieval changes retain measured benefit and non-regression safeguards -- A retrieval/ranking/selection/chunking/scoring change requires a **real golden miss proving a gain**; `eval:retrieval:quality` (36/36) must _improve_, not merely not-regress. Any PR touching these must run it locally before merge (a provider-touching command — report and ask). +- Follow `AGENTS.md` RAG ranking protection and `docs/rag-behaviour/` before reviewing protected + changes. Derive the golden population and metric definitions from the current harness and + fixtures; the historical 36-case figure is not a universal current count. +- Require evidence for the claimed retrieval benefit rather than changing ranking to satisfy + a misleading local eval. A retrieval/ranking/ordering behaviour change requires the root + policy's explicitly approved live eval-canary before/after pair: document/content recall + pinned at 1.0 and zero per-case reciprocal-rank regressions. A regression requires the + documented immediate revert and confirmation run. Static or offline proof cannot replace + that live gate; provider calls and paid canaries remain confirmation-required. +- Preserve the protected scoring, imputation and comparator contracts. Never apply tuner + recommendations or refuted ranking approaches without the measured evidence and authority + required by the RAG safeguards. - Do not force a pinned doc to rank #1 over equally-valid siblings (overfitting the golden set). - **Source-governance metadata must not weight retrieval selection ordering.** diff --git a/.claude/cloud-profile/CLAUDE.md b/.claude/cloud-profile/CLAUDE.md index eb76ffa852..e1fcb696b2 100644 --- a/.claude/cloud-profile/CLAUDE.md +++ b/.claude/cloud-profile/CLAUDE.md @@ -1,5 +1,16 @@ # How to talk to Josh +These are communication preferences, subject to the current system, developer and user +instructions and applicable project safeguards. Preserve decisive evidence and material +uncertainty even when a brief reply is preferred. They grant no tool, memory, provider, +publication or production authority. + +Installed skills are optional guidance for their actual task. Check current callable tools and +supported arguments; copied descriptions do not establish model, command, plugin or connector +availability. A missing generic skill or setup command does not block suitable authorised work. +Use the current project's task record and policy instead of importing a skill's assumed tracker, +old workstation state or startup ceremony. + Josh is a psychiatrist, not a software engineer. The goal is that he finishes reading and knows exactly where things stand and what to do — with no jargon and nothing to decode. diff --git a/.claude/cloud-profile/memory/MEMORY.md b/.claude/cloud-profile/memory/MEMORY.md index fc24c19891..43adce16ba 100644 --- a/.claude/cloud-profile/memory/MEMORY.md +++ b/.claude/cloud-profile/memory/MEMORY.md @@ -1,13 +1,12 @@ -- [RAG programme coordination state](rag-programme-coordination-state.md) — master plan v2 artifact, open PR order trap, R1-first + governance Option B decisions (2026-08-17) -- [Lighthouse mobile-root CLS flake](lighthouse-mobile-root-cls-flake.md) — the CI budget gate blocks unrelated PRs bimodally; rerun the job before hunting the diff -- [DB remediation coordination state](db-remediation-coordination-state.md) — live-drift #316: alignment fix LIVE on prod 2026-08-20; staging 1 version behind (blocked); D4 UNRESOLVED — assume merge=deploy; Phase 5 + reconcile left -- [Never print env values](never-print-env-values.md) — mask every value; an allowlist leaked a staging key on 2026-08-18 -- [Dev Drive is the project location](dev-drive-project-location.md) — D:\Repos\Database on ReFS; ignore the C: Codex/Gemini worktrees; 51% full and node_modules is most of it +# Reusable project guidance + +Use current repository instructions and verify the current host and task. Dated examples below +do not establish present machine paths, failures, services or task status. Historical coordination +boards and workstation state are archived in the repository and are not startup memory seeds. + +- [Never print env values](never-print-env-values.md) — names/presence only; preserve the lesson from a 2026-08-18 disclosure incident - [Checks that cannot fail](checks-that-cannot-fail.md) — mutation-test guards; stderr hooks, grep on binary, `| tail` masking exit codes, tautological re-checks -- [Hook exec-bit trap](claude-hook-exec-bit-trap.md) — core.fileMode=false hides a 100644 hook script; chmod is a no-op, use git update-index --chmod=+x -- [Local test failures (Windows)](local-test-failures-windows.md) — session-start-hook + worker-observability fail environmentally here; check disjoint run sets before chasing -- [Codex chat import tooling](codex-chat-import-tooling.md) — two-store split: transcript in ~/.claude/projects, sidebar entry in the app registry; scripts in ~/.claude/scripts -- [Ingestion review remediation state](ingestion-review-remediation-state.md) — live quality signal is synthetic 0.84 for all 2851 docs and feeds ranking; PR A′/C local, operator + canary pending -- [Plain, brief communication style](communication-style-plain-and-brief.md) — answer first, numbered steps, no internal detail unless asked; saved in user-level CLAUDE.md -- [Token usage hygiene](token-usage-hygiene.md) — no .claudeignore exists; grep before reading big dirs, delegate broad searches to Explore, never drop a rule to save tokens (AGENTS.md is a deliberately small core plus docs/agents/ reference files) -- [Claude plugin native deps trap](claude-plugin-native-deps-trap.md) — `claude plugin install` skips npm deps; npm 11 blocks native builds; prove with stats+search, not the postinstall warning +- [Hook exec-bit trap](claude-hook-exec-bit-trap.md) — verify hook index modes and the current host's Git configuration +- [Plain, brief communication style](communication-style-plain-and-brief.md) — answer first, numbered steps, no internal detail unless asked +- [Token usage hygiene](token-usage-hygiene.md) — search before reading large files; delegate only under current policy; preserve applicable instructions +- [Claude plugin native deps trap](claude-plugin-native-deps-trap.md) — verify actual dependency readiness; dated workstation symptoms do not establish current host state diff --git a/.claude/cloud-profile/memory/claude-hook-exec-bit-trap.md b/.claude/cloud-profile/memory/claude-hook-exec-bit-trap.md index d7ffafdca8..99d1855538 100644 --- a/.claude/cloud-profile/memory/claude-hook-exec-bit-trap.md +++ b/.claude/cloud-profile/memory/claude-hook-exec-bit-trap.md @@ -8,10 +8,14 @@ metadata: modified: 2026-08-18T09:08:45.187Z --- -A new `.claude/hooks/*.sh` script added from this Windows Dev Drive workstation will be committed -**`100644`** unless someone explicitly runs `git update-index --chmod=+x `. `core.fileMode` -is `false` (correct for Windows/ReFS — see [[dev-drive-project-location]]), so git ignores -filesystem permission bits and a local `chmod +x` changes nothing git can see. +Verify the current host's Git configuration and the index mode when adding `.claude/hooks/*.sh`. +On a host with `core.fileMode=false`, a local `chmod +x` may leave the index unchanged; use +`git update-index --chmod=+x ` when required and confirm the tracked mode. Do not assume +the current host uses Windows, ReFS or a previously recorded project location. + +The incident below describes the 2026-08-18 workstation, whose `core.fileMode` was `false`. +Its workstation record (`docs/archive/cloud-session-state-2026-10-16/dev-drive-project-location.md`) +is archived and does not establish current configuration. **Why:** found on 2026-08-18. `.claude/hooks/session-start.sh` was `100644` while `issues-surface.sh` and `pr-handoff-stop.sh` were both `100755`. It is also the only hook @@ -33,3 +37,5 @@ web container, exactly where a non-executable checkout cannot run. That script p Line endings are a sibling trap but currently clean: `.gitattributes` sets `* text=auto eol=lf` and all hook blobs measured CR=0. A CR in a shell blob fails on Linux as `/bin/bash^M: bad interpreter`. The same test pins this. + +Resolve repository paths against the verified current checkout root, not this installed memory directory. diff --git a/.claude/cloud-profile/memory/claude-plugin-native-deps-trap.md b/.claude/cloud-profile/memory/claude-plugin-native-deps-trap.md index 01a02d48fe..d969b839bc 100644 --- a/.claude/cloud-profile/memory/claude-plugin-native-deps-trap.md +++ b/.claude/cloud-profile/memory/claude-plugin-native-deps-trap.md @@ -1,18 +1,24 @@ --- name: claude-plugin-native-deps-trap -description: Claude Code plugins with native deps ship without node_modules; npm 11 then blocks their build scripts, so the feature silently half-works +description: Verify plugin dependency readiness; an August 2026 workstation incident exposed missing native dependencies and misleading build warnings metadata: type: reference --- -`claude plugin install` does NOT install a plugin's npm dependencies. For plugins with +Check the current plugin version, installation behavior and dependency readiness before proposing +a repair. The symptoms and versions below were observed on a workstation in August 2026; they do +not prove that the same dependencies, install policy or failure exists on the current host. +Plugin installation and memory access remain subject to the task's authorization and +current memory policy (`docs/agents/shared-context-and-memory.md`). + +In that incident, `claude plugin install` did not install the plugin's npm dependencies. For plugins with native modules (episodic-memory needs better-sqlite3, onnxruntime-node, sharp, protobufjs) the CLI and MCP server fail with `ERR_MODULE_NOT_FOUND` until you run `npm install --omit=dev` inside the plugin's cache directory (`~/.claude/plugins/cache////`). That install took ~9 minutes for episodic-memory 1.4.2. -Second trap: npm 11 on this machine blocks package install scripts by default +Second trap: npm 11 on that workstation blocked package install scripts by default (`npm warn allow-scripts`), so native rebuilds report failure even though the package files land. episodic-memory's own postinstall printed "rebuild of better-sqlite3 failed (status=1)". Despite that warning, better-sqlite3 and the @@ -21,4 +27,6 @@ cli/episodic-memory.js stats` and a real `search` that returned 81% matches. So postinstall warning as advisory, not proof of breakage; prove it with `stats` + `search` before either declaring it broken or declaring it fine. -Related: [[local-test-failures-windows]], [[checks-that-cannot-fail]]. +Related: archived Windows test incidents (`docs/archive/cloud-session-state-2026-10-16/local-test-failures-windows.md`), [[checks-that-cannot-fail]]. + +Resolve repository paths against the verified current checkout root, not this installed memory directory. diff --git a/.claude/cloud-profile/memory/communication-style-plain-and-brief.md b/.claude/cloud-profile/memory/communication-style-plain-and-brief.md index 501efdcb26..3ab77dada9 100644 --- a/.claude/cloud-profile/memory/communication-style-plain-and-brief.md +++ b/.claude/cloud-profile/memory/communication-style-plain-and-brief.md @@ -6,14 +6,18 @@ metadata: --- Josh asked (2026-08-18) for replies to be much simpler and shorter: state plainly what he needs -to do, skip the detail and complexity. Saved permanently in `C:\Users\joshs\.claude\CLAUDE.md`, -which loads in every project. +to do, skip the detail and complexity. At the time this was saved in the workstation's +`C:\Users\joshs\.claude\CLAUDE.md`. That historical location does not establish what is loaded +on the current host; follow the user's current instructions. **Why:** he is a psychiatrist, not a software engineer. Long technical answers full of file paths, gate names and reasoning bury the one thing he actually has to decide or do. **How to apply:** answer first, then numbered steps, under ~10 lines. One recommendation, not a menu. No jargon without a plain-English gloss. Keep full detail only for destructive actions, -exact commands to run, and when he explicitly asks why or for more. Offer with "Want the -detail?" instead of dumping it. See [[local-test-failures-windows]] for an example of the kind +exact commands to run, clinical/privacy/patient-facing matters, decisive verification evidence, +and when he explicitly asks why or for more. Offer with "Want the +detail?" instead of dumping it. See the archived Windows test incidents (`docs/archive/cloud-session-state-2026-10-16/local-test-failures-windows.md`) for an example of the kind of environment noise he does not need repeated back. + +Resolve repository paths against the verified current checkout root, not this installed memory directory. diff --git a/.claude/cloud-profile/memory/never-print-env-values.md b/.claude/cloud-profile/memory/never-print-env-values.md index 46667a5c31..7106632a64 100644 --- a/.claude/cloud-profile/memory/never-print-env-values.md +++ b/.claude/cloud-profile/memory/never-print-env-values.md @@ -1,6 +1,6 @@ --- name: never-print-env-values -description: "When inspecting a .env file, mask every value by default — a partial-mask script leaked a staging sb_secret_ key into a transcript on 2026-08-18" +description: "Report configuration names/presence without inspecting credential values; retain the lesson from a 2026-08-18 disclosure incident" metadata: node_type: memory type: feedback @@ -8,8 +8,16 @@ metadata: modified: 2026-08-18T05:59:12.504Z --- -When inspecting any `.env*` file, print **variable names and value _shapes_ only** (length, prefix, set/unset). Mask **every** value by default — never allowlist "the sensitive ones". +Use host readiness metadata or **variable names and presence only** when checking configuration. +Never print, inspect, decode, copy or persist credential values to diagnose a problem. Follow the +current credential and host policy (`docs/agents/codex-cloud-environment.md`); a dated +incident note does not authorize secret inspection. **Why:** on 2026-08-18 an env-inspection script special-cased only `SUPABASE_SERVICE_ROLE_KEY` and printed every other line raw. That dumped a live staging `sb_secret_…` key, the publishable key, and the anon JWT into the transcript, forcing a key rotation. The masking logic was correct for the one variable it anticipated and wrong for the file as a whole — an allowlist fails open on exactly the variable you did not think of. A denylist-by-default is the only safe shape, because you cannot enumerate in advance what a user has put in their env file. -**How to apply:** mask unconditionally, e.g. `printf "%s = <%d chars>\n" "$k" "${#v}"` for every key, and only unmask a specific variable when the user asks for that one and it is provably non-secret (a URL, a project ref, a boolean). Ask for verification of a credential by _shape_ (prefix, length, decoded JWT `ref`/`role` claim) rather than by printing it — decoding a JWT's claims to prove which project it targets is safe and was the right call in that same session. Also: deleting the line afterwards does not undo exposure; the value persists in the session transcript on disk, so rotation is the only real remedy — say so plainly and immediately rather than burying it. Related: [[db-remediation-coordination-state]]. +**How to apply:** Report names/presence or supported readiness evidence without reading out values. +Do not decode a JWT or inspect prefixes/lengths as a diagnostic shortcut. Deleting a printed value +afterwards does not undo exposure; use the authorized incident response process if exposure occurs. +Related: archived database coordination (`docs/archive/cloud-session-state-2026-10-16/db-remediation-coordination-state.md`). + +Resolve repository paths against the verified current checkout root, not this installed memory directory. diff --git a/.claude/cloud-profile/memory/seed-manifest.json b/.claude/cloud-profile/memory/seed-manifest.json new file mode 100644 index 0000000000..692e5c73ec --- /dev/null +++ b/.claude/cloud-profile/memory/seed-manifest.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "files": [ + "MEMORY.md", + "checks-that-cannot-fail.md", + "claude-hook-exec-bit-trap.md", + "claude-plugin-native-deps-trap.md", + "communication-style-plain-and-brief.md", + "never-print-env-values.md", + "token-usage-hygiene.md" + ] +} diff --git a/.claude/cloud-profile/memory/token-usage-hygiene.md b/.claude/cloud-profile/memory/token-usage-hygiene.md index a16bfd1c94..55ad212475 100644 --- a/.claude/cloud-profile/memory/token-usage-hygiene.md +++ b/.claude/cloud-profile/memory/token-usage-hygiene.md @@ -1,6 +1,6 @@ --- name: token-usage-hygiene -description: "Session habits that avoid burning tokens on unnecessary reads in the Database repo — no .claudeignore exists, so this is the real mechanism" +description: "Search before reading large files; follow current allocation and repository policies when limiting unnecessary context" metadata: node_type: memory type: feedback @@ -8,29 +8,30 @@ metadata: modified: 2026-08-19T08:02:34.883Z --- -Do not read entire large directories or dependency trees when only one fact is needed. In the Database repo -this matters most for `node_modules/next/dist/docs/` (AGENTS.md requires checking it for Next.js 16 breaking -changes before writing framework code) — grep for the specific topic/API first, then read only the matched -file, never the whole docs folder. +Do not read entire large directories or dependency trees when only one fact is needed. When the current +repository requires checking `node_modules/next/dist/docs/` for framework changes, search for the specific +topic/API first, then read only the matched file, never the whole docs folder. Verify the current +repository and its applicable instructions instead of assuming the older `Database` repository name. -For any search that needs more than 2-3 targeted Grep/Glob calls, delegate to the Explore agent instead of -reading many files inline in the main conversation. Keeping the main session's own context small is what -actually controls token cost turn over turn, since everything already in the conversation gets reprocessed on -every new turn — a subagent's exploration doesn't carry that cost back. +Use targeted searches and bounded reads. Delegate only when authorized and useful under +current agent allocation (`docs/agents/smart-agent-allocation.md`) and the +context-aware working defaults (`docs/agents/native-startup/contextual-working-defaults.md`). +An old search-count threshold does not authorize agents, select their model or establish that a named +agent exists on the current host. Do not speculatively load MCP tool schemas via ToolSearch "just in case" — only load the tools a task actually needs, and batch a known set into one call rather than loading them one at a time. -**Why:** Josh (the user, a psychiatrist and non-technical) asked directly on 2026-08-19 to reduce wasted token +**Historical context, 2026-08-19:** Josh (the user, a psychiatrist and non-technical) asked directly on 2026-08-19 to reduce wasted token usage without slowing down development, and specifically asked about a "claudeignore" mechanism. Checked with the claude-code-guide agent first rather than guessing: Claude Code has no `.claudeignore` file or per-file-read exclusion list, so that request can't be fulfilled as literally asked — this note is the real substitute. `.claude/settings.json`'s permission allowlist was already recently expanded (commit 89ae14fad) and is in good -shape, so there was nothing to add there. [[dev-drive-project-location]] separately notes `node_modules` is most +shape, so there was nothing to add there. The archived workstation record (`docs/archive/cloud-session-state-2026-10-16/dev-drive-project-location.md`) separately notes `node_modules` was most of the repo's disk footprint — the same directory is the main _token_ risk for reads, not just disk space. -**How to apply:** Follow this automatically in every Database repo session; it needs no action or approval from -Josh each time. Do not shrink `AGENTS.md`/`CLAUDE.md` by dropping or condensing rules to save tokens — those +**How to apply:** Use current repository instructions to choose the workflow. This note grants no new +authorization. Do not shrink `AGENTS.md`/`CLAUDE.md` by dropping or condensing rules to save tokens — those rules are shared safety and process contracts read by five different AI tooling systems (per the file's own "AI tooling map"), and cutting one is a project decision for the team, never something to do unilaterally for token savings. @@ -40,3 +41,5 @@ small always-loaded core — the boundaries that prevent irreversible harm, plus exact text — and an index. Every other rule keeps its heading there and its full text, verbatim, in a named file under `docs/agents/`. So a large `AGENTS.md` is no longer the expected state, and a rule that is not in the core has not been dropped; open the file the core names for it. Restoring the old single wall would undo that work. + +Resolve repository paths against the verified current checkout root, not this installed memory directory. diff --git a/.claude/cloud-profile/skills/choose-model/SKILL.md b/.claude/cloud-profile/skills/choose-model/SKILL.md index f74a066b2a..8d267f4f04 100644 --- a/.claude/cloud-profile/skills/choose-model/SKILL.md +++ b/.claude/cloud-profile/skills/choose-model/SKILL.md @@ -1,62 +1,43 @@ --- name: choose-model -description: Recommends which Claude model (Opus 4.8, Sonnet 5, Haiku 4.5, Fable 5) and what effort/reasoning level to use for an upcoming task, by weighing complexity, error cost, ambiguity, scope, latency/cost sensitivity, and creativity-vs-precision against each other. Use this whenever the user asks "which model should I use", "what model for this", wants help picking a model/effort before running something, is about to switch models via /model, or is about to delegate work to a subagent via the Agent tool and needs a model/effort recommendation first. Also trigger on softer phrasing like "is this overkill for Opus", "can Haiku handle this", or "how much reasoning effort does this need" — don't wait for the word "model" specifically. +description: Recommend a currently supported model capability and effort for a described task, considering uncertainty, error cost, scope and latency. Use when the user asks which model or reasoning level to use, or needs routing advice for authorised subagents. Verify current availability before naming a model or control. --- # Choose Model -Recommend a model + effort level for a task the user describes, by explicitly trading off the factors that pull in different directions — instead of defaulting to "use the biggest model to be safe." +Recommend an adequate supported capability for the task. Keep the user's main-chat selection +unchanged. A recommendation is not a setting change or evidence of execution. -## When invoked +## Establish current choices -The user gives a task description as the argument — sometimes a single sentence, sometimes a longer brief. If no task description is given (bare `/choose-model` with nothing to go on), ask one brief clarifying question about what the task actually is rather than guessing. +Inspect the current host's exposed model inventory and tool schema, or current authoritative +product documentation when access is authorised. Name an exact model, argument or effort value +only when that evidence supports it. Do not assume a copied model lineup, a named API skill, +a built-in command or an Agent effort parameter exists on this host. -## The lineup +If availability cannot be verified, recommend a capability class and state that limitation. +Continue suitable authorised work; do not use paid API workarounds, install missing skills or +change credentials to reproduce unavailable routing. Respect applicable project allocation, +independent review and explicitly selected specialist programme floors. -- **Opus 4.8** (`claude-opus-4-8`) — deepest reasoning, highest cost/latency. Reserve for genuine complexity or high stakes. -- **Sonnet 5** (`claude-sonnet-5`) — the default workhorse. Strong reasoning at moderate cost; right for most substantive coding and analysis tasks. -- **Haiku 4.5** (`claude-haiku-4-5-20251001`) — fast and cheap. Right for mechanical, well-specified, low-stakes work, or high-volume/repeated calls where speed compounds. -- **Fable 5** (`claude-fable-5`) — tuned for creative/narrative/conversational output rather than precise technical execution. +## Weigh the task -If exact model IDs or pricing may have drifted, the `claude-api` skill is the source of truth — consult it rather than trusting a hardcoded number here. +- Mechanical, well-specified, reversible work can use a fast, lower-cost supported route. +- Routine implementation or analysis needs a capable coding/reasoning route. +- Ambiguous architecture, concurrency, security, clinical or irreversible decisions need + sufficient reasoning and required specialist review for their consequences. +- Large context alone is not a reason to select the highest tier; reduce irrelevant input first. +- Creative work may benefit from a supported capability suited to tone or narrative. +- When adequate choices are tied, prefer the lower cost or latency. Do not claim unobserved + prices or performance. -"Effort" means whatever lever is actually in play for the context: the `reasoning_effort` parameter on an Agent subagent call, an extended-thinking/fast-mode toggle in an interactive session, or an explicit effort flag on a skill that supports one (e.g. `low`/`medium`/`high`/`max` on `code-review`). Recommend it as a level (low/medium/high) and let the user map it to whichever mechanism applies. +Use only the effort controls exposed by the relevant interface. Accept the user's ordinary-language +confirmation; no special marker or resubmission is needed. Missing model/effort selection means +routing cannot be enforced, not that an invented parameter should be supplied. -## The rubric +## Output -Don't score every factor out loud every time — read the task, note which factors actually matter for *this* task, and let those drive the call. The factors, and which way each one pushes: - -- **Complexity / reasoning depth** — multi-step logic, architectural tradeoffs, non-obvious bugs, math/algorithmic work → pushes toward Opus. Mechanical, single-step, well-trodden work → pushes toward Haiku. -- **Error cost / correctness sensitivity** — production data migrations, security-sensitive code, clinical/compliance-sensitive output, anything hard to reverse → pushes up the model tier even if the task itself looks simple, because the cost of a wrong answer dominates the cost of the call. Throwaway drafts, exploration, easily-reverted edits → pushes down. -- **Ambiguity** — requirements that need inference, judgment calls, or filling gaps → pushes up (a bigger model makes better judgment calls). A precisely specified instruction → pushes down, since there's little judgment left to exercise. -- **Scope / token volume** — large multi-file refactors or long-context synthesis benefit from Opus's reasoning over that volume; a small single-file edit or short answer doesn't need it. -- **Latency / cost sensitivity** — a tight interactive loop or many small repeated calls (e.g. batch-classifying items, parallel subagent fan-out) makes the cheaper/faster model's savings compound; a one-shot deep task makes quality dominate cost. -- **Creativity vs precision** — open-ended generation, tone, narrative → consider Fable. Deterministic, verifiable, technical output → Sonnet or Opus, never Fable. - -These factors often conflict (e.g. "high scope, low error cost" or "high ambiguity, latency-sensitive batch job"). When they do, say which factor won and why — that's the useful part of the recommendation, not the raw list. - -## Defaults, so you're not starting from scratch each time - -| Task shape | Model | Effort | -|---|---|---| -| Rename, formatting, boilerplate, single well-specified small edit | Haiku 4.5 | low | -| Typical feature implementation or bug fix with a clear repro | Sonnet 5 | medium | -| Ambiguous requirements needing real judgment, multi-file refactor, non-obvious bug | Sonnet 5 or Opus 4.8 | medium–high | -| Security-sensitive, production-data, or clinical/compliance-sensitive work | Opus 4.8 (even if the task itself looks small) | high | -| High-volume repeated/parallel subagent calls over independent items | Haiku 4.5 | low–medium | -| Creative writing, narrative, conversational tone work | Fable 5 | — | - -Treat this table as a prior, not a lookup — let the specific task override it when the factors point elsewhere. - -## Output format - -Keep it short: - -``` -Model: · Effort: -- -- -- -``` - -Only name the factors that actually drove the call — not a rote restatement of all six every time. If the task is a toss-up between two reasonable choices, say so in one line and pick the cheaper option (favor cost/latency when stakes are genuinely tied). +Keep it short: recommendation, the deciding factor, and any availability limitation. Distinguish +requested routing from actual execution telemetry; a tool accepting a request proves only that +it accepted the request. Ask a brief question only if the task itself is too unclear to recommend +an adequate capability. diff --git a/.claude/cloud-profile/skills/code-review/SKILL.md b/.claude/cloud-profile/skills/code-review/SKILL.md index e2995f21a7..49564f9fc1 100644 --- a/.claude/cloud-profile/skills/code-review/SKILL.md +++ b/.claude/cloud-profile/skills/code-review/SKILL.md @@ -1,23 +1,28 @@ --- name: code-review -description: Review the current working diff for correctness bugs plus reuse, simplification, and efficiency cleanups. Use when the user asks to review their changes, review the diff, or sanity-check code before committing or pushing. Supports effort levels (low/medium/high/max) and optional --comment (post inline PR comments) or --fix (apply findings to the working tree). +description: Review the current working diff for correctness bugs plus reuse, simplification, and efficiency cleanups. Use when the user asks to review their changes, review the diff, or sanity-check code before committing or pushing. Honour requested review depth and optional comments or fixes only when supported by current tools and task authority. --- # Code Review (working diff) -Thin wrapper over Claude Code's built-in diff review. +Use an available diff-review tool or inspect the working diff directly. Check the current +host's command/skill inventory and supported arguments; this copied wrapper does not prove +that a built-in review, effort control or paid review mode is available. Project review-ledger, +independence, provider and publication requirements remain binding. When invoked: -1. **Delegate to the built-in review.** If a built-in `code-review` command/skill is available and resolves to something *other than this file*, invoke it and forward every argument the user gave — effort level (`low`/`medium`/`high`/`max`), `--comment`, `--fix`, or `ultra`. The built-in owns the real behavior, including `ultra`'s multi-agent cloud review, which cannot be reproduced here. +1. **Establish the requested review scope before dispatch or inspection.** Check `git status --short` and task ownership. For a working-change review, include owned staged changes, unstaged changes and untracked files; read untracked file contents explicitly because `git diff` omits them. Preserve unrelated work and report any excluded scope. For an explicitly requested branch/PR review, record the reviewed HEAD and verified base commit, then compare those pinned revisions. Branch location alone must not replace a working-change review with committed changes only. Include owned uncommitted changes when the request covers them, and identify the evidence snapshot so later edits do not silently alter the reviewed inputs. -2. **Recursion guard.** If invoking `code-review` would resolve back to *this* skill (name collision), do **not** call it again — perform the review inline instead: - - Determine the diff: `git diff` (unstaged), `git diff --staged` (staged), or the branch diff vs the merge-base with the default branch (`git diff main...HEAD`). Prefer the branch diff when reviewing a feature branch. +2. **Use supported review tooling when suitable.** If an available review command/skill resolves to something other than this file, use only arguments its current schema supports and only actions already authorised. A request for review does not by itself authorise paid review or external comments. + +3. **Local fallback and recursion guard.** If suitable review tooling is unavailable or `code-review` resolves back to this skill, perform the review inline; do not call this skill recursively: + - Inspect the scope established above: `git diff` for unstaged changes, `git diff --staged` for staged changes, explicit reads for owned untracked files, and a comparison of the pinned base/HEAD commits for branch changes. Check for relevant drift before reporting conclusions. - Read each changed file for surrounding context, not just the hunk. - Report findings ranked most-severe first: correctness bugs first, then reuse / simplification / efficiency cleanups. For each finding give `file:line`, a one-sentence defect statement, and a concrete failure scenario (inputs/state → wrong result). - Scale breadth to the requested effort: `low`/`medium` = a few high-confidence findings; `high`/`max` = broader coverage that may include less certain findings. - - With `--fix`, apply the accepted findings to the working tree. With `--comment`, post them as inline PR comments via `gh`. + - Apply accepted fixes only when requested. Post external comments only with applicable explicit authority and available authenticated tooling; otherwise return the findings locally. Never invent findings — if the diff is clean, say so plainly. -> Note: `ultra` (multi-agent cloud review) is only available through the built-in command and is user-triggered/billed. This wrapper cannot launch it; hand off to the real `/code-review ultra` instead. +If a requested review mode is unavailable, report the concrete gap and offer the supported local review path. Do not claim that a named slash command or billing mode exists without current evidence. diff --git a/.claude/cloud-profile/skills/prompt/SKILL.md b/.claude/cloud-profile/skills/prompt/SKILL.md index 20c3dd435e..c49de1c4e1 100644 --- a/.claude/cloud-profile/skills/prompt/SKILL.md +++ b/.claude/cloud-profile/skills/prompt/SKILL.md @@ -1,6 +1,6 @@ --- name: prompt -description: Perfect a rough request or draft prompt using relevant context, attachments, files, and the minimum necessary skills or tools, then answer or execute it. Use when the user invokes $prompt or asks to improve, rewrite, sharpen, expand, contextualize, or optimize their prompt before acting. For code-changing prompts, also prepare a safe repository workflow: a fresh worktree and branch per task. Do not use for ordinary code or content review unless prompt improvement is requested. +description: Perfect a rough request or draft prompt using relevant context, attachments, files, and the minimum necessary skills or tools, then answer or execute it. Use when the user invokes $prompt or asks to improve, rewrite, sharpen, expand, contextualize, or optimize their prompt before acting. For code-changing prompts, verify task ownership and use an isolated checkout when needed; preserve verified same-task continuation. Do not use for ordinary code or content review unless prompt improvement is requested. --- # Prompt @@ -73,7 +73,7 @@ For machine-consumed output, specify an exact schema, required fields, permitted When the perfected task depends on local repository evidence or will write to a Git repository, read `references/coding-workflow.md` before relying on findings or editing. Apply its freshness rules to review and diagnosis; apply its branch, environment, implementation, and validation rules only to repository writes. Skip the reference for prompt-only or answer-only work that does not depend on repository state. -For any repository write, start the task in a fresh worktree on a new branch cut from the latest remote default branch — one task, one worktree, one branch. Never edit the main checkout or reuse another task's worktree; the reference gives the exact commands and exceptions. +Before repository writes, verify the checkout, branch, task ownership and existing changes. Continue verified same-task work, including owned uncommitted changes; isolate unrelated work when needed. The reference gives the branch, freshness and authority checks. ## API and ChatGPT prompt practice diff --git a/.claude/cloud-profile/skills/prompt/references/coding-workflow.md b/.claude/cloud-profile/skills/prompt/references/coding-workflow.md index 692d09a0c3..e97c5136ff 100644 --- a/.claude/cloud-profile/skills/prompt/references/coding-workflow.md +++ b/.claude/cloud-profile/skills/prompt/references/coding-workflow.md @@ -13,14 +13,13 @@ Higher-priority instructions and the repository's own `AGENTS.md` / `CLAUDE.md` ## Branch and worktree (repository writes) -- Start every task in its own fresh worktree on its own new branch. Fetch first, then branch off the latest remote default branch (`git fetch origin && git worktree add -b origin/`), using absolute paths for the worktree. Do not edit in the main checkout, and do not reuse another task's worktree or branch for unrelated work. -- A stale base is the most common multi-worktree trap: confirm the new branch actually starts from the current remote default branch, not a days-old local copy. -- Name the branch with a short kebab-case phrase that identifies the task, following the repository's existing prefix convention when one exists. -- Inspect first: current branch, upstream, other worktrees, and `git status`. Never assume the branch or remote. -- Do not commit directly to a protected or base branch (`main`, `master`, `develop`, `release/*`). If you find yourself on one, create the worktree/branch above before editing. -- If the environment already placed you in a dedicated per-task worktree on a fresh branch, use it as-is; do not nest another one. -- Preserve unrelated staged, unstaged, and untracked work. Do not stash, reset, clean, or discard changes you did not create. -- Commit or push only when the task explicitly asks for it. Never force-push, rebase shared history, delete branches, or merge into a base branch without explicit confirmation. +- Inspect the current repository, branch, HEAD, upstream, worktrees, active Git operation and changed paths. Establish task ownership before editing; never assume the branch or remote. +- Continue verified same-task work in the supplied task checkout. A dirty status or a branch behind the default branch alone does not require a fresh task, new worktree or discarded analysis. Inventory and preserve owned and unrelated changes; reassess relevant drift. +- For new unrelated work, use an isolated task branch/worktree when needed to protect concurrent changes. Follow repository-specific setup and isolation checks. A host-provided disposable task checkout may already supply that isolation; do not nest another worktree merely for procedure. +- Establish the appropriate base from available local evidence. Remote freshness checks such as `git fetch` require applicable provider/network authority; if unavailable, disclose the unverified remote state and continue independent authorised local work. +- Do not commit directly to a protected or base branch (`main`, `master`, `develop`, `release/*`). Establish an appropriate task branch before edits there. +- Preserve unrelated staged, unstaged, and untracked work. Do not stash, reset, clean, relocate or absorb changes you did not create. +- Commit or push only with applicable task authority. Never force-push, rebase shared history, delete branches, or merge into a base branch without applicable explicit confirmation. ## Environment (repository writes) diff --git a/.claude/hooks/testing-policy.sh b/.claude/hooks/testing-policy.sh index c0778d172c..c7051160e5 100755 --- a/.claude/hooks/testing-policy.sh +++ b/.claude/hooks/testing-policy.sh @@ -1,13 +1,9 @@ #!/usr/bin/env bash # SessionStart hook — state the verification-cost policy for cloud sessions. # -# A cloud session starts with no memory of what the last one learned the hard -# way, and the most expensive lesson in this repository is about the browser -# gate: `npm run verify:ui` is ~25 minutes, CI repeats it wholesale on every -# change that touches a browser surface, and a session that does not know this -# will spend those 25 minutes buying a verdict GitHub is about to reach anyway. -# Measured 2026-09-02 on two consecutive changes: 25.0m and 20.5m locally, while -# the focused selection for the same diffs took 37s and 6.1s and agreed. +# Give a fresh Cloud session the current scoped-verification guidance. CI coverage +# depends on the selected event, draft state, paths and successful prerequisites; +# advice about expected jobs is never evidence that those jobs passed. # # So this hook says, once per session and in the model's own context, which gate # to reach for first. It is scoped to cloud sessions because that is where the @@ -37,25 +33,31 @@ root="${CLAUDE_PROJECT_DIR:-}" read -r -d '' context <<'POLICY' || true [testing-policy] Verification cost policy for this cloud session. -CI re-runs the browser suite on every change that touches a browser surface, so a -full local `npm run verify:ui` (~25 min, 646 tests) buys a verdict GitHub is about -to reach. Do not spend it by default. +Use the smallest check that covers the diff's plausible failures. CI selection +depends on event, draft and scope conditions and successful prerequisites. A +selected or deferred check remains pending until successful execution is observed. +Skipped, missing or failed jobs are not passing evidence. -Before any browser run, ask the planner which part of the suite the diff can break: +Ask the planner which part of the browser suite the diff can break: npm run plan:browser # dry run: the level, the specs, and why - npm run plan:browser -- --run # execute the plan it printed + +Before executing browser checks, run `npm run ensure` and use its verified project +URL. Then `npm run plan:browser -- --run` executes the selected plan. It fails closed: shared foundations, an unattributable UI file, or unknown scope all escalate to the full suite on their own. You do not have to judge that. Reporting rules, which the cost saving depends on: -- A narrowed run is NOT the full gate. Say "focused browser proof at level , - full suite left to CI" — never "verify:ui passed". +- A narrowed run is NOT the full gate. Report focused browser proof at level + and name any broader checks still pending — never "verify:ui passed". +- Identify the jobs expected for this event and scope. Do not defer required local + proof on an assumption that CI will repeat it; verify the job conditions first. - Paste the decisive line of real output. Exit 0 alone is not proof. Unchanged by any of this: GitHub remains the authoritative merge gate and runs -exactly what it ran before. Never weaken a required check to save local time. +its configured scoped checks. Never weaken a required check to save local time. +Follow docs/agents/verification-gates.md for the complete current policy. POLICY # The context is a JSON string field, so the only characters that must be escaped diff --git a/.claude/settings.json b/.claude/settings.json index 9add13d7da..07648bbab9 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -480,9 +480,9 @@ "**Protected IaC scopes**: `supabase/migrations/**` and `supabase/schema.sql`, RLS policies, RPC and `SECURITY DEFINER` functions, storage bucket policies, `.github/workflows/**`, plus IAM, RBAC, networking, quota, and node-pool resources; anything whose name or tag carries `prod` or `production` as a whole word or name segment", "### User-specific", "**Primary use of Claude Code**: software development on this personal clinical-reference RAG prototype (Next.js 16 / TypeScript / Supabase / OpenAI), with heavy use of repo verification gates, PR handoff, and review subagents", - "**Trusted repo**: `D:\\Repos\\Database` and its worktrees under `.claude/worktrees/`, with remote `github.com/BigSimmo/PsychSift`. This remote is PUBLIC, so only that repo's own work may be committed or pushed there — content ported, repointed, or first read from outside this session's repo is not its own work, whoever directed the port. Visibility scopes confidential material only: secrets, clinical documents, and patient-identifiable data are never cleared into it by its visibility", + "**Trusted repo**: the currently verified checkout of `github.com/BigSimmo/PsychSift` and its task-owned worktrees, resolved from current Git identity rather than a fixed drive path. This remote is PUBLIC, so only that repo's own work may be committed or pushed there — content ported, repointed, or first read from outside this session's repo is not its own work, whoever directed the port. Visibility scopes confidential material only: secrets, clinical documents, and patient-identifiable data are never cleared into it by its visibility", "**Org-specific CLIs**: `supabase` (Supabase CLI), `gh` (GitHub CLI), repo `npm run` gates (`ensure`, `verify:cheap`, `verify:pr-local`, `verify:ui`, `test:focused`, `ledger:lookup`, `ledger:append`, `skills`), `sentry-cli`, `fnm`, `codex`", - "**Routine under this repo's prefix**: read-only and offline gate work inside `D:\\Repos\\Database` and its `.claude/worktrees/*` checkouts is routine; anything provider-backed (OpenAI, live Supabase, GitHub API writes, hosted CI, `verify:release`, non-offline `eval:*`) is confirmation-required by the repo's own policy" + "**Routine under this repo's prefix**: read-only and offline gate work inside that verified checkout and its task-owned worktrees is routine; anything provider-backed (OpenAI, live Supabase, GitHub API writes, hosted CI, `verify:release`, non-offline `eval:*`) remains subject to the repo's provider policy and existing task authorisation" ] }, "hooks": { diff --git a/.claude/skills/newtask/SKILL.md b/.claude/skills/newtask/SKILL.md index 9199e9d785..39e8134364 100644 --- a/.claude/skills/newtask/SKILL.md +++ b/.claude/skills/newtask/SKILL.md @@ -1,109 +1,77 @@ --- name: newtask -description: Bootstrap a clean session for new work in this repo — create a fresh git worktree off the latest origin/main, install deps, warm routes, and confirm the base is current. Use at the start of a task when you want an isolated, up-to-date working copy and want to avoid the stale-base, cold-worktree, and reserved-port traps this repo is prone to. +description: Prepare an isolated current task checkout using the current host's supported workflow. Check ownership and the base, restore dependencies only when needed, and start the app only for a task that needs it. --- -# newtask — start a clean, current working copy - -This repo moves fast and shares ~50 worktrees and one stash stack, so starting work on a -stale base or a cold worktree is the default failure. This skill sets up an isolated, -current worktree so new work starts clean. That count is not stable trivia — it was 48 on -2026-08-18 and reached 50 during a single session, because nothing reclaims a worktree -whose branch has landed. See the cleanup note at the end. - -## Before you start - -Check whether this task can ride an already-open PR you own or bundle with other queued -low-risk work instead of minting a new branch — see AGENTS.md "PR bundling (reduce -one-task-one-PR churn)". Only proceed to the steps below (which create a new branch and -worktree) once bundling doesn't apply: the work isn't independently low-risk by that -rule's test, or there's no suitable open PR to ride. - -**Ask that same open-PR list a second question: is someone already building this?** Some -ledger rows carry a progress marker in their prose (`IN PROGRESS`, `IMPLEMENTED in PR -#1766`), but there is no structured status field and no atomic claim — a marker is written -by whoever did the work, usually after the fact, and nothing requires or checks one, so its -absence means nothing. Two sessions can read the same queued `/issues` item and both build -it. That is not hypothetical — on -2026-08-09 two assistants took the same queued item about four hours apart and shipped the -same conversion twice (PR #1766, and PR #1767 closed as a duplicate), which also left two -divergent shapes to adjudicate afterwards. Before starting a queued item, scan the open PRs -for the **route, component, or surface** it touches, not just for a matching ledger id — the -duplicate PR will rarely quote the id. If you find one, stop and reconcile with it rather -than opening a second branch. This is one GitHub read (`mcp__github__list_pull_requests`, or -`gh pr list` where available) and it falls inside the existing provider-confirmation -boundary. If GitHub is unreachable, treat it as a warning and continue — an offline session -must still be able to start work. Recorded as `#292`. - -**If the PR-handoff stop hook has already armed in this session, that read is denied** — a -session that opened its own PR and then picks up another queued item cannot call -`list_pull_requests` or `gh pr list` at all (see `docs/pr-handoff-stop-cross-agent-gap.md`). -Do not unlock the hook for this; fall back to `git ls-remote --heads origin`, which needs no -provider tool and still answers the question, because a session building the same surface -almost always has a pushed branch named after it. Report which check you used. Observed -2026-08-14 on PR #1956. - -## Steps - -1. **Sync main.** `git fetch --quiet origin main`. -2. **Create an isolated worktree** off the latest main (never reuse another session's - checkout, and never switch the main checkout's branch). Put it under the project's - own worktree root on the **D: Dev Drive**, which is where every Claude Code worktree - already lives — not `../wt-`, which lands outside the project tree, and never - under `C:\Users\…`: - - ```bash - git worktree add -b claude/ D:/Repos/PsychSift/.claude/worktrees/ origin/main - ``` - - Use a short, descriptive ``. - - **Check free space first — this is a real constraint, not a formality.** `D:` is a - 50 GB ReFS Dev Drive that was 51% full on 2026-08-18, and each worktree's - `node_modules` costs ~0.9 GB (measured 51,735 files / 0.89 GB). ReFS supports - hardlinks, but npm extracts fresh copies rather than linking from the cache, so - nothing is shared: - - ```bash - df -h /d | tail -1 - ``` - - Under ~5 GB free, reclaim space before starting: `node scripts/clean-worktree.mjs --merged` - lists worktrees whose branch already landed. Worktrees under `C:\Users\joshs\.codex\` - and `C:\Users\joshs\.gemini\` belong to Codex and Antigravity sessions — leave them - alone and do not count them. - -3. **Install deps in the new worktree** (a cold worktree fails `vitest`/`tsc`). Prefer - `node scripts/setup-codex-worktree.mjs`, which reuses a byte-identical donor `node_modules` - before falling back to `npm ci --include=dev --prefer-offline --no-audit --no-fund`: - - ```bash - cd D:/Repos/PsychSift/.claude/worktrees/ - node scripts/setup-codex-worktree.mjs - ``` - - **Fallback — direct install when the setup helper cannot be used:** - - ```bash - npm ci --include=dev --prefer-offline --no-audit --no-fund - ``` - - `postinstall` writes the lock-parity stamp and installs the pre-push guards automatically. - -4. **Confirm the base is current:** `node scripts/check-base-freshness.mjs` — expect - `behind 0`. If it reports behind, the fetch/worktree base is stale; recreate. -5. **Warm routes before any browser/UI work** (fresh worktrees can hash onto a - Next-reserved port and 404 all routes until warmed): `npm run ensure`, then hit - `/` and `/applications` once. - -## Notes - -- Never `git stash` here — the stash stack is global across all worktrees. Use a - throwaway worktree or a patch file instead. -- Do the work on the `claude/` branch; commit only your own paths. -- When done, hand off with the `handoff` skill. -- **Worktrees are not free and nothing reclaims them automatically.** `npm run clean:worktree` - only prunes worktrees whose directory is already missing or that git has marked prunable — - a merged branch whose directory still exists is never a candidate, which is how 48 - accumulated. After the PR lands, `prlanded` should offer removal; otherwise run - `node scripts/clean-worktree.mjs --merged` periodically and remove what it lists. +# newtask — prepare the task checkout + +Read root `AGENTS.md` and the current task checkpoint. Establish repository identity, +branch, `HEAD`, upstream, registered worktrees, existing changes and ownership before +writing. Preserve unrelated work. A same-task continuation keeps its established +checkout and checkpoint; it does not start over merely because owned changes exist. + +## Check for overlapping work + +Before creating a new branch, check whether the work belongs on an existing owned PR +under the repository's bundling policy. When GitHub reads are authorised, inspect open +PRs for the actual route, component or surface, rather than only a ledger ID. A missing +progress marker does not establish that nobody owns the work. Reconcile confirmed +overlap before dependent edits. + +The duplicate implementation incident recorded as `#292` is historical evidence for +this check, not permission to call GitHub. Respect the provider boundary and any armed +PR-handoff stop hook. Do not bypass a denied PR read through another transport. +`git ls-remote` and `git fetch` also contact the remote, require network/authentication +and need applicable authority; they are not provider-free fallbacks. + +Without an authorised remote read, inspect local branch refs, registered worktrees, +current task records and available checkpoints. Record that remote ownership and base +freshness remain unverified. Continue independent authorised offline work; do not claim +that local branch names prove the absence of another implementation. + +## Prepare the checkout for the current host + +1. Follow `docs/agents/codex-cloud-environment.md` for Cloud and + `docs/agents/codex-desktop-worktree-setup.md` for Windows Desktop where applicable. + Use the current host's supported task/worktree workflow. A disposable Cloud task + checkout does not require a Windows task-start script or another worktree merely + because an older local example used one. +2. For a new local task needing isolation, create a task-specific non-protected branch + in an owned worktree. Determine its root from the current checkout and host policy; + do not switch or reuse another session's checkout. If an authorised fetch is needed, + verify repository identity and fetch `origin/main` before choosing the base. If + working offline, record the actual locally available base SHA and its freshness gap. +3. Check capacity on the actual destination volume before a dependency install or + build. Preserve other sessions' worktrees and services. Low capacity does not grant + cleanup authority; use the repository's scoped cleanup workflow only when authorised. +4. Restore dependencies only when the task needs them and current parity evidence + shows they are absent or stale. Inspect the setup helper and coordinator admission + first. Where supported, `node scripts/setup-codex-worktree.mjs` reuses an eligible + byte-identical donor before its documented npm fallback. Use the current host's + supported restoration route; do not force the legacy Cloud bootstrap or change + manifests/lockfiles merely to prepare a checkout. +5. Use `node scripts/check-base-freshness.mjs` when relevant to the task. Distinguish + comparison with a local tracking ref from a freshly authorised remote observation. + If the base moved, assess relevant drift and the task's sync policy; do not discard + a continuation or recreate a worktree automatically. +6. Start or verify the app only for a task that needs it, following `AGENTS.md` local + server safety: run `npm run ensure`, use its printed URL and verify project identity + before browser work. Do not warm unrelated routes or start a watcher during generic + onboarding. A documentation-only task needs no app server. + +## Historical Windows example + +On 2026-08-18 the local environment used `D:/Repos/PsychSift/.claude/worktrees/` +on a ReFS Dev Drive and reported roughly 48–50 worktrees. The then-observed disk +capacity, free-space percentage and dependency sizes are historical measurements. +They do not identify the current checkout, operating system, capacity or worktree +owner. Derive those facts from the current host instead of copying the old path. + +## Handoff and cleanup + +- Do not stash, reset, clean, relocate or absorb unrelated work. +- Follow the requested delivery workflow; commit or publish only with applicable authority. +- Use the `handoff` skill when available and relevant, without inventing missing tooling. +- A merged branch does not prove its worktree is disposable. Removal needs verified + ownership, current status and the repository's cleanup authority. Do not run periodic + cleanup or delete another task's checkout as part of task setup. diff --git a/.claude/skills/prompt/SKILL.md b/.claude/skills/prompt/SKILL.md index 20c3dd435e..c49de1c4e1 100644 --- a/.claude/skills/prompt/SKILL.md +++ b/.claude/skills/prompt/SKILL.md @@ -1,6 +1,6 @@ --- name: prompt -description: Perfect a rough request or draft prompt using relevant context, attachments, files, and the minimum necessary skills or tools, then answer or execute it. Use when the user invokes $prompt or asks to improve, rewrite, sharpen, expand, contextualize, or optimize their prompt before acting. For code-changing prompts, also prepare a safe repository workflow: a fresh worktree and branch per task. Do not use for ordinary code or content review unless prompt improvement is requested. +description: Perfect a rough request or draft prompt using relevant context, attachments, files, and the minimum necessary skills or tools, then answer or execute it. Use when the user invokes $prompt or asks to improve, rewrite, sharpen, expand, contextualize, or optimize their prompt before acting. For code-changing prompts, verify task ownership and use an isolated checkout when needed; preserve verified same-task continuation. Do not use for ordinary code or content review unless prompt improvement is requested. --- # Prompt @@ -73,7 +73,7 @@ For machine-consumed output, specify an exact schema, required fields, permitted When the perfected task depends on local repository evidence or will write to a Git repository, read `references/coding-workflow.md` before relying on findings or editing. Apply its freshness rules to review and diagnosis; apply its branch, environment, implementation, and validation rules only to repository writes. Skip the reference for prompt-only or answer-only work that does not depend on repository state. -For any repository write, start the task in a fresh worktree on a new branch cut from the latest remote default branch — one task, one worktree, one branch. Never edit the main checkout or reuse another task's worktree; the reference gives the exact commands and exceptions. +Before repository writes, verify the checkout, branch, task ownership and existing changes. Continue verified same-task work, including owned uncommitted changes; isolate unrelated work when needed. The reference gives the branch, freshness and authority checks. ## API and ChatGPT prompt practice diff --git a/.claude/skills/prompt/references/coding-workflow.md b/.claude/skills/prompt/references/coding-workflow.md index 010da13e78..e97c5136ff 100644 --- a/.claude/skills/prompt/references/coding-workflow.md +++ b/.claude/skills/prompt/references/coding-workflow.md @@ -13,14 +13,13 @@ Higher-priority instructions and the repository's own `AGENTS.md` / `CLAUDE.md` ## Branch and worktree (repository writes) -- Start every task in its own fresh worktree on its own new branch. Fetch first, then branch off the latest remote default branch (`git fetch origin && git worktree add -b origin/`), using absolute paths for the worktree. Do not edit in the main checkout, and do not reuse another task's worktree or branch for unrelated work. -- A stale base is the most common multi-worktree trap: confirm the new branch actually starts from the current remote default branch, not a days-old local copy. -- Name the branch with a short kebab-case phrase that identifies the task, following the repository's existing prefix convention when one exists. -- Inspect first: current branch, upstream, other worktrees, and `git status`. Never assume the branch or remote. -- Do not commit directly to a protected or base branch (`main`, `master`, `develop`, `release/*`). If you find yourself on one, create the worktree/branch above before editing. -- If the environment already placed you in a dedicated per-task worktree, verify it before trusting it as fresh: confirm the current branch, a clean `git status`, and that its merge base matches the latest remote default branch (`git fetch` first). Use it as-is only when those checks pass; otherwise create a new worktree and branch as above. Do not nest another worktree inside a verified one. -- Preserve unrelated staged, unstaged, and untracked work. Do not stash, reset, clean, or discard changes you did not create. -- Commit or push only when the task explicitly asks for it. Never force-push, rebase shared history, delete branches, or merge into a base branch without explicit confirmation. +- Inspect the current repository, branch, HEAD, upstream, worktrees, active Git operation and changed paths. Establish task ownership before editing; never assume the branch or remote. +- Continue verified same-task work in the supplied task checkout. A dirty status or a branch behind the default branch alone does not require a fresh task, new worktree or discarded analysis. Inventory and preserve owned and unrelated changes; reassess relevant drift. +- For new unrelated work, use an isolated task branch/worktree when needed to protect concurrent changes. Follow repository-specific setup and isolation checks. A host-provided disposable task checkout may already supply that isolation; do not nest another worktree merely for procedure. +- Establish the appropriate base from available local evidence. Remote freshness checks such as `git fetch` require applicable provider/network authority; if unavailable, disclose the unverified remote state and continue independent authorised local work. +- Do not commit directly to a protected or base branch (`main`, `master`, `develop`, `release/*`). Establish an appropriate task branch before edits there. +- Preserve unrelated staged, unstaged, and untracked work. Do not stash, reset, clean, relocate or absorb changes you did not create. +- Commit or push only with applicable task authority. Never force-push, rebase shared history, delete branches, or merge into a base branch without applicable explicit confirmation. ## Environment (repository writes) diff --git a/AGENTS.md b/AGENTS.md index ea32eadd43..90db61d5f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,8 +102,7 @@ For Windows Codex Desktop worktree bootstrap and dry-run instructions, see [`doc # Reasoning effort calibration -For the Codex reasoning-effort baseline, the Cloud `xhigh` confirmation gate, and the -plan-effort/build-effort table, see +For user-controlled Codex reasoning effort, confirmation and scoped specialist requirements, see [`docs/agents/codex-reasoning-effort.md`](docs/agents/codex-reasoning-effort.md). # Process hardening phases @@ -237,7 +236,7 @@ Read and follow [Publication workflows](docs/agents/native-startup/publication-w ## Codex Cloud environment -For the Codex Cloud environment specification, access profiles, MCP limits, and acceptance checks, see [`docs/agents/codex-cloud-environment.md`](docs/agents/codex-cloud-environment.md). +For current-host capability checks and the scope of the optional legacy Cloud setup, see [`docs/agents/codex-cloud-environment.md`](docs/agents/codex-cloud-environment.md). ## Cursor Cloud specific instructions (not Codex Cloud) diff --git a/README.md b/README.md index ceb9dcac86..2dbdcb185c 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,9 @@ then validates the installed metadata. It falls back to the locked install above when no safe local donor exists. Do not configure Windows Desktop worktrees to run the Cloud-only Bash setup script. -For Codex Cloud, use the tracked environment setup and acceptance contract in -[`docs/codex-cloud.md`](docs/codex-cloud.md). It installs the complete repository -toolchain, installs the profile-loading Node command shims, and distinguishes safe offline -tasks from explicitly connected provider tasks. Fresh Cloud validation runs the direct -`npm run check:codex-cloud` commands without manually sourcing a profile. +For Codex Cloud, start with the [current-host guide](docs/codex-cloud.md) and actual task +capabilities. The older repository-managed bootstrap is archived and applies only when explicitly +selected. Do not impose its profiles, command shims or startup checks on another host setup. 3. For offline app development, use a credential-free `.env.local` containing `NEXT_PUBLIC_DEMO_MODE=true`. Use the synthetic corpus; leave Supabase and @@ -156,10 +154,10 @@ checks; this documentation correction changes no runtime pins. ### Codex Cloud -For isolated provider-free Cloud work, create the environment with the exact values in -[`docs/codex-cloud.md`](docs/codex-cloud.md). Its checked-in setup and maintenance -scripts match the Node/npm, Deno, Python/OCR, and Playwright toolchain without copying -local credentials or enabling live provider access. +Use the [current Cloud policy](docs/agents/codex-cloud-environment.md) and host readiness evidence. +Provider-free work remains provider-free; it does not require a particular historical profile. +The [archived bootstrap](docs/archive/codex-cloud-legacy-profile-2026-10-16.md) retains its own +toolchain and acceptance contract for environments that explicitly select it. ## Environment Notes @@ -256,7 +254,7 @@ records vs archive). The most load-bearing entries: - `docs/site-map.md` — generated route map (`npm run docs:update`) - `docs/process-hardening.md` — verification gates, CI expectations, known limits - `docs/testing.md` — local test safety, focused/live commands, Playwright ownership, flake policy -- `docs/codex-cloud.md` — reproducible provider-free Codex Cloud setup and acceptance check +- `docs/codex-cloud.md` — current-host Cloud guide and explicit legacy-bootstrap selection - `docs/clinical-governance.md` — deployment and source governance checklist - `docs/deployment-architecture.md` — app/worker/Supabase deployment topology - `docs/supabase-migration-reconciliation.md` — migration drift and repair policy diff --git a/docs/README.md b/docs/README.md index 57d012183b..61d8e9f908 100644 --- a/docs/README.md +++ b/docs/README.md @@ -50,17 +50,17 @@ repository gate. The generated inventories remain governed by their own contract ## Core reference map -| Doc | What it is | -| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| [codebase-index.md](codebase-index.md) | Structured architecture map: layout, module map, Supabase schema, scripts, domain concepts | -| [README.md](README.md) | This index — every tracked document under `docs/`, categorised | -| [site-map.md](site-map.md) | **Generated** route map — regenerate with `npm run docs:update`, verify with `npm run sitemap:check` | -| [agents-guide.md](agents-guide.md) | Task navigator, platform setup and skill ownership; rules in `AGENTS.md` | -| [scripts-index.md](scripts-index.md) | Curated map of `scripts/` and the `package.json` command surface by purpose | -| [organisation/](organisation/README.md) | Organisation map: which area owns every file, checked by `npm run check:organisation` | -| [personal-practice/](personal-practice/README.md) | Personal practice area: what it owns and the rules every change there follows | -| [codex-cloud.md](codex-cloud.md) | Codex Cloud setup, access profiles, profile-loading command shims, GitHub exception, and acceptance checks | -| [claude-cloud.md](claude-cloud.md) | Claude Code on the web: the tiered container provisioner and the checked-in user profile | +| Doc | What it is | +| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| [codebase-index.md](codebase-index.md) | Structured architecture map: layout, module map, Supabase schema, scripts, domain concepts | +| [README.md](README.md) | This index — every tracked document under `docs/`, categorised | +| [site-map.md](site-map.md) | **Generated** route map — regenerate with `npm run docs:update`, verify with `npm run sitemap:check` | +| [agents-guide.md](agents-guide.md) | Task navigator, platform setup and skill ownership; rules in `AGENTS.md` | +| [scripts-index.md](scripts-index.md) | Curated map of `scripts/` and the `package.json` command surface by purpose | +| [organisation/](organisation/README.md) | Organisation map: which area owns every file, checked by `npm run check:organisation` | +| [personal-practice/](personal-practice/README.md) | Personal practice area: what it owns and the rules every change there follows | +| [codex-cloud.md](codex-cloud.md) | Current-host Cloud guide; archived legacy setup applies only when explicitly selected | +| [claude-cloud.md](claude-cloud.md) | Claude Code on the web: the tiered container provisioner and the checked-in user profile | ## Architecture @@ -222,7 +222,7 @@ Every remaining tracked document in this category, one line each; the descriptio - [productivity-workflows.md](productivity-workflows.md) — repo workflow planners (flightplan, triage, rag-lab, …) - [codex-review-protocol.md](codex-review-protocol.md) — shared review protocol for all review skills - [codex-prompt-playbook.md](codex-prompt-playbook.md) — copy/paste prompts for common repo work -- [codex-cloud.md](codex-cloud.md) — reproducible provider-free Codex Cloud environment and acceptance check +- [codex-cloud.md](codex-cloud.md) — current-host Cloud guide and explicit selection of the archived legacy setup - [claude-cloud.md](claude-cloud.md) — Claude Code on the web container parity: tiered provisioner and checked-in user profile - [branch-cleanup-guide.md](branch-cleanup-guide.md) — branch hygiene workflow - [branch-review-ledger.md](branch-review-ledger.md) — reviewed branch/SHA ledger; read with `npm run ledger:lookup` (historical tables + immutable records), write with `npm run ledger:append`, and convert a pre-system active-branch row with `npm run ledger:migrate-legacy` @@ -237,11 +237,11 @@ Every remaining tracked document in this category (process, plus the `agents/` r - [ci-operations.md](ci-operations.md) — CI Operations and Runner Usage Assessment — See also continuous-integration.md for pre-push safety controls and Guard 2 in-flight CI push guard details. - [agents/bug-hunter-shortcut.md](agents/bug-hunter-shortcut.md) — Bug-Hunter Shortcut — When the user types exactly bug-hunter as the entire task message, after trimming surrounding whitespace, treat it as a shortcut for targete… - [agents/claude-hook-scripts.md](agents/claude-hook-scripts.md) — Claude Code Hook Scripts — .claude/hooks/*.sh runs on Linux web containers as well as on the Windows workstation, and the workstation cannot see the thing that breaks… -- [agents/codex-cloud-environment.md](agents/codex-cloud-environment.md) — Codex Cloud Environment — Codex Cloud uses an isolated Linux container and does not inherit desktop files, credentials, OAuth sessions, MCP authentication, local serv… +- [agents/codex-cloud-environment.md](agents/codex-cloud-environment.md) — Canonical Cloud operating policy: current-host evidence, task continuation, tools, credentials and explicitly selected legacy setup. - [agents/codex-dependency-shortcut.md](agents/codex-dependency-shortcut.md) — Codex Dependency Shortcut — When the user types exactly dependency as the entire task message, after trimming surrounding whitespace, treat it as a shortcut for safe de… - [agents/codex-desktop-worktree-setup.md](agents/codex-desktop-worktree-setup.md) — Codex Desktop Worktree Setup — It must work before node_modules exists, validate Node 24/npm 11, reuse only a complete byte-identical local installation, and otherwise run… - [agents/codex-productivity-defaults.md](agents/codex-productivity-defaults.md) — Codex Productivity Defaults -- [agents/codex-reasoning-effort.md](agents/codex-reasoning-effort.md) — Codex Reasoning Effort Calibration +- [agents/codex-reasoning-effort.md](agents/codex-reasoning-effort.md) — User-selected model/effort, ordinary-language confirmation and scoped specialist contracts - [agents/codex-review-throttling.md](agents/codex-review-throttling.md) — Codex Review Throttling & Thread Resolution — Do not review branches opportunistically. - [agents/context7.md](agents/context7.md) — shared library-docs workflow, version matching, authentication and query privacy - [agents/cursor-cloud.md](agents/cursor-cloud.md) — Cursor Cloud Specific Instructions — Durable notes for Cloud Agents. @@ -251,7 +251,7 @@ Every remaining tracked document in this category (process, plus the `agents/` r - [agents/repository-skills-and-issues.md](agents/repository-skills-and-issues.md) — Repository Skills and Outstanding-Work Memory — Select repo-local skills under `.agents/skills/` when their descriptions match the actual task and their use materially helps; read an explicitly named skill before acting. - [agents/shared-context-and-memory.md](agents/shared-context-and-memory.md) — Context, adaptive agents and memory across local and cloud sessions — Read this reference when a task needs continuity, delegation, memory or a change of execution host. - [agents/test-deletion-guard.md](agents/test-deletion-guard.md) — Deleting tests, or letting a tool delete them for you — On 2026-08-31 a commit on PR #2481 titled "test(ui): -- [agents/smart-agent-allocation.md](agents/smart-agent-allocation.md) — Default model/effort selection, independent ownership and review, evidence reuse, and portable Codex Cloud delivery. +- [agents/smart-agent-allocation.md](agents/smart-agent-allocation.md) — Current supported model/effort selection, independent ownership/review and evidence reuse. - [agents/task-efficiency.md](agents/task-efficiency.md) - On-demand task brief, one continuation record and private measurement using existing workflows. - [agents/upload-shortcut.md](agents/upload-shortcut.md) — Upload Shortcut — When the user types exactly: - [agents/verification-gates.md](agents/verification-gates.md) — Verification Gates — the verification pyramid, gate receipts, and the browser-gate planner @@ -263,7 +263,7 @@ Every remaining tracked document in this category (process, plus the `agents/` r - [agents/native-startup/publication-workflows.md](agents/native-startup/publication-workflows.md) — Safe Git publication workflows, PR shortcuts, and anti-churn sync - [agents/native-startup/search-chrome-behaviour.md](agents/native-startup/search-chrome-behaviour.md) — Search chrome behaviour, composer ownership, and phone edge-to-edge contracts - [prompts/codex-architecture-maintainability-ultra-review.md](prompts/codex-architecture-maintainability-ultra-review.md) — Codex Local Ultra — Architecture & Maintainability Review Orchestrator — Perform a rigorous, evidence-based Architecture and Maintainability review of this repository using multi-agent coordination. -- [prompts/codex-cloud-design-status-semantics.md](prompts/codex-cloud-design-status-semantics.md) — Codex Cloud prompt — design-system clinical status semantics — Copy the complete prompt below into a new Codex Cloud task for the PsychSift (`BigSimmo/PsychSift`) repository. +- [prompts/codex-cloud-design-status-semantics.md](prompts/codex-cloud-design-status-semantics.md) — Retired implementation prompt; status semantics landed, human screenshot disposition remains separate. - [prompts/codex-cloud-detailed-task.md](prompts/codex-cloud-detailed-task.md) — Codex Cloud detailed-task prompt — Use this prompt when assigning a substantial implementation, refactor, defect fix, or other detailed task to Codex Cloud in this repository. - [prompts/codex-cloud-review/1-codex-full-stack-master-review-prompt.md](prompts/codex-cloud-review/1-codex-full-stack-master-review-prompt.md) — Codex Comprehensive Full-Stack Product, Design, Architecture, Engineering, Security, Quality and Refactoring Master Prompt — Keep this master specification as a normal Markdown file, for example: - [prompts/codex-cloud-review/2-codex-full-stack-master-prompt-review-and-stress-test.md](prompts/codex-cloud-review/2-codex-full-stack-master-prompt-review-and-stress-test.md) — Review and Adversarial Stress Test of the Codex Full-Stack Master Prompt — The original Cursor prompt had unusually strong domain coverage and quality controls. @@ -274,7 +274,7 @@ Every remaining tracked document in this category (process, plus the `agents/` r - [prompts/codex-performance-reliability-ultra-review.md](prompts/codex-performance-reliability-ultra-review.md) — Codex Local Ultra — Performance & Reliability Review Orchestrator — Perform a rigorous, evidence-based Performance and Reliability review of this repository using multi-agent coordination. - [prompts/codex-tests-quality-gates-ultra-review.md](prompts/codex-tests-quality-gates-ultra-review.md) — Codex Local Ultra — Tests & Quality Gates Review Orchestrator — Perform a rigorous, evidence-based Tests & Quality Gates review of this repository using multi-agent coordination. - [prompts/mode-aware-clinical-ask-codex-cloud-handover.md](prompts/mode-aware-clinical-ask-codex-cloud-handover.md) — Codex Cloud handover: implement Mode-aware Clinical Ask — Paste the prompt below into a fresh Codex Cloud task whose checkout contains this entire planning pack. -- [prompts/rag-coverage-gate-extraction.md](prompts/rag-coverage-gate-extraction.md) — X3 / #086 rag.ts coverage-gate extraction — Use this prompt in a fresh Codex Cloud task with CODEX_CLOUD_ACCESS_PROFILE=offline. +- [prompts/rag-coverage-gate-extraction.md](prompts/rag-coverage-gate-extraction.md) — Retired implementation prompt; extraction exists in the current tree. Historical task instructions are archived. - [codex/architecture-maintainability/README.md](codex/architecture-maintainability/README.md) — Architecture and maintainability review packets — Durable artifacts requested from the architecture and maintainability review prompt belong here. - [codex/data-database-safety/README.md](codex/data-database-safety/README.md) — Data and database safety review packets — Durable artifacts requested from the data and database safety review prompt belong here. - [codex/documentation-ownership/README.md](codex/documentation-ownership/README.md) — Documentation and ownership review packets — Durable artifacts requested from the documentation and ownership review prompt belong here. diff --git a/docs/agents-guide.md b/docs/agents-guide.md index 055ecec5a8..3ccba3b981 100644 --- a/docs/agents-guide.md +++ b/docs/agents-guide.md @@ -28,7 +28,7 @@ Also see [DOCS-SYSTEM.md](DOCS-SYSTEM.md) for the documentation operating system | Select verification, reuse evidence, choose browser coverage | [Verification gates](../AGENTS.md#process-hardening-phases) | | Start or prepare a Windows Desktop worktree | [Desktop worktree setup](agents/codex-desktop-worktree-setup.md) | | Use the dependency shortcut | [Dependency workflow](agents/codex-dependency-shortcut.md) | -| Work in Codex Cloud | [Cloud environment](agents/codex-cloud-environment.md) and [setup contract](codex-cloud.md) | +| Work in Codex Cloud | [Current Cloud policy](agents/codex-cloud-environment.md); [archived legacy setup, when selected](archive/codex-cloud-legacy-profile-2026-10-16.md#scope-and-selection) | | Work in Cursor Cloud | [Cursor Cloud](agents/cursor-cloud.md) | | Apply repository productivity defaults | [Productivity defaults](agents/codex-productivity-defaults.md) | | Choose a skill or handle outstanding work | [Skills and issue ledger](agents/repository-skills-and-issues.md#repository-productivity-skills), [.agents overview](../.agents/README.md) | diff --git a/docs/agents/codex-cloud-environment.md b/docs/agents/codex-cloud-environment.md index ce85f671ad..29a22397e8 100644 --- a/docs/agents/codex-cloud-environment.md +++ b/docs/agents/codex-cloud-environment.md @@ -1,130 +1,102 @@ -# Codex Cloud Environment +# Codex Cloud operating policy ## Codex Cloud environment -Codex Cloud uses an isolated Linux container and does not inherit desktop files, -credentials, OAuth sessions, MCP authentication, local services, or uncommitted work. -Use `docs/codex-cloud.md` as the environment contract: - -- Whenever agents are requested or otherwise permitted, follow the tracked - [smart agent allocation policy](smart-agent-allocation.md). It is discovered through - the root `AGENTS.md`, without desktop files. Verify available hosted model/effort - controls before dispatch; documentation is not execution proof. A local draft - must be published to the selected Cloud branch before a fresh task can load it. - -- Configure setup as `bash scripts/setup-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh`. -- Configure maintenance as `bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh`. -- For an explicitly owner-authorized GitHub shell environment, use - `bash scripts/run-codex-cloud-github.sh setup` and - `bash scripts/run-codex-cloud-github.sh maintenance` instead, with the connected profile. - The encrypted setup secret is consumed before profile scrubbing; only the standard `gh` - credential store persists for that authorized capability. See the scoped exception and - acceptance requirements in `docs/codex-cloud.md` (Setup and maintenance). -- Default to `CODEX_CLOUD_ACCESS_PROFILE=offline` for ordinary and protected RAG work. - Use `connected` only when the user explicitly authorizes the required provider access. -- When MCP tools are already callable in a Cloud session and the task needs them, use the host - plugin/connector inventory. The production Supabase target is limited to prompted, read-only - `docs` and `development` metadata tools; do not enable database, SQL, row, or log tools. - Write-capable Figma, Railway, and Sentry tools still require explicit confirmation. Paid API - canaries (`eval:rag`, `eval:retrieval:quality`, `eval:quality`, `verify:release`, - `test:live`, `check:supabase-project`) still need explicit confirmation. Project - `.codex/config.toml` keeps Desktop/CLI MCP entries `enabled = false` in git (`check:codex-cloud` - fails if any tracked entry is enabled). Opt in locally via `$CODEX_HOME/config.toml` (preferred) - or a never-committed project-file edit, then `codex mcp login railway`. Cloud setup never writes - Railway or Supabase MCP registrations to `$CODEX_HOME`. Hosted ChatGPT/Codex requires an - installed, workspace-authorized, OAuth-authenticated app, and a fresh task must prove the callable - inventory with read-only identity calls. Root `.mcp.json` is a static cross-client template, not - hosted runtime proof. -- Cloud has no Windows task-start script. Report that exact fact, then perform equivalent - read-only identity, branch, status, worktree, and Git-operation checks. Proceed only in a - clean disposable checkout on a task-specific non-protected branch. -- Cloud mirrors the tracked repository toolchain, not Windows files, `.env.local`, - desktop plugins, browser sessions, OAuth sessions, user-global skills, or uncommitted - work. Keep required workflows in tracked instructions, scripts, tests, and repo-local - skills. -- Repository setup cannot grant GitHub installation permissions, workspace RBAC, network - policy, or provider credentials. Treat those as product/account settings and verify them - separately without printing secret values. -- In a fresh Cloud task, run `bash scripts/check-codex-cloud-raw-env.sh` before sourcing a - profile or entering a login shell. It must report only provider variable names and presence, - never values. Treat exit `1` / `FAIL`+`STOP` as a hard stop for any unexpected inherited name. - Only exit `2` / `FAIL-KNOWN` for `OPENAI_BASE_URL` alone may use the restricted - profile-and-shim continue path; do not generalize that allowance. Exit `2` is still a failed raw - boundary — future automation must not treat non-1 as success or as a blind retry. That name can - redirect OpenAI-bound traffic, so never invoke OpenAI clients from the raw parent or any binary - that bypasses the profile/`node`/`npm`/`npx` scrub. Then run - `npm run check:codex-cloud` directly; it must report the static-and-environment PASS line. Run - `npm run check:codex-cloud -- --runtime` with `CODEX_CLOUD_EXPECTED_BASE_SHA` set to the - intended merge/base commit when the checkout has only a task HEAD. Setup and maintenance may - report freshness as unverified so provisioning remains repairable, but explicit acceptance must - not pass an arbitrary HEAD. The command shims load the generated profile for normal `node`, - `npm`, and `npx` work. Also run `npm run check:runtime` and - `npm run check:installed-lock-parity` before trusting a new or reset environment. A skipped - browser install is not full browser readiness. Output is limited to approved mode values, - presence booleans, full Git commit identities, and MCP server/command/environment-variable - names; never print credential values. -- Do not add OpenAI, Supabase, Railway, GitHub, database, or user credentials as ordinary - Cloud environment variables. Codex Cloud secrets are setup-only and unavailable to the - agent phase unless the platform explicitly exposes a secret to the named task phase; do not - copy them into files to bypass that boundary. -- Provider-backed checks, hosted CI mutations, deployment, production data access, and - Git publishing still require the explicit authorization defined above. -- The ordinary offline Cloud profile intentionally cannot perform authenticated production or - live-provider checks. `check:production-readiness` reports this as a provider capability gap. -- Authenticated live tests run through the manual - `.github/workflows/authenticated-live-tests.yml` GitHub Actions workflow, its explicit - dispatch confirmation, and the `Database / production` environment, never by exposing - credentials to the Codex Cloud agent shell. -- The active hosted workspace is **Personal Pro**. Use Railway's installed official ChatGPT app - with browser OAuth and **Allow read actions**; Personal Pro does not provide the dedicated-group - RBAC or per-tool action disabling assumed by Enterprise/Edu instructions. Prove Railway with the - callable tool inventory and a read-only identity/project-list call. Repository setup and local - MCP config cannot activate it, and the Codex Cloud connector page currently offers no Railway - connector. Use the documented split control plane: Codex Cloud for code and its native GitHub - connector, ChatGPT web for Railway and project-scoped read-only Supabase. Every provider change - still requires explicit approval. Enterprise/Edu custom-app controls are a future governance - option, not the current workspace classification. - CLI token auth is a separate operator capability: it requires a separately installed Railway CLI - and a dedicated - `RAILWAY_API_TOKEN`, and must never substitute `RAILWAY_TOKEN` or expose either token to an - ordinary agent shell. GitHub connector access, GitHub CLI authentication, the credential-free - `origin` URL, and shell Git authentication are separate capabilities. -- For an explicitly authorised GitHub task, use the authenticated GitHub connector/MCP - tools as the default remote control plane. Use them for repository, PR, issue, review - thread, and Actions work, including inline-thread replies/resolution, Actions - run/job/log/artifact inspection, and approved branch, file, or PR mutations. Missing - `gh`, shell GitHub credentials, or direct shell network access is not a loss of this - capability. The intended connection is `BigSimmo` with repository write access. - Reserve administrator access for separately approved operations. -- In Codex Cloud, use native Push, the authenticated GitHub connector, or GitHub's UI for branch - publication and cleanup. `CODEX_CLOUD_GITHUB_PAT` is excluded from every Cloud agent shell. - The helper `bash scripts/delete-codex-cloud-branch-with-pat.sh ` is - operator-only outside Codex Cloud; it must reject `CODEX_CLOUD=1`, validate the exact - non-protected ref and credential-free origin, and never print the token. If the native or - connector path is unavailable, report the platform limit rather than copying a PAT into a - profile, remote URL, cached file, or agent environment. -- Confirm the exact repository and PR/thread/job before a write, and verify the connector - result before treating the write as successful. A repository cannot sanitize a variable - already inherited by the top-level task process; the tracked shims protect normal - `node`/`npm`/`npx` commands. Report raw-parent exposure as a Codex Cloud launcher defect - rather than weakening the provider-variable contract. -- Cloud browser proof is Playwright/Chromium, Firefox, or WebKit container evidence, not - physical iPhone Safari/PWA acceptance. - -### Summary rules formerly carried inline in `AGENTS.md` - -These are the Codex Cloud bullets `AGENTS.md` carried inline until they were consolidated here. -They are reproduced verbatim; where they restate a rule above, both wordings stand. - -Codex Cloud uses an isolated Linux container and does not inherit desktop credentials, local services, or uncommitted work. Full environment specification and runbooks live in `docs/codex-cloud.md`. - -- Default to `CODEX_CLOUD_ACCESS_PROFILE=offline` for ordinary/RAG work; use `connected` only with explicit provider authorization. -- Personal Pro split control plane: Codex Cloud for code and GitHub connector; ChatGPT web for Railway and read-only Supabase metadata. -- Acceptance: run `bash scripts/check-codex-cloud-raw-env.sh`, `npm run check:codex-cloud`, and `npm run check:codex-cloud -- --runtime` (with `CODEX_CLOUD_EXPECTED_BASE_SHA`). -- Do not expose provider secrets (OpenAI, Supabase, Railway, GitHub PATs) in Cloud agent shells or committed config. -- Authenticated live tests run via `.github/workflows/authenticated-live-tests.yml` with manual dispatch, never from Cloud agent shells. -- Branch deletion helper `bash scripts/delete-codex-cloud-branch-with-pat.sh` is operator-only outside Cloud. +This is the canonical operating policy for Cloud tasks in this repository. Follow the current +system, developer and user instructions, then the applicable repository rules. Old chats, copied +prompts, archived runbooks and machine names are evidence to verify, not a second source of authority. + +### Start or resume the current task + +1. Check the repository, branch, commit, worktree, relevant changes and existing task checkpoint. + Preserve unrelated work. Resume verified same-task edits; a dirty working tree alone is not a + reason to discard work, demand a new task or restart completed analysis. +2. Inspect the current host's permissions, network policy and callable tools as needed. Host status, + saved configuration, successful tool execution and loaded instructions are separate evidence. + An empty capability-summary list does not negate tools that are actually callable. +3. Identify the concrete files and acceptance checks for the request. Continue independent + authorised work when a particular tool, dependency, provider or approval is unavailable. +4. Use the repository-supported toolchain and lockfiles. Run checks appropriate to the change, + through the normal wrappers/coordinator. Do not run a server, browser suite, install or broad + baseline merely to prove that a session started. + +### Models, agents and confirmation + +Respect the user's main-chat model and effort. Accept ordinary-language confirmation; do not ask +again for a special marker or resubmission. Recommendations do not attest runtime settings. +Use [reasoning guidance](codex-reasoning-effort.md) and +[agent allocation](smart-agent-allocation.md) when relevant. Select only models/arguments exposed +by current tools; old model identifiers and unavailable skills are not prerequisites for ordinary +work. Keep explicitly selected specialist programme review and evidence contracts scoped to that +programme. Do not copy its launch gates into unrelated tasks. + +### Tools, providers and credentials + +Use actual callable tools rather than assuming a particular account tier, connector name or +historical workstation setup. Repository MCP templates describe configuration, not hosted readiness. +Desktop files, local services, OAuth state and private environment files do not automatically transfer. + +Preserve existing task authorisation; do not repeatedly ask for the same authorised action. Paid +calls, provider operations, production/database changes and merges retain their applicable project +boundaries. A broad audit is not authority to change a live service or sign clinical/legal approvals. +Prefer authenticated connectors for authorised provider work where available. Missing shell GitHub +credentials, connector access and network access are distinct capabilities. + +Never print, copy, persist or forward credential values, or inspect them to diagnose readiness. +A variable name alone proves neither a leak nor effective access. Use names-only diagnostics and +host metadata. Do not retrieve a token, bypass host/network policy, or alter authentication to turn +a failing check green. Preserve proxy and CA configuration supplied by the host. + +Keep the production Supabase MCP template pinned to project `sjrfecxgysukkwxsowpy`, `read_only=true` +and `docs`/`development` metadata, excluding SQL, clinical rows and logs. Broader tooling requires +separate authorised configuration and project safeguards. Keep tracked MCP templates secret-free +and preserve their disabled-by-default settings; do not broaden them to imitate another host. + +### Legacy setup: explicit selection only + +The older offline/connected bootstrap is an opt-in setup, not a default requirement for every Cloud task. +The [archived legacy runbook](../archive/codex-cloud-legacy-profile-2026-10-16.md) applies only when +the owner or selected environment explicitly adopts it, or a task tests/repairs that bootstrap. +Linux, a `connected` host label, `CODEX_CLOUD=1` or an earlier probe does not select it. +Do not install its shims, force profile variables or rerun maintenance for an unrelated host. + +An explicit legacy environment/runtime check retains its fail-closed credential, profile, base, +toolchain and browser contracts. A configured legacy profile selects that validation; an invalid +profile must fail, not silently fall back to static checks. Static source checks do not establish +runtime readiness. Retained legacy scripts are operational tools, not general startup instructions. + +### Raw-boundary failure and local policy repair + +A failed raw-boundary check does not prohibit an explicitly requested local policy review or repair. +For a selected legacy profile, unexpected inherited provider names block its application, test, +setup, package-manager and provider execution. Permit bounded local instruction edits and +shell/Python document checks that do not load application dependencies, startup profiles or secrets. +Do not unset a name to manufacture a pass or treat a sanitised child as proof the parent was clean. +For other host-managed setups, follow current host boundaries and affected-operation checks; +do not turn a legacy probe result into a universal task stop or a claimed passing result. + +Investigate repeated failures using supported host configuration and names-only evidence. Repeating +an identical restart is not a repair. A repository edit cannot remove a variable already inherited +by a running process. Do not claim which component injected a value without evidence. + +### Activation and retained context + +Host settings, repository revisions and active chat context update separately. Changing an +environment does not edit Git files; local edits do not reach other checkouts until published and +integrated. Existing chats retain earlier messages, but those messages do not override newer user +instructions or the verified current policy. Re-read changed guidance instead of following a stale +checkpoint blindly. Use a supported reload or fresh task only when the host actually requires it. + +Cloud profiles must not seed old machine state or session coordination boards as current project +memory. Seed only reviewed durable guidance from an explicit source manifest; keep dated state in +the archive. A source cleanup does not authorise deleting or overwriting existing host-written +memories. Resolve conflicts using current instructions and evidence, not an old task list. + +Report the selected revision, completed checks and concrete remaining limitation. Distinguish +local source changes, publication, merge and observed activation. Never claim all environments +are updated based on a local check. Container browser evidence is not physical Safari/PWA proof. +Historical records stay out of startup instructions and automatic policy scanning unless requested. diff --git a/docs/agents/codex-reasoning-effort.md b/docs/agents/codex-reasoning-effort.md index f293fcd31e..906fd7bcb5 100644 --- a/docs/agents/codex-reasoning-effort.md +++ b/docs/agents/codex-reasoning-effort.md @@ -1,66 +1,26 @@ -# Codex Reasoning Effort Calibration +# Codex reasoning effort -# Reasoning effort calibration +The user owns the main-chat model and reasoning picker. Do not set repository-wide `model` or +`model_reasoning_effort` in `.codex/config.toml`, claim to change the running setting, or infer it +from old sessions. Without runtime telemetry, distinguish a user-confirmed setting from verified +execution metadata. -**Picker ownership.** Model and reasoning effort are user/task choices. Do not set `model` or -`model_reasoning_effort` in the repository `.codex/config.toml`: repository defaults override the -Desktop picker and make new tasks snap back after an update. Use the model and effort selected for -the task. For child-agent model and effort choices, apply -[smart agent allocation](smart-agent-allocation.md) automatically whenever agents are used. -Its routes are starting points; the risk calibration and required effort floors below still apply. -It does not change the running main-chat model or reasoning effort. +The owner can confirm a picker change in ordinary language; no literal marker or +resubmission is required. Preserve that confirmation instead of asking again. -**Cloud xhigh gate.** A running Cloud task cannot raise its own reasoning effort. Before substantive -inspection, planning, tool use, or edits, classify the request against the table and the risk rules -below. If `xhigh` is required and the prompt does not contain the exact marker `[xhigh-confirmed]`, -stop and ask the user to select `xhigh` in the Cloud reasoning control, then resubmit the same request -with `[xhigh-confirmed]`. Do not begin the work at `high`, and do not claim the runtime changed. When -the marker is present, treat it as the user's confirmation that `xhigh` was selected and proceed. -Requests classified as `high` or lower proceed without this gate. +Use the selected setting. Recommend higher effort only for a concrete unresolved risk, such as +ambiguous security boundaries, concurrency or irreversible changes. Ordinary inspection, policy +editing and implementation do not require an Extra High startup ceremony. A task's phase label or +size alone does not justify changing the user's choice. -Reasoning effort is a budget in the same way verification is a budget, and it is misspent the same -way — by defaulting to the maximum instead of matching the spend to the risk. **Scale effort to how -expensive the mistake is to undo (irreversibility × branching factor), never to the phase label.** -"Plan high, build lower" is a good default, not a rule; it is wrong often enough that it must be -chosen deliberately rather than assumed. +Before substantial work, give a short plan proportionate to uncertainty and consequences. Name +files/interfaces and the checks that will establish the result. Do not invent a plan/build effort +split or claim telemetry that the host does not expose. -**Why it is a good default.** Planning errors compound and implementation errors stay local: a wrong -approach throws away the build, a wrong identifier is one edit. A plan is also a few thousand output -tokens against a build's many long turns, so effort is cheapest exactly where it has the most leverage. - -**The mechanism that makes it work — do not skip this part.** A lower-effort build only succeeds -against a plan concrete enough to execute: named files, named symbols, ordered steps, and the gate -that will prove it. Downgrading the build against a vague plan does not save effort, it relocates the -thinking into the expensive phase. If the plan cannot name those things, the build is not eligible for -the downgrade. - -| Situation | Plan | Build | -| ---------------------------------------------------------------------------------------------- | ----------- | ----------- | -| Architecture, Supabase migrations/RLS, RAG ranking surfaces, auth/privacy, ingestion contracts | xhigh | high | -| Ordinary feature or UI work with a clear shape | high | medium–high | -| Mechanical and fully specified — ledger append, docs edit, rename (not dependency maintenance) | low or skip | medium | -| Debugging an unknown failure | low | high | - -**Where the default inverts and the build needs more than the plan.** These are plan-light and -execution-heavy; treating them as plan-heavy spends the budget in the wrong place: - -- **Debugging.** The plan is "find why X fails." The real reasoning is hypothesis-forming over local - runtime, logs, and repro state during the build — hosted providers still need explicit confirmation. -- **Constraint-dense implementation.** A one-sentence plan whose edit must simultaneously satisfy - button wiring, design tokens, the one-composer rule, unlayered CSS, and the tap-target and - phone-chrome contracts. Holding all of it at once is the hard part, not deciding what to do. -- **Areas where training data is stale.** Next 16 is the standing case. Effort does not repair a wrong - prior — reading `node_modules/next/dist/docs/` does. Raising effort instead of reading is itself the - failure mode. - -**Constrain xhigh planning output, not just its effort.** Extra-high planning over-produces: -alternatives, contingency branches, and surveys that are never used, paid for twice — once generating -and once reading. Ask for the chosen approach, the files, and the gate, not a survey. - -**State the split before non-trivial planning work.** One line before starting: plan effort, build -effort, and the risk that justifies them. It is cheap, it makes a wrong allocation visible while it is -still free to change, and it stops the blanket default from being applied silently. +For authorised subagents, use [current-tool agent allocation](smart-agent-allocation.md). An +explicit specialist programme may retain its own effort/review/evidence contract; keep that +contract scoped to the programme rather than applying it to all Cloud tasks. diff --git a/docs/agents/shared-context-and-memory.md b/docs/agents/shared-context-and-memory.md index 001b9a7b36..e82bb9e0b6 100644 --- a/docs/agents/shared-context-and-memory.md +++ b/docs/agents/shared-context-and-memory.md @@ -24,6 +24,6 @@ If a memory route is missing, inaccessible or empty, disclose that state and con ## Check the actual host -Cloud sessions use the selected repository revision and their own tools, access, runtime and account settings. Personal Windows paths, desktop plugins, local services, credentials and OAuth sessions do not automatically transfer. Repository guidance cannot install a connector or grant permissions. Check the current branch/commit and actual callable tools before relying on them. Preserve existing project setup, dependency, raw-environment and provider gates. Use supported account settings for authenticated connectors; never copy secrets into tracked files or bypass setup-only secret boundaries. +Cloud sessions use the selected repository revision and their own tools, access, runtime and account settings. Personal Windows paths, desktop plugins, local services, credentials and OAuth sessions do not automatically transfer. Repository guidance cannot install a connector or grant permissions. Check the current branch/commit and actual callable tools before relying on them. Follow [the current Cloud policy](codex-cloud-environment.md); preserve applicable dependency, provider and selected-environment setup/raw-boundary gates. Use supported account settings for authenticated connectors; never copy secrets into tracked files or bypass setup-only secret boundaries. To verify behaviour, use a small read-only synthetic task on the intended revision. Record native loading evidence where exposed, files actually opened, tool capabilities, scenario answers and host limitations. Observe decisions about a stale memory, wrong project, unavailable service, conflicting approval and context handoff. File equality and an agent saying it complied are weaker than observed actions; neither proves universal compliance or application deployment. diff --git a/docs/agents/smart-agent-allocation.md b/docs/agents/smart-agent-allocation.md index 1a6f66cb59..00305fc0da 100644 --- a/docs/agents/smart-agent-allocation.md +++ b/docs/agents/smart-agent-allocation.md @@ -8,26 +8,23 @@ clinical, privacy, provider and publication requirements take precedence. ## Choose the route for the work -Use the least costly supported route with sufficient capability for the difficulty, -uncertainty and consequences. These are starting points, not quotas or a claim of -current hosted availability: +Use the least costly currently supported route that can handle the task's uncertainty and +consequences. Inspect the live tool schema/model inventory before choosing; do not copy model IDs +from an older session, pin a model family as the permanent controller, or invent unavailable effort +controls. The user's main-chat selection stays unchanged. -- Luna (`gpt-5.6-luna`) / Medium: mechanical edits, inventories and straightforward - static checks. Terra (`gpt-5.6-terra`) / Medium: bounded low-risk analysis or - mechanical work where it is a better fit. -- Sol (`gpt-5.6-sol`) / Medium: well-defined implementation with settled interfaces. -- Sol / High: complex shared components, debugging across components and independent - review of substantive changes. -- Astra (`gpt-6-astra`) / High: architecture, ambiguous clinical/privacy/security - boundaries, concurrency, evidence contracts and difficult integration decisions. -- Astra / Extra High (`xhigh`): demonstrated unresolved reasoning problems or - required formal review. It is not the default for every task or final check. +- Mechanical edits and bounded inventories can use a lighter supported route. +- Settled implementation and routine review need a capable supported coding/review route. +- Architecture, ambiguous security/privacy boundaries, concurrency and evidence contracts need + sufficient reasoning and independent review for the risk. +- Escalate for a demonstrated unresolved problem or an explicit specialist requirement, rather + than automatically for every final check. -Use other supported effort levels when justified. Astra High is the preferred -controller for substantial programmes; preserve the user's selected main-chat -model and effort. Never pin them in the repository configuration. The -[reasoning calibration and Cloud confirmation requirements](codex-reasoning-effort.md) -remain binding; a lighter starting route cannot lower a required floor. +These are allocation guidelines, not proof of availability or a startup gate. Follow +[reasoning and confirmation guidance](codex-reasoning-effort.md); preserve explicitly selected +specialist programme review and evidence requirements. If a programme names an unavailable route, +report that specific capability gap rather than silently lowering its floor or imposing it on +unrelated tasks. ## Dispatch and coordinate @@ -85,4 +82,5 @@ claim documentation or static checks prove hosted execution. Official references: [instruction discovery](https://developers.openai.com/codex/guides/agents-md) and [subagent configuration](https://developers.openai.com/codex/subagents). The session's actual tools remain the authority for available controls. See the -[Cloud environment contract](../codex-cloud.md) for environment and provider limits. +[current Cloud policy](codex-cloud-environment.md) for environment and provider limits; its linked +legacy bootstrap runbook applies only when explicitly selected. diff --git a/docs/agents/verification-gates.md b/docs/agents/verification-gates.md index f6da3b1ed1..22c69cb6b9 100644 --- a/docs/agents/verification-gates.md +++ b/docs/agents/verification-gates.md @@ -175,6 +175,7 @@ Non-negotiable, and the reason the saving is allowed at all: UI jobs are skipped — the planner says so and tells you to consider `--full`, because there the local run is the only browser evidence there will be. -Cloud sessions are told this at SessionStart by `.claude/hooks/testing-policy.sh`, which -is read-only, exits 0 on every path, and states the reporting rules alongside the -commands. +Claude Code sessions with the configured SessionStart hook receive this guidance from +`.claude/hooks/testing-policy.sh`, which is read-only, exits 0 on every path, and states the +reporting rules alongside the commands. Other hosts must use this policy directly; the +repository hook does not prove guidance was loaded in those sessions. diff --git a/docs/archive/cloud-session-state-2026-10-16/README.md b/docs/archive/cloud-session-state-2026-10-16/README.md new file mode 100644 index 0000000000..52fd46a777 --- /dev/null +++ b/docs/archive/cloud-session-state-2026-10-16/README.md @@ -0,0 +1,19 @@ +# Archived Cloud session state + +Archived on 2026-10-16 from `.claude/cloud-profile/memory/`. These records describe earlier +sessions and workstation state. They are historical evidence, not active instructions, current +task status or current-host configuration. The profile applier does not copy this directory. + +The original record bytes, dates, session provenance and links are preserved: + +- [Workstation project location](dev-drive-project-location.md) +- [Database remediation coordination](db-remediation-coordination-state.md) +- [RAG programme coordination](rag-programme-coordination-state.md) +- [Ingestion review remediation](ingestion-review-remediation-state.md) +- [Lighthouse CLS incident](lighthouse-mobile-root-cls-flake.md) +- [Workstation test incidents](local-test-failures-windows.md) +- [Workstation chat-import tooling](codex-chat-import-tooling.md) + +Use the current repository instructions and verified task evidence before acting. This archive +does not authorize provider calls, reconciliation, cleanup, publication or deployment. Removing +these seeds from the repository does not remove or overwrite memories in an existing host. diff --git a/.claude/cloud-profile/memory/codex-chat-import-tooling.md b/docs/archive/cloud-session-state-2026-10-16/codex-chat-import-tooling.md similarity index 100% rename from .claude/cloud-profile/memory/codex-chat-import-tooling.md rename to docs/archive/cloud-session-state-2026-10-16/codex-chat-import-tooling.md diff --git a/.claude/cloud-profile/memory/db-remediation-coordination-state.md b/docs/archive/cloud-session-state-2026-10-16/db-remediation-coordination-state.md similarity index 100% rename from .claude/cloud-profile/memory/db-remediation-coordination-state.md rename to docs/archive/cloud-session-state-2026-10-16/db-remediation-coordination-state.md diff --git a/.claude/cloud-profile/memory/dev-drive-project-location.md b/docs/archive/cloud-session-state-2026-10-16/dev-drive-project-location.md similarity index 100% rename from .claude/cloud-profile/memory/dev-drive-project-location.md rename to docs/archive/cloud-session-state-2026-10-16/dev-drive-project-location.md diff --git a/.claude/cloud-profile/memory/ingestion-review-remediation-state.md b/docs/archive/cloud-session-state-2026-10-16/ingestion-review-remediation-state.md similarity index 100% rename from .claude/cloud-profile/memory/ingestion-review-remediation-state.md rename to docs/archive/cloud-session-state-2026-10-16/ingestion-review-remediation-state.md diff --git a/.claude/cloud-profile/memory/lighthouse-mobile-root-cls-flake.md b/docs/archive/cloud-session-state-2026-10-16/lighthouse-mobile-root-cls-flake.md similarity index 100% rename from .claude/cloud-profile/memory/lighthouse-mobile-root-cls-flake.md rename to docs/archive/cloud-session-state-2026-10-16/lighthouse-mobile-root-cls-flake.md diff --git a/.claude/cloud-profile/memory/local-test-failures-windows.md b/docs/archive/cloud-session-state-2026-10-16/local-test-failures-windows.md similarity index 100% rename from .claude/cloud-profile/memory/local-test-failures-windows.md rename to docs/archive/cloud-session-state-2026-10-16/local-test-failures-windows.md diff --git a/.claude/cloud-profile/memory/rag-programme-coordination-state.md b/docs/archive/cloud-session-state-2026-10-16/rag-programme-coordination-state.md similarity index 100% rename from .claude/cloud-profile/memory/rag-programme-coordination-state.md rename to docs/archive/cloud-session-state-2026-10-16/rag-programme-coordination-state.md diff --git a/docs/archive/codex-cloud-design-status-semantics-2026-10-16.md b/docs/archive/codex-cloud-design-status-semantics-2026-10-16.md new file mode 100644 index 0000000000..35f4f1db4b --- /dev/null +++ b/docs/archive/codex-cloud-design-status-semantics-2026-10-16.md @@ -0,0 +1,292 @@ +# Archived implementation prompt — codex-cloud-design-status-semantics + +Archived 2026-10-16 from `docs/prompts/codex-cloud-design-status-semantics.md` at `e0c62df33f6a6be32540092deab783807c9563a0` after the implementation was verified present. +This is historical task evidence, not current instructions or permission to resume work. +Use the [retirement notice](../prompts/codex-cloud-design-status-semantics.md), current repository policy and a separately +approved current task. Relative Markdown links retain their targets because `docs/archive/` +and `docs/prompts/` have the same directory depth. + +--- + +# Codex Cloud prompt — design-system clinical status semantics + +Copy the complete prompt below into a new Codex Cloud task for the **PsychSift (`BigSimmo/PsychSift`)** +repository. This task is deliberately limited to PR 1 of the +[design-system/live-design convergence programme](../plans/design-system-live-convergence-plan.md#pr-1--clinical-status-semantics-and-baseline-provenance). +Do not combine it with the later interaction, edge, motion, density, or shared-home tranches. + +--- + +## Perfected prompt + +You are the implementation owner for **PR 1 — clinical status semantics and baseline provenance** +in the PsychSift (`BigSimmo/PsychSift`) repository. Complete the smallest safe, independently revertible +change that removes the currently recorded colour-only status meaning and status-coloured numeral. +Also resolve the screenshot-provenance contradiction without ever representing automated review as +human approval. + +### Required outcomes + +1. Classify every currently flagged occurrence as clinical state, category identity, decoration, or + false positive, based on the rendered behavior and source context—not on the detector name alone. +2. For every real state, provide persistent visible text and a second non-colour visual channel such + as icon shape, border style, or pattern. An `aria-label` alone is not a repair for sighted users. +3. Keep numerals semantically neutral; show warning/success/state meaning beside the value rather + than by colouring the number. +4. Reduce both design-contract metrics to zero without moving the same debt elsewhere: + `colourOnlyStatusIndicators: 3 → 0` and `statusColouredNumerals: 1 → 0`. +5. Make screenshot provenance truthful. Never invent a reviewer, review time, approval, or evidence. + If no genuine human disposition is available, use only a schema-supported pending state. If the + schema or gate cannot represent that truth safely, do not change provenance; report the exact + blocker in the handoff for a human reviewer. +6. Preserve clinical wording, source governance, conservative missing/stale-data behavior, existing + component contracts, responsive behavior, and assistive-technology names. + +### Authoritative starting evidence + +Read these before editing: + +- Repository rules: [`AGENTS.md`](../../AGENTS.md) and [`CLAUDE.md`](../../CLAUDE.md). +- Cloud safety and browser readiness: [`docs/codex-cloud.md`](../codex-cloud.md). +- The bounded programme and PR 1 exit criteria: + [`docs/plans/design-system-live-convergence-plan.md`](../plans/design-system-live-convergence-plan.md). +- Design-system entry point and contracts: + [`docs/design-system/README.md`](../design-system/README.md), + [`SPEC.md`](../design-system/SPEC.md), [`TOKENS.md`](../design-system/TOKENS.md), + [`COMPONENTS.md`](../design-system/COMPONENTS.md), + [`ADOPTION.md`](../design-system/ADOPTION.md), and [`GATES.md`](../design-system/GATES.md). +- Current machine-readable debt: + [`scripts/design-system-contract-baseline.json`](../../scripts/design-system-contract-baseline.json). +- Current flagged owners: + [`src/components/calculators/calculator-ui.tsx`](../../src/components/calculators/calculator-ui.tsx), + [`src/components/ui-primitives.tsx`](../../src/components/ui-primitives.tsx), and + [`src/components/clinical-dashboard/visual-evidence.tsx`](../../src/components/clinical-dashboard/visual-evidence.tsx). +- Screenshot contract and current provenance: + [`docs/design-system/adoption-contract.json`](../design-system/adoption-contract.json) and + [`tests/__screenshots__/linux/provenance.json`](../../tests/__screenshots__/linux/provenance.json). +- Likely proof owners: + [`tests/ckb-v2-token-contract.test.ts`](../../tests/ckb-v2-token-contract.test.ts), + [`tests/design-token-contract.test.ts`](../../tests/design-token-contract.test.ts), + [`tests/design-system-adoption.test.ts`](../../tests/design-system-adoption.test.ts), and + [`tests/ui-accessibility.spec.ts`](../../tests/ui-accessibility.spec.ts). +- Review and PR policy: [`docs/codex-review-protocol.md`](../codex-review-protocol.md) and + [`.github/pull_request_template.md`](../../.github/pull_request_template.md). + +Treat the paths and counts above as starting evidence, not immutable truth. Re-measure at the task +HEAD before editing. Inspect actual definitions, consumers, tests, detector logic, and generated-file +ownership. Do not manually edit a generated baseline if a repository update command owns it. + +### Authority and hard safety boundaries + +- Work in the repository's **offline Cloud profile**. Require `CODEX_CLOUD=1`, + `CODEX_CLOUD_ACCESS_PROFILE=offline`, `RAG_PROVIDER_MODE=offline`, + `NEXT_PUBLIC_DEMO_MODE=true`, and `PLAYWRIGHT_OFFLINE_MODE=true`. +- Do not call OpenAI, Supabase, Railway, GitHub/GitLab APIs, hosted CI, analytics, email, or any other + provider. Do not access production/staging systems or live data. Do not deploy, migrate, reindex, + rotate credentials, print secrets, or create `.env*` files. +- Do not install or update dependencies, switch package managers, loosen engines, or create a new + lockfile. Use the installed Node 24/npm 11 toolchain and the existing `package-lock.json`. +- Do not reset, clean, stash, rebase, force-push, delete branches, overwrite unrelated work, or + weaken tests/contracts to make a check pass. +- You may edit only the three flagged owners, the smallest directly affected tests, and generated + design-contract artifacts that the repository's normal update path demonstrably owns. + `tests/__screenshots__/linux/provenance.json` is permitted only under the truthfulness rule above. + Stop before touching unrelated routes or broad shared foundations. +- A local task commit is authorized after verification. A push or pull request is **not** authorized + by this prompt. Use native Cloud publication controls only if a separate current-task instruction + explicitly authorizes publication; never introduce shell tokens as a workaround. + +### Phase 0 — prove Cloud isolation before any write + +1. Inspect `git status --short --branch`, `git rev-parse HEAD`, upstream/ahead/behind state, relevant + recent commits, and `git worktree list --porcelain`. Check for an active merge, rebase, cherry-pick, + or revert and for repo-owned install/test/build/server processes that could race this work. +2. The checkout must be clean. If it contains any staged, unstaged, or untracked content, stop and + report it; do not absorb, move, stash, delete, or overwrite it. +3. If detached or on `main`, `master`, `develop`, or `release/*`, create + `codex/cloud-design-status-semantics`. Otherwise retain the supplied task branch. +4. Run the repository isolation verifier exactly as documented by + [Prompt Perfector's repository workflow](../../.agents/skills/prompt-perfector/references/repository-workflow.md): + + ```bash + test "${CODEX_CLOUD:-}" = "1" + test -z "$(git status --porcelain --untracked-files=all)" + branch="$(git branch --show-current)" + repo="$(git rev-parse --show-toplevel)" + head="$(git rev-parse HEAD)" + node .agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs \ + --cloud --expected-repo "$repo" --expected-branch "$branch" --expected-head "$head" + ``` + + Proceed only if it emits `SAFE_TO_EDIT=true` and `PRECHECK_RESULT=SAFE`. Re-run it immediately + before the first edit. This verifies repository workflow isolation, not an OS-level sandbox. + +5. Confirm runtime/install/browser readiness without mutating dependencies: + + ```bash + node --version + npm --version + npm run check:codex-cloud + npm run check:installed-lock-parity + npm run check:playwright-browser-revision + ``` + + A browser-revision mismatch is an environment limitation, not permission to substitute a random + browser executable. Continue with static/DOM proof and hand browser evidence to matching CI/local + infrastructure as documented in `docs/codex-cloud.md`. + +### Phase 1 — inspect, classify, and lock the plan + +State one line before planning: **plan effort high; build effort medium-high; the risk is changing +clinical state meaning or falsely certifying visual evidence.** + +1. Read the repository skills for `plan`, `ui`, `clinical`, `test`, `review`, and `handover`; apply + only their relevant steps. Do not install new skills or invent a parallel workflow. +2. Because this is a review-sensitive task, run: + + ```bash + npm run ledger:lookup -- HEAD --scope "PR 1 clinical status semantics and baseline provenance" + ``` + + Follow the verdict and `docs/codex-review-protocol.md`. Do not hand-edit ledger records. + +3. Read the relevant installed Next.js 16 guide under `node_modules/next/dist/docs/` before changing + framework-facing UI behavior. Training-data assumptions are not evidence. +4. Run the repository workflows in evidence mode for the exact candidate paths: + + ```bash + npm run workflow:flightplan -- --write-evidence --files \ + src/components/calculators/calculator-ui.tsx,src/components/ui-primitives.tsx,src/components/clinical-dashboard/visual-evidence.tsx,scripts/design-system-contract-baseline.json,tests/__screenshots__/linux/provenance.json + npm run workflow:design-sweep -- --write-evidence + npm run workflow:clinical-proof -- --write-evidence + ``` + +5. Record the measured global counts and per-path counts before editing. Inspect the contract scanner + that produces them. For each of the four flagged occurrences, write a compact classification table: + owner/symbol, rendered state, user-visible meaning, clinical risk, classification, proposed + non-colour channel, and proof owner. +6. Inspect empty, missing, stale, loading, error, and success variants. Specifically try to disprove + that a visually positive state could imply “safe/current” when evidence is absent or stale. +7. Inspect the provenance schema, validation scripts, Git history for the record, and candidate hashes. + The current text may be contradictory; it is not proof that a human reviewed the images. +8. Before implementation, produce a short executable plan naming exact symbols, tests, generated + artifacts, commands, and rollback. If classification requires a clinical/product decision, or the + safe repair exceeds this tranche, stop with the evidence and one concise blocking question. + +### Phase 2 — implement the smallest complete repair + +1. Add or adjust the smallest regression tests first where practical. Tests must prove visible + non-colour differentiation and accessible names; they must not merely snapshot implementation + classes. +2. Prefer existing design-system primitives, semantic tokens, icons, and copy conventions. Do not add + new dependencies, raw palette values, one-off token aliases, decorative motion, or broad component + abstractions. +3. For each real status: + - keep explicit persistent text; + - add a second visual channel that survives authored-colour removal and forced colours; + - keep icon-only decoration hidden from assistive technology when adjacent text already names it; + - preserve keyboard/focus behavior and at least the existing hit target; + - preserve conservative wording for unknown/missing/stale data. +4. For the flagged numeral, remove status semantics from the number itself and render the state beside + it. Ensure reading order and the accessible name remain natural. +5. For a detector false positive, improve detector/test precision only with a focused proof that the + rendered UI never conveys state by colour. Do not add an allowlist merely to hit zero. +6. Update generated contract artifacts only through the existing repository generator/update path + identified during discovery. Confirm both the global count and each original owner's count fall; + search the full diff for relocated equivalents. +7. For provenance, choose exactly one truthful path: + - if genuine human review evidence is supplied in the task context, record only that real identity, + timestamp, source HEAD, and schema-valid disposition; + - otherwise, change the record to a schema-valid pending/non-human state only if repository + contracts explicitly support it and tests prove it; + - otherwise leave the file unchanged and make human disposition an explicit local handoff gate. + Never regenerate or adopt images merely to resolve metadata wording. +8. Reinspect the entire task diff for clinical copy drift, false reassurance, token bypasses, secrets, + unrelated formatting, generated noise, and unintended public contract changes. + +### Phase 3 — adversarial UI proof + +For browser work, first run `npm run ensure`, use only the printed URL, and verify +`/api/local-project-id` identifies this repository. Do not assume a port or attach to an unknown +server. Do not leave a watcher or server running after the task. + +Exercise every changed state at the smallest relevant desktop and phone viewport, including default, +dark, forced-colours, reduced-motion, monochrome (authored colours removed/grayscale), and print. +Check keyboard navigation, visible focus, screen-reader names/reading order, 200% zoom/reflow, loading, +empty, error, stale, and success states where the component supports them. The status must remain +distinguishable when all authored colours are removed, and the UI must not imply that missing or stale +clinical evidence is safe/current. + +Capture candidate evidence only when it helps review. Do not overwrite canonical screenshots or mark +them human-approved. If browser readiness fails, preserve the exact diagnostic and create a precise +local/CI handoff rather than weakening the matrix. + +### Phase 4 — risk-scaled verification + +Run heavy commands sequentially. Start narrowly and add a gate only when it covers a distinct plausible +failure. Determine exact focused selectors from the repository rather than inventing script names. + +Minimum expected ladder: + +1. Directly affected unit/DOM tests for the changed owners and contract scanner. +2. `npm run check:design-system-contract` — proves metric and adoption/design-sync contracts. +3. The focused accessibility/browser journey from `tests/ui-accessibility.spec.ts`, if the matching + locked Chromium revision is available. +4. `npm run check:production-readiness` because the change affects clinical status presentation. +5. Inspect selection with: + + ```bash + npm run verify:pr-local -- --dry-run --files + ``` + + Then run non-dry-run `npm run verify:pr-local` once for PR-ready local handoff if selected policy + requires it. Escalate to `npm run verify:ui` only if shared UI foundations changed or the selector + requires it; do not stack it automatically after focused browser proof. + +6. Run `npm run format`, inspect its diff, include intended formatting, then run + `npm run docs:check-links`, `npm run docs:check-scripts`, and `git diff --check` when documentation + changed. + +When a check fails, reduce it to the smallest reproducer, classify it as change-caused, pre-existing, +flaky, environmental, or provider-blocked, fix only change-caused failures, and rerun the invalidated +proof. Never claim an unrun or receipt-reused check was fresh; identify receipt reuse explicitly. + +### Phase 5 — review, commit, and local handoff + +1. Review the final diff under `docs/codex-review-protocol.md`, findings first and severity-ranked. + Resolve all change-caused high-confidence findings. Append the review record only through + `npm run ledger:append` with the actual full 40-character HEAD, scope, outcome, and checks. +2. Confirm final status contains only coherent task files and allowed workflow/ledger evidence. Exclude + `.local`, caches, logs, browser output, dependencies, secrets, and unrelated artifacts. +3. Create one clear local commit. Do not push or publish unless separately authorized in the active + Cloud task. Report the commit SHA and whether the branch has an upstream. +4. Produce this handoff packet: + - outcome and changed symbols/files; + - before/after global and per-path contract counts; + - classification and repair for each original occurrence; + - adversarial matrix with pass/fail/not-run and evidence path for every state/mode; + - exact commands and decisive results, including reused receipts; + - provenance disposition, evidence basis, and any human-approval gate still open; + - branch, worktree, commit, push, and PR state; + - provider/live checks not run and why; + - remaining risks and the single smallest next action. + +For the **local handoff**, give a copy/paste command sequence that begins by checking out the reported +branch/commit, confirms Node 24/npm 11 and lock parity, runs `npm run ensure`, verifies the printed +project identity URL, and executes only the browser modes that Cloud could not prove. Name the exact +routes, viewports, states, and expected visible/non-colour outcomes. If human screenshot disposition is +still required, list candidate paths and hashes and instruct the human to approve or reject them through +the repository's documented adoption workflow—never by directly editing “approved” metadata. + +### Stop conditions + +Stop and report evidence instead of guessing if: the checkout is dirty or non-isolated; runtime or +lock parity is invalid; a provider/live action becomes necessary; a real clinical/product decision is +required; a genuine reviewer identity is unavailable; the provenance schema cannot represent the +truth; the required repair expands into another convergence tranche; the matching browser cannot be +made available without an install/environment change; or unrelated failures prevent meaningful proof. + +Completion means the bounded implementation is locally committed, all available risk-relevant checks +are honestly reported, both targeted metrics are zero without debt relocation, clinical meaning is +preserved under adversarial modes, and any inherently human or provider-backed gate is handed off +explicitly. It does **not** mean claiming perfection, human approval, deployment, or production proof. diff --git a/docs/archive/codex-cloud-legacy-profile-2026-10-16.md b/docs/archive/codex-cloud-legacy-profile-2026-10-16.md new file mode 100644 index 0000000000..4f60d81c27 --- /dev/null +++ b/docs/archive/codex-cloud-legacy-profile-2026-10-16.md @@ -0,0 +1,672 @@ +# Archived Codex Cloud legacy profile runbook + +> Archived on 2026-10-16. These are the recorded instructions for an explicitly selected legacy +> repository-managed profile. They are not active startup policy for other Cloud tasks. Start with +> [the current Cloud guide](../codex-cloud.md) and [active policy](../agents/codex-cloud-environment.md). +> Historical account/connector assumptions below are not current-host evidence. + +_Updated 2026-10-16 — scope the legacy bootstrap to explicitly selected environments; operational evidence retains its original dates._ + +## Scope and selection + +This is the runbook for the repository-managed legacy offline/connected Cloud setup. It is not the +default configuration for every Cloud environment. Use [the current Cloud policy](../agents/codex-cloud-environment.md) +first. The instructions below apply when the owner or selected environment explicitly chooses +this setup, or when testing/repairing it; a Linux runtime, host `connected` label or `CODEX_CLOUD=1` +alone does not select it. General tasks must not be blocked solely by missing legacy profile values +or by applying this runbook's raw probe to a different host-managed environment. + +The account-tier statements, connector workarounds and pinned bootstrap versions below describe +this setup's recorded operating assumptions. Verify current host capabilities rather than treating +those statements as live account facts. Its executable checks remain unchanged and still govern +acceptance of this legacy setup. A failed check is not a pass in any environment. + +> This Bash setup is for Linux-based Codex Cloud environments. Codex Desktop on +> Windows uses `npm run setup:codex-worktree`; pointing Desktop at this Cloud +> script starts WSL outside the Windows worktree and cannot provision it. + +This repository supports reproducible Codex Cloud work with Node 24, npm 11, locked +development dependencies, Deno 2, Python/OCR tooling, and the Chromium, Firefox, and +WebKit Playwright browser matrix. The repository setup can prepare and validate the +container. It cannot grant GitHub installation permissions, workspace RBAC, agent-network +policy, or provider-account permissions; those are configured in Codex and each provider. + +For agent work, the root `AGENTS.md` points to the portable +[smart agent allocation policy](../agents/smart-agent-allocation.md). Apply it whenever +agents are used, using only the model, effort and concurrency controls exposed in +the current Cloud task. Publication to the selected branch and a fresh-task check +are required before claiming Cloud adoption; local instructions alone do not +establish it. This policy neither changes the main-chat model nor grants provider +access. Its Cloud delivery section defines fallback and evidence requirements. + +## Issues this setup resolves + +- Codex Cloud had no tracked setup or maintenance command on `main`. +- Two incompatible Cloud checkers existed only in dirty worktrees. +- The prompt-perfector bootstrap parsed the Windows task-start output incorrectly and + rejected Cloud's legitimate single primary checkout. +- Tool repair covered Node dependencies but not Deno, OCR, Python, or browsers. +- Provider-variable clearing was incomplete and the Cursor Cloud guidance was easy to + mistake for Codex Cloud guidance. +- GitHub connector access, shell Git credentials, network access, and provider credentials + were treated as one permission even though they are separate controls. +- The Codex Cloud CLI can emit a root `error.log` containing account/session metadata; the + repository now ignores that exact diagnostic path. + +## Create the environment + +In Codex environment settings, create an environment for `BigSimmo/PsychSift` using the +official [Cloud environments](https://learn.chatgpt.com/docs/environments/cloud-environment) +contract: + +| Setting | Value | +| --------------------- | ------------------------------------------------------------------------------------------- | +| Repository | `BigSimmo/PsychSift` | +| Base image | Default universal image | +| Node version | `24` | +| Python version | `3.12` | +| Setup command | `bash scripts/setup-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh` | +| Maintenance command | `bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh` | +| Environment variables | Use the complete profile below | + +Keep agent internet access off for this repository's ordinary Cloud environments. Package +installation happens during setup; ordinary structure-only work, including the RAG +decomposition prompt below, remains offline. The appended command-shim installer is required: +it makes every normal `node`, `npm`, and `npx` invocation load the generated sanitized +profile before starting Node. It is idempotent and builds the executable path from `nvm version` +instead of resolving through `PATH`, so maintenance cannot accidentally wrap an earlier wrapper. + +The setup command fails if the required Cloud toolchain cannot be installed. It intentionally does +not install Railway CLI: hosted Railway access comes from the authenticated workspace app, and the +CLI postinstall downloads a separate binary that may be blocked in the Cloud setup network. It pins +Codex CLI `0.147.0`, reviewed on 2026-08-22. OpenAI's +[official Codex CLI guide](https://learn.chatgpt.com/docs/codex/cli) supports Linux installation; +the npm package is used here so maintenance can verify an exact version without running an +unversioned installer. Set +`CODEX_CLOUD_SKIP_BROWSER_INSTALL=1` only for an explicitly source-only environment; that +environment is not full browser-ready. + +### Cloud compared with local development + +Cloud parity covers repository-owned tooling, not every capability of a developer workstation or +CI runner: + +| Boundary | What the repository provides | +| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Repository-owned setup | Pinned Node/npm, a lockfile-only dev install, Git hooks, Deno, the hashed Python/OCR environment, browser installation, and static/runtime checks. `--prefer-offline` reuses cached npm data when available but may still fetch missing packages; it is not offline mode. `--no-audit` and `--no-fund` keep provisioning deterministic and quiet, while dependency auditing remains a separately scoped CI gate. | +| Host/platform capability | GitHub installation permissions, callable connector tools, network policy, Docker or privileged operations, capacity, caches, and persistence are supplied by the selected host. Repository setup cannot grant or guarantee them; verify the needed capability in the current task. | +| Intentional security boundary | Cloud setup does not copy `.env*` files, credentials, OAuth sessions, provider URLs, mutable provider configuration, browser state, or local MCP authentication. Keep those differences from Desktop/operator workflows. | +| Optional or external acceptance | `CODEX_CLOUD_SKIP_BROWSER_INSTALL=1` creates a source-only environment. Provider-backed, production-like, physical-device, and physical Safari/PWA checks remain separate and require the authorization and environment described below. | + +See [Access profiles](#access-profiles), [GitHub access](#github-access), +[Playwright browser readiness](#playwright-browser-readiness-255), and [Acceptance](#acceptance) +for the authoritative task-level checks. Never repair a host capability gap by copying credentials, +tokens, provider URLs, OAuth state, `.env*` files, or mutable provider configuration into the Cloud +agent shell. + +### Playwright browser readiness (#255) + +Browser gates need the **locked** Playwright package and a Chromium revision that matches +`node_modules/playwright-core/browsers.json`. When a remote/Cloud image forces +`PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers` with `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` and that +tree only ships an older revision (for example 1194 while the lock expects 1234): + +1. Run `npm run check:installed-lock-parity` and `npm run check:playwright-browser-revision`. +2. Do **not** point `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` at the mismatched shell. +3. Delegate browser proof to CI Production UI, or refresh the environment image / install matching + browsers into a managed cache, or unset the container browser env vars so + `npx playwright install` can populate the managed path. + +`CODEX_CLOUD_SKIP_BROWSER_INSTALL=1` remains source-only: that environment is not browser-ready. + +## Access profiles + +### Offline (default) + +Use this for refactors, static checks, mocked tests, and all work that expressly forbids +providers: + +```text +CODEX_CLOUD=1 +CODEX_CLOUD_ACCESS_PROFILE=offline +RAG_PROVIDER_MODE=offline +NEXT_PUBLIC_DEMO_MODE=true +PLAYWRIGHT_OFFLINE_MODE=true +``` + +The generated shell profile removes known OpenAI, Supabase, Railway, GitHub/GitLab, +database, CI-trigger, test-user, and emergency-PAT variables. This prevents an unrelated +Cloud task from silently becoming provider-backed. + +Set all five offline values in the environment UI. The setup also writes the generated +profile to `.bashrc`, `.profile`, and `.bash_profile`. Covering `.bash_profile` is required: +Bash stops login-profile discovery at the first matching file, so an existing `.bash_profile` +can otherwise prevent `.profile` from running. Offline mode values are forced after inherited +platform values so stale `auto`/`false` settings cannot outrank the repository contract. +Do not manually source the profile as an acceptance workaround: start a fresh task and invoke +the direct `npm run check:codex-cloud` commands below. The tracked command shims make those +ordinary Node commands load the profile themselves. + +### Connected (explicit opt-in) + +Use `CODEX_CLOUD_ACCESS_PROFILE=connected` only in a separate environment whose tasks are +expected to call named providers. Configure the smallest domain/method allowlist and the +least-privileged credentials for those tasks. Never commit credentials or print their +values. The setup script does not call providers and does not prove provider authorization. + +Create or select a separate environment named `PsychSift - connected` for `BigSimmo/PsychSift`, +then set only these ordinary environment variables in +[Codex environment settings](https://chatgpt.com/codex/settings/environments): + +```text +CODEX_CLOUD=1 +CODEX_CLOUD_ACCESS_PROFILE=connected +RAG_PROVIDER_MODE=offline +NEXT_PUBLIC_DEMO_MODE=true +PLAYWRIGHT_OFFLINE_MODE=true +``` + +Keep OpenAI disabled unless a later task explicitly authorizes it. Do not add provider keys, +tokens, database URLs, service-role values, E2E credentials, or `ALLOW_PROVIDER_TESTS` to this +environment. The generated agent profile removes the complete provider-variable inventory in +both access profiles. Connected access records authorization intent and keeps the GitHub boundary +explicit, but it does not register hosted MCP apps, expose raw credentials to the shell, or guarantee +that every GitHub capability appears as a direct agent tool. For ordinary Cloud task publishing, +use the native Cloud diff/PR controls and verify the resulting GitHub branch and PR link. A +metadata-only `make_pr` response is not publication evidence. If a requested GitHub API is not +available, report that limitation rather than using shell credentials as a workaround. + +Codex Cloud secrets and ordinary environment variables have different exposure and lifecycle +properties. This repository has no mechanism that promotes setup-only OpenAI, Supabase, E2E, +or Railway secrets into the agent phase. Do not persist them in profiles, repository files, +`.env*`, caches, logs, or ordinary variables as a workaround. A `connected` profile models +explicit authorization and relaxes the offline-mode assertions, but it does not create +credentials. Authenticated Supabase/E2E tests use the protected manual workflow documented +below. Run other provider checks locally/operator-side or through a separately approved, +least-privilege provider mechanism. + +## GitHub access + +The GitHub connector is the supported repository/PR path. Follow the official +[Codex GitHub setup](https://help.openai.com/en/articles/11390924), authorize the +`BigSimmo/PsychSift` repository, and ensure the installation grants the user write access if +Cloud tasks must publish PRs. Repository discovery proves read access only. + +For an explicitly authorised GitHub task, use Cloud's authenticated GitHub integration +as the remote control plane. For ordinary Cloud work, publish through the native task +diff/PR controls and verify the returned GitHub branch and pull-request link. Do not infer +that GitHub is unavailable merely because `gh`, shell Git credentials, or a particular +direct agent tool are absent. The intended GitHub identity is `BigSimmo`. Use repository +write access for branch and pull-request publication; reserve administrator access for separately +approved operations. Some GitHub APIs, including review-thread or Actions management, may not be +exposed in every Cloud task. When the user explicitly authorises the shell fallback, configure it +only through an encrypted, setup-only `CODEX_CLOUD_GITHUB_PAT` environment secret and require the +live acceptance command below to pass before the task starts. Never place its value in a prompt, +repository file, ordinary environment variable, command argument, remote URL, log, or +documentation. + +GitHub connector permission is separate from credentials inside the agent shell. The connector, +native Push control, and GitHub UI remain the preferred Cloud publication and cleanup paths. The +explicit shell fallback necessarily leaves an authenticated `gh` credential helper available to +the agent, so it has a wider trust boundary than the connector. The setup secret itself +is still excluded from the agent environment and the generated shell profile. The live gate never +reads or prints the credential: it verifies the exact `BigSimmo` identity, `BigSimmo/PsychSift` +write permission, `repo`, `workflow`, `read:org`, and `gist` scopes, PR metadata/diff/check/comment/Actions +reads, review-thread reply and resolution schema, repository review-thread reads and resolve permission, +successful Actions job metadata, log access +through a body-free HTTP `HEAD` against a non-skipped job, the rerun command surface, the +credential-free origin, authenticated fetch, +and an ordinary feature-branch push through `--dry-run`. The probe alone disables local hooks so +the real-push verification suite cannot make setup hang; actual pushes retain those guards. It +uses an unguessable task-local probe name, checks that exact ref before and afterward, and fails if +any remote ref appears. +Each provider subprocess has a 30-second bound. Provider failures classified as transient (rate +limiting, GitHub 5xx, DNS/TLS, timeout, or connection reset) receive at most three attempts with +short exponential backoff. Authentication, permission, +schema, unsafe-origin, and branch-policy failures fail immediately rather than being hidden by retries. + +`bash scripts/delete-codex-cloud-branch-with-pat.sh ` is retained only for +an explicitly authorised operator running outside Codex Cloud. It rejects `CODEX_CLOUD=1`, +protected/invalid refs, and any origin other than the credential-free +`https://github.com/BigSimmo/PsychSift.git`. Never copy a PAT into a Cloud task, profile, checkout, +remote URL, cache, or log. + +Setup restores a missing `origin` to the credential-free URL +`https://github.com/BigSimmo/PsychSift.git`; it preserves an existing correct remote and fails +instead of overwriting a wrong or credential-bearing remote. When GitHub CLI authentication is +already available, setup asks `gh auth setup-git` to install its token-free helper command. It +never embeds a token or invents a PAT. It also fetches `origin/main`, stores the current task's merge +base outside the checkout, and exports +that exact 40-character SHA as `CODEX_CLOUD_EXPECTED_BASE_SHA` in subsequent agent shells. `git +ls-remote` and a dry-run push remain separate acceptance checks; if the connector does not expose +shell Git authentication, report that platform capability gap. + +Suggested GitHub acceptance task: + +```text +Read AGENTS.md and docs/codex-cloud.md. Create a task-specific branch, add one harmless +documentation-only line, commit it, and record its branch name and full 40-character HEAD SHA. +Use the Codex GitHub workflow to publish that exact existing branch and create a draft pull +request. Do not recreate, rename, amend, rebase, or rebuild the branch or commit. Do not merge. +Report whether repository clone, branch publication, and draft PR creation each succeeded, then +report the expected and published branch names and full HEAD SHAs and whether they match exactly. +If branch publication or draft PR creation succeeds, include its link. For each failed write, +report the failure and state that no link is available. Remove the draft branch/PR only after I +approve cleanup. +``` + +## Setup and maintenance + +For the native connector/operator path, configure the following complete commands in the +environment UI: + +```bash +bash scripts/setup-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh +``` + +```bash +bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh +``` + +For the explicitly authorised GitHub shell fallback, store the credential only as the encrypted +setup secret named `CODEX_CLOUD_GITHUB_PAT`, select `CODEX_CLOUD_ACCESS_PROFILE=connected`, and use +the tracked lifecycle wrapper below. Never substitute a literal credential. The token must carry +`repo`, `workflow`, `read:org`, and `gist`: `gh auth login --with-token` requires the latter two in +addition to the repository scope, while failed-job reruns require `workflow`. + +```bash +bash scripts/run-codex-cloud-github.sh setup +``` + +```bash +bash scripts/run-codex-cloud-github.sh maintenance +``` + +The repaired hosted environments use these commands when the checked-out branch contains the +wrapper, retaining the prior working inline commands for older branches. After this change is +merged, new `main` tasks adopt the tracked implementation automatically; opening a PR alone does +not change the scripts present on `main`. + +The final command is a fail-closed task admission gate, not an optional diagnostic. A cache hit, +accepted OAuth token, repository listing, or connected-environment label is insufficient by itself. +The wrapper validates authentication, identity and scopes before installing dependencies, suppresses +login output and shell tracing, and removes the setup-secret variable before invoking repository +setup. It deliberately uses `gh`'s standard credential store for this owner-authorized shell +capability; the credential must never be copied into repository files, shell profiles, remote URLs, +ordinary environment variables, or prompts. This is an explicit exception for GitHub shell +authentication, not authorization to persist OpenAI, Supabase, Railway or other provider secrets. +Maintenance reuses valid stored authentication when setup-only secrets are absent, and fails closed +if it is missing, expired, or insufficient. The full final gate still verifies PR/Actions reads and +the Git transport dry run after toolchain repair. An authentication-only PASS is not that verdict. +If a damaged cache lacks Node, the wrapper first restores the configured major using the existing +`nvm` installation so the authentication preflight does not block normal lifecycle recovery. + +After changing configuration or credentials, obtain fresh-task proof. Reset the environment cache +only if a new task still uses stale setup. Resume older tasks and verify them individually; a running +agent does not acquire changes automatically. Cloud may reconstruct a local commit under a different +SHA when resuming: verify the current diff, actual HEAD, destination branch, and remote PR before +publishing instead of trusting a SHA from an earlier answer. + +Credentials can expire or be revoked; no setup script can prevent that. Replace the encrypted secret, +then rerun the same live gate. Keep clinical providers offline and use an appropriate network +allowlist. See [the Cloud reliability audit](../audit/codex-cloud-reliability-20260907.md) for the +verified environments and remaining capability boundaries. + +The maintenance command reasserts the safe `origin`, runs static/effective environment +acceptance, then runtime acceptance. The shim installer runs after either lifecycle command and +repairs the normal Node command boundary. Any runtime, dependency, CLI, Deno, Python/OCR, or +browser drift reruns the full setup instead of repairing only `node_modules`. All profile +insertions, command shims, CLI installs, and remote repair are idempotent. + +### Automatic setup diagnostics + +Cloud setup tracks its current phase. If a command fails, the error trap runs +`npm run diagnose:codex-cloud` automatically and prints sanitized `ISSUE` and `FIX` lines after +the original error. The diagnostic checks the Node/npm contract, the active Cloud Python runtime, +the runtime-specific Python lock header, and the failed setup phase without printing environment +values or calling a provider. Run it manually in an agent shell when setup completed but the +runtime later appears stale: + +```bash +npm run diagnose:codex-cloud +``` + +The command is Cloud-only: outside a `CODEX_CLOUD=1` shell it reports `NOT_APPLICABLE` rather than +mistaking a desktop Python installation for Cloud runtime drift. + +The production worker image and Codex Cloud deliberately use separate hashed Python locks because +medspaCy 1.3.1 requires spaCy `<3.8` on Python 3.11 but `>=3.8` on Python 3.12. Production uses +`worker/python/requirements.txt` (Python 3.11); Cloud uses +`worker/python/requirements-cloud.txt` (Python 3.12). After changing +`worker/python/requirements.in`, regenerate and verify both locks with their matching interpreters. + +## Acceptance + +For an explicitly selected legacy profile, run this in a fresh Cloud task before relying on it: + +```text +Read all applicable AGENTS.md files and the selected legacy runbook docs/codex-cloud.md. State whether this is the +offline or connected profile. Report tool versions without printing environment values. +Run npm run check:codex-cloud, npm run check:runtime, +npm run check:installed-lock-parity, and npm run check:codex-cloud -- --runtime. Confirm that +setup exported CODEX_CLOUD_EXPECTED_BASE_SHA as a verified 40-character ancestor. Do not call a +provider unless this task explicitly names and authorizes that provider. Report the decisive +line from every command and any unrun check. +``` + +Expected decisive lines include: + +```text +[Codex Cloud Check] PASS: static and environment Cloud contracts match. +[Codex Cloud Check] PASS: static, environment, and runtime Cloud contracts match. +``` + +The effective-environment check runs automatically when `CODEX_CLOUD=1`, including without +`--runtime`, so a newly started agent shell cannot pass with stale modes. Its report prints only +approved mode values and presence booleans. The runtime check additionally verifies Node/npm +policy and installed-lock parity, the pinned Codex CLI, Deno 2, Python 3 and worker imports, +Tesseract, actual headless launch-and-close for Chromium/Firefox/WebKit, the Python requirements +fingerprint plus `pip check` and medspaCy/spaCy versions, the expected base commit as an ancestor +of HEAD, the `BigSimmo/PsychSift` origin identity, offline credential absence when applicable, +and obsolete npm proxy variable names without reading or printing their values. It reports +the full current HEAD, local main and origin/main when present, expected base, ancestry result, +and a separate freshness state. Setup and maintenance use the process-local +`CODEX_CLOUD_PROVISIONING=1` flag so an unavoidable task-only checkout reports +`freshness=unverified` without entering a repair loop. The explicit acceptance command does +not set that flag and fails unless the setup-generated `CODEX_CLOUD_EXPECTED_BASE_SHA` proves the +intended base. +MCP inspection emits server names, commands, and environment variable names only. + +A repository cannot remove a variable already inherited by the top-level task process. Before +sourcing any profile or invoking node/npm in a fresh task, run: + +```bash +bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh +``` + +The probe checks the complete provider-variable inventory and prints names only. Outcomes are +name-scoped in the probe itself: + +- exit `0` / `PASS` — raw boundary clean +- exit `1` / `FAIL` + `STOP` — any unexpected provider name (for example + `SUPABASE_SERVICE_ROLE_KEY` or `GH_TOKEN`); stop application, test, setup, package-manager + and provider execution and follow the recovery contract below +- exit `2` / `FAIL-KNOWN` + `CONTINUE-RESTRICTED` — only the documented Personal Pro launcher + defect name `OPENAI_BASE_URL` + +Exit `2` is still a failed raw boundary: humans/agents may continue only under this contract. +Do not treat “non-1” or “retry on any failure” as success in future automation — wire the three +states explicitly. When the probe reports `FAIL-KNOWN` for `OPENAI_BASE_URL` alone, preserve that +name-only output for OpenAI support and continue provider-free work only through the generated +profile and command shims, followed by a passing `npm run check:codex-cloud`. Do not generalize +that restricted path to any other inherited name. An inherited `OPENAI_BASE_URL` can redirect +OpenAI-bound traffic, so never call OpenAI clients from the raw parent process or from binaries +that bypass the profile/`node`/`npm`/`npx` shim scrub. A sanitized child shell is never proof +that the raw-parent boundary passed. + +### Raw-boundary failure and local policy repair + +A failed raw-boundary check does not prohibit an explicitly requested local policy review or repair. +This exception permits reading and editing repository instructions, checking local Git identity, +status and diffs, inspecting the probe and setup source, rerunning the names-only raw probe, and +checking document text, links or JSON with shell tools or Python's standard library. Such checks +must not load application code, dependencies, startup profiles or credentials, or contact providers. +Do not run Node/npm/npx, install packages, execute application tests or start services under this +exception. Report the official Node-based documentation gates as pending when the boundary blocks +them; a bounded document check is not a substitute for their verdict. + +Preserve the failing exit code and never print, read out, copy or persist credential values. Do not +unset an inherited name merely to obtain a clean child-process result, add it to an allowlist, or +describe profile scrubbing as a raw-parent pass. Editing policy does not fix credential inheritance. + +For recovery of a selected legacy profile, use the host's supported configuration to remove unexpected agent-phase credential +injection and start a fresh task. If the same name returns after a fresh task, report the repeated +name-only failure to the environment/launcher owner; another identical restart alone is not a fix. +The probe proves a name is present, not which host component supplied it or whether it grants access. +Keep GitHub shell authentication separate from authenticated connector access. After recovery, +require the raw PASS line and the existing environment/runtime acceptance checks before resuming +execution. Provider authorization and production safeguards continue to apply. + +### Policy and environment activation + +Treat these as separate layers when an update appears not to take effect: + +| Layer | What to verify | What an update does not prove | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Repository instructions | The task's branch/commit contains the intended `AGENTS.md`, its continuation and linked policy files; check local diffs too. | A host environment update does not change Git content. Local edits are not published to other checkouts. | +| Host environment and setup | The task selected the intended environment revision and setup/maintenance configuration. Check actual readiness and the names-only raw probe. | A saved setting or setup rerun does not remove credentials already inherited by a running parent process. | +| Active chat and client | Start a fresh task on the intended revision, or use a supported context reload and verify what was loaded. | Existing chats can retain older instructions; changing a file does not rewrite earlier messages. | +| Model and reasoning picker | Respect the user's selection and ordinary-language confirmation under [the reasoning policy](../agents/codex-reasoning-effort.md). | Repository prose cannot set or attest the running model/effort. | + +Publish and integrate policy changes through the normal reviewed repository workflow before claiming +they apply to every environment. Do not edit unrelated worktrees or host-wide settings to simulate +rollout. Record the exact revision and fresh-task evidence; until then, distinguish a local repair +from verified activation elsewhere. + +`npm run check:production-readiness` remains useful in the offline profile for local safeguards. +Missing Supabase/OpenAI agent-phase credentials are reported as a provider capability gap and do +not make the provider-free cache unhealthy. `ALLOW_PROVIDER_TESTS=true` expresses authorization, +not credential availability; live tests still fail closed with a sanitized capability-gap message +in the offline profile. + +## Provider acceptance + +Provider access is verified separately because a generic bootstrap must not make paid or +production-like calls. The active hosted workspace is **Personal Pro**. It does not have the +dedicated-group RBAC or per-tool action disabling assumed by Enterprise/Edu instructions. Use +Railway's installed official ChatGPT app, complete browser OAuth without static tokens or headers, +set the global app policy to **Allow read actions**, and leave changes approval-gated. Authorize +only workspace `bigsimmo's Projects` and project `PsychSift` +(`5deaad0b-675a-4c13-978e-5ca2b5b877f9`) where Railway offers that choice. Reduce read results to +non-secret account, project, workspace, environment, and service metadata. Railway's remote MCP +does not accept project tokens; install Railway CLI separately only for explicitly approved +local/operator workflows (for example `npm run check:env-parity -- --railway`). That CLI path is +not available in ordinary Cloud tasks and is not part of Cloud runtime acceptance. Prefer +`RAILWAY_API_TOKEN` for personal CLI auth; never substitute the project-scoped CI +`RAILWAY_TOKEN`. Enterprise/Edu custom-app controls are an optional future governance upgrade, +not the current operating target. + +The Supabase MCP entry is scoped to production project `sjrfecxgysukkwxsowpy`, forces +`read_only=true`, and exposes only documentation/development metadata tools. The database and +debugging groups are excluded so ordinary Cloud cannot execute SQL, read clinical rows, or inspect +production logs. Complete its browser OAuth flow for the organization containing `PsychSift Production` +and restart the client if tools do not appear. Schema writes, Edge Function deployment, branching, +and storage mutations require a separately configured non-production project or branch; do not +broaden the production entry. OpenAI generation, Supabase live data, Railway changes, hosted CI +reruns, ingestion, deployment, and release workflows remain separate explicit actions. + +Project `.codex/config.toml` is the checked-in Codex Desktop/CLI MCP template. Its URL-only entries +must stay `enabled = false` in git — `npm run check:codex-cloud` hard-fails on any tracked +`enabled = true`. A trusted local operator opts in outside the committed tree: prefer enabling +`railway` in `$CODEX_HOME/config.toml`, or make a never-committed local edit to the project file for +the session, then run `codex mcp login railway`. Setup does not copy any MCP server into +`$CODEX_HOME`. Hosted ChatGPT and Codex Cloud require the separately installed/authenticated +workspace app. Start a fresh task after consent and verify the actual callable inventory. +The root `.mcp.json` is a cross-client Desktop/CLI template and static allowlist only. It does not +prove hosted Cloud availability. The checked-in Context7 integration is +Cursor local stdio, separate from this Codex Cloud Railway + Supabase allowlist. +A separately selected hosted Context7 connector has its own authentication and +availability. See the [Context7 guide](../agents/context7.md) for version matching, +sanitised queries and the authorised CLI fallback; repository configuration does +not prove hosted access. + +### Personal Pro split control plane + +Personal Pro currently exposes GitHub, Slack, and Linear on the Codex connector settings page; it +does not expose Railway or Supabase there. Use the smallest functional split instead of copying +credentials into Cloud: + +- **Codex Cloud:** repository work, offline checks, and GitHub reads/publication through the native + GitHub connector or Cloud PR controls. +- **ChatGPT web:** Railway through the official OAuth app and Supabase through the pinned + project-scoped read-only app. Keep Railway on **Allow read actions** and ask before every change. +- **Desktop/CLI:** opt-in local MCP via `$CODEX_HOME/config.toml` (preferred) or a + never-committed local enable of the project `.codex/config.toml` `railway` entry, followed by + `codex mcp login railway`; this is a local operator fallback, never hosted proof. + +The repository checker prints these routes as sanitized `provider_route.*` lines. They describe +where a capability is allowed, not proof that a host installed or authenticated it. A fresh task +must still establish the callable inventory. + +Production Supabase stays project-scoped and `read_only=true`, with +`default_tools_approval_mode = "prompt"` so every production metadata/read call requires +confirmation. Do not use unrestricted SQL or query clinical rows. Railway, Figma, and Sentry +write-capable tools remain approval-gated. OAuth credentials stay in the host store—never the +tracked files or agent environment. + +For Figma, that means using the official app/plugin OAuth path for a named design task, not adding +Figma client IDs, client secrets, REST tokens, or npm registry tokens to Cloud environment +variables. Product truth remains `docs/design-system/` and the app code; design writes still need +explicit task approval. + +In a fresh connected task, first run the raw-shell probe and repository acceptance, then inspect +the actual callable tool inventory. A configured URL or `enabled = false` template is not runtime +proof. Verify Railway and Supabase with read-only identity/project metadata calls and report only +non-secret status; if either tool is absent, the host integration is not activated. + +### Connected-environment remediation checklist + +Use this checklist when a Cloud task reports a connected profile but cannot call providers, publish +its branch, or make the local health endpoint ready. Repository setup can prepare the boundary, but +the host must grant OAuth and repository access in a fresh task; do not try to repair those gaps by +copying credentials into the checkout. + +1. **Create the durable host environment.** In Codex environment settings, create or select + `PsychSift - connected` for `BigSimmo/PsychSift`. Set exactly the five non-secret values from the + connected profile above. Configure setup as + `bash scripts/setup-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh` and + maintenance as + `bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh`. + Do not add provider keys, database URLs, service-role credentials, test-user credentials, or + `ALLOW_PROVIDER_TESTS`. Connected setup writes only the managed shell-environment policy; it + never registers hosted MCP apps or writes OAuth tokens. +2. **Grant the host integrations.** In the Personal Pro workspace, install Railway's official + ChatGPT app, complete Railway OAuth, select **Allow read actions**, and keep all changes subject + to explicit approval. If a stale custom app or connector named `railway_cloud` shows an + authentication-expired reconnect banner, remove or reconnect that host-local app in the MCP/App + settings before starting a new task; repository setup cannot refresh hosted OAuth tokens. + Personal Pro has no dedicated-group RBAC or per-tool disabling, so do not claim those controls. + Authorize the Codex GitHub connector for + `BigSimmo/PsychSift` with repository write access. Complete Railway OAuth only for workspace + `bigsimmo's Projects` and project `PsychSift` (`5deaad0b-675a-4c13-978e-5ca2b5b877f9`). Complete + Supabase OAuth only for the organization containing `PsychSift Production`; retain project ref + `sjrfecxgysukkwxsowpy`, `read_only=true`, and the docs/development-only feature allowlist. Do not broaden the + production Supabase MCP to write access. Enable Figma or Sentry only for a task that names that + provider; their write-capable tools remain approval-gated. Railway's OAuth metadata advertises + `offline_access`; verify the scanned consent includes it and prove refresh behavior with a second + read-only call after the one-hour access-token lifetime. If no refresh token is issued, require + reauthentication instead of adding a token workaround. +3. **Verify activation in the host.** A setup rerun inside an already-running offline task can + validate a generated connected profile, but it does not grant OAuth or remove credentials from + the running parent process. Hosts may support tool refresh, client restart or a new task after + consent; use the supported route and inspect the tools actually exposed. A failed raw-parent + boundary still requires recovery and a fresh task under the contract above. When the host + exposes its app identifiers, pass that non-secret + inventory to the environment check with + `--hosted-app-inventory=github,railway,supabase`. The exact `=` form is required and the option + may appear only once; every other unsupported argument fails closed. The checker rejects stale + `railway_cloud`, shared sensitive-token patterns, and identifiers outside its reviewed connector + allowlist. It normalizes connector-name case and never echoes supplied values. Invalid input + suppresses capability output rather than printing a misleading unverified state. Inventory + remains explicitly unverified when the host does not provide it; repository config is never + substituted for this evidence. +4. **Prove the shell boundary before providers.** First run the direct raw-shell command above + before profiles or command shims. Then run `npm run check:codex-cloud`, + `npm run check:codex-cloud -- --runtime`, `npm run check:runtime`, and + `npm run check:installed-lock-parity`. Confirm the setup-generated + `CODEX_CLOUD_EXPECTED_BASE_SHA` is a full intended merge-base commit. Require the raw PASS line, + both Cloud PASS lines, correct runtime/lock + parity, `CODEX_CLOUD_ACCESS_PROFILE=connected`, no provider variable reported present, and a + credential-free matching origin. Repository MCP metadata is configuration evidence only. +5. **Prove each provider read-only.** Use the tools exposed by the fresh host session, not shell + tokens. For GitHub, read repository metadata and confirm `BigSimmo/PsychSift` plus the intended + identity. For Railway, read workspace/project/service metadata and confirm the IDs above without + triggering a deployment. Record the exact Railway inventory. Set the app to allow reads and ask + before changes. The Personal Pro global read-versus-change control does not provide tool-level + RBAC. If the account later moves to Enterprise/Edu, an admin may additionally allow only + `whoami`, `list-projects`, `list-services`, `list-feature-flags`, and `get-feature-flag`, while + disabling write/agent tools and newly discovered actions by default. For Supabase, read + project/schema metadata and confirm the pinned ref + without querying clinical row contents. Report only non-secret identity and status metadata. + OpenAI has no generic connected-profile credential: leave `RAG_PROVIDER_MODE=offline` until a + separately approved paid canary or protected workflow supplies its own credential boundary. +6. **Publish a task branch safely.** Work on a task-specific non-protected branch. Format, stage, and commit + the intended repository change, then publish that exact existing commit through the native GitHub + connector/Cloud PR workflow, and verify the remote branch and PR link. If shell Git + authentication is intentionally available, `git push --set-upstream origin ` is + acceptable after confirming the credential-free origin; otherwise a failed `git ls-remote` is + not repaired with an embedded PAT. Do not publish a generic detached `work` branch merely to + remove an informational "no upstream" warning. +7. **Separate local app proof from live readiness.** Run `npm run ensure`, use the printed URL, and + confirm `/api/local-project-id` before checking `/api/health`. In demo mode, an HTTP 503 with + missing Supabase configuration and skipped OpenAI is expected and must not be relabelled healthy. + Stop only the verified repository-owned server after the check. Production-connected health and + authenticated tests run through the protected operator/provider workflow below, not by placing + credentials in the Cloud agent shell. +8. **Treat swap as capacity control, not access setup.** Do not create persistent or ad-hoc swap by + default. First run the intended full unit suite and production build while observing available + memory. If they complete without memory pressure, record "swap not required." If a reproducible + out-of-memory failure remains after using the repository's coordinated single-heavy-command + workflow, resize the disposable environment or configure swap in the host image; do not commit a + swapfile, add it under the repository, or rely on an ephemeral in-task swapfile as a durable fix. + +Acceptance is complete only when evidence distinguishes these independent capabilities: + +| Capability | Required evidence | Not sufficient | +| ----------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| Connected boundary | Cloud environment and runtime PASS lines with provider variables absent | Editing the generated profile in one running task | +| GitHub read/write | Connector repository read plus verified publication of the exact task commit | Repository discovery, `make_pr` metadata, or a local commit | +| Railway read | Exact callable hosted-app tools plus OAuth metadata for the expected workspace/project | Installed Railway CLI or repository MCP config alone | +| Supabase read | OAuth-backed metadata for the pinned read-only project | A configured MCP URL without completed OAuth | +| OpenAI/live application | Explicitly approved paid canary or protected authenticated workflow | Setting `RAG_PROVIDER_MODE=auto` without credentials | +| Local application | Project identity plus the reported health status from the `ensure` URL | Assuming a localhost port or treating demo 503 as production-ready | +| Capacity | Full intended checks complete without OOM, or a host-level capacity change is proven | Swap size by itself | + +If a capability still fails, record the exact sanitized failure and its owner: repository setup, +Codex environment/OAuth, provider RBAC, GitHub installation, protected workflow, or host capacity. +Do not collapse those distinct boundaries into a generic "Cloud access" result. + +## Authenticated live testing + +Do not expose provider credentials to the Codex Cloud agent. Cloud secrets are removed +before the agent phase, and copying them into ordinary environment variables or files +would bypass that security boundary. + +Use the manual **Authenticated live tests** GitHub Actions workflow instead. It runs the +repository's explicit `npm run test:live` suite from protected `main`, requires the +`run-authenticated-live-tests` dispatch confirmation, records the run against the +`Database / production` environment, checks the expected Supabase project, and receives +only the GitHub secrets required by the current authenticated Supabase test. It is not +triggered by pushes, pull requests, or schedules. + +The suite is not read-only. Dispatching it explicitly authorizes the bounded mutations made +by the E2E user: production sign-in/sign-out, authenticated test requests, and rate-limit +row inserts or updates. It does not authorize unrelated production changes. + +To run it after a Codex Cloud change: + +1. Merge the reviewed change to `main` through the normal protected workflow. +2. Open **Actions → Authenticated live tests → Run workflow**. +3. Select `main` and choose `run-authenticated-live-tests` after reviewing the disclosed mutations. +4. Review the `Database / production` environment deployment and test log. + +As checked on 2026-09-27, the `Database / production` environment lists BigSimmo +as its only required reviewer, permits self-review and administrator bypass, and has no +environment secrets. The workflow's manual dispatch and explicit confirmation authorize +the bounded live-test mutations described above. The configured reviewer may pause this +one environment job, but it does not provide independent approval or gate Railway app +and worker deployments. Verify the current environment settings and an actual waiting +run before relying on a reviewer prompt. See +[`deployment-architecture.md`](../deployment-architecture.md#production-release-controls-checked-2026-09-27) +for the release-control scope and steps needed for independent approval. + +Adding a future provider to `*.live.test.ts` does not automatically grant its credentials. +Expand the workflow's secret list deliberately and preserve the project/target guard. + +## RAG X3 prompt + +The corrected structure-only extraction prompt is tracked at +[`prompts/rag-coverage-gate-extraction.md`](../prompts/rag-coverage-gate-extraction.md). It +uses the offline profile, keeps private coverage preparation/telemetry helpers in `rag.ts`, +and moves only the independently bounded evaluator. This avoids the import back-edge in the +older proposed three-function extraction. diff --git a/docs/archive/rag-coverage-gate-extraction-2026-10-16.md b/docs/archive/rag-coverage-gate-extraction-2026-10-16.md new file mode 100644 index 0000000000..b2b366c991 --- /dev/null +++ b/docs/archive/rag-coverage-gate-extraction-2026-10-16.md @@ -0,0 +1,163 @@ +# Archived implementation prompt — rag-coverage-gate-extraction + +Archived 2026-10-16 from `docs/prompts/rag-coverage-gate-extraction.md` at `e0c62df33f6a6be32540092deab783807c9563a0` after the implementation was verified present. +This is historical task evidence, not current instructions or permission to resume work. +Use the [retirement notice](../prompts/rag-coverage-gate-extraction.md), current repository policy and a separately +approved current task. Relative Markdown links retain their targets because `docs/archive/` +and `docs/prompts/` have the same directory depth. + +--- + +# X3 / #086 `rag.ts` coverage-gate extraction + +Use this prompt in a fresh Codex Cloud task with +`CODEX_CLOUD_ACCESS_PROFILE=offline`. It supersedes the older proposal to move the +three-function block through `applyCoverageGateTelemetry`; current dependency inspection shows +that the two private helpers are orchestration-owned and cannot move without a back-edge. + +## Perfected prompt + +Safely implement the X3 / #086 `rag.ts` decomposition in the PsychSift (`BigSimmo/PsychSift`) repository. + +### Objective + +Make one architecture-only extraction from `src/lib/rag/rag.ts` into the new file +**src/lib/rag/rag-coverage-gate.ts**. Preserve retrieval behavior, ordering, thresholds, +fallbacks, citations, scope enforcement, telemetry, and the public `@/lib/rag/rag` API. + +Move only: + +- `evaluateEvidenceCoverageGate` (anchor on the declaration, not stale line numbers); +- its directly owned private `visualEvidenceUnitTypes` constant. + +Keep `prepareCoverageGateResults` and `applyCoverageGateTelemetry` in `rag.ts`. They are +orchestration-owned and depend on `rag.ts` hydration, selection, timing, metadata-cache, and +telemetry state. Do not move them, inject their dependencies, or create an import back-edge. + +The evaluator's other dependencies already come from `@/lib/clinical-search`, +`@/lib/rag/rag-evidence-gates`, and the shared `SearchResult`/`RagQueryClass` types. Import those +directly in the new module. Preserve the evaluator body byte-for-byte wherever practical. + +RAG impact: no retrieval behaviour change — pure module extraction + +No live eval canary is required because this task changes no behavior. State that explicitly in +the handoff so a reviewer can disagree cheaply. + +### Provider and Git boundaries + +This task is completely provider-free even if the Cloud environment supports connected work. + +Do not call OpenAI, Supabase, Railway, GitHub/GitLab APIs, hosted CI, or production-like services. +Do not fetch, pull, push, commit, open a PR, inspect remote PR metadata, deploy, ingest, reindex, +run drift checks, start a server, or run any live retrieval/answer-generation/canary/release +workflow. Do not read or print credentials or environment-variable values. Leave publication as +a separate operator-authorized handoff. + +### Startup and mandatory stop checks + +1. Work only in the isolated Cloud checkout supplied for this task. +2. Read all applicable `AGENTS.md` files before editing. +3. Report exactly: `The Windows-only startup script is unavailable in Codex Cloud.` Then perform + equivalent read-only repository identity, branch, `HEAD`, upstream, status, staged/unstaged/ + untracked, worktree, Git-operation-marker, package-script, and recent-history checks. +4. Proceed only when `CODEX_CLOUD=1`, the checkout has exactly one Git worktree, the branch is a + clean non-protected task branch, and local `origin/main` is available as its base. Do not fetch. +5. Read `docs/codex-cloud.md` and run the dependency-free isolation verifier from the + prompt-perfector repository workflow with `--cloud` before editing. +6. Read the required RAG behavior, process-hardening, workorder, maintainability-budget, contract + test, and package-script documents named below. +7. Measure `rag.ts` with the exact line-count logic in + `scripts/check-maintainability-budgets.mjs` before editing. Expected base is approximately + 5,030 lines. +8. Stop without editing if `rag.ts` is already a small facade; the extraction already exists; the + three near-complete pipeline-copy modules from the rejected design exist; the target area is + dirty; the branch is protected; the checkout is not isolated; the evaluator requires a + signature change or import cycle; or expected reclaimed headroom is under 150 lines. + +Before editing, state exactly: + +`RAG protected surface: this task touches src/lib/rag/**. Intended impact is no retrieval behaviour change; this is a structure-only extraction.` + +### Required reading + +- `docs/rag-behaviour/README.md` and its linked behavior map, refuted approaches, and safeguards; +- `docs/process-hardening.md`, especially the `rag.ts` decomposition sections; +- `docs/maturity-backlog-workorders.md`, X3; +- `scripts/check-maintainability-budgets.mjs`; +- relevant architecture-boundary, retrieval-query-variants, RAG contract, cache, latency, and + early-exit tests; +- `package.json` verification scripts. + +### Implementation + +1. Create **src/lib/rag/rag-coverage-gate.ts** containing the moved constant and evaluator. +2. Import `SearchResult`, `RagQueryClass`, `classifyRagQuery`, and the evaluator's existing + evidence predicates from their current owner modules. Do not duplicate helpers. +3. In `rag.ts`, import the evaluator for its two internal call sites and explicitly re-export it + so `tests/retrieval-query-variants.test.ts` and all existing public consumers remain unchanged. + This re-export strategy preserves the public API and avoids consumer churn. +4. Do not reorder code, change a signature, rename a symbol, remove an `await`, change a literal, + threshold, comparator, default, fallback, telemetry field, timing boundary, cache/abort path, + scope check, evidence selection, or comment within the moved function. +5. Do not duplicate any orchestration pipeline or materially increase total RAG implementation + size beyond import/export overhead. +6. After proof, update only the `src/lib/rag/rag.ts` limit in + `scripts/check-maintainability-budgets.mjs` from 5,030 to the new exact measured line count. + Keep the reclaimed budget comment and do not raise any budget. +7. Update maintained architecture documentation only if module ownership/path mapping genuinely + changed. Do not modify #098, #099, #100, or #101. + +Preserve owner/document scope checks, admission-before-scope ordering, retrieval and released +result ordering, cache and abort semantics, conservative fallbacks, citation/numeric grounding, +source governance, telemetry names/timing boundaries, and error/rollback behavior. + +### Verification + +Respect the cross-worktree heavy-command coordinator. Never bypass or delete its lock. If an +exclusive gate is held by another worktree, report that command as unrun with the lock owner and +reason. + +Use Node 24, npm 11, and npm. Check that `node_modules` is populated, not merely present. If it is +absent or stale, use the repository Cloud maintenance/setup procedure only after the coordinator +allows installation; do not change manifests or lockfiles. + +Run narrowly, then widen locally: + +1. `npm run workflow:rag-lab -- --write-evidence` +2. `npm run test -- tests/retrieval-query-variants.test.ts tests/rag-tail-latency.test.ts tests/rag-shared-cache.test.ts tests/rag-variant-early-exit.test.ts` +3. `npm run check:maintainability-budgets` +4. `npm run check:rag:fixtures` +5. `npm run eval:rag:offline` +6. `npm run check:knip` +7. `npm run typecheck` +8. `npm run lint` +9. `npm run verify:cheap` +10. Run `npm run format` (whole-tree Prettier) and commit the result before pushing (or run `npm run format:check` when in a read-only/no-commit Cloud session). +11. `git diff --check` + +For every command, record its real exit status and quote the decisive output line. Do not pipe a +gate through `tail`; if output filtering is unavoidable, preserve the original process status. +An admission timeout or contention result is not a pass. + +### Final review and handoff + +Review the complete diff for accidental logic changes, copied pipeline bodies, changed ordering, +thresholds/defaults, missing exports, import cycles, unrelated files, generated artifacts, and +secrets. Confirm the worktree again immediately before handoff. + +Report: + +- the responsibility extracted and why it is cohesive; +- files changed; +- `rag.ts` line count before and after; +- total `src/lib/rag` LOC and diff statistics proving move rather than copy; +- the explicit `rag.ts` re-export strategy and preserved public import; +- every check with status and decisive line, plus every unrun check and why; +- current branch/worktree status and remaining coupling/risk; +- explicit confirmation that retrieval, ranking, clinical, provider, and safety behavior were + not intentionally changed; +- explicit confirmation that no provider/API call, live canary, commit, push, PR, hosted CI, + deployment, ingestion, reindex, or release occurred; +- a short handoff noting that #098 and #099 remain separate, and that the planned + **src/lib/rag/rag-hydration.ts** module is the intended #101 follow-up rather than part of this + task. diff --git a/docs/claude-cloud.md b/docs/claude-cloud.md index 101327e317..ed62307b6a 100644 --- a/docs/claude-cloud.md +++ b/docs/claude-cloud.md @@ -1,30 +1,36 @@ # Claude Code on the web — container parity -Claude Code on the web runs in a disposable Linux container that starts with the repository checkout -and nothing else. Everything that lives in the operator's Windows home directory is absent, and so are -Playwright, Python, Tesseract, Deno and the GitHub CLI. That is why a cloud session used to behave -noticeably differently from a local one, and why browser proof and worker checks failed there. - -This document is the contract for closing that gap. The Codex Cloud equivalent is +This document describes the repository's Claude web container provisioner. Inspect the current +host's permissions, tools and installed dependencies before relying on it; a fresh container need +not have the same capabilities as an earlier one. Workstation home files, credentials and services +do not automatically transfer. Earlier missing-tool incidents motivated this provisioner. +The Codex Cloud equivalent is [codex-cloud.md](codex-cloud.md); the two are deliberately separate environments with separate provisioners. ## What runs, and when -`.claude/settings.json` registers two `SessionStart` hooks in order: +`.claude/settings.json` registers these two provisioning commands under `SessionStart`, +alongside base-freshness, issue-queue, testing-policy and organisation context hooks: | Order | Command | Timeout | Does | | ----- | ----------------------------------------- | ------- | -------------------------------------------------- | | 1 | `.claude/hooks/session-start.sh` | 900s | Installs Node 24 to `$HOME/.node24`, then `npm ci` | | 2 | `scripts/setup-claude-cloud.sh --session` | 300s | Everything below | -Both no-op instantly unless `CLAUDE_CODE_REMOTE=true`, so a local Windows session is untouched. +Both no-op instantly unless `CLAUDE_CODE_REMOTE=true`. This guard selects their execution; it does +not attest that provisioning succeeded or that the current host has provider authority. ## The tiers `scripts/setup-claude-cloud.sh` is tiered, marker-guarded and idempotent. A warm container re-runs in about a second; a cold one can be resumed after an interrupted run without redoing completed work. +All selected tier names, including session and background overrides, are validated before any tier +runs. Listing tiers and the local no-op path create no cache or profile state. Cached `gh` and `deno` +markers also require their commands to work; Deno must still report major version 2. A marker alone +is not current runtime evidence. + | Tier | Installs | Cost when cold | | ---------- | ---------------------------------------------------------------------------------------- | -------------- | | `profile` | The checked-in user profile (see below) | under a second | @@ -70,27 +76,47 @@ empty string to disable it), and `CLAUDE_CLOUD_BROWSERS`. ## The checked-in user profile -`.claude/cloud-profile/` is a snapshot of the operator's user-level Claude Code configuration. The +`.claude/cloud-profile/` is a reviewed, portable subset of the operator's user-level Claude Code configuration. The repository cannot reach into a Windows home directory, so the snapshot travels in-tree and `scripts/apply-claude-cloud-profile.mjs` unpacks it into the container's `~/.claude`. -| Path | Becomes | Merge rule | -| ------------------------------------- | ----------------------------------- | ------------------------------------------ | -| `.claude/cloud-profile/CLAUDE.md` | `~/.claude/CLAUDE.md` | Replaced; any existing copy backed up once | -| `.claude/cloud-profile/settings.json` | `~/.claude/settings.json` | **Deep-merged**, profile wins per key | -| `.claude/cloud-profile/skills/` | `~/.claude/skills/` | Replaced per skill | -| `.claude/cloud-profile/memory/` | `~/.claude/projects//memory/` | Copied **only when absent** | +| Path | Becomes | Merge rule | +| ------------------------------------- | ----------------------------------- | --------------------------------------------- | +| `.claude/cloud-profile/CLAUDE.md` | `~/.claude/CLAUDE.md` | Replaced; any existing copy backed up once | +| `.claude/cloud-profile/settings.json` | `~/.claude/settings.json` | **Deep-merged**, profile wins per key | +| `.claude/cloud-profile/skills/` | `~/.claude/skills/` | Replaced per skill | +| `.claude/cloud-profile/memory/` | `~/.claude/projects//memory/` | Reusable guidance copied **only when absent** | Any part can be skipped for a targeted re-run — `node scripts/apply-claude-cloud-profile.mjs --skip=skills,memory` — and `--dry-run` reports without writing. +The memory seed directory carries curated reusable guidance and dated examples. Its +`seed-manifest.json` lists the only Markdown files the applier may copy. The manifest must use +unique plain filenames naming existing regular files and must include `MEMORY.md`; index links +must refer to listed seeds. Invalid manifests or index links reject the selected profile before +settings, instructions or skills are changed. An unlisted +file placed in the source directory is not installed, and the archive is never a fallback source. +Keep the manifest, reviewed seed content and seed index aligned when refreshing the profile. + +The seed directory must not copy old coordination boards, open-task queues, workstation locations or prior session authority +into a new host. The seven records retired on 2026-10-16 are preserved byte-for-byte in +[the Cloud session-state archive](archive/cloud-session-state-2026-10-16/README.md), outside the +applier's source directory. Their original dates and links remain historical evidence. + +This source cleanup affects fresh seeding only. Existing host-authored memories and indexes are +preserved, including files with retired seed names. The applier neither purges host memory nor +rewrites an existing `MEMORY.md`; do not claim that updating this repository cleans an already +provisioned host. Follow the current repository instructions and verify current task facts when +retained memory disagrees. + Three rules are load-bearing: - **Settings are merged, never replaced.** Whatever the platform put in `~/.claude/settings.json` — auth state, machine ids, feature flags — survives. Only the profile's own keys are imposed. -- **Memories are never clobbered.** A memory the container writes during a session is newer than the - snapshot and must win, so the copy is skip-if-present. Clobbering would silently delete work. +- **Memories are never clobbered.** The copy is skip-if-present to preserve host-written work. + Existence or recency does not make memory authoritative; current instructions and verified facts + win when content conflicts. Correcting host memory requires its applicable exact-content approval. - **The applier refuses to run unless `CLAUDE_CODE_REMOTE=true`**, or `--force` is passed. The operator's real `~/.claude` is the _origin_ of this snapshot; overwriting it with a copy of itself is the one genuinely destructive thing this tooling could do. `scripts/setup-claude-cloud.sh` carries @@ -99,10 +125,16 @@ Three rules are load-bearing: ### Refreshing the snapshot The profile is a snapshot, not a live link — it goes stale as the workstation configuration changes. -Refresh it deliberately by copying the current `~/.claude/CLAUDE.md`, `~/.claude/skills/*` (resolving -the symlinks into `~/.agents/skills`) and the project's memory directory back into -`.claude/cloud-profile/`, then commit. Keep `settings.json` hand-maintained: the workstation copy -carries Windows-only entries (the Shellular notification hook, Desktop Commander MCP permissions) that +Refresh it deliberately by reviewing the current `~/.claude/CLAUDE.md` and selected `~/.claude/skills/*` +(resolving symlinks into `~/.agents/skills`) before copying portable content into `.claude/cloud-profile/`. +Retain reviewed repository adaptations, including current-tool routing and same-task continuation; +a workstation copy must not restore stale model inventories, mandatory remote setup or unavailable +commands. These vendored files are maintained here; changing them does not update their external origin. Review project +memories individually and copy only reusable guidance, checking the seed index too. Preserve +dated session records in `docs/archive/` instead of importing a workstation's entire memory +directory as active guidance. Commit the reviewed source changes through the authorised workflow. +Keep `settings.json` hand-maintained: the workstation copy carries Windows-only entries +(the Shellular notification hook, Desktop Commander MCP permissions) that must **not** be pushed into a Linux container. Two things about `skills/` in particular: @@ -127,17 +159,16 @@ on a valid intermediate state. Both properties are pinned by that test file. Provisioning cannot supply credentials or authorise connections. In a fresh cloud container: -1. **Credentials — and cloud has nowhere safe to put them.** A cloud environment's variables box is - not a secrets store: Anthropic's own guidance is not to put API keys or credentials there, because - every value is readable by anyone using the environment, and `Static API tokens and credentials` - is listed as unavailable in cloud sessions with "no dedicated secrets store exists yet". Nothing - in this repository can change that. +1. **Credentials and provider authority.** Earlier Claude web documentation described shared-readable + environment variables and no dedicated secrets store. Treat that as historical platform evidence, + not proof of the current host's account settings. Inspect supported host metadata and current + documentation when authorised; never inspect credential values to establish readiness or paste + secrets into a shared-readable variables box. - So treat **demo mode as the normal cloud posture.** With Supabase and OpenAI configuration absent, - `isDemoMode()` serves the synthetic corpus and the app works — that is designed behaviour, not a - failure. Cloud is then good for code, tests, docs and review, and is simply not where live-data or - provider-backed work happens; that stays on the workstation, which is also where the - provider-confirmation rules in `AGENTS.md` assume it happens. + Demo mode is the default when Supabase and OpenAI configuration is absent: `isDemoMode()` serves + synthetic data. Live/provider work needs applicable user authority, a supported authenticated + route and the project safeguards. The provider-confirmation boundary applies on every host; + it does not assume that the work happens on a workstation. Provisioning grants no such authority. Live mode would need `NEXT_PUBLIC_SUPABASE_URL`, `SUPABASE_PROJECT_REF`, `SUPABASE_PROJECT_NAME`, `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`, `SUPABASE_SERVICE_ROLE_KEY` and `OPENAI_API_KEY`, plus @@ -145,24 +176,25 @@ Provisioning cannot supply credentials or authorise connections. In a fresh clou row-level security on a database holding clinical documents, so it is the one value that should never be pasted into a shared-readable box under any circumstances. -2. **`gh` is installed and authenticated — but only REST is served.** The `gh` tier puts the binary on - `PATH`; it does not log it in. Anthropic keeps git credentials outside the sandbox deliberately — - cloning, branching and pushing go through a credential proxy — so the container holds no token of - its own. This entry used to predict that `gh api` / `gh pr` would therefore fail on - authentication, and record that as unverified. **A real session settled it on 2026-08-21, and the - prediction was wrong in a way worth knowing:** the platform does inject GitHub access, `gh api` - REST calls succeed, and the wall is at the API layer instead. +2. **GitHub access must be verified for the current session.** The `gh` tier installs a binary; + it does not log it in or prove connector access. Git transport, shell REST/GraphQL and hosted + connectors can have separate permissions. Prefer currently callable authenticated connectors + for authorised work; no token copying or login repair is implied by this runbook. + + **Historical session, 2026-08-21:** injected GitHub REST access worked while selected GraphQL + operations failed. The following records that session's limits and alternatives, not a current + guarantee or authority to make those calls. - `gh` GraphQL is refused with `HTTP 403: This GraphQL query is not enabled for this session — only + In that session, `gh` GraphQL was refused with `HTTP 403: This GraphQL query is not enabled for this session — only the pinned set of PR-review operations is served. Use REST via gh api repos/{owner}/{repo}/...`. - The trap is that this takes out subcommands that look purely RESTful: `gh pr create` and - `gh pr view` both fail on their GraphQL repo-info preamble before doing any work, and the error - names GraphQL rather than the subcommand, so it reads like a broken install rather than a scoped - permission. + That also affected subcommands that looked purely RESTful: `gh pr create` and + `gh pr view` failed on their GraphQL repo-info preamble before doing any work. The error + named GraphQL rather than the subcommand, which looked like a broken install rather than + a scoped permission. What worked in that session, all through the injected credential: reading pull requests and their commits, files and check runs; posting a review-thread reply; deleting a review comment; and - creating a pull request. Use REST or the GitHub MCP tools and none of it is blocked: + creating a pull request. That session used REST or GitHub MCP tools for those operations: ```bash gh api repos/{owner}/{repo}/pulls/{n} --jq '{state,merged,mergeable_state}' @@ -171,34 +203,31 @@ the pinned set of PR-review operations is served. Use REST via gh api repos/{own gh api repos/{owner}/{repo}/pulls --method POST --input payload.json ``` - Review-thread **resolution** is the one job REST cannot do — it is GraphQL-only, and that mutation - is outside the pinned set. Use the `resolve_review_thread` GitHub MCP tool, which reaches GitHub - through Anthropic's servers rather than the container, and takes a thread node id from - `pull_request_read` with `method: "get_review_comments"`. + Review-thread **resolution** needed GraphQL and that mutation was outside the pinned set. + The session used the hosted `resolve_review_thread` GitHub MCP tool instead. Verify current tool names and argument schemas; + do not assume `resolve_review_thread` or `pull_request_read` exists on the current host. Treat the pinned set as liable to change: prefer the MCP tools when they cover the job, and fall back to REST rather than assuming a GraphQL path will keep working. -3. **MCP sign-in.** `.mcp.json` declares the Railway and Supabase HTTP servers and - `.claude/settings.json` enables them, so both reach a cloud session. Authenticating them there is - a separate matter. Anthropic documents browser-based interactive auth as unsupported in cloud - sessions, so the in-container OAuth path is doubtful. The route that does work is a **claude.ai - connector**: connectors added at claude.ai/customize/connectors authenticate once in your browser - on the claude.ai side, and their traffic reaches a cloud session through Anthropic servers rather - than the container network. Prefer a connector over the `.mcp.json` entry for anything you need in - cloud, and treat the `.mcp.json` route as unverified there. +3. **MCP sign-in.** Tracked MCP files are secret-free templates, not proof of hosted registration, + authentication or loaded tools. Keep disabled-by-default servers disabled and preserve the + Supabase project/read-only feature scope. Inspect actual callable tools; a hosted connector + configured through supported account settings is a separate route. Browser OAuth and connector + support vary by host. Neither this document nor provisioner activation grants provider access. 4. **Plugins.** The supported route is the repository's `.claude/settings.json`: Claude Code installs anything in its `enabledPlugins` from the declared `extraKnownMarketplaces` when the session - starts. That is what makes plugins work here. The `plugins` tier is a repair path for when that - install does not happen, and it also runs `npm install` inside each plugin, which the installer - itself does not do — a plugin with runtime dependencies otherwise unpacks clean and fails at first - use. A tier-installed plugin only takes effect in the _next_ session. -5. **Network egress allowlist for Playwright browsers and CI traces.** In Claude Code on the web - (`CLAUDE_CODE_REMOTE=true`), container egress is mediated by Anthropic's network proxy (`/root/.ccr/`). - The default egress policy blocks the mirrors needed for Playwright browser installation and GitHub - Actions trace artifact downloads. - - **The three blocked hostnames (verified by request, not inferred):** + starts, where supported by the current host. Verify actual installation and loaded skills. + The `plugins` tier is a repair path and runs `npm install` inside each plugin because earlier + installs omitted runtime dependencies. Check current readiness before choosing that repair. + Installation is separate from activation; use a supported reload or fresh session if required + by the current host. +5. **Network egress for Playwright browsers and CI traces.** Earlier containers used a proxy at + `/root/.ccr/` and blocked the mirrors below. These are recorded observations, not a diagnosis of + this host. Check current network policy and dependency versions before proposing a repair; + preserve host proxy/CA settings and do not bypass a denied route. + + **Hostnames blocked in the recorded environment (verified then by request):** - `cdn.playwright.dev` (returns `HTTP 403 request blocked`): Primary mirror in `playwright-core@1.63.0`'s `PLAYWRIGHT_CDN_MIRRORS`. Without access, `scripts/setup-claude-cloud.sh browsers` fails to download Chromium, Firefox, and WebKit binaries. @@ -218,19 +247,21 @@ the pinned set of PR-review operations is served. Use REST via gh api repos/{own by `pdfjs-dist@6.3.289`, causing document-viewer failures in `tests/ui-smoke.spec.ts`). **Remediation (Operator environment configuration):** - Add the following hostnames to the Claude Cloud environment egress allowlist in container network settings: + If the current authorised workflow needs these hosts and current evidence confirms they are blocked, + the operator can review the applicable environment egress settings for: - `cdn.playwright.dev` - `playwright.download.prss.microsoft.com` - `*.blob.core.windows.net` - This is an operator configuration in the Claude Cloud environment settings (tracked under task `#PM71Z1`); - no repository code change is required. + The earlier operator request was tracked under task `#PM71Z1`; recheck its current status rather + than treating this record as an open task. Any host-policy change needs applicable authority. ## What cannot be replicated -Desktop Commander, the Docker MCP toolbox, the frontend-checklist MCP server, Claude in Chrome, -computer-use, and the Shellular desktop notification hook are all bound to the Windows workstation. -Cloud sessions will not have them, and no provisioning step can change that. +The historical workstation used Desktop Commander, a Docker MCP toolbox, the frontend-checklist MCP +server, Claude in Chrome, computer-use and the Shellular notification hook. Their workstation +configuration does not transfer through this provisioner. Check current host tools instead of +assuming either their availability or permanent absence. Cloud browser evidence is Playwright/Chromium, Firefox or WebKit container evidence. It does not close physical Safari or installed-PWA acceptance gaps — the same limit `codex-cloud.md` records. diff --git a/docs/codex-cloud.md b/docs/codex-cloud.md index ded6914f47..3e18196fb2 100644 --- a/docs/codex-cloud.md +++ b/docs/codex-cloud.md @@ -1,611 +1,30 @@ -# Codex Cloud environment +# Codex Cloud guide -_Updated 2026-10-08 — corrected documentation guidance; operational evidence retains its original dates._ +_Updated 2026-10-16. This page is navigation; the linked operating policy is the authority._ -> This Bash setup is for Linux-based Codex Cloud environments. Codex Desktop on -> Windows uses `npm run setup:codex-worktree`; pointing Desktop at this Cloud -> script starts WSL outside the Windows worktree and cannot provision it. +## Scope and selection -This repository supports reproducible Codex Cloud work with Node 24, npm 11, locked -development dependencies, Deno 2, Python/OCR tooling, and the Chromium, Firefox, and -WebKit Playwright browser matrix. The repository setup can prepare and validate the -container. It cannot grant GitHub installation permissions, workspace RBAC, agent-network -policy, or provider-account permissions; those are configured in Codex and each provider. +Use current-host capability checks. Read [the canonical Cloud operating policy](agents/codex-cloud-environment.md) +for startup/resume, models, tools, authorisation, credentials and failure handling. Do not apply a +copied task prompt or historical setup as a universal Cloud prerequisite. -For agent work, the root `AGENTS.md` points to the portable -[smart agent allocation policy](agents/smart-agent-allocation.md). Apply it whenever -agents are used, using only the model, effort and concurrency controls exposed in -the current Cloud task. Publication to the selected branch and a fresh-task check -are required before claiming Cloud adoption; local instructions alone do not -establish it. This policy neither changes the main-chat model nor grants provider -access. Its Cloud delivery section defines fallback and evidence requirements. +The [archived legacy bootstrap](archive/codex-cloud-legacy-profile-2026-10-16.md) +applies only when explicitly selected by the owner or environment, or when testing/repairing it. +Its executable checks remain fail-closed. The archive is not a fallback operating policy. -## Issues this setup resolves +## Tools and authentication -- Codex Cloud had no tracked setup or maintenance command on `main`. -- Two incompatible Cloud checkers existed only in dirty worktrees. -- The prompt-perfector bootstrap parsed the Windows task-start output incorrectly and - rejected Cloud's legitimate single primary checkout. -- Tool repair covered Node dependencies but not Deno, OCR, Python, or browsers. -- Provider-variable clearing was incomplete and the Cursor Cloud guidance was easy to - mistake for Codex Cloud guidance. -- GitHub connector access, shell Git credentials, network access, and provider credentials - were treated as one permission even though they are separate controls. -- The Codex Cloud CLI can emit a root `error.log` containing account/session metadata; the - repository now ignores that exact diagnostic path. +For the cross-client Desktop/CLI template versus a separately installed/authenticated workspace app, +see [tools and providers](agents/codex-cloud-environment.md#tools-providers-and-credentials). +Readiness comes from the current host and actual callable tools, not a recorded account tier. -## Create the environment +### Raw-boundary failure and local policy repair -In Codex environment settings, create an environment for `BigSimmo/PsychSift` using the -official [Cloud environments](https://learn.chatgpt.com/docs/environments/cloud-environment) -contract: +See [the current recovery policy](agents/codex-cloud-environment.md#raw-boundary-failure-and-local-policy-repair). +It defines the selected legacy boundary and bounded local repair; no credential bypass is authorised. -| Setting | Value | -| --------------------- | ------------------------------------------------------------------------------------------- | -| Repository | `BigSimmo/PsychSift` | -| Base image | Default universal image | -| Node version | `24` | -| Python version | `3.12` | -| Setup command | `bash scripts/setup-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh` | -| Maintenance command | `bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh` | -| Environment variables | Use the complete profile below | +### Policy and environment activation -Keep agent internet access off for this repository's ordinary Cloud environments. Package -installation happens during setup; ordinary structure-only work, including the RAG -decomposition prompt below, remains offline. The appended command-shim installer is required: -it makes every normal `node`, `npm`, and `npx` invocation load the generated sanitized -profile before starting Node. It is idempotent and builds the executable path from `nvm version` -instead of resolving through `PATH`, so maintenance cannot accidentally wrap an earlier wrapper. - -The setup command fails if the required Cloud toolchain cannot be installed. It intentionally does -not install Railway CLI: hosted Railway access comes from the authenticated workspace app, and the -CLI postinstall downloads a separate binary that may be blocked in the Cloud setup network. It pins -Codex CLI `0.147.0`, reviewed on 2026-08-22. OpenAI's -[official Codex CLI guide](https://learn.chatgpt.com/docs/codex/cli) supports Linux installation; -the npm package is used here so maintenance can verify an exact version without running an -unversioned installer. Set -`CODEX_CLOUD_SKIP_BROWSER_INSTALL=1` only for an explicitly source-only environment; that -environment is not full browser-ready. - -### Cloud compared with local development - -Cloud parity covers repository-owned tooling, not every capability of a developer workstation or -CI runner: - -| Boundary | What the repository provides | -| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Repository-owned setup | Pinned Node/npm, a lockfile-only dev install, Git hooks, Deno, the hashed Python/OCR environment, browser installation, and static/runtime checks. `--prefer-offline` reuses cached npm data when available but may still fetch missing packages; it is not offline mode. `--no-audit` and `--no-fund` keep provisioning deterministic and quiet, while dependency auditing remains a separately scoped CI gate. | -| Host/platform capability | GitHub installation permissions, callable connector tools, network policy, Docker or privileged operations, capacity, caches, and persistence are supplied by the selected host. Repository setup cannot grant or guarantee them; verify the needed capability in the current task. | -| Intentional security boundary | Cloud setup does not copy `.env*` files, credentials, OAuth sessions, provider URLs, mutable provider configuration, browser state, or local MCP authentication. Keep those differences from Desktop/operator workflows. | -| Optional or external acceptance | `CODEX_CLOUD_SKIP_BROWSER_INSTALL=1` creates a source-only environment. Provider-backed, production-like, physical-device, and physical Safari/PWA checks remain separate and require the authorization and environment described below. | - -See [Access profiles](#access-profiles), [GitHub access](#github-access), -[Playwright browser readiness](#playwright-browser-readiness-255), and [Acceptance](#acceptance) -for the authoritative task-level checks. Never repair a host capability gap by copying credentials, -tokens, provider URLs, OAuth state, `.env*` files, or mutable provider configuration into the Cloud -agent shell. - -### Playwright browser readiness (#255) - -Browser gates need the **locked** Playwright package and a Chromium revision that matches -`node_modules/playwright-core/browsers.json`. When a remote/Cloud image forces -`PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers` with `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` and that -tree only ships an older revision (for example 1194 while the lock expects 1234): - -1. Run `npm run check:installed-lock-parity` and `npm run check:playwright-browser-revision`. -2. Do **not** point `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` at the mismatched shell. -3. Delegate browser proof to CI Production UI, or refresh the environment image / install matching - browsers into a managed cache, or unset the container browser env vars so - `npx playwright install` can populate the managed path. - -`CODEX_CLOUD_SKIP_BROWSER_INSTALL=1` remains source-only: that environment is not browser-ready. - -## Access profiles - -### Offline (default) - -Use this for refactors, static checks, mocked tests, and all work that expressly forbids -providers: - -```text -CODEX_CLOUD=1 -CODEX_CLOUD_ACCESS_PROFILE=offline -RAG_PROVIDER_MODE=offline -NEXT_PUBLIC_DEMO_MODE=true -PLAYWRIGHT_OFFLINE_MODE=true -``` - -The generated shell profile removes known OpenAI, Supabase, Railway, GitHub/GitLab, -database, CI-trigger, test-user, and emergency-PAT variables. This prevents an unrelated -Cloud task from silently becoming provider-backed. - -Set all five offline values in the environment UI. The setup also writes the generated -profile to `.bashrc`, `.profile`, and `.bash_profile`. Covering `.bash_profile` is required: -Bash stops login-profile discovery at the first matching file, so an existing `.bash_profile` -can otherwise prevent `.profile` from running. Offline mode values are forced after inherited -platform values so stale `auto`/`false` settings cannot outrank the repository contract. -Do not manually source the profile as an acceptance workaround: start a fresh task and invoke -the direct `npm run check:codex-cloud` commands below. The tracked command shims make those -ordinary Node commands load the profile themselves. - -### Connected (explicit opt-in) - -Use `CODEX_CLOUD_ACCESS_PROFILE=connected` only in a separate environment whose tasks are -expected to call named providers. Configure the smallest domain/method allowlist and the -least-privileged credentials for those tasks. Never commit credentials or print their -values. The setup script does not call providers and does not prove provider authorization. - -Create or select a separate environment named `PsychSift - connected` for `BigSimmo/PsychSift`, -then set only these ordinary environment variables in -[Codex environment settings](https://chatgpt.com/codex/settings/environments): - -```text -CODEX_CLOUD=1 -CODEX_CLOUD_ACCESS_PROFILE=connected -RAG_PROVIDER_MODE=offline -NEXT_PUBLIC_DEMO_MODE=true -PLAYWRIGHT_OFFLINE_MODE=true -``` - -Keep OpenAI disabled unless a later task explicitly authorizes it. Do not add provider keys, -tokens, database URLs, service-role values, E2E credentials, or `ALLOW_PROVIDER_TESTS` to this -environment. The generated agent profile removes the complete provider-variable inventory in -both access profiles. Connected access records authorization intent and keeps the GitHub boundary -explicit, but it does not register hosted MCP apps, expose raw credentials to the shell, or guarantee -that every GitHub capability appears as a direct agent tool. For ordinary Cloud task publishing, -use the native Cloud diff/PR controls and verify the resulting GitHub branch and PR link. A -metadata-only `make_pr` response is not publication evidence. If a requested GitHub API is not -available, report that limitation rather than using shell credentials as a workaround. - -Codex Cloud secrets and ordinary environment variables have different exposure and lifecycle -properties. This repository has no mechanism that promotes setup-only OpenAI, Supabase, E2E, -or Railway secrets into the agent phase. Do not persist them in profiles, repository files, -`.env*`, caches, logs, or ordinary variables as a workaround. A `connected` profile models -explicit authorization and relaxes the offline-mode assertions, but it does not create -credentials. Authenticated Supabase/E2E tests use the protected manual workflow documented -below. Run other provider checks locally/operator-side or through a separately approved, -least-privilege provider mechanism. - -## GitHub access - -The GitHub connector is the supported repository/PR path. Follow the official -[Codex GitHub setup](https://help.openai.com/en/articles/11390924), authorize the -`BigSimmo/PsychSift` repository, and ensure the installation grants the user write access if -Cloud tasks must publish PRs. Repository discovery proves read access only. - -For an explicitly authorised GitHub task, use Cloud's authenticated GitHub integration -as the remote control plane. For ordinary Cloud work, publish through the native task -diff/PR controls and verify the returned GitHub branch and pull-request link. Do not infer -that GitHub is unavailable merely because `gh`, shell Git credentials, or a particular -direct agent tool are absent. The intended GitHub identity is `BigSimmo`. Use repository -write access for branch and pull-request publication; reserve administrator access for separately -approved operations. Some GitHub APIs, including review-thread or Actions management, may not be -exposed in every Cloud task. When the user explicitly authorises the shell fallback, configure it -only through an encrypted, setup-only `CODEX_CLOUD_GITHUB_PAT` environment secret and require the -live acceptance command below to pass before the task starts. Never place its value in a prompt, -repository file, ordinary environment variable, command argument, remote URL, log, or -documentation. - -GitHub connector permission is separate from credentials inside the agent shell. The connector, -native Push control, and GitHub UI remain the preferred Cloud publication and cleanup paths. The -explicit shell fallback necessarily leaves an authenticated `gh` credential helper available to -the agent, so it has a wider trust boundary than the connector. The setup secret itself -is still excluded from the agent environment and the generated shell profile. The live gate never -reads or prints the credential: it verifies the exact `BigSimmo` identity, `BigSimmo/PsychSift` -write permission, `repo`, `workflow`, `read:org`, and `gist` scopes, PR metadata/diff/check/comment/Actions -reads, review-thread reply and resolution schema, repository review-thread reads and resolve permission, -successful Actions job metadata, log access -through a body-free HTTP `HEAD` against a non-skipped job, the rerun command surface, the -credential-free origin, authenticated fetch, -and an ordinary feature-branch push through `--dry-run`. The probe alone disables local hooks so -the real-push verification suite cannot make setup hang; actual pushes retain those guards. It -uses an unguessable task-local probe name, checks that exact ref before and afterward, and fails if -any remote ref appears. -Each provider subprocess has a 30-second bound. Provider failures classified as transient (rate -limiting, GitHub 5xx, DNS/TLS, timeout, or connection reset) receive at most three attempts with -short exponential backoff. Authentication, permission, -schema, unsafe-origin, and branch-policy failures fail immediately rather than being hidden by retries. - -`bash scripts/delete-codex-cloud-branch-with-pat.sh ` is retained only for -an explicitly authorised operator running outside Codex Cloud. It rejects `CODEX_CLOUD=1`, -protected/invalid refs, and any origin other than the credential-free -`https://github.com/BigSimmo/PsychSift.git`. Never copy a PAT into a Cloud task, profile, checkout, -remote URL, cache, or log. - -Setup restores a missing `origin` to the credential-free URL -`https://github.com/BigSimmo/PsychSift.git`; it preserves an existing correct remote and fails -instead of overwriting a wrong or credential-bearing remote. When GitHub CLI authentication is -already available, setup asks `gh auth setup-git` to install its token-free helper command. It -never embeds a token or invents a PAT. It also fetches `origin/main`, stores the current task's merge -base outside the checkout, and exports -that exact 40-character SHA as `CODEX_CLOUD_EXPECTED_BASE_SHA` in subsequent agent shells. `git -ls-remote` and a dry-run push remain separate acceptance checks; if the connector does not expose -shell Git authentication, report that platform capability gap. - -Suggested GitHub acceptance task: - -```text -Read AGENTS.md and docs/codex-cloud.md. Create a task-specific branch, add one harmless -documentation-only line, commit it, and record its branch name and full 40-character HEAD SHA. -Use the Codex GitHub workflow to publish that exact existing branch and create a draft pull -request. Do not recreate, rename, amend, rebase, or rebuild the branch or commit. Do not merge. -Report whether repository clone, branch publication, and draft PR creation each succeeded, then -report the expected and published branch names and full HEAD SHAs and whether they match exactly. -If branch publication or draft PR creation succeeds, include its link. For each failed write, -report the failure and state that no link is available. Remove the draft branch/PR only after I -approve cleanup. -``` - -## Setup and maintenance - -For the native connector/operator path, configure the following complete commands in the -environment UI: - -```bash -bash scripts/setup-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh -``` - -```bash -bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh -``` - -For the explicitly authorised GitHub shell fallback, store the credential only as the encrypted -setup secret named `CODEX_CLOUD_GITHUB_PAT`, select `CODEX_CLOUD_ACCESS_PROFILE=connected`, and use -the tracked lifecycle wrapper below. Never substitute a literal credential. The token must carry -`repo`, `workflow`, `read:org`, and `gist`: `gh auth login --with-token` requires the latter two in -addition to the repository scope, while failed-job reruns require `workflow`. - -```bash -bash scripts/run-codex-cloud-github.sh setup -``` - -```bash -bash scripts/run-codex-cloud-github.sh maintenance -``` - -The repaired hosted environments use these commands when the checked-out branch contains the -wrapper, retaining the prior working inline commands for older branches. After this change is -merged, new `main` tasks adopt the tracked implementation automatically; opening a PR alone does -not change the scripts present on `main`. - -The final command is a fail-closed task admission gate, not an optional diagnostic. A cache hit, -accepted OAuth token, repository listing, or connected-environment label is insufficient by itself. -The wrapper validates authentication, identity and scopes before installing dependencies, suppresses -login output and shell tracing, and removes the setup-secret variable before invoking repository -setup. It deliberately uses `gh`'s standard credential store for this owner-authorized shell -capability; the credential must never be copied into repository files, shell profiles, remote URLs, -ordinary environment variables, or prompts. This is an explicit exception for GitHub shell -authentication, not authorization to persist OpenAI, Supabase, Railway or other provider secrets. -Maintenance reuses valid stored authentication when setup-only secrets are absent, and fails closed -if it is missing, expired, or insufficient. The full final gate still verifies PR/Actions reads and -the Git transport dry run after toolchain repair. An authentication-only PASS is not that verdict. -If a damaged cache lacks Node, the wrapper first restores the configured major using the existing -`nvm` installation so the authentication preflight does not block normal lifecycle recovery. - -After changing configuration or credentials, obtain fresh-task proof. Reset the environment cache -only if a new task still uses stale setup. Resume older tasks and verify them individually; a running -agent does not acquire changes automatically. Cloud may reconstruct a local commit under a different -SHA when resuming: verify the current diff, actual HEAD, destination branch, and remote PR before -publishing instead of trusting a SHA from an earlier answer. - -Credentials can expire or be revoked; no setup script can prevent that. Replace the encrypted secret, -then rerun the same live gate. Keep clinical providers offline and use an appropriate network -allowlist. See [the Cloud reliability audit](audit/codex-cloud-reliability-20260907.md) for the -verified environments and remaining capability boundaries. - -The maintenance command reasserts the safe `origin`, runs static/effective environment -acceptance, then runtime acceptance. The shim installer runs after either lifecycle command and -repairs the normal Node command boundary. Any runtime, dependency, CLI, Deno, Python/OCR, or -browser drift reruns the full setup instead of repairing only `node_modules`. All profile -insertions, command shims, CLI installs, and remote repair are idempotent. - -### Automatic setup diagnostics - -Cloud setup tracks its current phase. If a command fails, the error trap runs -`npm run diagnose:codex-cloud` automatically and prints sanitized `ISSUE` and `FIX` lines after -the original error. The diagnostic checks the Node/npm contract, the active Cloud Python runtime, -the runtime-specific Python lock header, and the failed setup phase without printing environment -values or calling a provider. Run it manually in an agent shell when setup completed but the -runtime later appears stale: - -```bash -npm run diagnose:codex-cloud -``` - -The command is Cloud-only: outside a `CODEX_CLOUD=1` shell it reports `NOT_APPLICABLE` rather than -mistaking a desktop Python installation for Cloud runtime drift. - -The production worker image and Codex Cloud deliberately use separate hashed Python locks because -medspaCy 1.3.1 requires spaCy `<3.8` on Python 3.11 but `>=3.8` on Python 3.12. Production uses -`worker/python/requirements.txt` (Python 3.11); Cloud uses -`worker/python/requirements-cloud.txt` (Python 3.12). After changing -`worker/python/requirements.in`, regenerate and verify both locks with their matching interpreters. - -## Acceptance - -Run this in a fresh Cloud task before relying on the environment: - -```text -Read all applicable AGENTS.md files and docs/codex-cloud.md. State whether this is the -offline or connected profile. Report tool versions without printing environment values. -Run npm run check:codex-cloud, npm run check:runtime, -npm run check:installed-lock-parity, and npm run check:codex-cloud -- --runtime. Confirm that -setup exported CODEX_CLOUD_EXPECTED_BASE_SHA as a verified 40-character ancestor. Do not call a -provider unless this task explicitly names and authorizes that provider. Report the decisive -line from every command and any unrun check. -``` - -Expected decisive lines include: - -```text -[Codex Cloud Check] PASS: static and environment Cloud contracts match. -[Codex Cloud Check] PASS: static, environment, and runtime Cloud contracts match. -``` - -The effective-environment check runs automatically when `CODEX_CLOUD=1`, including without -`--runtime`, so a newly started agent shell cannot pass with stale modes. Its report prints only -approved mode values and presence booleans. The runtime check additionally verifies Node/npm -policy and installed-lock parity, the pinned Codex CLI, Deno 2, Python 3 and worker imports, -Tesseract, actual headless launch-and-close for Chromium/Firefox/WebKit, the Python requirements -fingerprint plus `pip check` and medspaCy/spaCy versions, the expected base commit as an ancestor -of HEAD, the `BigSimmo/PsychSift` origin identity, offline credential absence when applicable, -and obsolete npm proxy variable names without reading or printing their values. It reports -the full current HEAD, local main and origin/main when present, expected base, ancestry result, -and a separate freshness state. Setup and maintenance use the process-local -`CODEX_CLOUD_PROVISIONING=1` flag so an unavoidable task-only checkout reports -`freshness=unverified` without entering a repair loop. The explicit acceptance command does -not set that flag and fails unless the setup-generated `CODEX_CLOUD_EXPECTED_BASE_SHA` proves the -intended base. -MCP inspection emits server names, commands, and environment variable names only. - -A repository cannot remove a variable already inherited by the top-level task process. Before -sourcing any profile or invoking node/npm in a fresh task, run: - -```bash -bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh -``` - -The probe checks the complete provider-variable inventory and prints names only. Outcomes are -name-scoped in the probe itself: - -- exit `0` / `PASS` — raw boundary clean -- exit `1` / `FAIL` + `STOP` — any unexpected provider name (for example - `SUPABASE_SERVICE_ROLE_KEY`); start another fresh task and do not continue -- exit `2` / `FAIL-KNOWN` + `CONTINUE-RESTRICTED` — only the documented Personal Pro launcher - defect name `OPENAI_BASE_URL` - -Exit `2` is still a failed raw boundary: humans/agents may continue only under this contract. -Do not treat “non-1” or “retry on any failure” as success in future automation — wire the three -states explicitly. When the probe reports `FAIL-KNOWN` for `OPENAI_BASE_URL` alone, preserve that -name-only output for OpenAI support and continue provider-free work only through the generated -profile and command shims, followed by a passing `npm run check:codex-cloud`. Do not generalize -that restricted path to any other inherited name. An inherited `OPENAI_BASE_URL` can redirect -OpenAI-bound traffic, so never call OpenAI clients from the raw parent process or from binaries -that bypass the profile/`node`/`npm`/`npx` shim scrub. A sanitized child shell is never proof -that the raw-parent boundary passed. - -`npm run check:production-readiness` remains useful in the offline profile for local safeguards. -Missing Supabase/OpenAI agent-phase credentials are reported as a provider capability gap and do -not make the provider-free cache unhealthy. `ALLOW_PROVIDER_TESTS=true` expresses authorization, -not credential availability; live tests still fail closed with a sanitized capability-gap message -in the offline profile. - -## Provider acceptance - -Provider access is verified separately because a generic bootstrap must not make paid or -production-like calls. The active hosted workspace is **Personal Pro**. It does not have the -dedicated-group RBAC or per-tool action disabling assumed by Enterprise/Edu instructions. Use -Railway's installed official ChatGPT app, complete browser OAuth without static tokens or headers, -set the global app policy to **Allow read actions**, and leave changes approval-gated. Authorize -only workspace `bigsimmo's Projects` and project `PsychSift` -(`5deaad0b-675a-4c13-978e-5ca2b5b877f9`) where Railway offers that choice. Reduce read results to -non-secret account, project, workspace, environment, and service metadata. Railway's remote MCP -does not accept project tokens; install Railway CLI separately only for explicitly approved -local/operator workflows (for example `npm run check:env-parity -- --railway`). That CLI path is -not available in ordinary Cloud tasks and is not part of Cloud runtime acceptance. Prefer -`RAILWAY_API_TOKEN` for personal CLI auth; never substitute the project-scoped CI -`RAILWAY_TOKEN`. Enterprise/Edu custom-app controls are an optional future governance upgrade, -not the current operating target. - -The Supabase MCP entry is scoped to production project `sjrfecxgysukkwxsowpy`, forces -`read_only=true`, and exposes only documentation/development metadata tools. The database and -debugging groups are excluded so ordinary Cloud cannot execute SQL, read clinical rows, or inspect -production logs. Complete its browser OAuth flow for the organization containing `PsychSift Production` -and restart the client if tools do not appear. Schema writes, Edge Function deployment, branching, -and storage mutations require a separately configured non-production project or branch; do not -broaden the production entry. OpenAI generation, Supabase live data, Railway changes, hosted CI -reruns, ingestion, deployment, and release workflows remain separate explicit actions. - -Project `.codex/config.toml` is the checked-in Codex Desktop/CLI MCP template. Its URL-only entries -must stay `enabled = false` in git — `npm run check:codex-cloud` hard-fails on any tracked -`enabled = true`. A trusted local operator opts in outside the committed tree: prefer enabling -`railway` in `$CODEX_HOME/config.toml`, or make a never-committed local edit to the project file for -the session, then run `codex mcp login railway`. Setup does not copy any MCP server into -`$CODEX_HOME`. Hosted ChatGPT and Codex Cloud require the separately installed/authenticated -workspace app. Start a fresh task after consent and verify the actual callable inventory. -The root `.mcp.json` is a cross-client Desktop/CLI template and static allowlist only. It does not -prove hosted Cloud availability. The checked-in Context7 integration is -Cursor local stdio, separate from this Codex Cloud Railway + Supabase allowlist. -A separately selected hosted Context7 connector has its own authentication and -availability. See the [Context7 guide](agents/context7.md) for version matching, -sanitised queries and the authorised CLI fallback; repository configuration does -not prove hosted access. - -### Personal Pro split control plane - -Personal Pro currently exposes GitHub, Slack, and Linear on the Codex connector settings page; it -does not expose Railway or Supabase there. Use the smallest functional split instead of copying -credentials into Cloud: - -- **Codex Cloud:** repository work, offline checks, and GitHub reads/publication through the native - GitHub connector or Cloud PR controls. -- **ChatGPT web:** Railway through the official OAuth app and Supabase through the pinned - project-scoped read-only app. Keep Railway on **Allow read actions** and ask before every change. -- **Desktop/CLI:** opt-in local MCP via `$CODEX_HOME/config.toml` (preferred) or a - never-committed local enable of the project `.codex/config.toml` `railway` entry, followed by - `codex mcp login railway`; this is a local operator fallback, never hosted proof. - -The repository checker prints these routes as sanitized `provider_route.*` lines. They describe -where a capability is allowed, not proof that a host installed or authenticated it. A fresh task -must still establish the callable inventory. - -Production Supabase stays project-scoped and `read_only=true`, with -`default_tools_approval_mode = "prompt"` so every production metadata/read call requires -confirmation. Do not use unrestricted SQL or query clinical rows. Railway, Figma, and Sentry -write-capable tools remain approval-gated. OAuth credentials stay in the host store—never the -tracked files or agent environment. - -For Figma, that means using the official app/plugin OAuth path for a named design task, not adding -Figma client IDs, client secrets, REST tokens, or npm registry tokens to Cloud environment -variables. Product truth remains `docs/design-system/` and the app code; design writes still need -explicit task approval. - -In a fresh connected task, first run the raw-shell probe and repository acceptance, then inspect -the actual callable tool inventory. A configured URL or `enabled = false` template is not runtime -proof. Verify Railway and Supabase with read-only identity/project metadata calls and report only -non-secret status; if either tool is absent, the host integration is not activated. - -### Connected-environment remediation checklist - -Use this checklist when a Cloud task reports a connected profile but cannot call providers, publish -its branch, or make the local health endpoint ready. Repository setup can prepare the boundary, but -the host must grant OAuth and repository access in a fresh task; do not try to repair those gaps by -copying credentials into the checkout. - -1. **Create the durable host environment.** In Codex environment settings, create or select - `PsychSift - connected` for `BigSimmo/PsychSift`. Set exactly the five non-secret values from the - connected profile above. Configure setup as - `bash scripts/setup-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh` and - maintenance as - `bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh`. - Do not add provider keys, database URLs, service-role credentials, test-user credentials, or - `ALLOW_PROVIDER_TESTS`. Connected setup writes only the managed shell-environment policy; it - never registers hosted MCP apps or writes OAuth tokens. -2. **Grant the host integrations.** In the Personal Pro workspace, install Railway's official - ChatGPT app, complete Railway OAuth, select **Allow read actions**, and keep all changes subject - to explicit approval. If a stale custom app or connector named `railway_cloud` shows an - authentication-expired reconnect banner, remove or reconnect that host-local app in the MCP/App - settings before starting a new task; repository setup cannot refresh hosted OAuth tokens. - Personal Pro has no dedicated-group RBAC or per-tool disabling, so do not claim those controls. - Authorize the Codex GitHub connector for - `BigSimmo/PsychSift` with repository write access. Complete Railway OAuth only for workspace - `bigsimmo's Projects` and project `PsychSift` (`5deaad0b-675a-4c13-978e-5ca2b5b877f9`). Complete - Supabase OAuth only for the organization containing `PsychSift Production`; retain project ref - `sjrfecxgysukkwxsowpy`, `read_only=true`, and the docs/development-only feature allowlist. Do not broaden the - production Supabase MCP to write access. Enable Figma or Sentry only for a task that names that - provider; their write-capable tools remain approval-gated. Railway's OAuth metadata advertises - `offline_access`; verify the scanned consent includes it and prove refresh behavior with a second - read-only call after the one-hour access-token lifetime. If no refresh token is issued, require - reauthentication instead of adding a token workaround. -3. **Start a fresh task.** OAuth tools and environment values are fixed when the task starts. A - setup rerun inside an already-running offline task can validate a generated connected profile, - but it cannot inject host MCP tools or retroactively grant OAuth. Restart the MCP client or open - a new task after consent. When the host exposes its app identifiers, pass that non-secret - inventory to the environment check with - `--hosted-app-inventory=github,railway,supabase`. The exact `=` form is required and the option - may appear only once; every other unsupported argument fails closed. The checker rejects stale - `railway_cloud`, shared sensitive-token patterns, and identifiers outside its reviewed connector - allowlist. It normalizes connector-name case and never echoes supplied values. Invalid input - suppresses capability output rather than printing a misleading unverified state. Inventory - remains explicitly unverified when the host does not provide it; repository config is never - substituted for this evidence. -4. **Prove the shell boundary before providers.** First run the direct raw-shell command above - before profiles or command shims. Then run `npm run check:codex-cloud`, - `npm run check:codex-cloud -- --runtime`, `npm run check:runtime`, and - `npm run check:installed-lock-parity`. Confirm the setup-generated - `CODEX_CLOUD_EXPECTED_BASE_SHA` is a full intended merge-base commit. Require the raw PASS line, - both Cloud PASS lines, correct runtime/lock - parity, `CODEX_CLOUD_ACCESS_PROFILE=connected`, no provider variable reported present, and a - credential-free matching origin. Repository MCP metadata is configuration evidence only. -5. **Prove each provider read-only.** Use the tools exposed by the fresh host session, not shell - tokens. For GitHub, read repository metadata and confirm `BigSimmo/PsychSift` plus the intended - identity. For Railway, read workspace/project/service metadata and confirm the IDs above without - triggering a deployment. Record the exact Railway inventory. Set the app to allow reads and ask - before changes. The Personal Pro global read-versus-change control does not provide tool-level - RBAC. If the account later moves to Enterprise/Edu, an admin may additionally allow only - `whoami`, `list-projects`, `list-services`, `list-feature-flags`, and `get-feature-flag`, while - disabling write/agent tools and newly discovered actions by default. For Supabase, read - project/schema metadata and confirm the pinned ref - without querying clinical row contents. Report only non-secret identity and status metadata. - OpenAI has no generic connected-profile credential: leave `RAG_PROVIDER_MODE=offline` until a - separately approved paid canary or protected workflow supplies its own credential boundary. -6. **Publish a task branch safely.** Work on a task-specific non-protected branch. Format, stage, and commit - the intended repository change, then publish that exact existing commit through the native GitHub - connector/Cloud PR workflow, and verify the remote branch and PR link. If shell Git - authentication is intentionally available, `git push --set-upstream origin ` is - acceptable after confirming the credential-free origin; otherwise a failed `git ls-remote` is - not repaired with an embedded PAT. Do not publish a generic detached `work` branch merely to - remove an informational "no upstream" warning. -7. **Separate local app proof from live readiness.** Run `npm run ensure`, use the printed URL, and - confirm `/api/local-project-id` before checking `/api/health`. In demo mode, an HTTP 503 with - missing Supabase configuration and skipped OpenAI is expected and must not be relabelled healthy. - Stop only the verified repository-owned server after the check. Production-connected health and - authenticated tests run through the protected operator/provider workflow below, not by placing - credentials in the Cloud agent shell. -8. **Treat swap as capacity control, not access setup.** Do not create persistent or ad-hoc swap by - default. First run the intended full unit suite and production build while observing available - memory. If they complete without memory pressure, record "swap not required." If a reproducible - out-of-memory failure remains after using the repository's coordinated single-heavy-command - workflow, resize the disposable environment or configure swap in the host image; do not commit a - swapfile, add it under the repository, or rely on an ephemeral in-task swapfile as a durable fix. - -Acceptance is complete only when evidence distinguishes these independent capabilities: - -| Capability | Required evidence | Not sufficient | -| ----------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| Connected boundary | Cloud environment and runtime PASS lines with provider variables absent | Editing the generated profile in one running task | -| GitHub read/write | Connector repository read plus verified publication of the exact task commit | Repository discovery, `make_pr` metadata, or a local commit | -| Railway read | Exact callable hosted-app tools plus OAuth metadata for the expected workspace/project | Installed Railway CLI or repository MCP config alone | -| Supabase read | OAuth-backed metadata for the pinned read-only project | A configured MCP URL without completed OAuth | -| OpenAI/live application | Explicitly approved paid canary or protected authenticated workflow | Setting `RAG_PROVIDER_MODE=auto` without credentials | -| Local application | Project identity plus the reported health status from the `ensure` URL | Assuming a localhost port or treating demo 503 as production-ready | -| Capacity | Full intended checks complete without OOM, or a host-level capacity change is proven | Swap size by itself | - -If a capability still fails, record the exact sanitized failure and its owner: repository setup, -Codex environment/OAuth, provider RBAC, GitHub installation, protected workflow, or host capacity. -Do not collapse those distinct boundaries into a generic "Cloud access" result. - -## Authenticated live testing - -Do not expose provider credentials to the Codex Cloud agent. Cloud secrets are removed -before the agent phase, and copying them into ordinary environment variables or files -would bypass that security boundary. - -Use the manual **Authenticated live tests** GitHub Actions workflow instead. It runs the -repository's explicit `npm run test:live` suite from protected `main`, requires the -`run-authenticated-live-tests` dispatch confirmation, records the run against the -`Database / production` environment, checks the expected Supabase project, and receives -only the GitHub secrets required by the current authenticated Supabase test. It is not -triggered by pushes, pull requests, or schedules. - -The suite is not read-only. Dispatching it explicitly authorizes the bounded mutations made -by the E2E user: production sign-in/sign-out, authenticated test requests, and rate-limit -row inserts or updates. It does not authorize unrelated production changes. - -To run it after a Codex Cloud change: - -1. Merge the reviewed change to `main` through the normal protected workflow. -2. Open **Actions → Authenticated live tests → Run workflow**. -3. Select `main` and choose `run-authenticated-live-tests` after reviewing the disclosed mutations. -4. Review the `Database / production` environment deployment and test log. - -As checked on 2026-09-27, the `Database / production` environment lists BigSimmo -as its only required reviewer, permits self-review and administrator bypass, and has no -environment secrets. The workflow's manual dispatch and explicit confirmation authorize -the bounded live-test mutations described above. The configured reviewer may pause this -one environment job, but it does not provide independent approval or gate Railway app -and worker deployments. Verify the current environment settings and an actual waiting -run before relying on a reviewer prompt. See -[`deployment-architecture.md`](deployment-architecture.md#production-release-controls-checked-2026-09-27) -for the release-control scope and steps needed for independent approval. - -Adding a future provider to `*.live.test.ts` does not automatically grant its credentials. -Expand the workflow's secret list deliberately and preserve the project/target guard. - -## RAG X3 prompt - -The corrected structure-only extraction prompt is tracked at -[`prompts/rag-coverage-gate-extraction.md`](prompts/rag-coverage-gate-extraction.md). It -uses the offline profile, keeps private coverage preparation/telemetry helpers in `rag.ts`, -and moves only the independently bounded evaluator. This avoids the import back-edge in the -older proposed three-function extraction. +See [activation and retained context](agents/codex-cloud-environment.md#activation-and-retained-context). +Host settings, repository content and loaded chat context are separate layers. Publication and +verified activation remain separate from a local edit. diff --git a/docs/decisions/2026-10-16-cloud-legacy-profile-scope.md b/docs/decisions/2026-10-16-cloud-legacy-profile-scope.md new file mode 100644 index 0000000000..8461f3647c --- /dev/null +++ b/docs/decisions/2026-10-16-cloud-legacy-profile-scope.md @@ -0,0 +1,11 @@ +# Decision: make the older Cloud bootstrap an explicitly selected setup + +- **Status:** decided 2026-10-16 +- **Source:** `docs/agents/codex-cloud-environment.md`, find "opt-in setup" +- **Retires:** `cloud-legacy-profile-default` + +Following the owner's instruction to edit the old environment settings, current tasks use verified +host capabilities. The older repository-managed profiles and their acceptance gates apply when +explicitly selected, rather than governing every Cloud environment automatically. + +If this summary and the source ever differ, the source wins. diff --git a/docs/decisions/2026-10-16-cloud-operating-policy-overhaul.md b/docs/decisions/2026-10-16-cloud-operating-policy-overhaul.md new file mode 100644 index 0000000000..71659aa2cd --- /dev/null +++ b/docs/decisions/2026-10-16-cloud-operating-policy-overhaul.md @@ -0,0 +1,29 @@ +# Decision: consolidate Cloud instructions and prevent stale launch rules returning + +- **Status:** decided 2026-10-16 +- **Source:** `docs/agents/codex-cloud-environment.md`, find "canonical operating policy" +- **Retires:** `cloud-unavailable-route-defaults` + +The owner requested a full Cloud instruction overhaul and purge of stale instructions. General +tasks follow the canonical current-host policy; obsolete task prompts are retired, model routing +uses current tools, and copied prompts and programme lifecycle instructions are covered by the +retired-rule guard. Selected specialist and production safeguards retain their own authority. +Historical machine/session state is archived rather than seeded into fresh Cloud memory; existing +host-written memories remain untouched. + +The follow-up audit applies the same policy to executable entry points and installed guidance. +Legacy setup and maintenance check inherited boundaries before runtime work; malformed selectors +cannot silently select installers or runtime probes. Profile repairs preserve invalid host settings +without echoing their contents and retain existing memory links. Copied skills and SessionStart +context follow current capabilities, task ownership and observed verification rather than dated +model lists, fresh-checkout ceremonies or assumed CI results. Temporary-profile and synthetic +environment regressions verify these paths without changing a real host. + +Further failure-path checks require valid complete tier selections and memory-source admission +before profile writes, current CLI readiness alongside cache markers, nonempty matching dependency +fingerprints and valid explicitly configured Git bases. GitHub lifecycle preflight retains only the +documented setup-PAT exception; other inherited provider names fail before authentication. A local +filesystem path is not evidence of a GitHub remote identity. Diagnostics follow the declared Node +engine range rather than accepting every version in the same major. + +If this summary and the source ever differ, the source wins. diff --git a/docs/decisions/2026-10-16-cloud-policy-repair-boundary.md b/docs/decisions/2026-10-16-cloud-policy-repair-boundary.md new file mode 100644 index 0000000000..ec3ecf1bdf --- /dev/null +++ b/docs/decisions/2026-10-16-cloud-policy-repair-boundary.md @@ -0,0 +1,11 @@ +# Decision: permit bounded local policy repair after a failed environment probe + +- **Status:** decided 2026-10-16 +- **Source:** `docs/agents/codex-cloud-environment.md`, find "A failed raw-boundary check does not prohibit an explicitly requested local policy review or repair." + +Following the owner's request to review and edit the environment policies, bounded local policy +inspection and repair can proceed after a failed probe. In a selected legacy profile, application +and provider execution remain blocked; neither a policy edit nor a sanitized child process repairs +its raw parent boundary. See the separate legacy-profile scope decision for other host environments. + +If this summary and the source ever differ, the source wins. diff --git a/docs/decisions/2026-10-16-remove-obsolete-cloud-startup-instructions.md b/docs/decisions/2026-10-16-remove-obsolete-cloud-startup-instructions.md new file mode 100644 index 0000000000..b4b0400ba5 --- /dev/null +++ b/docs/decisions/2026-10-16-remove-obsolete-cloud-startup-instructions.md @@ -0,0 +1,11 @@ +# Decision: remove obsolete Cloud startup instructions from active guidance + +- **Status:** decided 2026-10-16 +- **Source:** `docs/codex-cloud.md`, find "Use current-host capability checks." +- **Retires:** `cloud-variable-proves-host` + +Following the owner's request to remove older Cloud policies, the detailed legacy bootstrap is +archived and the active guide uses current-host evidence. Copyable prompts and workflow helpers +must not reintroduce a legacy environment marker as proof of the supplied Cloud checkout. + +If this summary and the source ever differ, the source wins. diff --git a/docs/decisions/2026-10-16-user-controlled-reasoning-effort.md b/docs/decisions/2026-10-16-user-controlled-reasoning-effort.md new file mode 100644 index 0000000000..1c91cd4167 --- /dev/null +++ b/docs/decisions/2026-10-16-user-controlled-reasoning-effort.md @@ -0,0 +1,11 @@ +# Decision: respect the reasoning picker and ordinary-language confirmation + +- **Status:** decided 2026-10-16 +- **Source:** `docs/agents/codex-reasoning-effort.md`, find "no literal marker or" +- **Retires:** `cloud-xhigh-marker-gate` + +Following the owner's request to correct these policies, the general reasoning guidance accepts +ordinary-language confirmation and does not require a marker or resubmission. Risk-based effort +recommendations remain, alongside explicit specialist programme and review contracts. + +If this summary and the source ever differ, the source wins. diff --git a/docs/organisation/retired-rules.json b/docs/organisation/retired-rules.json index 4ed2f2c9bc..0e73155fe7 100644 --- a/docs/organisation/retired-rules.json +++ b/docs/organisation/retired-rules.json @@ -1,6 +1,38 @@ { "version": 1, "rules": [ + { + "id": "cloud-unavailable-route-defaults", + "retired": "2026-10-16", + "summary": "General routing no longer imposes historical model IDs, an Astra controller or unavailable Ultra-mode subagents. Use current supported tool controls; specialist programme contracts remain scoped.", + "pattern": "Astra High is the preferred controller|Ultra-mode subagents|Luna \\(`gpt-5\\.6-luna`\\)|Terra \\(`gpt-5\\.6-terra`\\)", + "allowedContext": "no longer|retir(ed|ement)|removed|formerly|previously|superseded|dropped|replaced|historical|not required", + "decision": "docs/decisions/2026-10-16-cloud-operating-policy-overhaul.md" + }, + { + "id": "cloud-variable-proves-host", + "retired": "2026-10-16", + "summary": "A legacy CODEX_CLOUD marker is no longer required to recognise a host-managed Cloud checkout. Explicit workflow selection and verified repository/branch/HEAD checks remain mandatory where the isolation verifier is used.", + "pattern": "Proceed only when `CODEX_CLOUD=1`|Require `CODEX_CLOUD=1`|first line is the repository.s exact xhigh confirmation marker|only when the xhigh prompt marker is present", + "allowedContext": "no longer|retir(ed|ement)|removed|formerly|previously|superseded|dropped|replaced|legacy|not required", + "decision": "docs/decisions/2026-10-16-remove-obsolete-cloud-startup-instructions.md" + }, + { + "id": "cloud-legacy-profile-default", + "retired": "2026-10-16", + "summary": "The older offline/connected bootstrap is no longer imposed on every Cloud task. Use current host capabilities and select the legacy profile explicitly; its executable credential and acceptance checks remain intact.", + "pattern": "Default to `CODEX_CLOUD_ACCESS_PROFILE=offline`|The active hosted workspace is \\*\\*Personal Pro\\*\\*|both wordings stand", + "allowedContext": "no longer|retir(ed|ement)|removed|formerly|previously|superseded|dropped|replaced|legacy", + "decision": "docs/decisions/2026-10-16-cloud-legacy-profile-scope.md" + }, + { + "id": "cloud-xhigh-marker-gate", + "retired": "2026-10-16", + "summary": "The general Cloud xhigh marker/resubmission gate is retired. Respect user-selected effort and ordinary-language confirmation; specialist programme launch contracts remain separate.", + "pattern": "Cloud xhigh gate|Cloud `xhigh` confirmation gate|prompt does not contain the exact marker|resubmit the same request", + "allowedContext": "no longer|retir(ed|ement)|removed|formerly|previously|superseded|dropped|replaced", + "decision": "docs/decisions/2026-10-16-user-controlled-reasoning-effort.md" + }, { "id": "owner-approved-label-gate", "retired": "2026-09-25", diff --git a/docs/process-hardening.md b/docs/process-hardening.md index 0de7711c3f..4d075983ba 100644 --- a/docs/process-hardening.md +++ b/docs/process-hardening.md @@ -7,7 +7,8 @@ This document turns the current process review into phased, durable repo practic The [Cloud reliability audit](audit/codex-cloud-reliability-20260907.md) records passing hosted GitHub/runtime checks and the remaining external boundaries: the known raw `OPENAI_BASE_URL` launcher injection and incomplete production privacy/governance evidence. Keep the raw guard -and release validator fail-closed. GitHub connectivity is not clinical production acceptance; +fail-closed when the legacy setup is selected under [the current Cloud policy](agents/codex-cloud-environment.md). +Keep the release validator fail-closed for applicable release work. GitHub connectivity is not clinical production acceptance; new lifecycle code still needs hosted branch evidence before its PR can claim that proof. ## Testing speed playbook (pointer) diff --git a/docs/prompts/codex-architecture-maintainability-ultra-review.md b/docs/prompts/codex-architecture-maintainability-ultra-review.md index 8e46a2af01..0676115f6d 100644 --- a/docs/prompts/codex-architecture-maintainability-ultra-review.md +++ b/docs/prompts/codex-architecture-maintainability-ultra-review.md @@ -1,4 +1,4 @@ -# Codex Local Ultra — Architecture & Maintainability Review Orchestrator +# Codex — Architecture & Maintainability Review Orchestrator ## Mission @@ -75,7 +75,12 @@ If a document is missing: Act as the lead coordinator and sole writer of the final report. -Actually use Ultra-mode subagents for independent analysis. Do not merely describe delegation. +Use the current host's callable subagent tools for independent analysis when available +and authorised. Preserve the user's selected main-chat model and effort. Choose child +routes through `docs/agents/smart-agent-allocation.md` using supported controls; report +requested settings separately from observed execution. This prompt does not select a +model or require a special host mode. Use the sequential fallback below when delegation +is unavailable or disallowed. ## 1.1 First agent wave: parallel read-only discovery @@ -223,7 +228,7 @@ The lead coordinator must: - Decide the validation sequence - Remain the sole writer of the final report and any later in-scope corrections to review artifacts -If subagent spawning is unavailable: +If subagent spawning is unavailable or disallowed: - Perform the same five lanes sequentially - Explicitly report the limitation @@ -499,7 +504,7 @@ Never commit or push. After the lead coordinator synthesises findings and runs first-pass structural validation, spawn three fresh read-only reviewer agents in parallel. -If fresh reviewer subagents are unavailable: +If fresh reviewer subagents are unavailable or disallowed: - Perform the same three reviewer lanes sequentially after the first-pass synthesis - Explicitly report that independent parallel verification was unavailable diff --git a/docs/prompts/codex-cloud-design-status-semantics.md b/docs/prompts/codex-cloud-design-status-semantics.md index f2148108cc..c663a33c52 100644 --- a/docs/prompts/codex-cloud-design-status-semantics.md +++ b/docs/prompts/codex-cloud-design-status-semantics.md @@ -1,282 +1,15 @@ -# Codex Cloud prompt — design-system clinical status semantics +# Clinical status semantics — retired implementation prompt -Copy the complete prompt below into a new Codex Cloud task for the **PsychSift (`BigSimmo/PsychSift`)** -repository. This task is deliberately limited to PR 1 of the -[design-system/live-design convergence programme](../plans/design-system-live-convergence-plan.md#pr-1--clinical-status-semantics-and-baseline-provenance). -Do not combine it with the later interaction, edge, motion, density, or shared-home tranches. +The [programme's PR 1 status](../plans/design-system-live-convergence-plan.md#pr-1--clinical-status-semantics-and-baseline-provenance) +records the status-semantics implementation complete. Both corresponding +[contract metrics](../../scripts/design-system-contract-baseline.json) are pinned at zero. +Do not relaunch the completed implementation tranche. ---- +Human screenshot disposition remains a separate open gate. Use the maintained programme plan and +[design-system adoption workflow](../design-system/ADOPTION.md); only a genuine human review can +supply that approval. Do not weaken the provenance schema or substitute automated approval. -## Perfected prompt - -You are the implementation owner for **PR 1 — clinical status semantics and baseline provenance** -in the PsychSift (`BigSimmo/PsychSift`) repository. Complete the smallest safe, independently revertible -change that removes the currently recorded colour-only status meaning and status-coloured numeral. -Also resolve the screenshot-provenance contradiction without ever representing automated review as -human approval. - -### Required outcomes - -1. Classify every currently flagged occurrence as clinical state, category identity, decoration, or - false positive, based on the rendered behavior and source context—not on the detector name alone. -2. For every real state, provide persistent visible text and a second non-colour visual channel such - as icon shape, border style, or pattern. An `aria-label` alone is not a repair for sighted users. -3. Keep numerals semantically neutral; show warning/success/state meaning beside the value rather - than by colouring the number. -4. Reduce both design-contract metrics to zero without moving the same debt elsewhere: - `colourOnlyStatusIndicators: 3 → 0` and `statusColouredNumerals: 1 → 0`. -5. Make screenshot provenance truthful. Never invent a reviewer, review time, approval, or evidence. - If no genuine human disposition is available, use only a schema-supported pending state. If the - schema or gate cannot represent that truth safely, do not change provenance; report the exact - blocker in the handoff for a human reviewer. -6. Preserve clinical wording, source governance, conservative missing/stale-data behavior, existing - component contracts, responsive behavior, and assistive-technology names. - -### Authoritative starting evidence - -Read these before editing: - -- Repository rules: [`AGENTS.md`](../../AGENTS.md) and [`CLAUDE.md`](../../CLAUDE.md). -- Cloud safety and browser readiness: [`docs/codex-cloud.md`](../codex-cloud.md). -- The bounded programme and PR 1 exit criteria: - [`docs/plans/design-system-live-convergence-plan.md`](../plans/design-system-live-convergence-plan.md). -- Design-system entry point and contracts: - [`docs/design-system/README.md`](../design-system/README.md), - [`SPEC.md`](../design-system/SPEC.md), [`TOKENS.md`](../design-system/TOKENS.md), - [`COMPONENTS.md`](../design-system/COMPONENTS.md), - [`ADOPTION.md`](../design-system/ADOPTION.md), and [`GATES.md`](../design-system/GATES.md). -- Current machine-readable debt: - [`scripts/design-system-contract-baseline.json`](../../scripts/design-system-contract-baseline.json). -- Current flagged owners: - [`src/components/calculators/calculator-ui.tsx`](../../src/components/calculators/calculator-ui.tsx), - [`src/components/ui-primitives.tsx`](../../src/components/ui-primitives.tsx), and - [`src/components/clinical-dashboard/visual-evidence.tsx`](../../src/components/clinical-dashboard/visual-evidence.tsx). -- Screenshot contract and current provenance: - [`docs/design-system/adoption-contract.json`](../design-system/adoption-contract.json) and - [`tests/__screenshots__/linux/provenance.json`](../../tests/__screenshots__/linux/provenance.json). -- Likely proof owners: - [`tests/ckb-v2-token-contract.test.ts`](../../tests/ckb-v2-token-contract.test.ts), - [`tests/design-token-contract.test.ts`](../../tests/design-token-contract.test.ts), - [`tests/design-system-adoption.test.ts`](../../tests/design-system-adoption.test.ts), and - [`tests/ui-accessibility.spec.ts`](../../tests/ui-accessibility.spec.ts). -- Review and PR policy: [`docs/codex-review-protocol.md`](../codex-review-protocol.md) and - [`.github/pull_request_template.md`](../../.github/pull_request_template.md). - -Treat the paths and counts above as starting evidence, not immutable truth. Re-measure at the task -HEAD before editing. Inspect actual definitions, consumers, tests, detector logic, and generated-file -ownership. Do not manually edit a generated baseline if a repository update command owns it. - -### Authority and hard safety boundaries - -- Work in the repository's **offline Cloud profile**. Require `CODEX_CLOUD=1`, - `CODEX_CLOUD_ACCESS_PROFILE=offline`, `RAG_PROVIDER_MODE=offline`, - `NEXT_PUBLIC_DEMO_MODE=true`, and `PLAYWRIGHT_OFFLINE_MODE=true`. -- Do not call OpenAI, Supabase, Railway, GitHub/GitLab APIs, hosted CI, analytics, email, or any other - provider. Do not access production/staging systems or live data. Do not deploy, migrate, reindex, - rotate credentials, print secrets, or create `.env*` files. -- Do not install or update dependencies, switch package managers, loosen engines, or create a new - lockfile. Use the installed Node 24/npm 11 toolchain and the existing `package-lock.json`. -- Do not reset, clean, stash, rebase, force-push, delete branches, overwrite unrelated work, or - weaken tests/contracts to make a check pass. -- You may edit only the three flagged owners, the smallest directly affected tests, and generated - design-contract artifacts that the repository's normal update path demonstrably owns. - `tests/__screenshots__/linux/provenance.json` is permitted only under the truthfulness rule above. - Stop before touching unrelated routes or broad shared foundations. -- A local task commit is authorized after verification. A push or pull request is **not** authorized - by this prompt. Use native Cloud publication controls only if a separate current-task instruction - explicitly authorizes publication; never introduce shell tokens as a workaround. - -### Phase 0 — prove Cloud isolation before any write - -1. Inspect `git status --short --branch`, `git rev-parse HEAD`, upstream/ahead/behind state, relevant - recent commits, and `git worktree list --porcelain`. Check for an active merge, rebase, cherry-pick, - or revert and for repo-owned install/test/build/server processes that could race this work. -2. The checkout must be clean. If it contains any staged, unstaged, or untracked content, stop and - report it; do not absorb, move, stash, delete, or overwrite it. -3. If detached or on `main`, `master`, `develop`, or `release/*`, create - `codex/cloud-design-status-semantics`. Otherwise retain the supplied task branch. -4. Run the repository isolation verifier exactly as documented by - [Prompt Perfector's repository workflow](../../.agents/skills/prompt-perfector/references/repository-workflow.md): - - ```bash - test "${CODEX_CLOUD:-}" = "1" - test -z "$(git status --porcelain --untracked-files=all)" - branch="$(git branch --show-current)" - repo="$(git rev-parse --show-toplevel)" - head="$(git rev-parse HEAD)" - node .agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs \ - --cloud --expected-repo "$repo" --expected-branch "$branch" --expected-head "$head" - ``` - - Proceed only if it emits `SAFE_TO_EDIT=true` and `PRECHECK_RESULT=SAFE`. Re-run it immediately - before the first edit. This verifies repository workflow isolation, not an OS-level sandbox. - -5. Confirm runtime/install/browser readiness without mutating dependencies: - - ```bash - node --version - npm --version - npm run check:codex-cloud - npm run check:installed-lock-parity - npm run check:playwright-browser-revision - ``` - - A browser-revision mismatch is an environment limitation, not permission to substitute a random - browser executable. Continue with static/DOM proof and hand browser evidence to matching CI/local - infrastructure as documented in `docs/codex-cloud.md`. - -### Phase 1 — inspect, classify, and lock the plan - -State one line before planning: **plan effort high; build effort medium-high; the risk is changing -clinical state meaning or falsely certifying visual evidence.** - -1. Read the repository skills for `plan`, `ui`, `clinical`, `test`, `review`, and `handover`; apply - only their relevant steps. Do not install new skills or invent a parallel workflow. -2. Because this is a review-sensitive task, run: - - ```bash - npm run ledger:lookup -- HEAD --scope "PR 1 clinical status semantics and baseline provenance" - ``` - - Follow the verdict and `docs/codex-review-protocol.md`. Do not hand-edit ledger records. - -3. Read the relevant installed Next.js 16 guide under `node_modules/next/dist/docs/` before changing - framework-facing UI behavior. Training-data assumptions are not evidence. -4. Run the repository workflows in evidence mode for the exact candidate paths: - - ```bash - npm run workflow:flightplan -- --write-evidence --files \ - src/components/calculators/calculator-ui.tsx,src/components/ui-primitives.tsx,src/components/clinical-dashboard/visual-evidence.tsx,scripts/design-system-contract-baseline.json,tests/__screenshots__/linux/provenance.json - npm run workflow:design-sweep -- --write-evidence - npm run workflow:clinical-proof -- --write-evidence - ``` - -5. Record the measured global counts and per-path counts before editing. Inspect the contract scanner - that produces them. For each of the four flagged occurrences, write a compact classification table: - owner/symbol, rendered state, user-visible meaning, clinical risk, classification, proposed - non-colour channel, and proof owner. -6. Inspect empty, missing, stale, loading, error, and success variants. Specifically try to disprove - that a visually positive state could imply “safe/current” when evidence is absent or stale. -7. Inspect the provenance schema, validation scripts, Git history for the record, and candidate hashes. - The current text may be contradictory; it is not proof that a human reviewed the images. -8. Before implementation, produce a short executable plan naming exact symbols, tests, generated - artifacts, commands, and rollback. If classification requires a clinical/product decision, or the - safe repair exceeds this tranche, stop with the evidence and one concise blocking question. - -### Phase 2 — implement the smallest complete repair - -1. Add or adjust the smallest regression tests first where practical. Tests must prove visible - non-colour differentiation and accessible names; they must not merely snapshot implementation - classes. -2. Prefer existing design-system primitives, semantic tokens, icons, and copy conventions. Do not add - new dependencies, raw palette values, one-off token aliases, decorative motion, or broad component - abstractions. -3. For each real status: - - keep explicit persistent text; - - add a second visual channel that survives authored-colour removal and forced colours; - - keep icon-only decoration hidden from assistive technology when adjacent text already names it; - - preserve keyboard/focus behavior and at least the existing hit target; - - preserve conservative wording for unknown/missing/stale data. -4. For the flagged numeral, remove status semantics from the number itself and render the state beside - it. Ensure reading order and the accessible name remain natural. -5. For a detector false positive, improve detector/test precision only with a focused proof that the - rendered UI never conveys state by colour. Do not add an allowlist merely to hit zero. -6. Update generated contract artifacts only through the existing repository generator/update path - identified during discovery. Confirm both the global count and each original owner's count fall; - search the full diff for relocated equivalents. -7. For provenance, choose exactly one truthful path: - - if genuine human review evidence is supplied in the task context, record only that real identity, - timestamp, source HEAD, and schema-valid disposition; - - otherwise, change the record to a schema-valid pending/non-human state only if repository - contracts explicitly support it and tests prove it; - - otherwise leave the file unchanged and make human disposition an explicit local handoff gate. - Never regenerate or adopt images merely to resolve metadata wording. -8. Reinspect the entire task diff for clinical copy drift, false reassurance, token bypasses, secrets, - unrelated formatting, generated noise, and unintended public contract changes. - -### Phase 3 — adversarial UI proof - -For browser work, first run `npm run ensure`, use only the printed URL, and verify -`/api/local-project-id` identifies this repository. Do not assume a port or attach to an unknown -server. Do not leave a watcher or server running after the task. - -Exercise every changed state at the smallest relevant desktop and phone viewport, including default, -dark, forced-colours, reduced-motion, monochrome (authored colours removed/grayscale), and print. -Check keyboard navigation, visible focus, screen-reader names/reading order, 200% zoom/reflow, loading, -empty, error, stale, and success states where the component supports them. The status must remain -distinguishable when all authored colours are removed, and the UI must not imply that missing or stale -clinical evidence is safe/current. - -Capture candidate evidence only when it helps review. Do not overwrite canonical screenshots or mark -them human-approved. If browser readiness fails, preserve the exact diagnostic and create a precise -local/CI handoff rather than weakening the matrix. - -### Phase 4 — risk-scaled verification - -Run heavy commands sequentially. Start narrowly and add a gate only when it covers a distinct plausible -failure. Determine exact focused selectors from the repository rather than inventing script names. - -Minimum expected ladder: - -1. Directly affected unit/DOM tests for the changed owners and contract scanner. -2. `npm run check:design-system-contract` — proves metric and adoption/design-sync contracts. -3. The focused accessibility/browser journey from `tests/ui-accessibility.spec.ts`, if the matching - locked Chromium revision is available. -4. `npm run check:production-readiness` because the change affects clinical status presentation. -5. Inspect selection with: - - ```bash - npm run verify:pr-local -- --dry-run --files - ``` - - Then run non-dry-run `npm run verify:pr-local` once for PR-ready local handoff if selected policy - requires it. Escalate to `npm run verify:ui` only if shared UI foundations changed or the selector - requires it; do not stack it automatically after focused browser proof. - -6. Run `npm run format`, inspect its diff, include intended formatting, then run - `npm run docs:check-links`, `npm run docs:check-scripts`, and `git diff --check` when documentation - changed. - -When a check fails, reduce it to the smallest reproducer, classify it as change-caused, pre-existing, -flaky, environmental, or provider-blocked, fix only change-caused failures, and rerun the invalidated -proof. Never claim an unrun or receipt-reused check was fresh; identify receipt reuse explicitly. - -### Phase 5 — review, commit, and local handoff - -1. Review the final diff under `docs/codex-review-protocol.md`, findings first and severity-ranked. - Resolve all change-caused high-confidence findings. Append the review record only through - `npm run ledger:append` with the actual full 40-character HEAD, scope, outcome, and checks. -2. Confirm final status contains only coherent task files and allowed workflow/ledger evidence. Exclude - `.local`, caches, logs, browser output, dependencies, secrets, and unrelated artifacts. -3. Create one clear local commit. Do not push or publish unless separately authorized in the active - Cloud task. Report the commit SHA and whether the branch has an upstream. -4. Produce this handoff packet: - - outcome and changed symbols/files; - - before/after global and per-path contract counts; - - classification and repair for each original occurrence; - - adversarial matrix with pass/fail/not-run and evidence path for every state/mode; - - exact commands and decisive results, including reused receipts; - - provenance disposition, evidence basis, and any human-approval gate still open; - - branch, worktree, commit, push, and PR state; - - provider/live checks not run and why; - - remaining risks and the single smallest next action. - -For the **local handoff**, give a copy/paste command sequence that begins by checking out the reported -branch/commit, confirms Node 24/npm 11 and lock parity, runs `npm run ensure`, verifies the printed -project identity URL, and executes only the browser modes that Cloud could not prove. Name the exact -routes, viewports, states, and expected visible/non-colour outcomes. If human screenshot disposition is -still required, list candidate paths and hashes and instruct the human to approve or reject them through -the repository's documented adoption workflow—never by directly editing “approved” metadata. - -### Stop conditions - -Stop and report evidence instead of guessing if: the checkout is dirty or non-isolated; runtime or -lock parity is invalid; a provider/live action becomes necessary; a real clinical/product decision is -required; a genuine reviewer identity is unavailable; the provenance schema cannot represent the -truth; the required repair expands into another convergence tranche; the matching browser cannot be -made available without an install/environment change; or unrelated failures prevent meaningful proof. - -Completion means the bounded implementation is locally committed, all available risk-relevant checks -are honestly reported, both targeted metrics are zero without debt relocation, clinical meaning is -preserved under adversarial modes, and any inherently human or provider-backed gate is handed off -explicitly. It does **not** mean claiming perfection, human approval, deployment, or production proof. +The [original implementation prompt](../archive/codex-cloud-design-status-semantics-2026-10-16.md) +is historical task evidence. For any new authorised work, follow [repository instructions](../../AGENTS.md) +and [current Cloud policy](../agents/codex-cloud-environment.md). This notice grants no new +implementation, provider, commit or publication authority. diff --git a/docs/prompts/codex-data-database-safety-ultra-review.md b/docs/prompts/codex-data-database-safety-ultra-review.md index 417a89cfe2..6ad96673e2 100644 --- a/docs/prompts/codex-data-database-safety-ultra-review.md +++ b/docs/prompts/codex-data-database-safety-ultra-review.md @@ -1,4 +1,4 @@ -# Codex Local Ultra — Data & Database Safety Review Orchestrator +# Codex — Data & Database Safety Review Orchestrator ## Mission @@ -85,7 +85,12 @@ If a document is missing: Act as the lead coordinator and sole writer of the final report. -Actually use Ultra-mode subagents for independent analysis. Do not merely describe delegation. +Use the current host's callable subagent tools for independent analysis when available +and authorised. Preserve the user's selected main-chat model and effort. Choose child +routes through `docs/agents/smart-agent-allocation.md` using supported controls; report +requested settings separately from observed execution. This prompt does not select a +model or require a special host mode. Use the sequential fallback below when delegation +is unavailable or disallowed. ## 1.1 First agent wave: parallel read-only discovery @@ -219,7 +224,7 @@ The lead coordinator must: - Decide the validation sequence - Remain the sole writer of the final report and any later in-scope corrections to review artifacts -If subagent spawning is unavailable: +If subagent spawning is unavailable or disallowed: - Perform the same five lanes sequentially - Explicitly report the limitation @@ -485,7 +490,7 @@ Never commit or push. After the lead coordinator synthesises findings and runs first-pass validation, spawn three fresh read-only reviewer agents in parallel. -If fresh reviewer subagents are unavailable: +If fresh reviewer subagents are unavailable or disallowed: - Perform the same three reviewer lanes sequentially after the first-pass synthesis - Explicitly report that independent parallel verification was unavailable diff --git a/docs/prompts/codex-documentation-ownership-ultra-review.md b/docs/prompts/codex-documentation-ownership-ultra-review.md index e253b82813..b6afb64343 100644 --- a/docs/prompts/codex-documentation-ownership-ultra-review.md +++ b/docs/prompts/codex-documentation-ownership-ultra-review.md @@ -1,4 +1,4 @@ -# Codex Local Ultra — Documentation & Ownership Review Orchestrator +# Codex — Documentation & Ownership Review Orchestrator ## Mission @@ -87,7 +87,12 @@ If a document is missing: Act as the lead coordinator and sole writer of the final report. -Actually use Ultra-mode subagents for independent analysis. Do not merely describe delegation. +Use the current host's callable subagent tools for independent analysis when available +and authorised. Preserve the user's selected main-chat model and effort. Choose child +routes through `docs/agents/smart-agent-allocation.md` using supported controls; report +requested settings separately from observed execution. This prompt does not select a +model or require a special host mode. Use the sequential fallback below when delegation +is unavailable or disallowed. ## 1.1 First agent wave: parallel read-only discovery @@ -216,7 +221,7 @@ The lead coordinator must: - Decide the validation sequence - Remain the sole writer of the final report and any later in-scope corrections to review artifacts -If subagent spawning is unavailable: +If subagent spawning is unavailable or disallowed: - Perform the same five lanes sequentially - Explicitly report the limitation @@ -475,7 +480,7 @@ Never commit or push. After the lead coordinator synthesises findings and runs first-pass validation, spawn three fresh read-only reviewer agents in parallel. -If fresh reviewer subagents are unavailable: +If fresh reviewer subagents are unavailable or disallowed: - Perform the same three reviewer lanes sequentially after the first-pass synthesis - Explicitly report that independent parallel verification was unavailable diff --git a/docs/prompts/codex-functional-correctness-ultra-review.md b/docs/prompts/codex-functional-correctness-ultra-review.md index a64b8f2690..b8287cb75b 100644 --- a/docs/prompts/codex-functional-correctness-ultra-review.md +++ b/docs/prompts/codex-functional-correctness-ultra-review.md @@ -1,4 +1,4 @@ -# Codex Local Ultra — Functional Correctness Review Orchestrator +# Codex — Functional Correctness Review Orchestrator ## Mission @@ -86,7 +86,12 @@ If a document is missing: Act as the lead coordinator and sole writer of the final report. -Actually use Ultra-mode subagents for independent analysis. Do not merely describe delegation. +Use the current host's callable subagent tools for independent analysis when available +and authorised. Preserve the user's selected main-chat model and effort. Choose child +routes through `docs/agents/smart-agent-allocation.md` using supported controls; report +requested settings separately from observed execution. This prompt does not select a +model or require a special host mode. Use the sequential fallback below when delegation +is unavailable or disallowed. ## 1.1 First agent wave: parallel read-only discovery @@ -224,7 +229,7 @@ The lead coordinator must: - Decide the reproduction and validation sequence - Remain the sole writer of the final report and any later in-scope corrections to review artifacts -If subagent spawning is unavailable: +If subagent spawning is unavailable or disallowed: - Perform the same five lanes sequentially - Explicitly report the limitation @@ -500,6 +505,12 @@ Never commit or push. After the lead coordinator synthesises findings and runs first-pass proofs, spawn three fresh read-only reviewer agents in parallel. +If fresh reviewer subagents are unavailable or disallowed: + +- Perform the same three reviewer lanes sequentially after the first-pass synthesis +- Explicitly report that independent verification was unavailable +- Do not describe the sequential pass as independent verification, and do not omit any lane + ## Reviewer 1 — Reproduction integrity reviewer Review: diff --git a/docs/prompts/codex-performance-reliability-ultra-review.md b/docs/prompts/codex-performance-reliability-ultra-review.md index 0a0dcb3354..ebfe02d810 100644 --- a/docs/prompts/codex-performance-reliability-ultra-review.md +++ b/docs/prompts/codex-performance-reliability-ultra-review.md @@ -1,4 +1,4 @@ -# Codex Local Ultra — Performance & Reliability Review Orchestrator +# Codex — Performance & Reliability Review Orchestrator ## Mission @@ -68,7 +68,12 @@ If a document is missing: Act as the lead coordinator and sole writer of the final report. -Actually use Ultra-mode subagents for independent analysis. Do not merely describe delegation. +Use the current host's callable subagent tools for independent analysis when available +and authorised. Preserve the user's selected main-chat model and effort. Choose child +routes through `docs/agents/smart-agent-allocation.md` using supported controls; report +requested settings separately from observed execution. This prompt does not select a +model or require a special host mode. Use the sequential fallback below when delegation +is unavailable or disallowed. ## 1.1 First agent wave: parallel read-only discovery @@ -207,7 +212,7 @@ The lead coordinator must: - Decide the measurement sequence - Remain the sole writer of the final report and any later in-scope corrections to review artifacts -If subagent spawning is unavailable: +If subagent spawning is unavailable or disallowed: - Perform the same five lanes sequentially - Explicitly report the limitation @@ -475,6 +480,12 @@ Never commit or push. After the lead coordinator synthesises findings and runs first-pass measurements, spawn three fresh read-only reviewer agents in parallel. +If fresh reviewer subagents are unavailable or disallowed: + +- Perform the same three reviewer lanes sequentially after the first-pass synthesis +- Explicitly report that independent verification was unavailable +- Do not describe the sequential pass as independent verification, and do not omit any lane + ## Reviewer 1 — Measurement integrity reviewer Review: diff --git a/docs/prompts/codex-tests-quality-gates-ultra-review.md b/docs/prompts/codex-tests-quality-gates-ultra-review.md index f21dbb9831..a647b28346 100644 --- a/docs/prompts/codex-tests-quality-gates-ultra-review.md +++ b/docs/prompts/codex-tests-quality-gates-ultra-review.md @@ -1,4 +1,4 @@ -# Codex Local Ultra — Tests & Quality Gates Review Orchestrator +# Codex — Tests & Quality Gates Review Orchestrator ## Mission @@ -84,7 +84,12 @@ If a document is missing: Act as the lead coordinator and sole writer of the final report. -Actually use Ultra-mode subagents for independent analysis. Do not merely describe delegation. +Use the current host's callable subagent tools for independent analysis when available +and authorised. Preserve the user's selected main-chat model and effort. Choose child +routes through `docs/agents/smart-agent-allocation.md` using supported controls; report +requested settings separately from observed execution. This prompt does not select a +model or require a special host mode. Use the sequential fallback below when delegation +is unavailable or disallowed. ## 1.1 First agent wave: parallel read-only discovery @@ -221,7 +226,7 @@ The lead coordinator must: - Decide the measurement sequence - Remain the sole writer of the final report and any later in-scope corrections to review artifacts -If subagent spawning is unavailable: +If subagent spawning is unavailable or disallowed: - Perform the same five lanes sequentially - Explicitly report the limitation @@ -484,6 +489,12 @@ Never commit or push. After the lead coordinator synthesises findings and runs first-pass measurements, spawn three fresh read-only reviewer agents in parallel. +If fresh reviewer subagents are unavailable or disallowed: + +- Perform the same three reviewer lanes sequentially after the first-pass synthesis +- Explicitly report that independent verification was unavailable +- Do not describe the sequential pass as independent verification, and do not omit any lane + ## Reviewer 1 — False-green and flake reviewer Review: diff --git a/docs/prompts/mode-aware-clinical-ask-codex-cloud-handover.md b/docs/prompts/mode-aware-clinical-ask-codex-cloud-handover.md index 232db34a30..56d527fd09 100644 --- a/docs/prompts/mode-aware-clinical-ask-codex-cloud-handover.md +++ b/docs/prompts/mode-aware-clinical-ask-codex-cloud-handover.md @@ -136,26 +136,32 @@ merge, rebase, or pull the stale original planning branch into the implementatio ## Preflight and isolation -1. Work from a fresh, clean Codex Cloud checkout based on current `origin/main`. Fetching for read-only - comparison is allowed. Do not edit protected `main`, a detached checkout, or a dirty/shared worktree. +1. For a new task, work from a fresh, clean Codex Cloud checkout based on current `origin/main`. + For a same-task continuation, retain the established task checkout and verify its original base, + current `HEAD`, owned changes and checkpoint. Fetching for read-only comparison is allowed. + Do not edit protected `main`, a detached checkout or another task's worktree. Preserve unrelated work. 2. Create or use a task-specific non-protected branch following the Cloud isolation contract. Do not run the Windows task-start script in Linux and do not overwrite, stash, reset, clean, or absorb existing work. -3. Run `bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh`, then the acceptance commands named in - `docs/codex-cloud.md`: `npm run check:codex-cloud`, `npm run check:runtime`, - `npm run check:installed-lock-parity`, `npm run check:playwright-browser-revision`, and - `npm run check:codex-cloud -- --runtime`. Record every exit code and decisive line without printing - environment values. +3. Read `docs/codex-cloud.md` and establish current-host readiness. Run the runtime, installed-lock + and browser-revision checks relevant to this implementation. The archived raw-probe/profile + acceptance procedure applies only if that legacy setup is explicitly selected. Record each + check's scope, exit code and decisive line without printing credential values. 4. Inspect branch, upstream, full HEAD, worktree list, concise status, relevant recent history, runtime, package-manager version, and active repository-owned processes. -5. In Cloud, keep the repository's strict Node 24 and npm 11 contract. Use the documented Cloud setup; - do not weaken engines or package-manager checks. +5. Keep the repository's strict Node 24 and npm 11 contract. Use the current host's supported + provisioning; do not weaken engines or package-manager checks or force a legacy bootstrap. 6. If the prompt-perfector verifier exists, run it before editing. For a clean Cloud task, use its `--cloud` mode with the expected repository, branch, and HEAD. Proceed only on - `SAFE_TO_EDIT=true` and `PRECHECK_RESULT=SAFE`. + `SAFE_TO_EDIT=true` and `PRECHECK_RESULT=SAFE`. For an intentional same-task dirty + continuation, first run without `--allow-dirty`, inventory every changed path and record the + emitted `SAFE_STATUS_HASH`. Rerun with `--allow-dirty --expected-status-hash ` plus the + same `--cloud`, expected repository, branch and `HEAD`, requiring both SAFE outputs. + The hash does not grant ownership of unrelated changes. 7. Run the plan's repository flightplan against the refreshed path set and retain its new evidence. Use its current classification to refine verification, not to broaden product scope. -Stop before editing if the checkout is dirty or protected, the binding files are missing, another +Stop before dependent edits if dirty-state ownership is unresolved, the continuation status hash changed, +the branch is protected, the binding files are missing, another task owns the worktree, the plan has already been partially implemented in an incompatible shape, or the current mainline creates a material design conflict. Report all blockers together in one concise ask. Do not ask low-yield or routine questions that inspection can answer. @@ -249,8 +255,10 @@ the approved contract. Do not replace source evidence with generated summaries, thought/provider internals/retrieval scores, log raw questions or context, or weaken validation, access scope, clinical safety, tests, types, or source governance to get green checks. -Load `superpowers:executing-plans` first. It owns admission, the sequential task graph, blocker handling, and -closeout. Then inspect the current session's actual skills and tools: +Inspect the current session's actual skills and tools first. When available, load +`superpowers:executing-plans` for admission, the sequential task graph, blocker handling and +closeout. If that skill is unavailable, apply the approved plan's task order, verification and +checkpoint contracts directly and report that limitation; do not invent a skill invocation. - If `superpowers:subagent-driven-development`, its review/finish dependencies, callable `spawn_agent`, `followup_task`, `wait_agent`, and `list_agents` equivalents, and a worker slot are all available, use the @@ -258,14 +266,19 @@ closeout. Then inspect the current session's actual skills and tools: dispatch one fresh clean-context implementer per task; require a local task commit; verify the report; dispatch a fresh read-only task reviewer; send fixes back to the same implementer; and advance only after approval. Never run parallel implementation agents in the shared checkout. -- If any capability is absent, record the reason once and use the plan's **inline executing-plans route** for the - identical checkboxes. Do not pause merely because a local-only plugin or subagent tool is unavailable, and do +- If SDD is unavailable, use the plan's inline executing-plans route when that skill exists; otherwise + record `Execution route: direct sequential` and follow the identical checkboxes with current tools. + Record the missing capability once. Do not pause merely because a plugin or subagent tool is unavailable, and do not fabricate subagent/review evidence. -For every subagent spawn, explicitly select a model from the current allowed list, set reasoning effort, and use -clean context rather than inheriting the full controller conversation. Use bounded 5–10 minute event waits rather -than short polling. The controller must verify diffs, commits, and decisive test output itself. After Task 12, run -one fresh whole-branch review and at most one fix wave before the selected final gate. +For each authorised subagent spawn, use the current host's supported model/effort controls and +`docs/agents/smart-agent-allocation.md`, preserving required review floors. If those controls are +absent, report that the requested route cannot be enforced; never invent arguments or claim an +observed route. Use bounded context and event waits suited to the actual tools. The controller must +verify diffs, commits and decisive test output itself. After Task 12, obtain +one fresh whole-branch review and at most one fix wave before the selected final gate. If an +independent reviewer is unavailable, continue authorised local verification and leave that +independent review explicitly unmet; a controller self-review does not satisfy it. Do not force all 12 tasks into one Cloud context. Use clean, reviewed task commits and the plan's ignored SDD ledger as same-checkout compaction recovery. Prefer checkpoint tranches 1–4, 5–8, and 9–12. If session budget is diff --git a/docs/prompts/rag-coverage-gate-extraction.md b/docs/prompts/rag-coverage-gate-extraction.md index 5e28d3049b..8382ca74cd 100644 --- a/docs/prompts/rag-coverage-gate-extraction.md +++ b/docs/prompts/rag-coverage-gate-extraction.md @@ -1,153 +1,10 @@ -# X3 / #086 `rag.ts` coverage-gate extraction +# Coverage-gate extraction — retired implementation prompt -Use this prompt in a fresh Codex Cloud task with -`CODEX_CLOUD_ACCESS_PROFILE=offline`. It supersedes the older proposal to move the -three-function block through `applyCoverageGateTelemetry`; current dependency inspection shows -that the two private helpers are orchestration-owned and cannot move without a back-edge. +The extraction is implemented: [rag-coverage-gate.ts](../../src/lib/rag/rag-coverage-gate.ts) +owns `evaluateEvidenceCoverageGate`, and [rag.ts](../../src/lib/rag/rag.ts) imports and re-exports it. +Do not relaunch the old extraction against the current tree. -## Perfected prompt - -Safely implement the X3 / #086 `rag.ts` decomposition in the PsychSift (`BigSimmo/PsychSift`) repository. - -### Objective - -Make one architecture-only extraction from `src/lib/rag/rag.ts` into the new file -**src/lib/rag/rag-coverage-gate.ts**. Preserve retrieval behavior, ordering, thresholds, -fallbacks, citations, scope enforcement, telemetry, and the public `@/lib/rag/rag` API. - -Move only: - -- `evaluateEvidenceCoverageGate` (anchor on the declaration, not stale line numbers); -- its directly owned private `visualEvidenceUnitTypes` constant. - -Keep `prepareCoverageGateResults` and `applyCoverageGateTelemetry` in `rag.ts`. They are -orchestration-owned and depend on `rag.ts` hydration, selection, timing, metadata-cache, and -telemetry state. Do not move them, inject their dependencies, or create an import back-edge. - -The evaluator's other dependencies already come from `@/lib/clinical-search`, -`@/lib/rag/rag-evidence-gates`, and the shared `SearchResult`/`RagQueryClass` types. Import those -directly in the new module. Preserve the evaluator body byte-for-byte wherever practical. - -RAG impact: no retrieval behaviour change — pure module extraction - -No live eval canary is required because this task changes no behavior. State that explicitly in -the handoff so a reviewer can disagree cheaply. - -### Provider and Git boundaries - -This task is completely provider-free even if the Cloud environment supports connected work. - -Do not call OpenAI, Supabase, Railway, GitHub/GitLab APIs, hosted CI, or production-like services. -Do not fetch, pull, push, commit, open a PR, inspect remote PR metadata, deploy, ingest, reindex, -run drift checks, start a server, or run any live retrieval/answer-generation/canary/release -workflow. Do not read or print credentials or environment-variable values. Leave publication as -a separate operator-authorized handoff. - -### Startup and mandatory stop checks - -1. Work only in the isolated Cloud checkout supplied for this task. -2. Read all applicable `AGENTS.md` files before editing. -3. Report exactly: `The Windows-only startup script is unavailable in Codex Cloud.` Then perform - equivalent read-only repository identity, branch, `HEAD`, upstream, status, staged/unstaged/ - untracked, worktree, Git-operation-marker, package-script, and recent-history checks. -4. Proceed only when `CODEX_CLOUD=1`, the checkout has exactly one Git worktree, the branch is a - clean non-protected task branch, and local `origin/main` is available as its base. Do not fetch. -5. Read `docs/codex-cloud.md` and run the dependency-free isolation verifier from the - prompt-perfector repository workflow with `--cloud` before editing. -6. Read the required RAG behavior, process-hardening, workorder, maintainability-budget, contract - test, and package-script documents named below. -7. Measure `rag.ts` with the exact line-count logic in - `scripts/check-maintainability-budgets.mjs` before editing. Expected base is approximately - 5,030 lines. -8. Stop without editing if `rag.ts` is already a small facade; the extraction already exists; the - three near-complete pipeline-copy modules from the rejected design exist; the target area is - dirty; the branch is protected; the checkout is not isolated; the evaluator requires a - signature change or import cycle; or expected reclaimed headroom is under 150 lines. - -Before editing, state exactly: - -`RAG protected surface: this task touches src/lib/rag/**. Intended impact is no retrieval behaviour change; this is a structure-only extraction.` - -### Required reading - -- `docs/rag-behaviour/README.md` and its linked behavior map, refuted approaches, and safeguards; -- `docs/process-hardening.md`, especially the `rag.ts` decomposition sections; -- `docs/maturity-backlog-workorders.md`, X3; -- `scripts/check-maintainability-budgets.mjs`; -- relevant architecture-boundary, retrieval-query-variants, RAG contract, cache, latency, and - early-exit tests; -- `package.json` verification scripts. - -### Implementation - -1. Create **src/lib/rag/rag-coverage-gate.ts** containing the moved constant and evaluator. -2. Import `SearchResult`, `RagQueryClass`, `classifyRagQuery`, and the evaluator's existing - evidence predicates from their current owner modules. Do not duplicate helpers. -3. In `rag.ts`, import the evaluator for its two internal call sites and explicitly re-export it - so `tests/retrieval-query-variants.test.ts` and all existing public consumers remain unchanged. - This re-export strategy preserves the public API and avoids consumer churn. -4. Do not reorder code, change a signature, rename a symbol, remove an `await`, change a literal, - threshold, comparator, default, fallback, telemetry field, timing boundary, cache/abort path, - scope check, evidence selection, or comment within the moved function. -5. Do not duplicate any orchestration pipeline or materially increase total RAG implementation - size beyond import/export overhead. -6. After proof, update only the `src/lib/rag/rag.ts` limit in - `scripts/check-maintainability-budgets.mjs` from 5,030 to the new exact measured line count. - Keep the reclaimed budget comment and do not raise any budget. -7. Update maintained architecture documentation only if module ownership/path mapping genuinely - changed. Do not modify #098, #099, #100, or #101. - -Preserve owner/document scope checks, admission-before-scope ordering, retrieval and released -result ordering, cache and abort semantics, conservative fallbacks, citation/numeric grounding, -source governance, telemetry names/timing boundaries, and error/rollback behavior. - -### Verification - -Respect the cross-worktree heavy-command coordinator. Never bypass or delete its lock. If an -exclusive gate is held by another worktree, report that command as unrun with the lock owner and -reason. - -Use Node 24, npm 11, and npm. Check that `node_modules` is populated, not merely present. If it is -absent or stale, use the repository Cloud maintenance/setup procedure only after the coordinator -allows installation; do not change manifests or lockfiles. - -Run narrowly, then widen locally: - -1. `npm run workflow:rag-lab -- --write-evidence` -2. `npm run test -- tests/retrieval-query-variants.test.ts tests/rag-tail-latency.test.ts tests/rag-shared-cache.test.ts tests/rag-variant-early-exit.test.ts` -3. `npm run check:maintainability-budgets` -4. `npm run check:rag:fixtures` -5. `npm run eval:rag:offline` -6. `npm run check:knip` -7. `npm run typecheck` -8. `npm run lint` -9. `npm run verify:cheap` -10. Run `npm run format` (whole-tree Prettier) and commit the result before pushing (or run `npm run format:check` when in a read-only/no-commit Cloud session). -11. `git diff --check` - -For every command, record its real exit status and quote the decisive output line. Do not pipe a -gate through `tail`; if output filtering is unavoidable, preserve the original process status. -An admission timeout or contention result is not a pass. - -### Final review and handoff - -Review the complete diff for accidental logic changes, copied pipeline bodies, changed ordering, -thresholds/defaults, missing exports, import cycles, unrelated files, generated artifacts, and -secrets. Confirm the worktree again immediately before handoff. - -Report: - -- the responsibility extracted and why it is cohesive; -- files changed; -- `rag.ts` line count before and after; -- total `src/lib/rag` LOC and diff statistics proving move rather than copy; -- the explicit `rag.ts` re-export strategy and preserved public import; -- every check with status and decisive line, plus every unrun check and why; -- current branch/worktree status and remaining coupling/risk; -- explicit confirmation that retrieval, ranking, clinical, provider, and safety behavior were - not intentionally changed; -- explicit confirmation that no provider/API call, live canary, commit, push, PR, hosted CI, - deployment, ingestion, reindex, or release occurred; -- a short handoff noting that #098 and #099 remain separate, and that the planned - **src/lib/rag/rag-hydration.ts** module is the intended #101 follow-up rather than part of this - task. +The [original prompt](../archive/rag-coverage-gate-extraction-2026-10-16.md) is retained as historical +task evidence. For new work, establish a current objective and follow [repository instructions](../../AGENTS.md), +[current Cloud policy](../agents/codex-cloud-environment.md) and [RAG safeguards](../rag-behaviour/README.md). +The archived prompt grants no provider, canary, commit or publication authority. diff --git a/docs/scripts-index.md b/docs/scripts-index.md index 8b191e29af..be7ee21c06 100644 --- a/docs/scripts-index.md +++ b/docs/scripts-index.md @@ -17,30 +17,30 @@ migration has shipped (see `docs/maturity-backlog-workorders.md` L1). ## Runner & guard infrastructure [infra] -| Script | Role | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | -| `ci-coverage.mjs` | Derives whether `.github/workflows/ci.yml` actually re-runs a named gate for a given change scope (step/job `if:` guards evaluated, not just presence in the YAML); used by `browser-test-plan.mjs` to report honest CI coverage | -| `gate-receipts.mjs` | Content-addressed memoisation of `lint`/`typecheck`/Vitest so an unchanged gate is never rerun locally (never in CI) | -| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | -| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | -| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | -| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | -| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `check-codex-cloud-raw-env.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `check-github-shell-access.mjs`, `ensure-codex-cloud-git-remote.mjs` | Reproducible Codex Cloud toolchain/profile setup, profile-loading Node command shims, a pre-profile name-only credential probe (`FAIL-KNOWN`/`exit 2` only for documented `OPENAI_BASE_URL`; hard `STOP` otherwise), an operator-only non-Cloud PAT branch-deletion helper, sanitized Personal Pro capability/route acceptance, fail-closed GitHub shell control-plane acceptance (`check:github-shell-access` = offline self-test; `check:github-shell-access:live` = provider-backed identity/permission/API plus non-mutating push proof), and safe credential-free `origin` repair | -| `setup-claude-cloud.sh`, `apply-claude-cloud-profile.mjs` | Claude Code on the web container parity. Tiered, marker-guarded provisioner (`profile`, `plugins`, `gh`, `deno`, `browsers`, `python`) wired as the second `SessionStart` hook via `--session`, which runs the cheap tiers inline and detaches the ten-minute browser/OCR ones; plus the applier that unpacks `.claude/cloud-profile/` into the container's `~/.claude` (settings deep-merged, memories never clobbered). Both refuse to run unless `CLAUDE_CODE_REMOTE=true`. See `docs/claude-cloud.md` | -| `ci-change-scope.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:pr-policy`/`check:pr-mergeability`) | -| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | -| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | -| `check-ledger-stamp-retention.mjs` | On-demand branch-safety check (`check:ledger-stamp-retention`, not wired into CI/`verify:cheap`): derives touched `docs/outstanding-issues.md` row IDs from git history between merge-base and `HEAD`, then verifies each still carries its branch content post-merge/rebase, catching silent conflict-resolution loss | -| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | -| `audit-merge-loss.mjs` | Advisory blob-comparison sweep for merged PRs a later merge resolution silently reverted (`audit:merge-loss`); names the PR and files and exits 0 — a deliberate revert is identical at blob level, so a positive needs human confirmation | -| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | -| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | -| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | -| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs`, `worktree-inventory.mjs` | Cross-worktree lease arbitration for the primary checkout, plus report-only registered-worktree and explicit-root fleet inventory | -| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | -| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | -| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | +| Script | Role | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | +| `ci-coverage.mjs` | Derives whether `.github/workflows/ci.yml` actually re-runs a named gate for a given change scope (step/job `if:` guards evaluated, not just presence in the YAML); used by `browser-test-plan.mjs` to report honest CI coverage | +| `gate-receipts.mjs` | Content-addressed memoisation of `lint`/`typecheck`/Vitest so an unchanged gate is never rerun locally (never in CI) | +| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | +| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | +| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | +| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | +| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `check-codex-cloud-raw-env.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `check-github-shell-access.mjs`, `ensure-codex-cloud-git-remote.mjs` | Opt-in legacy Codex Cloud toolchain/profile setup, profile-loading Node command shims, a pre-profile name-only credential probe (`FAIL-KNOWN`/`exit 2` only for documented `OPENAI_BASE_URL`; hard `STOP` otherwise), an operator-only non-Cloud PAT branch-deletion helper, sanitized Personal Pro capability/route acceptance, fail-closed GitHub shell control-plane acceptance (`check:github-shell-access` = offline self-test; `check:github-shell-access:live` = provider-backed identity/permission/API plus non-mutating push proof), and safe credential-free `origin` repair | +| `setup-claude-cloud.sh`, `apply-claude-cloud-profile.mjs` | Claude Code on the web container parity. Tiered, marker-guarded provisioner (`profile`, `plugins`, `gh`, `deno`, `browsers`, `python`) wired as the second `SessionStart` hook via `--session`, which runs the cheap tiers inline and detaches the ten-minute browser/OCR ones; plus the applier that unpacks `.claude/cloud-profile/` into the container's `~/.claude` (settings deep-merged, memories never clobbered). Both refuse to run unless `CLAUDE_CODE_REMOTE=true`. See `docs/claude-cloud.md` | +| `ci-change-scope.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:pr-policy`/`check:pr-mergeability`) | +| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | +| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | +| `check-ledger-stamp-retention.mjs` | On-demand branch-safety check (`check:ledger-stamp-retention`, not wired into CI/`verify:cheap`): derives touched `docs/outstanding-issues.md` row IDs from git history between merge-base and `HEAD`, then verifies each still carries its branch content post-merge/rebase, catching silent conflict-resolution loss | +| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | +| `audit-merge-loss.mjs` | Advisory blob-comparison sweep for merged PRs a later merge resolution silently reverted (`audit:merge-loss`); names the PR and files and exits 0 — a deliberate revert is identical at blob level, so a positive needs human confirmation | +| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | +| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | +| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | +| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs`, `worktree-inventory.mjs` | Cross-worktree lease arbitration for the primary checkout, plus report-only registered-worktree and explicit-root fleet inventory | +| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | +| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | +| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | ## Verification gates [live] @@ -70,8 +70,8 @@ contract), `check-lighthouse-budget.mjs` + `run-lighthouse-budget.mjs`, `measure guard `check-pr-policy-workflow.mjs`. `audit-formatting-fixtures.ts` checks the formatting fixtures themselves. `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, and -`install-codex-cloud-command-shims.sh` reproduce the provider-free Cloud runtime documented in -`docs/codex-cloud.md`; `npm run check:codex-cloud` verifies that +`install-codex-cloud-command-shims.sh` reproduce the explicitly selected legacy profile documented in +`docs/archive/codex-cloud-legacy-profile-2026-10-16.md`; `npm run check:codex-cloud` verifies that the runtime pins, install commands, offline defaults, and documentation stay aligned. Programme execution helpers (no retrieval/ranking behaviour change): `rag-phase-launch-check.mjs`, `rag-task-brief.mjs`, `check-rag-phase-receipts.mjs`, and `build-rag-plan-packages.mjs`. diff --git a/docs/superpowers/plans/2026-08-22-mode-aware-clinical-ask-implementation.md b/docs/superpowers/plans/2026-08-22-mode-aware-clinical-ask-implementation.md index 47ac54d124..a8a9a1c357 100644 --- a/docs/superpowers/plans/2026-08-22-mode-aware-clinical-ask-implementation.md +++ b/docs/superpowers/plans/2026-08-22-mode-aware-clinical-ask-implementation.md @@ -1,10 +1,14 @@ # Mode-aware Clinical Ask Implementation Plan -> **For agentic workers:** REQUIRED SUB-SKILLS: load `superpowers:executing-plans` for plan admission, -> sequential task tracking, and branch closeout; use `superpowers:subagent-driven-development` for the -> preferred per-task implementer/reviewer loop when those skills and Codex multi-agent tools are actually -> available. Otherwise execute the identical task graph inline with `superpowers:executing-plans`. Neither -> route authorises push, PR mutation, merge, deployment, hosted migration, or live-provider work. +_Cloud admission refreshed 2026-10-16 to follow current-host policy; implementation scope and +clinical acceptance criteria are unchanged._ + +> **For agentic workers:** use `superpowers:executing-plans` and +> `superpowers:subagent-driven-development` when available. Otherwise follow the same admission, +> task tracking, verification and independent-review contracts directly with current host tools. +> Missing optional skills are not a general task blocker; unavailable independent review remains +> explicitly unmet. Neither route authorises push, PR mutation, merge, deployment, hosted migration, +> or live-provider work. **Goal:** Add an explicit typed-or-dictated Clinical Ask action to Services, Forms, Differentials, Formulation, DSM-5 Diagnosis, Specifiers, and Therapy that returns the best-supported mode-shaped response from governed @@ -60,10 +64,11 @@ binding product, clinical, privacy, evidence, or interaction decision, stop and ## Codex Cloud execution contract -### Route selection: use both skills without making the feature plugin-dependent +### Route selection: use available skills without making the feature plugin-dependent -`superpowers:executing-plans` owns plan admission, plan review, sequential advancement through Tasks 1–12, -blocker handling, completion evidence, and the final `superpowers:finishing-a-development-branch` decision. +When available, `superpowers:executing-plans` supports plan admission, plan review, sequential advancement through Tasks 1–12, +blocker handling, completion evidence, and branch closeout. Without it, follow the same lifecycle directly; +do not install a plugin or stop the task merely to obtain the skill. Inside that lifecycle, prefer `superpowers:subagent-driven-development` for each task when the current Codex Cloud task exposes the named skills and the callable multi-agent tools. This is one controller and one shared checkout; implementation agents are never parallel. @@ -81,8 +86,10 @@ requires all of: Repository setup does not install Superpowers or enable Codex multi-agent support. Never infer capability from a local Desktop profile, plugin cache, repository prose, or `[features] multi_agent=true` on another machine. Do not edit tracked product files, weaken Cloud setup, or copy a local plugin into the repository to manufacture the -capability. If any preferred-route requirement is absent, record `Execution route: inline executing-plans` and -continue the same plan in the controller. Missing SDD capability is not a product blocker. +capability. If SDD is unavailable but executing-plans exists, record `Execution route: inline executing-plans`. +If the execution skills are unavailable, record `Execution route: direct sequential` and follow the same +checkboxes, verification and independent-review requirements with current tools. Missing optional skills +are not a product blocker; unavailable independent review remains explicitly unmet. Do not switch routes in the middle of a task. If a subagent tool fails, finish or roll back only the current task-owned uncommitted increment, update the ledger, and resume inline at the first incomplete checkbox. A route @@ -90,21 +97,27 @@ change never permits duplicate implementation or parallel writers. ### Cloud admission gate -Run from a fresh Codex Cloud task using the repository's **offline** profile. Read `AGENTS.md`, -`docs/codex-cloud.md`, `.agents/skills/prompt-perfector/references/repository-workflow.md`, this complete plan, -the binding specification, and the ADR before writing. Then: - -1. Run `bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh`; accept only the documented provider-free - result and never print environment values. -2. Run `npm run check:codex-cloud`, `npm run check:runtime`, `npm run check:installed-lock-parity`, - `npm run check:playwright-browser-revision`, and `npm run check:codex-cloud -- --runtime`. Record the decisive - line and exit code from each. Do not rerun setup if these are already green. -3. Confirm `CODEX_CLOUD_EXPECTED_BASE_SHA` is reported as a verified 40-character ancestor, the origin identity - is `BigSimmo/Database`, and current base freshness is known. If the host supplied a shallow checkout, use the - repository maintenance/refresh path; do not reason about missing history as if it were complete. -4. Confirm the checkout is clean, on a task-specific non-protected branch, and has no active Git operation. Run - the prompt-perfector verifier with `--cloud` immediately before the first write and proceed only on - `SAFE_TO_EDIT=true` and `PRECHECK_RESULT=SAFE`. +Run this provider-free task using the current host's supported setup. Read `AGENTS.md`, +`docs/agents/codex-cloud-environment.md`, `docs/codex-cloud.md`, +`.agents/skills/prompt-perfector/references/repository-workflow.md`, this complete plan, the binding +specification, and the ADR before writing. Then: + +1. Establish current-host capabilities and readiness without printing credential values. The archived + raw-probe/profile acceptance applies only when the owner or environment explicitly selects that legacy + setup; preserve its fail-closed checks in that case. Do not force the setup on another Cloud host. +2. Run `npm run check:runtime`, `npm run check:installed-lock-parity`, and + `npm run check:playwright-browser-revision` for this implementation. Record the decisive line and exit + code from each. Run `check:codex-cloud` static/runtime acceptance only for the selected legacy profile. +3. Confirm repository identity is `BigSimmo/PsychSift` and verify the intended base commit and ancestry + from the actual checkout. The legacy profile additionally requires its verified + `CODEX_CLOUD_EXPECTED_BASE_SHA`. If the host supplied a shallow checkout, use an authorised + maintenance/refresh path; do not reason about missing history as if it were complete. +4. Confirm the task-specific non-protected branch, expected repository/HEAD and absence of an active Git + operation. A new task starts clean. For verified same-task continuation, inventory every changed path, + preserve unrelated work, and bind the continuation to the verifier's `SAFE_STATUS_HASH` using + `--allow-dirty --expected-status-hash` plus exact expected repository, branch and HEAD. Use `--cloud` + only after current-host evidence establishes the supplied Cloud workflow. Proceed only on + `SAFE_TO_EDIT=true` and `PRECHECK_RESULT=SAFE`; dirty state alone does not require a new session. 5. Build `planned_files` from every path in the plan's `Create:` and `Modify:` lists, joined by commas, then run `npm run workflow:flightplan -- --write-evidence --files "$planned_files"` and retain its new `.local` receipt. The expected domains are `ui`, `database`, `retrieval`, `clinical`, and `privacy`; treat actual output as diff --git a/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md b/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md index 5875e25c77..dc82626f8b 100644 --- a/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md +++ b/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md @@ -2,7 +2,7 @@ Use the final handover message's filled prompt, not this unfilled template. Run one fresh Cloud task per P00–P17 phase. Before launch, set `TARGET_PHASE`, `PACKAGE_SOURCE_REF`, immutable `PACKAGE_HEAD_SHA`, `PACKAGE_BASE_SHA`, `PROGRAMME_BRANCH` and the matching UI effort from the manifest. -For a high phase, the prompt begins with `TARGET_PHASE=...` and contains no xhigh confirmation claim. For an xhigh phase, the first line is the repository's exact xhigh confirmation marker and the second line declares the target. A running task cannot raise itself. +Declare `TARGET_PHASE=...` and the selected effort. For an xhigh phase, the user must confirm that xhigh is selected; ordinary-language confirmation is sufficient. No literal prompt token or resubmission is required. A high phase omits the xhigh attestation flag. A running task cannot raise itself. ## Shared prompt body @@ -14,17 +14,17 @@ The programme must deliver repository-wide retrieval across approved public cont This prompt authorizes read-only repository inspection, offline/provider-free work for the one target phase, synthetic tests, task/correction/phase-receipt commits, and pushing only the exact accepted tip to the named programme branch. For P17 only, it also authorizes the separate atomic `PROGRAMME.json` metadata commit after the full-programme review passes. It does not authorize protected content, source browsing/acquisition, hosted Supabase, migrations, hosted types/functions, providers/live evals, reindex, deployment, activation, rollback, cleanup, merging, another PR or branch/worktree deletion. -### First command and bootstrap +### Current host readiness and programme acceptance -Before login/profile/setup, run the raw Cloud environment check exactly as `cloud/START-HERE.md` requires. Exit 1 hard-stops; exit 2 uses only the documented `OPENAI_BASE_URL` restricted profile/shim path. Then perform Cloud-equivalent identity/branch/status checks, setup and shims, direct static Cloud check, runtime Cloud check pinned to `PACKAGE_BASE_SHA`, runtime and lock parity. +Follow the current-host policy in `docs/agents/codex-cloud-environment.md` and the readiness order in `cloud/START-HERE.md`. Establish repository identity, branch and status, host capabilities, runtime and lock parity. Run the legacy raw probe, setup, shims and Cloud acceptance checks only if the owner or selected environment configuration explicitly adopts that repository-managed setup. In that setup, preserve its raw exit codes, recovery, restrictions and base-pinned acceptance; its bounded policy-repair exception does not authorize programme execution or establish acceptance. Other host-managed environments use their supported readiness checks. Keep programme execution provider-free on every host. -For P00, fetch `PACKAGE_SOURCE_REF`, verify exact `PACKAGE_HEAD_SHA`, create `PROGRAMME_BRANCH` from it and run the package publish check. For P01–P17, fetch the exact remote programme tip with no rebase. Run the tracked launch helper with the declared target/effort (and the xhigh-confirmed flag only for an xhigh launch), package parity and `npm run plans:rag:receipts:check -- --before TARGET_PHASE`. The declared target must be exactly next; never select a different phase after launch. +For P00, fetch `PACKAGE_SOURCE_REF`, verify exact `PACKAGE_HEAD_SHA`, create `PROGRAMME_BRANCH` from it and run the package publish check. For P01–P17, fetch the exact remote programme tip with no rebase. Run the tracked launch helper with the declared target/effort (and `--xhigh-confirmed` only after the user confirms an xhigh selection, including in ordinary language), package parity and `npm run plans:rag:receipts:check -- --before TARGET_PHASE`. The declared target must be exactly next; never select a different phase after launch. ### Required knowledge and capabilities Read completely: root `AGENTS.md`; Cloud `START-HERE.md`; manifest; execution order; approval matrix; SDD and connected-execution contracts; verification matrix; route-evidence, phase and programme schemas/templates; both RAG specifications; the selected task plan; `.agents/skills/rag-cloud-sdd/SKILL.md`; and every phase skill profile. -Hash the tracked controller skill, helper and resolved phase skills. Dispatch one fresh read-only probe subagent and record its ID/tool/authoritative dispatch metadata. Stop with `BLOCKED_MISSING_SUBAGENT_RUNTIME` or `BLOCKED_MISSING_CAPABILITY` rather than manually substituting. +Hash the tracked controller skill, helper and resolved phase skills. Assign the fresh read-only probe a bounded inspection of target-phase dependencies, accepted receipts and relevant source conflicts; retain its findings and ID/tool/authoritative dispatch metadata. Do not dispatch a dummy task solely to demonstrate tool availability. Stop with `BLOCKED_MISSING_SUBAGENT_RUNTIME` or `BLOCKED_MISSING_CAPABILITY` rather than manually substituting. Prove controller and every subagent model/effort from sanitized host or dispatch metadata. Save the host-emitted record directly in the tracked route-evidence JSON shape; never author it from model prose. The checker parses it and binds source event, agent, host, provider, model, effort, mapping, fallback and escalation fields to the receipt. This is structural provenance validation, not a cryptographic host signature: if the Cloud host cannot directly expose the required record, stop with `BLOCKED_MODEL_ROUTE_UNVERIFIED`. Codex Cloud requires the exact manifest route with no provider mapping or fallback. diff --git a/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md b/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md index 5ba05a7b68..9de3aec39a 100644 --- a/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md +++ b/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md @@ -7,15 +7,25 @@ This package executes P00–P17 only. One fresh Cloud session completes exactly The user prompt must declare `TARGET_PHASE`, `PACKAGE_SOURCE_REF`, `PACKAGE_HEAD_SHA`, `PACKAGE_BASE_SHA`, `PROGRAMME_BRANCH` and selected Cloud effort. Do not inspect receipts to choose a phase after launch. Confirm the declared target is exactly next using the receipt checker. - Use high for P00, P04, P10 and P11. -- Use xhigh for every other P01–P17 phase. The prompt must begin with the exact repository xhigh confirmation marker and the user must select xhigh in the Cloud control before launch. +- Use xhigh for every other P01–P17 phase. The user must select xhigh in the Cloud control before launch and confirm that selection; ordinary-language confirmation is sufficient. No literal prompt token or resubmission is required. -If target, prompt marker or UI effort is wrong, stop before substantive inspection. Cloud cannot raise its own effort. +If target or UI effort is wrong, stop before substantive inspection. Cloud cannot raise its own effort. If confirmation is missing, request it once; continue after the user confirms. -As the first repository-local validation after the raw boundary and setup, run `node scripts/rag-phase-launch-check.mjs --target TARGET_PHASE --effort SELECTED_EFFORT`. Add `--xhigh-confirmed` only when the xhigh prompt marker is present. A failure stops the task. +After the applicable host readiness checks below, run `node scripts/rag-phase-launch-check.mjs --target TARGET_PHASE --effort SELECTED_EFFORT` as the first programme-local validation. Add `--xhigh-confirmed` only when the user has confirmed xhigh selection, including in ordinary language. This flag records user attestation, not observed runtime effort; authoritative route evidence remains required. A failure stops phase execution. -## Raw environment boundary — first command +## Current host readiness and optional legacy setup -Before a login shell, profile, setup script or OpenAI-capable binary, run: +Follow `docs/agents/codex-cloud-environment.md` for the current host's readiness, +capabilities and credential boundaries. Linux/Cloud execution, `CODEX_CLOUD=1` or +an earlier probe does not select the repository's legacy offline/connected setup. +Keep this programme provider-free on every host. Cloud has no Windows task-start +script: perform the equivalent read-only repository identity, branch, HEAD, status +and worktree checks. Require a clean disposable checkout on a task-specific +non-protected branch. + +Only when the owner or selected environment configuration explicitly adopts the +legacy setup, follow `docs/codex-cloud.md#scope-and-selection`. Before a login shell, +profile, setup script or OpenAI-capable binary in that selected setup, run: ```bash set +e @@ -24,19 +34,25 @@ raw_status=$? set -e ``` -Exit 0 continues. Exit 1 is a hard stop and requires a fresh task. Exit 2 continues only for the documented `OPENAI_BASE_URL`-alone restricted path: never run OpenAI clients from the raw parent or bypass the generated profile and `node`/`npm`/`npx` shims. No other inherited name may use that path. - -Cloud has no Windows task-start script. Perform the equivalent read-only repository identity, branch, HEAD, status and worktree checks. Require a clean disposable checkout on a task-specific non-protected branch. - -## Setup and acceptance order - -After the raw boundary: +For that legacy setup, exit 0 continues. Exit 1 blocks programme execution and +requires the documented recovery and fresh task; the runbook's bounded local +policy-repair exception remains separate from acceptance. Exit 2 continues only +for the documented `OPENAI_BASE_URL`-alone restricted path: never run OpenAI clients +from the raw parent or bypass the generated profile and `node`/`npm`/`npx` shims. +No other inherited name may use that path. After its accepted raw boundary, run: ```bash bash scripts/setup-codex-cloud.sh bash scripts/install-codex-cloud-command-shims.sh npm run check:codex-cloud CODEX_CLOUD_EXPECTED_BASE_SHA="$PACKAGE_BASE_SHA" npm run check:codex-cloud -- --runtime +``` + +For other host-managed environments, use the current host's supported readiness +checks; do not run legacy setup or install its shims to satisfy this package. +Then, on every host, run the programme's repository acceptance checks: + +```bash npm run check:runtime npm run check:installed-lock-parity npm run plans:rag:check @@ -52,7 +68,7 @@ Compute the committed generated Cloud package hash with the receipt checker. Rec ## Mandatory SDD capability probe -Read `.agents/skills/rag-cloud-sdd/SKILL.md`, `sdd-execution.md`, the manifest skill profiles and the selected repo-local skill files completely. Hash them, `scripts/rag-phase-launch-check.mjs` and `scripts/rag-task-brief.mjs`. Dispatch one fresh read-only probe agent and record its agent ID and authoritative dispatch metadata. No actual fresh subagent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; no controller/helper/required skill means `BLOCKED_MISSING_CAPABILITY`. +Read `.agents/skills/rag-cloud-sdd/SKILL.md`, `sdd-execution.md`, the manifest skill profiles and the selected repo-local skill files completely. Hash them, `scripts/rag-phase-launch-check.mjs` and `scripts/rag-task-brief.mjs`. Assign the fresh read-only probe a useful bounded inspection of target-phase dependencies, accepted receipts and relevant source conflicts. Retain its findings, agent ID and authoritative dispatch metadata; do not dispatch a dummy task solely to demonstrate tool availability. No actual fresh subagent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; no controller/helper/required skill means `BLOCKED_MISSING_CAPABILITY`. Prove controller and every dispatched agent route from sanitized host metadata using `route-evidence.schema.json`. Store the host-emitted record directly; do not synthesize it from model prose. The checker parses and binds its routing fields, but cannot turn an unsigned self-authored file into host proof. Cloud requires Codex, exact model and exact effort with no mapping or fallback. Stop with `BLOCKED_MODEL_ROUTE_UNVERIFIED` if the host cannot directly provide the evidence. diff --git a/docs/superpowers/rag-upgrade/canonical/execution-order.md b/docs/superpowers/rag-upgrade/canonical/execution-order.md index ca0cfce17e..481ce81760 100644 --- a/docs/superpowers/rag-upgrade/canonical/execution-order.md +++ b/docs/superpowers/rag-upgrade/canonical/execution-order.md @@ -15,7 +15,7 @@ Every phase owns exactly the plan tasks listed in `programme-manifest.json`. Eve | high | P00, P04, P10, P11 | high | manifest high/xhigh | | xhigh | P01–P03, P05–P09, P12A–P17 | high | xhigh | -The exact lettered phase list is in the manifest and is machine-validated. `TARGET_PHASE` must be present before substantive inspection. A running Cloud task cannot raise its own effort. An xhigh target therefore starts from the xhigh launch prompt with the repository confirmation marker; a high target starts from the high prompt. Implementations remain high because the task plans name files, interfaces, ordered steps and proof gates. P00 and P11 may use Terra/high implementers; all other implementation and all reviews use Sol at the manifest effort. No provider mapping or silent fallback is allowed. +The exact lettered phase list is in the manifest and is machine-validated. `TARGET_PHASE` must be present before substantive inspection. A running Cloud task cannot raise its own effort. An xhigh target requires user confirmation of the selected effort, which may be in ordinary language; `--xhigh-confirmed` records that attestation without a literal prompt token or resubmission. A high target omits that flag. Authoritative runtime route evidence remains required. Implementations remain high because the task plans name files, interfaces, ordered steps and proof gates. P00 and P11 may use Terra/high implementers; all other implementation and all reviews use Sol at the manifest effort. No provider mapping or silent fallback is allowed. Before dispatch, prove the controller route and one real fresh subagent dispatch from authoritative sanitized host metadata. Each task receipt binds the controller, implementer, task reviewer and phase reviewer separately. Self-report is invalid evidence. diff --git a/docs/superpowers/rag-upgrade/canonical/programme-manifest.json b/docs/superpowers/rag-upgrade/canonical/programme-manifest.json index 9853c5d624..1a0c658df1 100644 --- a/docs/superpowers/rag-upgrade/canonical/programme-manifest.json +++ b/docs/superpowers/rag-upgrade/canonical/programme-manifest.json @@ -232,7 +232,7 @@ "P17" ], "xhighConfirmationMarker": "[xhigh-confirmed]", - "launchContract": "TARGET_PHASE must be supplied before repository inspection. The Cloud UI effort must match the target launch set. An xhigh target prompt must begin with the confirmation marker; a high target prompt must not claim that marker.", + "launchContract": "TARGET_PHASE must be supplied before repository inspection. The Cloud UI effort must match the target launch set. For xhigh, ordinary-language user confirmation is sufficient and --xhigh-confirmed records that attestation; no literal prompt token or resubmission is required. For high, omit that flag. xhighConfirmationMarker is retained only as legacy compatibility metadata; it is not a launch prerequisite. Authoritative runtime route evidence remains required.", "allowedEscalations": [ { "fromModel": "gpt-5.6-terra", diff --git a/docs/superpowers/rag-upgrade/canonical/sdd-execution.md b/docs/superpowers/rag-upgrade/canonical/sdd-execution.md index 2c7044cc06..a7ed9c80bd 100644 --- a/docs/superpowers/rag-upgrade/canonical/sdd-execution.md +++ b/docs/superpowers/rag-upgrade/canonical/sdd-execution.md @@ -4,9 +4,9 @@ The tracked `.agents/skills/rag-cloud-sdd/SKILL.md` is the self-contained Cloud ## Capability and route preflight -Before repository inspection, the launch prompt supplies `TARGET_PHASE` and the selected high/xhigh effort. Verify the target against the manifest and accepted receipts without selecting another phase. An xhigh target must carry the exact repository confirmation marker. Stop on wrong target or effort. +Before repository inspection, the launch prompt supplies `TARGET_PHASE` and the selected high/xhigh effort. Verify the target against the manifest and accepted receipts without selecting another phase. An xhigh target requires user confirmation of the selected xhigh effort; ordinary language is sufficient and no literal prompt token or resubmission is required. Pass `--xhigh-confirmed` only after that confirmation; it is user attestation, not runtime evidence. Stop on wrong target or effort and retain authoritative route checks. -Read and hash the controller skill/helper and every repo-local skill in the phase profiles. Dispatch one fresh read-only probe agent and record the dispatch tool, agent ID and authoritative host metadata. No callable fresh-agent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; a missing skill/helper means `BLOCKED_MISSING_CAPABILITY`. +Read and hash the controller skill/helper and every repo-local skill in the phase profiles. Assign the fresh read-only probe a useful bounded inspection of target-phase dependencies, accepted receipts and relevant source conflicts. Retain its findings, dispatch tool, agent ID and authoritative host metadata; do not dispatch a dummy task solely to demonstrate tool availability. No callable fresh-agent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; a missing skill/helper means `BLOCKED_MISSING_CAPABILITY`. Authoritative route evidence comes only from sanitized Codex host runtime or dispatch metadata exported directly into `route-evidence.schema.json`. The checker parses and binds source event, agent ID, host, provider, planned/actual model and effort, mapping, fallback and escalation. Model prose and controller-authored substitutes are not evidence, and the structural checker does not cryptographically attest an unsigned record. If the host cannot directly supply the record, stop with `BLOCKED_MODEL_ROUTE_UNVERIFIED`. Cloud routes must be Codex with exact IDs and effort, no provider mapping and no fallback. diff --git a/docs/superpowers/rag-upgrade/cloud/START-HERE.md b/docs/superpowers/rag-upgrade/cloud/START-HERE.md index 5ba05a7b68..9de3aec39a 100644 --- a/docs/superpowers/rag-upgrade/cloud/START-HERE.md +++ b/docs/superpowers/rag-upgrade/cloud/START-HERE.md @@ -7,15 +7,25 @@ This package executes P00–P17 only. One fresh Cloud session completes exactly The user prompt must declare `TARGET_PHASE`, `PACKAGE_SOURCE_REF`, `PACKAGE_HEAD_SHA`, `PACKAGE_BASE_SHA`, `PROGRAMME_BRANCH` and selected Cloud effort. Do not inspect receipts to choose a phase after launch. Confirm the declared target is exactly next using the receipt checker. - Use high for P00, P04, P10 and P11. -- Use xhigh for every other P01–P17 phase. The prompt must begin with the exact repository xhigh confirmation marker and the user must select xhigh in the Cloud control before launch. +- Use xhigh for every other P01–P17 phase. The user must select xhigh in the Cloud control before launch and confirm that selection; ordinary-language confirmation is sufficient. No literal prompt token or resubmission is required. -If target, prompt marker or UI effort is wrong, stop before substantive inspection. Cloud cannot raise its own effort. +If target or UI effort is wrong, stop before substantive inspection. Cloud cannot raise its own effort. If confirmation is missing, request it once; continue after the user confirms. -As the first repository-local validation after the raw boundary and setup, run `node scripts/rag-phase-launch-check.mjs --target TARGET_PHASE --effort SELECTED_EFFORT`. Add `--xhigh-confirmed` only when the xhigh prompt marker is present. A failure stops the task. +After the applicable host readiness checks below, run `node scripts/rag-phase-launch-check.mjs --target TARGET_PHASE --effort SELECTED_EFFORT` as the first programme-local validation. Add `--xhigh-confirmed` only when the user has confirmed xhigh selection, including in ordinary language. This flag records user attestation, not observed runtime effort; authoritative route evidence remains required. A failure stops phase execution. -## Raw environment boundary — first command +## Current host readiness and optional legacy setup -Before a login shell, profile, setup script or OpenAI-capable binary, run: +Follow `docs/agents/codex-cloud-environment.md` for the current host's readiness, +capabilities and credential boundaries. Linux/Cloud execution, `CODEX_CLOUD=1` or +an earlier probe does not select the repository's legacy offline/connected setup. +Keep this programme provider-free on every host. Cloud has no Windows task-start +script: perform the equivalent read-only repository identity, branch, HEAD, status +and worktree checks. Require a clean disposable checkout on a task-specific +non-protected branch. + +Only when the owner or selected environment configuration explicitly adopts the +legacy setup, follow `docs/codex-cloud.md#scope-and-selection`. Before a login shell, +profile, setup script or OpenAI-capable binary in that selected setup, run: ```bash set +e @@ -24,19 +34,25 @@ raw_status=$? set -e ``` -Exit 0 continues. Exit 1 is a hard stop and requires a fresh task. Exit 2 continues only for the documented `OPENAI_BASE_URL`-alone restricted path: never run OpenAI clients from the raw parent or bypass the generated profile and `node`/`npm`/`npx` shims. No other inherited name may use that path. - -Cloud has no Windows task-start script. Perform the equivalent read-only repository identity, branch, HEAD, status and worktree checks. Require a clean disposable checkout on a task-specific non-protected branch. - -## Setup and acceptance order - -After the raw boundary: +For that legacy setup, exit 0 continues. Exit 1 blocks programme execution and +requires the documented recovery and fresh task; the runbook's bounded local +policy-repair exception remains separate from acceptance. Exit 2 continues only +for the documented `OPENAI_BASE_URL`-alone restricted path: never run OpenAI clients +from the raw parent or bypass the generated profile and `node`/`npm`/`npx` shims. +No other inherited name may use that path. After its accepted raw boundary, run: ```bash bash scripts/setup-codex-cloud.sh bash scripts/install-codex-cloud-command-shims.sh npm run check:codex-cloud CODEX_CLOUD_EXPECTED_BASE_SHA="$PACKAGE_BASE_SHA" npm run check:codex-cloud -- --runtime +``` + +For other host-managed environments, use the current host's supported readiness +checks; do not run legacy setup or install its shims to satisfy this package. +Then, on every host, run the programme's repository acceptance checks: + +```bash npm run check:runtime npm run check:installed-lock-parity npm run plans:rag:check @@ -52,7 +68,7 @@ Compute the committed generated Cloud package hash with the receipt checker. Rec ## Mandatory SDD capability probe -Read `.agents/skills/rag-cloud-sdd/SKILL.md`, `sdd-execution.md`, the manifest skill profiles and the selected repo-local skill files completely. Hash them, `scripts/rag-phase-launch-check.mjs` and `scripts/rag-task-brief.mjs`. Dispatch one fresh read-only probe agent and record its agent ID and authoritative dispatch metadata. No actual fresh subagent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; no controller/helper/required skill means `BLOCKED_MISSING_CAPABILITY`. +Read `.agents/skills/rag-cloud-sdd/SKILL.md`, `sdd-execution.md`, the manifest skill profiles and the selected repo-local skill files completely. Hash them, `scripts/rag-phase-launch-check.mjs` and `scripts/rag-task-brief.mjs`. Assign the fresh read-only probe a useful bounded inspection of target-phase dependencies, accepted receipts and relevant source conflicts. Retain its findings, agent ID and authoritative dispatch metadata; do not dispatch a dummy task solely to demonstrate tool availability. No actual fresh subagent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; no controller/helper/required skill means `BLOCKED_MISSING_CAPABILITY`. Prove controller and every dispatched agent route from sanitized host metadata using `route-evidence.schema.json`. Store the host-emitted record directly; do not synthesize it from model prose. The checker parses and binds its routing fields, but cannot turn an unsigned self-authored file into host proof. Cloud requires Codex, exact model and exact effort with no mapping or fallback. Stop with `BLOCKED_MODEL_ROUTE_UNVERIFIED` if the host cannot directly provide the evidence. diff --git a/docs/superpowers/rag-upgrade/cloud/execution-order.md b/docs/superpowers/rag-upgrade/cloud/execution-order.md index ca0cfce17e..481ce81760 100644 --- a/docs/superpowers/rag-upgrade/cloud/execution-order.md +++ b/docs/superpowers/rag-upgrade/cloud/execution-order.md @@ -15,7 +15,7 @@ Every phase owns exactly the plan tasks listed in `programme-manifest.json`. Eve | high | P00, P04, P10, P11 | high | manifest high/xhigh | | xhigh | P01–P03, P05–P09, P12A–P17 | high | xhigh | -The exact lettered phase list is in the manifest and is machine-validated. `TARGET_PHASE` must be present before substantive inspection. A running Cloud task cannot raise its own effort. An xhigh target therefore starts from the xhigh launch prompt with the repository confirmation marker; a high target starts from the high prompt. Implementations remain high because the task plans name files, interfaces, ordered steps and proof gates. P00 and P11 may use Terra/high implementers; all other implementation and all reviews use Sol at the manifest effort. No provider mapping or silent fallback is allowed. +The exact lettered phase list is in the manifest and is machine-validated. `TARGET_PHASE` must be present before substantive inspection. A running Cloud task cannot raise its own effort. An xhigh target requires user confirmation of the selected effort, which may be in ordinary language; `--xhigh-confirmed` records that attestation without a literal prompt token or resubmission. A high target omits that flag. Authoritative runtime route evidence remains required. Implementations remain high because the task plans name files, interfaces, ordered steps and proof gates. P00 and P11 may use Terra/high implementers; all other implementation and all reviews use Sol at the manifest effort. No provider mapping or silent fallback is allowed. Before dispatch, prove the controller route and one real fresh subagent dispatch from authoritative sanitized host metadata. Each task receipt binds the controller, implementer, task reviewer and phase reviewer separately. Self-report is invalid evidence. diff --git a/docs/superpowers/rag-upgrade/cloud/programme-manifest.json b/docs/superpowers/rag-upgrade/cloud/programme-manifest.json index 9853c5d624..1a0c658df1 100644 --- a/docs/superpowers/rag-upgrade/cloud/programme-manifest.json +++ b/docs/superpowers/rag-upgrade/cloud/programme-manifest.json @@ -232,7 +232,7 @@ "P17" ], "xhighConfirmationMarker": "[xhigh-confirmed]", - "launchContract": "TARGET_PHASE must be supplied before repository inspection. The Cloud UI effort must match the target launch set. An xhigh target prompt must begin with the confirmation marker; a high target prompt must not claim that marker.", + "launchContract": "TARGET_PHASE must be supplied before repository inspection. The Cloud UI effort must match the target launch set. For xhigh, ordinary-language user confirmation is sufficient and --xhigh-confirmed records that attestation; no literal prompt token or resubmission is required. For high, omit that flag. xhighConfirmationMarker is retained only as legacy compatibility metadata; it is not a launch prerequisite. Authoritative runtime route evidence remains required.", "allowedEscalations": [ { "fromModel": "gpt-5.6-terra", diff --git a/docs/superpowers/rag-upgrade/cloud/sdd-execution.md b/docs/superpowers/rag-upgrade/cloud/sdd-execution.md index 2c7044cc06..a7ed9c80bd 100644 --- a/docs/superpowers/rag-upgrade/cloud/sdd-execution.md +++ b/docs/superpowers/rag-upgrade/cloud/sdd-execution.md @@ -4,9 +4,9 @@ The tracked `.agents/skills/rag-cloud-sdd/SKILL.md` is the self-contained Cloud ## Capability and route preflight -Before repository inspection, the launch prompt supplies `TARGET_PHASE` and the selected high/xhigh effort. Verify the target against the manifest and accepted receipts without selecting another phase. An xhigh target must carry the exact repository confirmation marker. Stop on wrong target or effort. +Before repository inspection, the launch prompt supplies `TARGET_PHASE` and the selected high/xhigh effort. Verify the target against the manifest and accepted receipts without selecting another phase. An xhigh target requires user confirmation of the selected xhigh effort; ordinary language is sufficient and no literal prompt token or resubmission is required. Pass `--xhigh-confirmed` only after that confirmation; it is user attestation, not runtime evidence. Stop on wrong target or effort and retain authoritative route checks. -Read and hash the controller skill/helper and every repo-local skill in the phase profiles. Dispatch one fresh read-only probe agent and record the dispatch tool, agent ID and authoritative host metadata. No callable fresh-agent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; a missing skill/helper means `BLOCKED_MISSING_CAPABILITY`. +Read and hash the controller skill/helper and every repo-local skill in the phase profiles. Assign the fresh read-only probe a useful bounded inspection of target-phase dependencies, accepted receipts and relevant source conflicts. Retain its findings, dispatch tool, agent ID and authoritative host metadata; do not dispatch a dummy task solely to demonstrate tool availability. No callable fresh-agent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; a missing skill/helper means `BLOCKED_MISSING_CAPABILITY`. Authoritative route evidence comes only from sanitized Codex host runtime or dispatch metadata exported directly into `route-evidence.schema.json`. The checker parses and binds source event, agent ID, host, provider, planned/actual model and effort, mapping, fallback and escalation. Model prose and controller-authored substitutes are not evidence, and the structural checker does not cryptographically attest an unsigned record. If the host cannot directly supply the record, stop with `BLOCKED_MODEL_ROUTE_UNVERIFIED`. Cloud routes must be Codex with exact IDs and effort, no provider mapping and no fallback. diff --git a/docs/superpowers/rag-upgrade/local/execution-order.md b/docs/superpowers/rag-upgrade/local/execution-order.md index ca0cfce17e..481ce81760 100644 --- a/docs/superpowers/rag-upgrade/local/execution-order.md +++ b/docs/superpowers/rag-upgrade/local/execution-order.md @@ -15,7 +15,7 @@ Every phase owns exactly the plan tasks listed in `programme-manifest.json`. Eve | high | P00, P04, P10, P11 | high | manifest high/xhigh | | xhigh | P01–P03, P05–P09, P12A–P17 | high | xhigh | -The exact lettered phase list is in the manifest and is machine-validated. `TARGET_PHASE` must be present before substantive inspection. A running Cloud task cannot raise its own effort. An xhigh target therefore starts from the xhigh launch prompt with the repository confirmation marker; a high target starts from the high prompt. Implementations remain high because the task plans name files, interfaces, ordered steps and proof gates. P00 and P11 may use Terra/high implementers; all other implementation and all reviews use Sol at the manifest effort. No provider mapping or silent fallback is allowed. +The exact lettered phase list is in the manifest and is machine-validated. `TARGET_PHASE` must be present before substantive inspection. A running Cloud task cannot raise its own effort. An xhigh target requires user confirmation of the selected effort, which may be in ordinary language; `--xhigh-confirmed` records that attestation without a literal prompt token or resubmission. A high target omits that flag. Authoritative runtime route evidence remains required. Implementations remain high because the task plans name files, interfaces, ordered steps and proof gates. P00 and P11 may use Terra/high implementers; all other implementation and all reviews use Sol at the manifest effort. No provider mapping or silent fallback is allowed. Before dispatch, prove the controller route and one real fresh subagent dispatch from authoritative sanitized host metadata. Each task receipt binds the controller, implementer, task reviewer and phase reviewer separately. Self-report is invalid evidence. diff --git a/docs/superpowers/rag-upgrade/local/programme-manifest.json b/docs/superpowers/rag-upgrade/local/programme-manifest.json index 9853c5d624..1a0c658df1 100644 --- a/docs/superpowers/rag-upgrade/local/programme-manifest.json +++ b/docs/superpowers/rag-upgrade/local/programme-manifest.json @@ -232,7 +232,7 @@ "P17" ], "xhighConfirmationMarker": "[xhigh-confirmed]", - "launchContract": "TARGET_PHASE must be supplied before repository inspection. The Cloud UI effort must match the target launch set. An xhigh target prompt must begin with the confirmation marker; a high target prompt must not claim that marker.", + "launchContract": "TARGET_PHASE must be supplied before repository inspection. The Cloud UI effort must match the target launch set. For xhigh, ordinary-language user confirmation is sufficient and --xhigh-confirmed records that attestation; no literal prompt token or resubmission is required. For high, omit that flag. xhighConfirmationMarker is retained only as legacy compatibility metadata; it is not a launch prerequisite. Authoritative runtime route evidence remains required.", "allowedEscalations": [ { "fromModel": "gpt-5.6-terra", diff --git a/docs/superpowers/rag-upgrade/local/sdd-execution.md b/docs/superpowers/rag-upgrade/local/sdd-execution.md index 2c7044cc06..a7ed9c80bd 100644 --- a/docs/superpowers/rag-upgrade/local/sdd-execution.md +++ b/docs/superpowers/rag-upgrade/local/sdd-execution.md @@ -4,9 +4,9 @@ The tracked `.agents/skills/rag-cloud-sdd/SKILL.md` is the self-contained Cloud ## Capability and route preflight -Before repository inspection, the launch prompt supplies `TARGET_PHASE` and the selected high/xhigh effort. Verify the target against the manifest and accepted receipts without selecting another phase. An xhigh target must carry the exact repository confirmation marker. Stop on wrong target or effort. +Before repository inspection, the launch prompt supplies `TARGET_PHASE` and the selected high/xhigh effort. Verify the target against the manifest and accepted receipts without selecting another phase. An xhigh target requires user confirmation of the selected xhigh effort; ordinary language is sufficient and no literal prompt token or resubmission is required. Pass `--xhigh-confirmed` only after that confirmation; it is user attestation, not runtime evidence. Stop on wrong target or effort and retain authoritative route checks. -Read and hash the controller skill/helper and every repo-local skill in the phase profiles. Dispatch one fresh read-only probe agent and record the dispatch tool, agent ID and authoritative host metadata. No callable fresh-agent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; a missing skill/helper means `BLOCKED_MISSING_CAPABILITY`. +Read and hash the controller skill/helper and every repo-local skill in the phase profiles. Assign the fresh read-only probe a useful bounded inspection of target-phase dependencies, accepted receipts and relevant source conflicts. Retain its findings, dispatch tool, agent ID and authoritative host metadata; do not dispatch a dummy task solely to demonstrate tool availability. No callable fresh-agent runtime means `BLOCKED_MISSING_SUBAGENT_RUNTIME`; a missing skill/helper means `BLOCKED_MISSING_CAPABILITY`. Authoritative route evidence comes only from sanitized Codex host runtime or dispatch metadata exported directly into `route-evidence.schema.json`. The checker parses and binds source event, agent ID, host, provider, planned/actual model and effort, mapping, fallback and escalation. Model prose and controller-authored substitutes are not evidence, and the structural checker does not cryptographically attest an unsigned record. If the host cannot directly supply the record, stop with `BLOCKED_MODEL_ROUTE_UNVERIFIED`. Cloud routes must be Codex with exact IDs and effort, no provider mapping and no fallback. diff --git a/plugins/clinical-kb/README.md b/plugins/clinical-kb/README.md index 5e2cfe5e4a..aa82464335 100644 --- a/plugins/clinical-kb/README.md +++ b/plugins/clinical-kb/README.md @@ -11,17 +11,27 @@ This plugin follows the repo-local structure used by `openai/plugins`: The plugin currently ships repo guidance only. It does not add MCP servers, app connectors, commands, hooks, dependencies, or runtime code. -Codex plugin discovery is marketplace-based. In a workspace marketplace, -`./plugins/clinical-kb` resolves to `D:\Repos\Database\plugins\clinical-kb`. -Restart Codex in this workspace if the plugin does not appear immediately. +Codex plugin discovery is marketplace-based. Resolve `./plugins/clinical-kb` from the +actual repository root reported by `git rev-parse --show-toplevel`; no Windows path +identifies every checkout. Verify that root belongs to `BigSimmo/PsychSift` before +registration. The marketplace files do not prove that this host supports, installed +or loaded the plugin. -If your Codex install requires explicit local marketplace registration, add the repo root as -a plugin marketplace, then install the plugin by marketplace name: +If the current Codex host supports local marketplace registration and plugin installation +is authorised, add that verified checkout root, then install by marketplace name. For a +POSIX shell with the supported CLI: -```powershell -codex plugin marketplace add D:\Repos\Database +```bash +task_repo_root="$(git rev-parse --show-toplevel)" +codex plugin marketplace add "$task_repo_root" codex plugin add clinical-kb@clinical-kb-local ``` -For API-key-login marketplace flows, use `clinical-kb-api-local` as the marketplace name. -Start a new Codex thread after installing or updating the plugin so the skill list refreshes. +For API-key-login marketplace flows, the alternate marketplace name is `clinical-kb-api-local`. +Use the current host's supported plugin refresh or a fresh chat, then inspect the actual +skill inventory. Do not copy credentials or force a local CLI installation to imitate +hosted plugin availability. + +The [current Cloud policy](../../docs/agents/codex-cloud-environment.md), +[user-controlled reasoning policy](../../docs/agents/codex-reasoning-effort.md) and +[agent allocation guidance](../../docs/agents/smart-agent-allocation.md) remain authoritative. diff --git a/plugins/clinical-kb/skills/clinical-kb-workflow/SKILL.md b/plugins/clinical-kb/skills/clinical-kb-workflow/SKILL.md index 08ff9e6462..1d08df4eb1 100644 --- a/plugins/clinical-kb/skills/clinical-kb-workflow/SKILL.md +++ b/plugins/clinical-kb/skills/clinical-kb-workflow/SKILL.md @@ -1,12 +1,17 @@ --- name: clinical-kb-workflow -description: Use when working in the D:\Repos\Database PsychSift repo, especially for local run, UI/browser QA, Supabase/OpenAI/RAG changes, clinical governance, dependency/upload shortcuts, or choosing verification. +description: Use when working in the verified PsychSift checkout, especially for local run, UI/browser QA, Supabase/OpenAI/RAG changes, clinical governance, dependency/upload shortcuts, or choosing verification. --- # PsychSift Workflow -Use this skill for `D:\Repos\Database`, the PsychSift Next.js clinical reference RAG app. +Use this skill for the PsychSift Next.js clinical reference RAG app. Establish the actual +checkout root with `git rev-parse --show-toplevel` and verify repository identity and ownership. Root `AGENTS.md` remains authoritative. If these notes drift, inspect the repo before acting. +Follow the [current Cloud policy](../../../../docs/agents/codex-cloud-environment.md), +[user-controlled reasoning guidance](../../../../docs/agents/codex-reasoning-effort.md) and +[agent allocation policy](../../../../docs/agents/smart-agent-allocation.md); this plugin neither +selects a host profile nor changes the user's main-chat model or effort. ## Repo Basics @@ -27,8 +32,11 @@ Root `AGENTS.md` remains authoritative. If these notes drift, inspect the repo b ## OpenAI And API Cost Safety - Do not run OpenAI API-backed tasks, live evals, ingestion enrichment, embeddings, image captioning, or provider-backed answer generation unless the user explicitly asks for API usage. -- Prefer local/static/mocked checks. When a no-API guard is needed, clear `OPENAI_API_KEY`, `OPENAI_ORG_ID`, and `OPENAI_PROJECT_ID` for that command. -- Safe local server checks such as `npm run ensure` do not require provider calls. +- Prefer repository-supported local/static/mocked or offline test launchers. Inspect their current + scope before execution; do not alter authentication or unset credentials as an acceptance workaround. + A sanitized child process does not prove the raw host boundary accepted a selected legacy profile. +- `npm run ensure` verifies local server ownership; it does not by itself prove the selected app + configuration is provider-free or authorise live requests. Apply current host and task boundaries. - Broad browser aggregates can be slower or flaky in no-API mode; prefer focused Vitest or focused Chromium specs first. ## Next.js Changes diff --git a/scripts/apply-claude-cloud-profile.mjs b/scripts/apply-claude-cloud-profile.mjs index fea42865e7..a80c3ffda9 100755 --- a/scripts/apply-claude-cloud-profile.mjs +++ b/scripts/apply-claude-cloud-profile.mjs @@ -4,7 +4,7 @@ * * Claude Code on the web starts from a bare Linux container holding the repository checkout and * nothing else. Everything that lives in the operator's Windows home directory — the personal - * CLAUDE.md writing-style file, the enabled user skills, this project's saved memories, and the + * CLAUDE.md writing-style file, the enabled user skills, curated project guidance seeds, and the * user-level settings that turn on auto permission mode and the four plugins — is absent, so a cloud * session behaves noticeably differently from a local one. The repository cannot reach into that home * directory, so the durable fix is to carry a copy in-tree (.claude/cloud-profile/) and unpack it into @@ -16,6 +16,8 @@ * Windows workstation must never have its real ~/.claude overwritten by a checked-in snapshot. * - Memory files are copied only when absent. Memories the container writes during a session are * newer than the snapshot and must win; clobbering them would silently delete work. + * Only seed-manifest.json entries are installed; unlisted files and archived session state are + * never fallback sources. Existing host memory is never purged. * - ~/.claude/settings.json is DEEP-MERGED, not replaced, with the profile winning per key, so * anything the platform put there (auth state, machine ids, feature flags) survives. * - An existing ~/.claude/CLAUDE.md is backed up once to CLAUDE.md.pre-cloud-profile before the @@ -27,7 +29,19 @@ */ import { createHash } from "node:crypto"; -import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs"; +import { + constants, + copyFileSync, + cpSync, + existsSync, + lstatSync, + mkdirSync, + readFileSync, + readdirSync, + renameSync, + rmSync, + writeFileSync, +} from "node:fs"; import { homedir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -40,6 +54,12 @@ const args = process.argv.slice(2); const force = args.includes("--force"); const dryRun = args.includes("--dry-run"); const quiet = args.includes("--quiet"); +for (const arg of args) { + if (!["--force", "--dry-run", "--quiet"].includes(arg) && !arg.startsWith("--skip=")) { + console.error("[cloud-profile] ERROR: unknown argument; use --force, --dry-run, --quiet or --skip="); + process.exit(2); + } +} // --skip=settings,claude-md,skills,memory. The skills part is several megabytes across ~500 files, so // re-copying it is by far the slowest thing here; skipping it keeps a targeted re-run (and the tests @@ -105,9 +125,12 @@ function deepMerge(base, overlay) { function readJsonOr(path, fallback) { if (!existsSync(path)) return fallback; + const content = readFileSync(path, "utf8"); try { - return JSON.parse(readFileSync(path, "utf8")); - } catch (error) { + const parsed = JSON.parse(content); + if (!isPlainObject(parsed)) throw new Error("settings must be an object"); + return parsed; + } catch { // A corrupt settings file must not take the session down with it. Preserve it and start clean: // losing the merge is recoverable, losing whatever the platform wrote there may not be. let backup = `${path}.unparseable`; @@ -117,7 +140,9 @@ function readJsonOr(path, fallback) { suffix += 1; } if (!dryRun) renameSync(path, backup); - note(`WARNING: ${path} was not valid JSON (${error.message}); preserved as ${backup}`); + // JSON.parse errors can contain snippets of private host settings. Keep the content in the + // backup only, and report neither the parser message nor the original text. + note(`WARNING: ${path} was not a valid settings object; preserved as ${backup}`); return fallback; } } @@ -126,13 +151,24 @@ function sha256(path) { return createHash("sha256").update(readFileSync(path)).digest("hex"); } +// Validate the selected seed source before changing settings, instructions or skills. A rejected +// manifest must not leave an otherwise-applied profile behind. +const profileMemory = join(profileDir, "memory"); +const memoryFiles = existsSync(profileMemory) && !skipped.has("memory") ? memorySeedNames(profileMemory) : []; + // --------------------------------------------------------------------------- // 1. user-level settings — deep merge, profile wins // --------------------------------------------------------------------------- const profileSettingsPath = join(profileDir, "settings.json"); if (existsSync(profileSettingsPath) && !skipped.has("settings")) { const settingsPath = join(claudeHome, "settings.json"); - const profileSettings = JSON.parse(readFileSync(profileSettingsPath, "utf8")); + let profileSettings; + try { + profileSettings = JSON.parse(readFileSync(profileSettingsPath, "utf8")); + } catch { + fail("profile settings must be valid JSON"); + } + if (!isPlainObject(profileSettings)) fail("profile settings must be an object"); const existing = readJsonOr(settingsPath, {}); const merged = deepMerge(existing, profileSettings); const serialised = `${JSON.stringify(merged, null, 2)}\n`; @@ -193,9 +229,48 @@ if (existsSync(profileSkills) && !skipped.has("skills")) { } // --------------------------------------------------------------------------- -// 4. project memories — copy only what is absent, so container-written memories always win +// 4. curated project memories — copy only listed, absent files; host memories always win // --------------------------------------------------------------------------- -const profileMemory = join(profileDir, "memory"); +function memorySeedNames(directory) { + let manifest; + try { + manifest = JSON.parse(readFileSync(join(directory, "seed-manifest.json"), "utf8")); + } catch { + fail("memory seed manifest is missing or invalid JSON"); + } + if ( + !isPlainObject(manifest) || + manifest.version !== 1 || + !Array.isArray(manifest.files) || + Object.keys(manifest).some((key) => !["version", "files"].includes(key)) + ) { + fail("memory seed manifest must declare version 1 and a files array"); + } + const names = new Set(); + for (const name of manifest.files) { + if (typeof name !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(name)) { + fail("memory seed manifest entries must be plain Markdown filenames"); + } + const key = name.toLowerCase(); + if (names.has(key)) fail("memory seed manifest contains duplicate filenames"); + names.add(key); + const source = join(directory, name); + if (!existsSync(source) || !lstatSync(source).isFile()) { + fail("memory seed manifest entry is missing or is not a regular file"); + } + } + if (!manifest.files.includes("MEMORY.md")) fail("memory seed manifest must include MEMORY.md"); + const index = readFileSync(join(directory, "MEMORY.md"), "utf8"); + const targets = [ + ...Array.from(index.matchAll(/\[[^\]]*\]\(([^)]+)\)/g), (match) => match[1]), + ...Array.from(index.matchAll(/\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g), (match) => `${match[1]}.md`), + ]; + if (targets.some((target) => !manifest.files.includes(target))) { + fail("memory seed index refers to an unlisted seed"); + } + return manifest.files; +} + if (existsSync(profileMemory) && !skipped.has("memory")) { // Claude Code keys project state by the working directory with path separators and the Windows // drive colon flattened to dashes: D:\Repos\Database -> D--Repos-Database, and in a Linux container @@ -204,16 +279,26 @@ if (existsSync(profileMemory) && !skipped.has("memory")) { const slug = projectDir.replace(/[/\\:]/g, "-"); const target = join(claudeHome, "projects", slug, "memory"); ensureDir(target); - const files = readdirSync(profileMemory).filter((file) => file.endsWith(".md")); let copied = 0; let kept = 0; - for (const file of files) { + for (const file of memoryFiles) { const destination = join(target, file); - if (existsSync(destination)) { + // A dangling symlink is still a host-owned record. existsSync follows it and would treat it as + // absent, replacing the link. Exclusive creation also preserves a file created by another run + // after this check. + if (lstatSync(destination, { throwIfNoEntry: false })) { kept += 1; continue; } - if (!dryRun) cpSync(join(profileMemory, file), destination); + if (!dryRun) { + try { + copyFileSync(join(profileMemory, file), destination, constants.COPYFILE_EXCL); + } catch (error) { + if (error.code !== "EEXIST") throw error; + kept += 1; + continue; + } + } copied += 1; } note(`memories: ${copied} installed, ${kept} already present (kept) in projects/${slug}/memory`); diff --git a/scripts/build-rag-plan-packages.mjs b/scripts/build-rag-plan-packages.mjs index 2b8d771762..4bbeb85ce7 100644 --- a/scripts/build-rag-plan-packages.mjs +++ b/scripts/build-rag-plan-packages.mjs @@ -167,8 +167,10 @@ function validateCanonicalBodies() { ) { errors.push("programme-manifest.json: adaptive effort launch sets must partition every Cloud phase exactly once"); } + // Preserve legacy manifest metadata for compatibility, without requiring a prompt token. + // Actual launch confirmation is the CLI user-attestation flag; routes prove runtime effort. if (manifest.adaptiveEffortPolicy?.xhighConfirmationMarker !== "[xhigh-confirmed]") { - errors.push("programme-manifest.json: xhigh confirmation marker must match the repository Cloud gate"); + errors.push("programme-manifest.json: retain the legacy xhigh marker metadata for compatibility"); } if (manifest.defaultAgentPolicy?.allowModelFallback !== false) { errors.push("programme-manifest.json: model fallback must remain disabled"); diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index 9ae1c096ca..54d561a1b0 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -21,8 +21,6 @@ export const expectedCloudCliVersions = Object.freeze({ codex: "0.147.0", }); -export const expectedHostedWorkspaceClass = "personal-pro"; - export const expectedMcpConfiguration = Object.freeze({ // Canonical form matches `.mcp.json` (no trailing slash). railwayUrl: "https://mcp.railway.com", @@ -548,6 +546,17 @@ export function codexCloudValidationScope({ runtime = false, environment = false return "static"; } +/** + * Select legacy acceptance deliberately; a generic Cloud host marker is insufficient. + * @param {string[]} [args] + * @param {NodeJS.ProcessEnv | Record} [env] + */ +export function codexCloudValidationSelection(args = [], env = process.env) { + const runtime = args.includes("--runtime"); + const environment = runtime || args.includes("--environment") || env.CODEX_CLOUD_ACCESS_PROFILE !== undefined; + return { runtime, environment }; +} + function commandAvailable(command) { return spawnSync(command, ["--version"], { encoding: "utf8", shell: false }).status === 0; } @@ -560,7 +569,9 @@ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { const mcpServers = options.mcpServers ?? parseMcpServerMetadata(read(".mcp.json")); const checkout = options.checkout ?? gitCheckoutFreshness(repoRoot, env); const lines = [ - `hosted_workspace.class_documented=${expectedHostedWorkspaceClass}`, + "validation.contract=legacy-repository-managed-cloud-profile", + "hosted_workspace.class=unverified", + "provider_routes=external-unverified", `CODEX_CLOUD=${approvedModeValue(env.CODEX_CLOUD, ["1"])}`, `CODEX_CLOUD_ACCESS_PROFILE=${approvedModeValue(env.CODEX_CLOUD_ACCESS_PROFILE ?? "offline", ["offline", "connected"])}`, `RAG_PROVIDER_MODE=${approvedModeValue(env.RAG_PROVIDER_MODE, ["offline"])}`, @@ -569,9 +580,6 @@ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { ]; for (const name of providerCredentialVariables) lines.push(`${name}.present=${Boolean(env[name])}`); lines.push(hostedAppInventoryCapabilityLine(options.hostedAppInventory ?? null)); - lines.push("provider_route.github=codex-native-connector"); - lines.push("provider_route.railway=chatgpt-official-app"); - lines.push("provider_route.supabase=chatgpt-project-scoped-read-only-app"); lines.push(`codex.cli_available=${codexCliAvailable}`); lines.push(pythonWorkerVersionLine(env.CODEX_CLOUD_OCR_PYTHON)); lines.push(`git.origin_configured=${origin.configured}`); @@ -621,7 +629,7 @@ function fullGitRevision(root, ref) { } function normalizedExpectedBase(root, value) { - if (!value) return "unset"; + if (value === undefined) return "unset"; const candidate = String(value); if (!/^[0-9a-f]{40}$/i.test(candidate)) return "invalid"; const revision = fullGitRevision(root, `${candidate}^{commit}`); @@ -647,7 +655,7 @@ export function gitCheckoutFreshness(root = process.cwd(), env = process.env) { const freshness = expectedBaseAncestor === "true" ? "verified" - : expectedBaseAncestor === "false" + : expectedBase === "invalid" || expectedBaseAncestor === "false" ? "invalid" : taskOnly ? "unverified" @@ -705,6 +713,15 @@ export function pythonWorkerImportError(pythonCommand, run = spawnSync) { return `Python worker imports failed: ${pythonWorkerImports.join(", ")}.`; } +/** + * @param {string} pythonCommand + * @param {( + * command: string, + * args: string[], + * options?: { encoding?: BufferEncoding; shell?: boolean }, + * ) => { status: number | null }} [run] + * @returns {string[]} + */ export function pythonWorkerDependencyErrors(pythonCommand, run = spawnSync) { if (!pythonCommand || !executableFile(pythonCommand)) { return ["The configured Codex Cloud OCR Python executable is unavailable."]; @@ -719,13 +736,13 @@ export function pythonWorkerDependencyErrors(pythonCommand, run = spawnSync) { const requirements = readFileSync(path.join(repoRoot, "worker/python/requirements-cloud.txt")); const expectedHash = createHash("sha256").update(requirements).digest("hex"); const markerPath = path.resolve(path.dirname(pythonCommand), "..", ".requirements-cloud.sha256"); - let installedHash = ""; + let installedHash = null; try { installedHash = readFileSync(markerPath, "utf8").trim(); } catch { errors.push("Python worker requirements fingerprint is missing; rerun Cloud setup."); } - if (installedHash && installedHash !== expectedHash) { + if (installedHash !== null && installedHash !== expectedHash) { errors.push("Python worker requirements fingerprint is stale; rerun Cloud setup."); } @@ -1155,13 +1172,15 @@ function validateCheckoutAndRawEnvironmentContracts(sources, errors) { function validateGuideAndMcpContracts(sources, errors) { const { guide, mcp, codexProjectConfig } = sources; for (const [pattern, message] of [ - [/bash scripts\/setup-codex-cloud\.sh/, "The guide must provide the setup command."], - [/install-codex-cloud-command-shims\.sh/, "The guide must document the command-shim workaround."], - [/CODEX_CLOUD_ACCESS_PROFILE=connected/, "The guide must document connected access."], - [/CODEX_CLOUD_GITHUB_PAT/, "The guide must document the narrowly scoped PAT exception."], - [/GitHub connector/, "The guide must document GitHub connector access."], - [/Personal Pro/, "The guide must identify the active Personal Pro workspace."], - [/split control plane/, "The guide must document the Personal Pro provider workaround."], + [/current-host capability checks/, "The active Cloud guide must use current-host capability checks."], + [ + /applies only when explicitly selected/, + "The active Cloud guide must scope the legacy profile to explicit selection.", + ], + [ + /archive\/codex-cloud-legacy-profile-2026-10-16\.md/, + "The active Cloud guide must link the archived legacy bootstrap runbook.", + ], ]) { requireMatch(errors, guide, pattern, message); } @@ -1287,13 +1306,15 @@ async function appendBrowserRuntimeErrors(env, errors) { } } -function appendCheckoutRuntimeErrors(env, errors) { +/** @param {NodeJS.ProcessEnv | Record} [env] */ +export function validateCodexCloudCheckout(root = repoRoot, env = process.env) { + const errors = []; const obsoleteProxyNames = obsoleteNpmProxyVariables(env); if (obsoleteProxyNames.length > 0) { errors.push(`Obsolete npm proxy variable names are set: ${obsoleteProxyNames.join(", ")}.`); } - const baseline = localGitBaseline(repoRoot, env); - const checkout = gitCheckoutFreshness(repoRoot, env); + const baseline = localGitBaseline(root, env); + const checkout = gitCheckoutFreshness(root, env); if (!baseline) { errors.push("Neither local main, origin/main, nor a Cloud task HEAD is available."); } else if (baseline === "HEAD" && checkout.freshness === "unverified" && env.CODEX_CLOUD_PROVISIONING !== "1") { @@ -1301,9 +1322,12 @@ function appendCheckoutRuntimeErrors(env, errors) { "Checkout freshness is unverified: set CODEX_CLOUD_EXPECTED_BASE_SHA to the intended merge/base commit.", ); } - if (checkout.expectedBaseAncestor === "false") { + if (checkout.expectedBase === "invalid") { + errors.push("CODEX_CLOUD_EXPECTED_BASE_SHA must identify an available 40-character commit SHA."); + } else if (checkout.expectedBaseAncestor === "false") { errors.push("CODEX_CLOUD_EXPECTED_BASE_SHA is not an ancestor of the current HEAD."); } + return errors; } function appendOriginRuntimeErrors(errors) { @@ -1315,6 +1339,8 @@ function appendOriginRuntimeErrors(errors) { export async function validateCodexCloudRuntime(env = process.env) { const errors = validateCodexCloudEnvironment(env); + // A rejected legacy boundary must not execute runtime binaries or load dependencies. + if (errors.length > 0) return errors; // Hosted Railway access is the OAuth ChatGPT/Codex app, not CLI token auth. // Do not reintroduce `railway --version` or RAILWAY_API_TOKEN-vs-RAILWAY_TOKEN @@ -1323,7 +1349,7 @@ export async function validateCodexCloudRuntime(env = process.env) { appendPythonWorkerErrors(env, errors); appendRepositoryRuntimeErrors(errors); await appendBrowserRuntimeErrors(env, errors); - appendCheckoutRuntimeErrors(env, errors); + errors.push(...validateCodexCloudCheckout(repoRoot, env)); appendOriginRuntimeErrors(errors); return errors; } @@ -1336,12 +1362,13 @@ if (isDirectEntrypoint(import.meta.url)) { const hostedAppInventoryErrors = validateHostedAppInventory(hostedAppInventory); const hostedAppInputErrors = [...hostedAppArgumentErrors, ...hostedAppInventoryErrors]; errors.push(...hostedAppInputErrors); - const runtime = process.argv.includes("--runtime"); - const environment = runtime || process.env.CODEX_CLOUD === "1" || process.argv.includes("--environment"); - if (runtime) errors.push(...(await validateCodexCloudRuntime())); + const { runtime, environment } = codexCloudValidationSelection(commandArguments); + const runtimeAllowed = runtime && errors.length === 0; + if (runtimeAllowed) errors.push(...(await validateCodexCloudRuntime())); else if (environment) errors.push(...validateCodexCloudEnvironment()); if ( - runtime && + runtimeAllowed && + errors.length === 0 && process.env.CODEX_CLOUD_PROVISIONING === "1" && gitCheckoutFreshness(repoRoot).freshness === "unverified" ) { @@ -1349,8 +1376,8 @@ if (isDirectEntrypoint(import.meta.url)) { "[Codex Cloud Check] WARN: checkout freshness is unverified during provisioning; run explicit acceptance with CODEX_CLOUD_EXPECTED_BASE_SHA.", ); } - if (environment && hostedAppInputErrors.length === 0) { - console.log("[Codex Cloud Environment] sanitized effective modes and capabilities:"); + if (environment && errors.length === 0) { + console.log("[Codex Cloud Environment] sanitized legacy profile modes and capabilities:"); for (const line of sanitizedCloudCapabilityLines(process.env, { hostedAppInventory })) console.log(` ${line}`); } if (errors.length > 0) { diff --git a/scripts/diagnose-codex-cloud.mjs b/scripts/diagnose-codex-cloud.mjs index 2e24b36ba1..54e088f0a1 100644 --- a/scripts/diagnose-codex-cloud.mjs +++ b/scripts/diagnose-codex-cloud.mjs @@ -1,11 +1,15 @@ #!/usr/bin/env node -import { existsSync, readFileSync } from "node:fs"; +import { readFileSync } from "node:fs"; import { spawnSync } from "node:child_process"; import { isDirectEntrypoint } from "./lib/is-entrypoint.mjs"; +import { configuredProviderCredentialNames } from "./check-codex-cloud-setup.mjs"; +import { nodeVersionSatisfiesRange } from "./setup-codex-worktree.mjs"; import { lockPythonVersion, workerPythonLockTargets } from "./worker-python-lock-config.mjs"; +const repositoryNodeRange = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).engines.node; + function major(version) { return version?.match(/\d+/)?.[0] ?? null; } @@ -17,13 +21,14 @@ export function diagnoseCodexCloud({ cloudLockContents, setupStep, setupExitCode, + expectedNodeRange = repositoryNodeRange, }) { const issues = []; - if (major(nodeVersion) !== "24") { + if (!nodeVersionSatisfiesRange(nodeVersion, expectedNodeRange)) { issues.push({ code: "NODE_RUNTIME", - issue: `Node 24.x is required; detected ${nodeVersion || "unavailable"}.`, - fix: "Select Node 24 in the Cloud environment or rerun setup with nvm available.", + issue: `Node ${expectedNodeRange} is required; detected ${nodeVersion || "unavailable"}.`, + fix: "Select the Node range from package.json engines.node in the Cloud environment or rerun setup with nvm available.", }); } if (major(npmVersion) !== "11") { @@ -93,29 +98,111 @@ function npmVersion() { return commandVersion("npm"); } -function argument(name) { - const index = process.argv.indexOf(name); - return index === -1 ? null : (process.argv[index + 1] ?? null); +/** + * Parse only documented flags; never echo arbitrary argument or profile values. + * @param {string[]} [args] + * @param {NodeJS.ProcessEnv | Record} [env] + */ +export function parseDiagnosticArguments(args = [], env = process.env) { + const options = {}; + const errors = []; + const valueFlags = new Set(["--python-bin", "--setup-step", "--exit-code"]); + for (let index = 0; index < args.length; index += 1) { + const flag = args[index]; + if (Object.hasOwn(options, flag)) { + errors.push("Diagnostic flags may be supplied only once."); + break; + } + if (flag === "--legacy-profile") { + options[flag] = true; + } else if (valueFlags.has(flag)) { + const value = args[index + 1]; + if (!value || value.startsWith("--")) { + errors.push(`${flag} requires a value.`); + break; + } + options[flag] = value; + index += 1; + } else { + errors.push("Unsupported Cloud-diagnostic argument."); + break; + } + } + if ( + env.CODEX_CLOUD_ACCESS_PROFILE !== undefined && + !["offline", "connected"].includes(env.CODEX_CLOUD_ACCESS_PROFILE) + ) { + errors.push("CODEX_CLOUD_ACCESS_PROFILE must be offline or connected."); + } + if (options["--exit-code"] !== undefined && !/^(?:0|[1-9]\d{0,2})$/.test(options["--exit-code"])) { + errors.push("--exit-code must be an integer from 0 to 255."); + } else if (Number(options["--exit-code"]) > 255) { + errors.push("--exit-code must be an integer from 0 to 255."); + } + if (options["--setup-step"] !== undefined && !/^[a-z][a-z0-9-]{0,63}$/.test(options["--setup-step"])) { + errors.push("--setup-step must be a short setup step identifier."); + } + if (args.length > 0 && !legacyCloudDiagnosticsSelected(args, env)) { + errors.push("Diagnostic options require --legacy-profile or CODEX_CLOUD_ACCESS_PROFILE."); + } + return { options, errors }; +} + +/** + * Diagnose the legacy toolchain only after explicit selection, independent of host branding. + * @param {string[]} [args] + * @param {NodeJS.ProcessEnv | Record} [env] + */ +export function legacyCloudDiagnosticsSelected(args = [], env = process.env) { + return args.includes("--legacy-profile") || env.CODEX_CLOUD_ACCESS_PROFILE !== undefined; } export function main() { - if (process.env.CODEX_CLOUD !== "1") { + const args = process.argv.slice(2); + const { options, errors } = parseDiagnosticArguments(args); + if (errors.length > 0) { + console.error(`[Codex Cloud Diagnose] status=INVALID_INPUT issues=${errors.length}`); + for (const error of errors) console.error(`[Codex Cloud Diagnose] FAIL: ${error}`); + return 1; + } + if (!legacyCloudDiagnosticsSelected(args)) { console.log("[Codex Cloud Diagnose] status=NOT_APPLICABLE issues=0"); console.log( - "[Codex Cloud Diagnose] INFO DESKTOP_CONTEXT: Run this command inside a Codex Cloud shell; local Python versions are not Cloud runtime evidence.", + "[Codex Cloud Diagnose] INFO PROFILE_NOT_SELECTED: Select the legacy profile explicitly with --legacy-profile or CODEX_CLOUD_ACCESS_PROFILE; local Python versions are not Cloud runtime evidence.", ); return 0; } - const cloudLock = workerPythonLockTargets.cloud.outputFile; - const pythonBin = argument("--python-bin") || process.env.CODEX_CLOUD_OCR_PYTHON || "python3"; + const providerNames = configuredProviderCredentialNames(); + if (providerNames.length > 0) { + console.error("[Codex Cloud Diagnose] status=BLOCKED_ENVIRONMENT issues=1"); + console.error( + `[Codex Cloud Diagnose] FAIL: Legacy diagnostics cannot probe runtimes with provider environment variables: ${providerNames.join(", ")}.`, + ); + return 1; + } + + const cloudLock = new URL(`../${workerPythonLockTargets.cloud.outputFile}`, import.meta.url); + let cloudLockContents = null; + try { + cloudLockContents = readFileSync(cloudLock, "utf8"); + } catch (error) { + if (error.code !== "ENOENT") { + console.error("[Codex Cloud Diagnose] status=DEGRADED issues=1"); + console.error( + "[Codex Cloud Diagnose] ISSUE CLOUD_PYTHON_LOCK_UNREADABLE: The repository Cloud Python lock could not be read.", + ); + return 1; + } + } + const pythonBin = options["--python-bin"] || process.env.CODEX_CLOUD_OCR_PYTHON || "python3"; const issues = diagnoseCodexCloud({ nodeVersion: commandVersion(process.execPath, ["--version"]), npmVersion: npmVersion(), pythonVersion: commandVersion(pythonBin), - cloudLockContents: existsSync(cloudLock) ? readFileSync(cloudLock, "utf8") : null, - setupStep: argument("--setup-step"), - setupExitCode: argument("--exit-code"), + cloudLockContents, + setupStep: options["--setup-step"], + setupExitCode: options["--exit-code"], }); console.log(`[Codex Cloud Diagnose] status=${issues.length === 0 ? "HEALTHY" : "DEGRADED"} issues=${issues.length}`); for (const issue of issues) { diff --git a/scripts/ensure-codex-cloud-git-remote.mjs b/scripts/ensure-codex-cloud-git-remote.mjs index ffd025a988..160e30582f 100644 --- a/scripts/ensure-codex-cloud-git-remote.mjs +++ b/scripts/ensure-codex-cloud-git-remote.mjs @@ -28,11 +28,13 @@ export function remoteContainsCredentials(remoteUrl) { export function remoteRepositoryIdentity(remoteUrl) { if (typeof remoteUrl !== "string" || remoteContainsCredentials(remoteUrl)) return null; - const normalized = remoteUrl - .trim() - .replace(/^git@github\.com:/i, "") - .replace(/^ssh:\/\/git@github\.com\//i, "") - .replace(/^https?:\/\/github\.com\//i, "") + const githubPrefix = /^(?:git@github\.com:|ssh:\/\/git@github\.com\/|https?:\/\/github\.com\/)/i; + const candidate = remoteUrl.trim(); + // A matching path alone can be a local filesystem remote. Establish the GitHub + // transport/host before interpreting owner/repository as provider identity. + if (!githubPrefix.test(candidate)) return null; + const normalized = candidate + .replace(githubPrefix, "") .replace(/\.git$/i, "") .replace(/^\/+|\/+$/g, ""); return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalized) ? normalized : null; diff --git a/scripts/install-codex-cloud-command-shims.sh b/scripts/install-codex-cloud-command-shims.sh index ff87b5450f..b26cb263a3 100644 --- a/scripts/install-codex-cloud-command-shims.sh +++ b/scripts/install-codex-cloud-command-shims.sh @@ -10,6 +10,18 @@ fail() { repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || fail "Run this script from the PsychSift repository." cd "$repo_root" +# The documented launcher-name exception may use an existing generated profile; +# unexpected provider names must never be hidden by sourcing that profile. +if bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh; then + : +else + raw_boundary_status="$?" + case "$raw_boundary_status" in + 2) ;; # Restricted continuation; the raw boundary remains failed. + *) exit "$raw_boundary_status" ;; + esac +fi + expected_node_major="$(tr -cd '0-9' < .node-version)" [[ -n "$expected_node_major" ]] || fail "Could not read the Node major from .node-version." diff --git a/scripts/maintain-codex-cloud.sh b/scripts/maintain-codex-cloud.sh index 9872fe061c..fb716d0125 100644 --- a/scripts/maintain-codex-cloud.sh +++ b/scripts/maintain-codex-cloud.sh @@ -2,18 +2,60 @@ set -Eeuo pipefail +fail() { + printf '[codex-cloud:maintenance] ERROR: %s\n' "$1" >&2 + exit 1 +} + repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || { printf '[codex-cloud:maintenance] ERROR: Run this script from the PsychSift repository.\n' >&2 exit 1 } cd "$repo_root" -export CODEX_CLOUD=1 +if bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh; then + : +else + raw_boundary_status="$?" + exit "$raw_boundary_status" +fi -if [[ -f "$HOME/.clinical-kb-codex-cloud.sh" ]]; then - # shellcheck source=/dev/null - source "$HOME/.clinical-kb-codex-cloud.sh" +# A cached profile must not hide an invalid inherited selection. Retain an +# explicit valid selection so a different cached profile cannot replace it. +requested_cloud_profile_set="${CODEX_CLOUD_ACCESS_PROFILE+yes}" +requested_cloud_profile="${CODEX_CLOUD_ACCESS_PROFILE-}" +if [[ "$requested_cloud_profile_set" = "yes" ]]; then + case "$requested_cloud_profile" in + offline|connected) ;; + *) fail "Unsupported CODEX_CLOUD_ACCESS_PROFILE; expected offline or connected." ;; + esac fi +if [[ "${RAG_PROVIDER_MODE+yes}" = "yes" && "$RAG_PROVIDER_MODE" != "offline" ]]; then + fail "The selected legacy Codex Cloud profile must keep RAG_PROVIDER_MODE=offline." +fi + +runtime_profile="$HOME/.clinical-kb-codex-cloud.sh" +if [[ ! -f "$runtime_profile" ]]; then + printf '[codex-cloud:maintenance] Generated runtime profile unavailable; rerunning full setup.\n' + exec bash scripts/setup-codex-cloud.sh +fi + +load_runtime_profile() { + # shellcheck source=/dev/null + source "$runtime_profile" + case "${CODEX_CLOUD_ACCESS_PROFILE-}" in + offline|connected) ;; + *) fail "Cached CODEX_CLOUD_ACCESS_PROFILE is invalid; rerun setup with the intended profile." ;; + esac + if [[ "$requested_cloud_profile_set" = "yes" && "$CODEX_CLOUD_ACCESS_PROFILE" != "$requested_cloud_profile" ]]; then + fail "Cached CODEX_CLOUD_ACCESS_PROFILE differs from the requested profile; rerun setup with the intended profile." + fi + [[ "${RAG_PROVIDER_MODE-}" = "offline" ]] || + fail "The cached legacy Codex Cloud profile must keep RAG_PROVIDER_MODE=offline; rerun setup." +} + +export CODEX_CLOUD=1 +load_runtime_profile if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then printf '[codex-cloud:maintenance] Node/npm unavailable; rerunning full setup.\n' @@ -22,8 +64,7 @@ fi node scripts/ensure-codex-cloud-git-remote.mjs --configure-gh-helper bash scripts/refresh-codex-cloud-base.sh -# shellcheck source=/dev/null -source "$HOME/.clinical-kb-codex-cloud.sh" +load_runtime_profile npm run check:codex-cloud if ! CODEX_CLOUD_PROVISIONING=1 npm run check:codex-cloud -- --runtime; then printf '[codex-cloud:maintenance] Runtime or toolchain drift detected; rerunning full setup.\n' diff --git a/scripts/organisation/check-retired-rules.mjs b/scripts/organisation/check-retired-rules.mjs index 065b23b0f5..1b5f76b03c 100644 --- a/scripts/organisation/check-retired-rules.mjs +++ b/scripts/organisation/check-retired-rules.mjs @@ -1,8 +1,8 @@ #!/usr/bin/env node // Retired-rules check (organisation framework, suggestion 1). When the owner drops a rule, its key // phrase goes on docs/organisation/retired-rules.json. This check flags any live instruction file -// (AGENTS.md, CLAUDE.md, the agent guides, skills and agent definitions) that still states one of -// those rules as current, so agents stop following instructions that no longer apply. +// (entrypoints, agent guides, skills, copyable prompts and binding launch contracts) that still +// states one of those rules as current, so agents stop following instructions that no longer apply. // // A line hits a rule when it matches the rule's `pattern` and does not match its `allowedContext` // (wording that records the retirement, such as "there is no ... gate" or "no longer"). Matching is @@ -38,14 +38,36 @@ import { export const RULES_FILE = "docs/organisation/retired-rules.json"; -/** The live instruction files: exact files, and folders (ending in "/") whose files all count. */ +const RAG_PROGRAMME_ROOT = "docs/superpowers/rag-upgrade"; +const RAG_LIFECYCLE_FILES = [ + "approval-matrix.md", + "connected-execution.md", + "execution-order.md", + "programme-manifest.json", + "sdd-execution.md", +]; + +/** Live exact files and folders (ending in "/"), subject to the explicit source-input exclusions. */ export const INSTRUCTION_SCOPE = Object.freeze([ "AGENTS.md", "PROJECT-INSTRUCTIONS-CONTINUATION.md", "CLAUDE.md", "GEMINI.md", "docs/agents-guide.md", + "docs/codex-cloud.md", "docs/codex-review-protocol.md", + // Copyable task prompts are instructions too; verbatim review inputs are excluded below. + "docs/prompts/", + // This dated implementation plan remains a binding task contract, not historical evidence. + "docs/superpowers/plans/2026-08-22-mode-aware-clinical-ask-implementation.md", + `${RAG_PROGRAMME_ROOT}/CLOUD-EXECUTION-PROMPT.md`, + `${RAG_PROGRAMME_ROOT}/canonical/START-HERE.cloud.md`, + `${RAG_PROGRAMME_ROOT}/canonical/START-HERE.local.md`, + `${RAG_PROGRAMME_ROOT}/cloud/START-HERE.md`, + `${RAG_PROGRAMME_ROOT}/local/START-HERE.md`, + ...["canonical", "cloud", "local"].flatMap((directory) => + RAG_LIFECYCLE_FILES.map((file) => `${RAG_PROGRAMME_ROOT}/${directory}/${file}`), + ), ".github/pull_request_template.md", ".agents/README.md", "docs/agents/", @@ -56,6 +78,7 @@ export const INSTRUCTION_SCOPE = Object.freeze([ ".agents/skills/", ".cursor/agents/", ".cursor/skills/", + "plugins/clinical-kb/skills/", // Orientation docs every tool is pointed at; a dropped rule restated here misleads just as much. "README.md", "CONTRIBUTING.md", @@ -68,6 +91,10 @@ export const INSTRUCTION_SCOPE = Object.freeze([ ".codex/", ]); +// These are preserved source inputs, not the repository's active working agreement. +// Dated plans, audits, archives and execution evidence otherwise remain outside the scope above. +const INSTRUCTION_EXCLUSIONS = Object.freeze(["docs/prompts/codex-cloud-review/"]); + const PATHSPECS = INSTRUCTION_SCOPE.map((entry) => `:(literal)${entry.replace(/\/$/, "")}`); const RULE_KEYS = new Set(["id", "retired", "summary", "pattern", "allowedContext", "record", "decision"]); const REQUIRED_RULE_KEYS = ["id", "retired", "summary", "pattern", "allowedContext"]; @@ -75,6 +102,7 @@ const EXCERPT_MAX = 160; export function isInstructionFile(file) { const normal = file.replaceAll("\\", "/"); + if (INSTRUCTION_EXCLUSIONS.some((prefix) => normal.startsWith(prefix))) return false; return INSTRUCTION_SCOPE.some((entry) => (entry.endsWith("/") ? normal.startsWith(entry) : normal === entry)); } diff --git a/scripts/rag-phase-launch-check.mjs b/scripts/rag-phase-launch-check.mjs index 9bb087a6ae..4c8d96427f 100644 --- a/scripts/rag-phase-launch-check.mjs +++ b/scripts/rag-phase-launch-check.mjs @@ -37,12 +37,17 @@ const expectedEffort = manifest.adaptiveEffortPolicy.highLaunchPhases.includes(t if (selectedEffort !== expectedEffort) { fail(`Cloud effort must be ${expectedEffort} for ${target}`, "BLOCKED_WRONG_REASONING_EFFORT"); } +// User attestation of the selected effort, including ordinary-language confirmation. +// Runtime model/effort proof remains the route-evidence contract. const xhighConfirmed = process.argv.includes("--xhigh-confirmed"); if (expectedEffort === "xhigh" && !xhighConfirmed) { - fail("xhigh target lacks the user confirmation marker", "BLOCKED_XHIGH_CONFIRMATION_MISSING"); + fail( + "xhigh target needs user confirmation of the selected effort; ordinary language is sufficient, then pass --xhigh-confirmed", + "BLOCKED_XHIGH_CONFIRMATION_MISSING", + ); } if (expectedEffort === "high" && xhighConfirmed) { - fail("high target must use the high launch prompt", "BLOCKED_WRONG_LAUNCH_PROFILE"); + fail("high target must omit the xhigh user-attestation flag", "BLOCKED_WRONG_LAUNCH_PROFILE"); } console.log(`[rag-phase-launch] PASS: ${target} requires ${expectedEffort}; launch profile matches.`); diff --git a/scripts/run-codex-cloud-github.sh b/scripts/run-codex-cloud-github.sh index 71261995d0..18fde44f11 100755 --- a/scripts/run-codex-cloud-github.sh +++ b/scripts/run-codex-cloud-github.sh @@ -20,8 +20,29 @@ case "$1" in *) fail 'Usage: bash scripts/run-codex-cloud-github.sh setup|maintenance' ;; esac +# Reject an invalid retrieval selection before authentication can persist a +# credential. The connected legacy lifecycle remains provider-free for RAG. +rag_provider_mode="${RAG_PROVIDER_MODE-offline}" +[[ "$rag_provider_mode" == "offline" ]] || + fail 'GH_RAG_MODE_INVALID: the connected legacy profile requires RAG_PROVIDER_MODE=offline.' +export RAG_PROVIDER_MODE="$rag_provider_mode" + repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || fail 'GH_REPOSITORY_REQUIRED' cd "$repo_root" + +# Only the explicitly authorized setup-only PAT is exempt here. Validate every +# other inherited provider name before login, runtime restoration or helpers. +# Suppress the child's PASS line: this exception is not a raw-parent pass. +if ( + unset CODEX_CLOUD_GITHUB_PAT + bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh >/dev/null +); then + : +else + raw_boundary_status="$?" + exit "$raw_boundary_status" +fi + command -v gh >/dev/null 2>&1 || fail 'GH_CLI_MISSING' command -v timeout >/dev/null 2>&1 || fail 'GH_TIMEOUT_COMMAND_MISSING' @@ -30,8 +51,6 @@ export GH_PROMPT_DISABLED=1 export GH_COLOR_LABELS=0 export NO_COLOR=1 export NODE_USE_ENV_PROXY=1 -# Ambient tokens must not shadow the deliberately configured gh credential. -unset GH_TOKEN GITHUB_TOKEN unset npm_config_http_proxy npm_config_https_proxy npm_config_proxy if [[ -n "${CODEX_CLOUD_GITHUB_PAT:-}" ]]; then diff --git a/scripts/setup-claude-cloud.sh b/scripts/setup-claude-cloud.sh index f6fd026445..9547a919ea 100755 --- a/scripts/setup-claude-cloud.sh +++ b/scripts/setup-claude-cloud.sh @@ -36,45 +36,16 @@ ALL_TIERS=(profile plugins gh deno browsers python) log() { printf '[claude-cloud] %s\n' "$*"; } warn() { printf '[claude-cloud] WARNING: %s\n' "$*" >&2; } -# Resolve the checkout from this script's OWN location, never from the caller's cwd and never from -# `git rev-parse --show-toplevel`. This repository keeps its Claude worktrees at -# .claude/worktrees/, i.e. inside the main working tree, so `--show-toplevel` run from inside -# one returns the MAIN repository instead of the worktree — verified 2026-08-21. A provisioner that -# believed it would npm/pip against a different checkout than the session is actually using. -# CLAUDE_PROJECT_DIR wins when set, because that is the session's own answer to the same question. -repo_root="${CLAUDE_PROJECT_DIR:-"$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"}" -cd "$repo_root" || exit 1 - -marker_dir="$HOME/.cache/clinical-kb-claude-cloud" -mkdir -p "$marker_dir" - +# Install the session exit contract before resolving the checkout or creating markers: either can +# fail before provisioning begins. Detect --session independently of argument order. selected=() force=0 allow_local=0 session_mode=0 +lock_dir="" for arg in "$@"; do - case "$arg" in - --list) - printf '%s\n' "${ALL_TIERS[@]}" - exit 0 - ;; - --force) force=1 ;; - --allow-local) allow_local=1 ;; - --session) session_mode=1 ;; - --tier=*) selected+=("${arg#--tier=}") ;; - --tier) ;; - --*) warn "ignoring unknown option $arg" ;; - *) selected+=("$arg") ;; - esac + [ "$arg" != "--session" ] || session_mode=1 done -if [ "${#selected[@]}" -eq 0 ] && [ "$session_mode" -eq 0 ]; then - selected=("${ALL_TIERS[@]}") -fi - -# Single exit path. It releases the provisioning lock if this run holds one, and — in session mode — -# forces success, because a SessionStart hook must never be able to fail a session. Outside session -# mode the real exit code survives, so a human or a gate running this directly still sees failure. -lock_dir="" cleanup() { local code=$? [ -n "$lock_dir" ] && rm -rf "$lock_dir" 2>/dev/null @@ -85,15 +56,107 @@ cleanup() { } trap cleanup EXIT -# Hard guard. Every tier here writes outside the repository — into ~/.claude, into apt, into a global -# npm prefix. That is correct for a disposable Linux container and wrong for the operator's Windows -# workstation, whose real ~/.claude is the ORIGIN of the checked-in profile. Getting this backwards -# would overwrite the source of truth with a snapshot of itself. +argument_error() { + warn "$1" + if [ "$session_mode" -eq 1 ]; then + printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"claude-cloud provisioning incomplete: invalid tier arguments; no tiers were run"}}' + fi + exit 2 +} + +# A missing --tier value or a misspelled option must never select every installer by falling +# through to the no-arguments default. Validate the complete command before entering any tier. +list_tiers=0 +while [ "$#" -gt 0 ]; do + case "$1" in + --list) list_tiers=1 ;; + --force) force=1 ;; + --allow-local) allow_local=1 ;; + --session) ;; + --tier=*) + [ -n "${1#--tier=}" ] || argument_error "--tier requires a tier name" + selected+=("${1#--tier=}") + ;; + --tier) + [ "$#" -gt 1 ] && [[ "$2" != --* ]] || argument_error "--tier requires a tier name" + shift + selected+=("$1") + ;; + --*) argument_error "unknown option $1" ;; + *) selected+=("$1") ;; + esac + shift +done + +if [ "$list_tiers" -eq 1 ]; then + printf '%s\n' "${ALL_TIERS[@]}" + exit 0 +fi + +# Every provisioning tier writes outside the checkout. Local no-op and metadata paths must return +# before checking the checkout or creating any cache state. if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ] && [ "$allow_local" -eq 0 ]; then log "not a Claude Code cloud container (CLAUDE_CODE_REMOTE is not true); nothing to do" log "to provision this machine anyway, re-run with --allow-local" exit 0 fi + +if [ "${#selected[@]}" -eq 0 ]; then + if [ "$session_mode" -eq 1 ]; then + read -r -a selected <<< "${CLAUDE_CLOUD_SESSION_TIERS-profile plugins gh deno}" + else + selected=("${ALL_TIERS[@]}") + fi +fi +background_tiers=() +if [ "$session_mode" -eq 1 ]; then + read -r -a background_tiers <<< "${CLAUDE_CLOUD_BACKGROUND_TIERS-browsers python}" +fi +# Check the entire selection, including detached work, before any valid earlier tier can mutate +# the profile. Do not report invalid override values inside JSON context. +known_tier() { + local candidate + for candidate in "${ALL_TIERS[@]}"; do + [ "$1" != "$candidate" ] || return 0 + done + return 1 +} +for tier in "${selected[@]}" "${background_tiers[@]}"; do + if ! known_tier "$tier"; then + warn "unknown tier '$tier' (known: ${ALL_TIERS[*]})" + if [ "$session_mode" -eq 1 ]; then + printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"claude-cloud provisioning incomplete: invalid tier selection; no tiers were run"}}' + fi + exit 1 + fi +done + +# Resolve the checkout from this script's OWN location, never from the caller's cwd and never from +# `git rev-parse --show-toplevel`. This repository keeps its Claude worktrees at +# .claude/worktrees/, i.e. inside the main working tree, so `--show-toplevel` run from inside +# one returns the MAIN repository instead of the worktree — verified 2026-08-21. A provisioner that +# believed it would npm/pip against a different checkout than the session is actually using. +# CLAUDE_PROJECT_DIR wins when set, because that is the session's own answer to the same question. +repo_root="${CLAUDE_PROJECT_DIR:-"$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"}" +if ! cd "$repo_root"; then + if [ "$session_mode" -eq 1 ]; then + printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"claude-cloud provisioning incomplete: project directory is unavailable; no tiers were run"}}' + fi + exit 1 +fi + +marker_dir="$HOME/.cache/clinical-kb-claude-cloud" +if ! mkdir -p "$marker_dir"; then + if [ "$session_mode" -eq 1 ]; then + printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"claude-cloud provisioning incomplete: marker directory is unavailable; no tiers were run"}}' + fi + exit 1 +fi + +# Hard guard. Every tier here writes outside the repository — into ~/.claude, into apt, into a global +# npm prefix. That is correct for a disposable Linux container and wrong for the operator's Windows +# workstation, whose real ~/.claude is the ORIGIN of the checked-in profile. Getting this backwards +# would overwrite the source of truth with a snapshot of itself. if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then warn "--allow-local on a non-cloud machine: the profile tier will OVERWRITE ~/.claude/CLAUDE.md and" warn "~/.claude/skills/* with the checked-in snapshot in .claude/cloud-profile/. On the workstation" @@ -111,7 +174,14 @@ tier_is_done() { local tier="$1" signature="$2" marker="$marker_dir/$1.marker" [ "$force" -eq 0 ] || return 1 [ -f "$marker" ] || return 1 - [ "$(cat "$marker" 2>/dev/null)" = "$signature" ] + [ "$(cat "$marker" 2>/dev/null)" = "$signature" ] || return 1 + # A cache marker outlives a binary removed from PATH or replaced by an unsupported version. + # Validate the cheap CLI prerequisites before reusing their installation receipt. + case "$tier" in + gh) have gh && gh --version >/dev/null 2>&1 ;; + deno) have deno && [ "$(deno --version 2>/dev/null | sed -n '1s/^deno \([0-9]*\).*/\1/p')" = "2" ] ;; + *) return 0 ;; + esac } mark_tier_done() { @@ -367,12 +437,6 @@ tier_signature() { # Run # -------------------------------------------------------------------------------------------- -# Session mode: the SessionStart hook's entry point. Fast tiers run inline here; the slow ones are -# re-launched detached below, after the inline work finishes. -if [ "$session_mode" -eq 1 ] && [ "${#selected[@]}" -eq 0 ]; then - read -r -a selected <<< "${CLAUDE_CLOUD_SESSION_TIERS-profile plugins gh deno}" -fi - # Locking is PER TIER, not per run. A global lock would deadlock the design: the session hook forks a # detached child for the slow tiers while the parent still holds the lock, and the child would see it # held and quietly do nothing. Tiers are independent, so a per-tier lock is both the correct @@ -401,15 +465,6 @@ release_tier_lock() { } for tier in "${selected[@]}"; do - case " ${ALL_TIERS[*]} " in - *" $tier "*) ;; - *) - warn "unknown tier '$tier' (known: ${ALL_TIERS[*]})" - failed+=("$tier") - continue - ;; - esac - case "$skip_list" in *",$tier,"*) log "tier ${tier}: skipped by CLAUDE_CLOUD_SKIP_TIERS" @@ -448,7 +503,6 @@ done # ~/.cache/ms-playwright and the OCR venv in ~/.cache — so they cannot race a build or test the model # starts meanwhile. if [ "$session_mode" -eq 1 ]; then - read -r -a background_tiers <<< "${CLAUDE_CLOUD_BACKGROUND_TIERS-browsers python}" pending=() for tier in "${background_tiers[@]}"; do # Signature, not bare marker existence. A marker whose signature no longer matches means the diff --git a/scripts/setup-codex-cloud.sh b/scripts/setup-codex-cloud.sh index ca03a4e556..aa3fafa964 100755 --- a/scripts/setup-codex-cloud.sh +++ b/scripts/setup-codex-cloud.sh @@ -40,17 +40,43 @@ diagnose_setup_failure() { if [[ -n "$diagnostic_python_bin" ]]; then diagnostic_args+=(--python-bin "$diagnostic_python_bin") fi - CODEX_CLOUD=1 node scripts/diagnose-codex-cloud.mjs "${diagnostic_args[@]}" || true + CODEX_CLOUD=1 node scripts/diagnose-codex-cloud.mjs --legacy-profile "${diagnostic_args[@]}" || true else printf '[codex-cloud:setup] FIX: review the first error above, then retry setup.\n' >&2 fi exit "$status" } -trap diagnose_setup_failure ERR - repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || fail "Run this script from the PsychSift repository." cd "$repo_root" +# Validate the inherited boundary before loading profiles or invoking runtimes. +# A failing probe must not trigger Node diagnostics or become a scrubbed pass. +if bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh; then + : +else + raw_boundary_status="$?" + exit "$raw_boundary_status" +fi + +# Validate selection before runtime discovery, nvm or package-manager work. +# Missing selects the legacy default; an explicitly empty value is invalid. +setup_step="runtime-profile" +access_profile="${CODEX_CLOUD_ACCESS_PROFILE-offline}" +case "$access_profile" in + offline|connected) ;; + *) fail "Unsupported CODEX_CLOUD_ACCESS_PROFILE; expected offline or connected." ;; +esac + +# Pin configured retrieval mode without permitting provider execution. +if [[ "$access_profile" = "connected" ]]; then + rag_provider_mode="${RAG_PROVIDER_MODE:-offline}" + [[ "$rag_provider_mode" = "offline" ]] || + fail "The selected legacy Codex Cloud profile must keep RAG_PROVIDER_MODE=offline; run live OpenAI checks only in the protected provider workflow." +else + rag_provider_mode="offline" +fi +trap diagnose_setup_failure ERR + expected_node_major="$(tr -cd '0-9' < .node-version)" expected_node_range="$(sed -n 's/.*"node"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' package.json | head -n 1)" expected_node_floor="$(printf '%s\n' "$expected_node_range" | sed -n 's/^>=\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\) <[0-9][0-9]*$/\1/p')" @@ -123,25 +149,6 @@ if [[ "$policy_only_harness" != "1" ]]; then fi fi -setup_step="runtime-profile" -access_profile="${CODEX_CLOUD_ACCESS_PROFILE:-offline}" -case "$access_profile" in - offline|connected) ;; - *) fail "Unsupported CODEX_CLOUD_ACCESS_PROFILE: $access_profile" ;; -esac - -# Resolve the retrieval mode at setup time so the generated profile pins the -# value configured in the Codex environment. The agent shell does not inherit -# these variables, so a runtime `${RAG_PROVIDER_MODE:-auto}` fallback would -# override a connected environment configured for offline retrieval. -if [[ "$access_profile" = "connected" ]]; then - rag_provider_mode="${RAG_PROVIDER_MODE:-offline}" - [[ "$rag_provider_mode" = "offline" ]] || - fail "Ordinary Codex Cloud must keep RAG_PROVIDER_MODE=offline; run live OpenAI checks only in the protected provider workflow." -else - rag_provider_mode="offline" -fi - runtime_profile="$HOME/.clinical-kb-codex-cloud.sh" setup_step="runtime-profile" cat > "$runtime_profile" < = {}, extraArgs: string[] = ["--skip=skills"]) { - return spawnSync(process.execPath, [applier, ...extraArgs], { +function runApplier( + configDir: string, + env: Record = {}, + extraArgs: string[] = ["--skip=skills"], + scriptPath = applier, +) { + return spawnSync(process.execPath, [scriptPath, ...extraArgs], { encoding: "utf8", env: { ...process.env, @@ -81,6 +107,26 @@ function runApplier(configDir: string, env: Record = {}, extraAr }); } +function makeMemoryProfileFixture() { + const fixtureRoot = makeConfigDir(); + const fixtureScript = join(fixtureRoot, "scripts", "apply-claude-cloud-profile.mjs"); + const memorySource = join(fixtureRoot, ".claude", "cloud-profile", "memory"); + mkdirSync(join(fixtureRoot, "scripts")); + mkdirSync(join(fixtureRoot, ".claude", "cloud-profile"), { recursive: true }); + cpSync(applier, fixtureScript); + cpSync(join(profileDir, "memory"), memorySource, { recursive: true }); + const configDir = makeConfigDir(); + const slug = repoRoot.replace(/[/\\:]/g, "-"); + return { + configDir, + fixtureRoot, + fixtureScript, + memorySource, + memoryTarget: join(configDir, "projects", slug, "memory"), + run: () => runApplier(configDir, {}, ["--skip=settings,claude-md,skills"], fixtureScript), + }; +} + afterEach(() => { while (temporaryDirectories.length > 0) { // Bounded retries are a repo-wide contract for recursive fixture cleanup @@ -137,6 +183,64 @@ describe("apply-claude-cloud-profile", () => { expect(Object.keys(merged.enabledPlugins).length).toBeGreaterThan(0); }); + it("rejects a misspelled applier option before writing the profile", () => { + const configDir = makeConfigDir(); + const result = runApplier(configDir, {}, ["--dryrun", "--skip=skills,memory,claude-md"]); + expect(result.status).toBe(2); + expect(existsSync(join(configDir, "settings.json"))).toBe(false); + }); + + it.each(["null", '["host data"]', '"host data"'])( + "preserves nonobject host settings %s before replacing them", + (settings) => { + const configDir = makeConfigDir(); + const settingsPath = join(configDir, "settings.json"); + writeFileSync(settingsPath, settings); + const result = runApplier(configDir); + expect(result.status, result.stderr).toBe(0); + expect(readFileSync(`${settingsPath}.unparseable`, "utf8")).toBe(settings); + expect(JSON.parse(readFileSync(settingsPath, "utf8")).permissions.defaultMode).toBe("auto"); + }, + ); + + it("does not echo malformed host settings in the JSON parse warning", () => { + const configDir = makeConfigDir(); + const syntheticSecret = "SYNTHETIC_PRIVATE_SETTING"; + writeFileSync(join(configDir, "settings.json"), syntheticSecret); + const result = runApplier(configDir); + expect(result.status).toBe(0); + expect(`${result.stdout}\n${result.stderr}`).not.toContain(syntheticSecret); + expect(readFileSync(join(configDir, "settings.json.unparseable"), "utf8")).toBe(syntheticSecret); + }); + + it("rejects nonobject profile settings without replacing host settings", () => { + const fixture = makeMemoryProfileFixture(); + writeFileSync(join(fixture.fixtureRoot, ".claude", "cloud-profile", "settings.json"), "[]"); + writeFileSync(join(fixture.configDir, "settings.json"), '{"host":"preserve"}'); + const result = runApplier(fixture.configDir, {}, ["--skip=skills,memory,claude-md"], fixture.fixtureScript); + expect(result.status).toBe(1); + expect(readFileSync(join(fixture.configDir, "settings.json"), "utf8")).toBe('{"host":"preserve"}'); + }); + + it("validates memory seeds before changing any other profile part", () => { + const fixture = makeMemoryProfileFixture(); + const source = join(fixture.fixtureRoot, ".claude", "cloud-profile"); + writeFileSync(join(source, "settings.json"), '{"profileSetting":true}'); + writeFileSync(join(source, "CLAUDE.md"), "profile instructions\n"); + writeFileSync(join(fixture.memorySource, "seed-manifest.json"), "invalid manifest"); + writeFileSync(join(fixture.configDir, "settings.json"), '{"hostSetting":true}'); + writeFileSync(join(fixture.configDir, "CLAUDE.md"), "host instructions\n"); + + const result = runApplier(fixture.configDir, {}, ["--skip=skills"], fixture.fixtureScript); + + expect(result.status).toBe(1); + expect(result.stderr).toContain("memory seed manifest"); + expect(readFileSync(join(fixture.configDir, "settings.json"), "utf8")).toBe('{"hostSetting":true}'); + expect(readFileSync(join(fixture.configDir, "CLAUDE.md"), "utf8")).toBe("host instructions\n"); + expect(existsSync(join(fixture.configDir, "CLAUDE.md.pre-cloud-profile"))).toBe(false); + expect(existsSync(fixture.memoryTarget)).toBe(false); + }); + it("preserves a newly malformed settings file when an earlier backup already exists", () => { const configDir = makeConfigDir(); const settingsPath = join(configDir, "settings.json"); @@ -174,7 +278,117 @@ describe("apply-claude-cloud-profile", () => { expect(runApplier(configDir).status).toBe(0); expect(readFileSync(join(memoryDir, firstMemory), "utf8")).toBe("newer, written inside the container\n"); - expect(readdirSync(memoryDir).length).toBe(readdirSync(join(profileDir, "memory")).length); + const manifest = JSON.parse(readFileSync(join(profileDir, "memory", "seed-manifest.json"), "utf8")); + expect(readdirSync(memoryDir).length).toBe(manifest.files.length); + }); + + it("seeds reusable guidance without retired workstation state or coordination tasks", () => { + const configDir = makeConfigDir(); + const slug = repoRoot.replace(/[/\\:]/g, "-"); + const memoryDir = join(configDir, "projects", slug, "memory"); + + const result = runApplier(configDir); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); + + const seededNames = readdirSync(memoryDir); + const index = readFileSync(join(memoryDir, "MEMORY.md"), "utf8"); + for (const name of retiredSessionMemoryNames) { + expect(seededNames).not.toContain(name); + expect(index).not.toContain(name); + expect(existsSync(join(repoRoot, "docs", "archive", "cloud-session-state-2026-10-16", name))).toBe(true); + } + expect(index).not.toMatch(/R1-first|D4 UNRESOLVED|Phase 5 \+ reconcile left|PR A′\/C local|D:\\Repos\\Database/); + expect(seededNames).toContain("never-print-env-values.md"); + expect(seededNames).not.toContain("seed-manifest.json"); + expect(readFileSync(join(memoryDir, "never-print-env-values.md"), "utf8")).toBe( + readFileSync(join(profileDir, "memory", "never-print-env-values.md"), "utf8"), + ); + // Guidance is installed outside the checkout: sibling links must still resolve there, + // while repository references explicitly use the verified checkout root. + for (const name of seededNames) { + const content = readFileSync(join(memoryDir, name), "utf8"); + for (const match of content.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) { + expect(existsSync(join(memoryDir, match[1])), `${name}: ${match[1]}`).toBe(true); + } + for (const match of content.matchAll(/`(docs\/[^`]+\.md)`/g)) { + expect(existsSync(join(repoRoot, match[1])), `${name}: ${match[1]}`).toBe(true); + expect(content).toContain("Resolve repository paths against the verified current checkout root"); + } + } + }); + + it("preserves existing host memories and indexes even when their seed names were retired", () => { + const configDir = makeConfigDir(); + const slug = repoRoot.replace(/[/\\:]/g, "-"); + const memoryDir = join(configDir, "projects", slug, "memory"); + mkdirSync(memoryDir, { recursive: true }); + const hostIndex = "Host-authored index; retained session references are the host's records.\n"; + writeFileSync(join(memoryDir, "MEMORY.md"), hostIndex); + for (const name of retiredSessionMemoryNames) writeFileSync(join(memoryDir, name), `Host-authored ${name}\n`); + + const result = runApplier(configDir); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); + + expect(readFileSync(join(memoryDir, "MEMORY.md"), "utf8")).toBe(hostIndex); + for (const name of retiredSessionMemoryNames) { + expect(readFileSync(join(memoryDir, name), "utf8")).toBe(`Host-authored ${name}\n`); + } + expect(existsSync(join(memoryDir, "never-print-env-values.md"))).toBe(true); + }); + + it.skipIf(process.platform === "win32")("preserves a host memory symlink whose target is temporarily absent", () => { + const fixture = makeMemoryProfileFixture(); + mkdirSync(fixture.memoryTarget, { recursive: true }); + const hostTarget = join(fixture.configDir, "unmounted-host-index.md"); + const index = join(fixture.memoryTarget, "MEMORY.md"); + symlinkSync(hostTarget, index); + const result = fixture.run(); + expect(result.status, result.stderr).toBe(0); + expect(lstatSync(index).isSymbolicLink()).toBe(true); + expect(existsSync(hostTarget)).toBe(false); + }); + + it("does not seed an unlisted session file added to the source directory", () => { + const fixture = makeMemoryProfileFixture(); + writeFileSync(join(fixture.memorySource, "stale-session.md"), "Resume the obsolete task queue.\n"); + + const result = fixture.run(); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); + expect(existsSync(join(fixture.memoryTarget, "stale-session.md"))).toBe(false); + expect(existsSync(join(fixture.memoryTarget, "never-print-env-values.md"))).toBe(true); + }); + + it.each([ + ["../outside.md", "plain Markdown filenames"], + ["nested/unsafe.md", "plain Markdown filenames"], + ["C:\\unsafe.md", "plain Markdown filenames"], + [17, "plain Markdown filenames"], + ["MEMORY.md", "duplicate filenames"], + ["memory.md", "duplicate filenames"], + ["missing.md", "missing or is not a regular file"], + ])("rejects invalid memory seed entry %j without seeding any memories", (entry, message) => { + const fixture = makeMemoryProfileFixture(); + const manifestPath = join(fixture.memorySource, "seed-manifest.json"); + const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); + manifest.files.push(entry); + writeFileSync(manifestPath, JSON.stringify(manifest)); + + const result = fixture.run(); + expect(result.status).toBe(1); + expect(result.stderr).toContain(message); + expect(existsSync(fixture.memoryTarget)).toBe(false); + }); + + it("rejects an index that tries to restore an unlisted session record", () => { + const fixture = makeMemoryProfileFixture(); + writeFileSync(join(fixture.memorySource, "stale-session.md"), "Resume the obsolete task queue.\n"); + const indexPath = join(fixture.memorySource, "MEMORY.md"); + writeFileSync(indexPath, `${readFileSync(indexPath, "utf8")}\n- [Old task](stale-session.md)\n`); + + const result = fixture.run(); + expect(result.status).toBe(1); + expect(result.stderr).toContain("memory seed index refers to an unlisted seed"); + expect(existsSync(fixture.memoryTarget)).toBe(false); }); it.skipIf(!hasVendoredSkills)("replaces a skill entry that is not a directory", () => { @@ -221,8 +435,8 @@ describe("setup-claude-cloud", () => { // here because the provisioner is a shell script, where `$HOME` is just an environment variable — // the applier needs CLAUDE_CONFIG_DIR instead, for the Windows reason recorded at the top of this // file. - function runProvisioner(args: string[], env: Record = {}) { - return spawnSync(bashCommand, [provisioner, ...args], { + function runProvisioner(args: string[], env: Record = {}, scriptPath = provisioner) { + return spawnSync(bashCommand, [scriptPath, ...args], { encoding: "utf8", env: { ...process.env, CLAUDE_CODE_REMOTE: "", HOME: makeSandboxHome(), ...env }, }); @@ -241,6 +455,112 @@ describe("setup-claude-cloud", () => { expect(result.stdout.trim().split(/\r?\n/)).toEqual(["profile", "plugins", "gh", "deno", "browsers", "python"]); }); + it.each([ + { invalidTier: "definitely-not-a-tier", sessionMode: false }, + { invalidTier: "profile plugins", sessionMode: false }, + { invalidTier: "*", sessionMode: false }, + { invalidTier: "definitely-not-a-tier", sessionMode: true }, + ])( + "validates selected tier $invalidTier before provisioning (session=$sessionMode)", + ({ invalidTier, sessionMode }) => { + const project = makeConfigDir(); + const home = makeSandboxHome(); + mkdirSync(join(project, "scripts")); + const script = join(project, "scripts", "setup-claude-cloud.sh"); + cpSync(provisioner, script); + writeFileSync( + join(project, "scripts", "apply-claude-cloud-profile.mjs"), + 'import { writeFileSync } from "node:fs"; writeFileSync(process.env.CLAUDE_CONFIG_DIR + "/mutation", "profile applied");', + ); + const args = sessionMode ? ["--session"] : []; + const result = runProvisioner( + [...args, "profile", invalidTier], + { + HOME: home, + CLAUDE_CODE_REMOTE: "true", + CLAUDE_PROJECT_DIR: project, + CLAUDE_CONFIG_DIR: home, + CLAUDE_CLOUD_SKIP_TIERS: "", + CLAUDE_CLOUD_BACKGROUND_TIERS: "", + }, + script, + ); + expect(result.status).toBe(sessionMode ? 0 : 1); + expect(result.stderr).toContain("unknown tier"); + expect(existsSync(join(home, "mutation"))).toBe(false); + expect(existsSync(markerDirFor(home))).toBe(false); + }, + ); + + it.each(["CLAUDE_CLOUD_SESSION_TIERS", "CLAUDE_CLOUD_BACKGROUND_TIERS"])( + "validates the %s override before provisioning", + (override) => { + const home = makeSandboxHome(); + const result = runProvisioner(["--session"], { + HOME: home, + CLAUDE_CODE_REMOTE: "true", + CLAUDE_CLOUD_SESSION_TIERS: "profile", + CLAUDE_CLOUD_BACKGROUND_TIERS: "", + [override]: "definitely-not-a-tier", + // If validation regresses, keep this fixture from running any real installers. + CLAUDE_CLOUD_SKIP_TIERS: "profile,plugins,gh,deno,browsers,python", + }); + expect(result.status).toBe(0); + expect(result.stderr).toContain("unknown tier"); + expect(result.stdout).not.toMatch(/tier \w+:|provisioning in the background/); + expect(existsSync(markerDirFor(home))).toBe(false); + }, + ); + + it("does not trust a cached deno marker when the runtime no longer works", () => { + const home = makeSandboxHome(); + const binaries = join(home, "bin"); + mkdirSync(binaries); + for (const [name, body] of [ + ["deno", "exit 127"], + ["npm", "exit 97"], + ]) { + const path = join(binaries, name); + writeFileSync(path, `#!/bin/bash\n${body}\n`); + chmodSync(path, 0o755); + } + mkdirSync(markerDirFor(home), { recursive: true }); + writeFileSync(join(markerDirFor(home), "deno.marker"), "v2\n"); + const result = runProvisioner(["deno"], { + HOME: home, + CLAUDE_CODE_REMOTE: "true", + CLAUDE_CLOUD_SKIP_TIERS: "", + PATH: `${binaries}${process.platform === "win32" ? ";" : ":"}${process.env.PATH}`, + }); + expect(result.status).toBe(1); + expect(result.stdout).toContain("tier deno: provisioning"); + expect(result.stdout).not.toContain("already provisioned"); + expect(result.stdout).not.toContain("all selected tiers complete"); + }); + + it.each([["--tier"], ["--tier="], ["--tier", "--force"], ["--nonsense"], ["deno", "--nonsense"]])( + "rejects malformed selector arguments %j before entering any tier", + (...args) => { + const result = runProvisioner(["--allow-local", ...args], { + CLAUDE_CLOUD_SKIP_TIERS: "profile,plugins,gh,deno,browsers,python", + }); + expect(result.status).toBe(2); + expect(result.stdout).not.toMatch(/tier \w+:/); + }, + ); + + it("keeps a session nonfatal and reports when its project directory is unavailable", () => { + const result = runProvisioner(["--session", "deno"], { + CLAUDE_CODE_REMOTE: "true", + CLAUDE_PROJECT_DIR: join(makeSandboxHome(), "missing-project"), + CLAUDE_CLOUD_BACKGROUND_TIERS: "", + }); + expect(result.status).toBe(0); + const envelope = result.stdout.split(/\r?\n/).find((line) => line.startsWith('{"hookSpecificOutput"')); + expect(envelope).toBeDefined(); + expect(JSON.parse(envelope!).hookSpecificOutput.additionalContext).toContain("project directory"); + }); + it("repairs plugin npm dependencies even when the claude CLI is absent", () => { // Claude Code installs the repo's declared enabledPlugins itself, but neither it nor // `claude plugin install` installs a plugin's npm dependencies — a plugin with runtime @@ -292,7 +612,7 @@ describe("setup-claude-cloud", () => { expect(result.status).toBe(0); expect(result.stdout).toMatch(/"hookEventName":"SessionStart"/); expect(result.stdout).toMatch( - /"additionalContext":"claude-cloud provisioning incomplete:[^"]*definitely-not-a-tier/, + /"additionalContext":"claude-cloud provisioning incomplete:[^"]*invalid tier selection/, ); }); @@ -315,7 +635,7 @@ describe("setup-claude-cloud", () => { expect(result.stdout).not.toMatch(/all selected tiers complete/); }); - it("resolves its marker directory from the sandbox HOME it was given", () => { + it("keeps tier listing read-only even when its project directory is unavailable", () => { // Guards the isolation the test above depends on. This suite used to plant its lock in the REAL // ~/.cache/clinical-kb-claude-cloud, so it both read and wrote the machine's actual provisioning // state. A completed-tier marker there short-circuited the tier before the code under test ran, @@ -328,15 +648,17 @@ describe("setup-claude-cloud", () => { // hook detaches the browsers and python tiers, so a marker can legitimately appear there mid-test // and fail the comparison while the spawn was correctly sandboxed all along. // - // `--list` is enough because the script creates its marker directory before it parses arguments, - // so this observes the HOME resolution itself and nothing downstream of it. + // Listing is metadata only: it must need neither a working checkout nor a writable cache. const sandboxHome = makeSandboxHome(); expect(existsSync(markerDirFor(sandboxHome))).toBe(false); - const result = runProvisioner(["--list"], { HOME: sandboxHome }); + const result = runProvisioner(["--list"], { + HOME: sandboxHome, + CLAUDE_PROJECT_DIR: join(sandboxHome, "missing-project"), + }); expect(result.status).toBe(0); - expect(existsSync(markerDirFor(sandboxHome))).toBe(true); + expect(existsSync(markerDirFor(sandboxHome))).toBe(false); }); it("tries the pinned Python minor before ever falling back to a different python3", () => { @@ -381,6 +703,21 @@ describe("setup-claude-cloud", () => { }); describe("SessionStart registration", () => { + it("injects conditional CI guidance and requires ensure before browser execution", () => { + const result = spawnSync(bashCommand, [join(repoRoot, ".claude", "hooks", "testing-policy.sh")], { + encoding: "utf8", + input: "malformed hook payload is harmless", + env: { ...process.env, CLAUDE_CODE_REMOTE: "true", CLAUDE_PROJECT_DIR: repoRoot }, + }); + expect(result.status).toBe(0); + const context = JSON.parse(result.stdout).hookSpecificOutput.additionalContext; + expect(context).toContain("npm run ensure"); + expect(context).toContain("event, draft and scope"); + expect(context).toContain("pending until"); + expect(context).not.toMatch(/646|25 min|CI re-runs the browser suite on every change/); + expect(context).not.toContain("full suite left to CI"); + }); + it("runs the provisioner after Node and node_modules exist", () => { // The browsers tier shells out to ./node_modules/.bin/playwright, so ordering is load-bearing: // session-start.sh is what installs Node and runs npm ci. diff --git a/tests/cloud-installed-guidance.test.ts b/tests/cloud-installed-guidance.test.ts new file mode 100644 index 0000000000..0ea808d7f7 --- /dev/null +++ b/tests/cloud-installed-guidance.test.ts @@ -0,0 +1,83 @@ +import { spawnSync } from "node:child_process"; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +const repoRoot = join(import.meta.dirname, ".."); +let fixtureRoot: string; +let installedRoot: string; + +beforeAll(() => { + fixtureRoot = mkdtempSync(join(tmpdir(), "cloud-guidance-")); + const sourceRoot = join(fixtureRoot, "source"); + const profileRoot = join(sourceRoot, ".claude", "cloud-profile"); + installedRoot = join(fixtureRoot, "installed"); + mkdirSync(join(sourceRoot, "scripts"), { recursive: true }); + mkdirSync(join(profileRoot, "skills"), { recursive: true }); + cpSync( + join(repoRoot, "scripts", "apply-claude-cloud-profile.mjs"), + join(sourceRoot, "scripts", "apply-claude-cloud-profile.mjs"), + ); + cpSync(join(repoRoot, ".claude", "cloud-profile", "CLAUDE.md"), join(profileRoot, "CLAUDE.md")); + for (const name of ["choose-model", "prompt", "code-review"]) { + cpSync(join(repoRoot, ".claude", "cloud-profile", "skills", name), join(profileRoot, "skills", name), { + recursive: true, + }); + } + const env: NodeJS.ProcessEnv = { NODE_ENV: "test", CLAUDE_CONFIG_DIR: installedRoot }; + for (const name of ["PATH", "SystemRoot", "WINDIR", "TEMP", "TMP"]) { + if (process.env[name] !== undefined) env[name] = process.env[name]; + } + const result = spawnSync( + process.execPath, + [join(sourceRoot, "scripts", "apply-claude-cloud-profile.mjs"), "--force", "--skip=settings,memory"], + { env, encoding: "utf8", timeout: 10_000 }, + ); + expect(result.error).toBeUndefined(); + expect(result.status, result.stderr).toBe(0); +}); + +afterAll(() => { + if (fixtureRoot) rmSync(fixtureRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); +}); + +describe("guidance actually installed into a Cloud profile", () => { + it("preserves current project authority and treats copied skills as capability claims to verify", () => { + const text = readFileSync(join(installedRoot, "CLAUDE.md"), "utf8"); + expect(text).toContain("current system, developer and user"); + expect(text).toContain("applicable project safeguards"); + expect(text).toContain("copied descriptions do not establish model, command, plugin or connector"); + expect(text).toContain("old workstation state or startup ceremony"); + }); + + it("does not install fictional model lineups or require unavailable routing controls", () => { + const text = readFileSync(join(installedRoot, "skills", "choose-model", "SKILL.md"), "utf8"); + expect(text).not.toMatch(/Opus 4\.8|Sonnet 5|Fable/); + expect(text).toContain("current host's exposed model inventory and tool schema"); + expect(text).toContain("Use only the effort controls exposed by the relevant interface"); + expect(text).toContain("no special marker or resubmission"); + }); + + it("ships the same continuation and provider boundaries in native and Cloud prompt references", () => { + const relative = join("prompt", "references", "coding-workflow.md"); + const installed = readFileSync(join(installedRoot, "skills", relative), "utf8"); + expect(installed).toBe(readFileSync(join(repoRoot, ".claude", "skills", relative), "utf8")); + expect(installed).toContain("Continue verified same-task work"); + expect(installed).toContain( + "Remote freshness checks such as `git fetch` require applicable provider/network authority", + ); + expect(installed).toContain("Commit or push only with applicable task authority"); + expect(installed).not.toContain("Create a fresh branch and worktree"); + }); + + it("includes uncommitted and untracked work when the installed review skill selects its evidence", () => { + const text = readFileSync(join(installedRoot, "skills", "code-review", "SKILL.md"), "utf8"); + expect(text).toContain("Check `git status --short` and task ownership"); + expect(text).toContain("include owned staged changes, unstaged changes and untracked files"); + expect(text).toContain("read untracked file contents explicitly because `git diff` omits them"); + expect(text).toContain("record the reviewed HEAD and verified base commit"); + expect(text).toContain("A request for review does not by itself authorise paid review or external comments"); + expect(text).not.toContain("prefer `git diff main...HEAD`"); + }); +}); diff --git a/tests/codex-cloud-diagnose.test.ts b/tests/codex-cloud-diagnose.test.ts index 7536b4e5fc..69b64be85a 100644 --- a/tests/codex-cloud-diagnose.test.ts +++ b/tests/codex-cloud-diagnose.test.ts @@ -1,8 +1,15 @@ import { spawnSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; import { describe, expect, it } from "vitest"; -import { diagnoseCodexCloud } from "../scripts/diagnose-codex-cloud.mjs"; +import { + diagnoseCodexCloud, + legacyCloudDiagnosticsSelected, + parseDiagnosticArguments, +} from "../scripts/diagnose-codex-cloud.mjs"; const healthy = { nodeVersion: "24.18.1", @@ -19,6 +26,28 @@ describe("Codex Cloud diagnostics", () => { expect(diagnoseCodexCloud(healthy)).toEqual([]); }); + it.each([ + ["24.14.99", false], + ["24.15.0", true], + ["24.18.1", true], + ["25.0.0", false], + ["unavailable", false], + ])("checks Node %s against the repository engine range", (nodeVersion, accepted) => { + const nodeRange = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).engines.node; + const issues = diagnoseCodexCloud({ ...healthy, nodeVersion }); + expect(issues.map((issue) => issue.code)).toEqual(accepted ? [] : ["NODE_RUNTIME"]); + if (!accepted) expect(issues[0]?.issue).toContain(`Node ${nodeRange} is required`); + }); + + it("keeps the pure diagnostic API usable with an explicitly supplied engine contract", () => { + expect(diagnoseCodexCloud({ ...healthy, nodeVersion: "25.1.2", expectedNodeRange: ">=25.1.2 <26" })).toEqual([]); + expect( + diagnoseCodexCloud({ ...healthy, nodeVersion: "25.1.1", expectedNodeRange: ">=25.1.2 <26" }).map( + (issue) => issue.code, + ), + ).toEqual(["NODE_RUNTIME"]); + }); + it("explains the Python lock mismatch that previously stopped setup", () => { const issues = diagnoseCodexCloud({ ...healthy, @@ -38,14 +67,14 @@ describe("Codex Cloud diagnostics", () => { expect(issues[0]?.fix).toContain("Python 3.12"); }); - it("does not mistake a desktop Python version for Cloud runtime drift", () => { - const environment = { ...process.env }; - delete environment.CODEX_CLOUD; - delete environment.CODEX_CLOUD_OCR_PYTHON; + it.each([ + ["desktop", undefined], + ["host-managed Cloud", "1"], + ])("does not apply legacy Python requirements to an unselected %s host", (_name, marker) => { const result = spawnSync(process.execPath, ["scripts/diagnose-codex-cloud.mjs"], { cwd: process.cwd(), encoding: "utf8", - env: environment, + env: { PATH: process.env.PATH, NODE_ENV: "test", CODEX_CLOUD: marker }, }); expect(result.status, result.stderr).toBe(0); @@ -53,4 +82,105 @@ describe("Codex Cloud diagnostics", () => { expect(result.stdout).toContain("local Python versions are not Cloud runtime evidence"); expect(result.stdout).not.toContain("CLOUD_PYTHON_RUNTIME"); }); + + it.each([ + [[], {}, false], + [[], { CODEX_CLOUD: "1" }, false], + [[], { CODEX_CLOUD_ACCESS_PROFILE: "offline" }, true], + [[], { CODEX_CLOUD_ACCESS_PROFILE: "connected" }, true], + [[], { CODEX_CLOUD_ACCESS_PROFILE: "invalid" }, true], + [[], { CODEX_CLOUD_ACCESS_PROFILE: "" }, true], + [["--legacy-profile"], {}, true], + ])("requires explicit selection before legacy runtime diagnosis: %j %j", (args, environment, expected) => { + expect(legacyCloudDiagnosticsSelected(args, environment)).toBe(expected); + }); + + it.each([ + ["unknown flag", ["--unknown"], {}], + ["missing Python path", ["--legacy-profile", "--python-bin"], {}], + ["flag instead of Python path", ["--legacy-profile", "--python-bin", "--exit-code", "1"], {}], + ["duplicate flag", ["--legacy-profile", "--legacy-profile"], {}], + ["invalid exit code", ["--legacy-profile", "--exit-code", "invalid"], {}], + ["out of range exit code", ["--legacy-profile", "--exit-code", "256"], {}], + ["invalid step", ["--legacy-profile", "--setup-step", "invalid step"], {}], + ["unselected options", ["--exit-code", "1"], {}], + ["invalid configured profile", [], { CODEX_CLOUD_ACCESS_PROFILE: "invalid" }], + ["empty configured profile", [], { CODEX_CLOUD_ACCESS_PROFILE: "" }], + ])("fails safely on %s before runtime diagnosis", (_label, args, environment) => { + const result = spawnSync(process.execPath, ["scripts/diagnose-codex-cloud.mjs", ...args], { + cwd: process.cwd(), + encoding: "utf8", + env: { PATH: process.env.PATH, NODE_ENV: "test", ...environment }, + }); + expect(result.status, result.stderr).toBe(1); + expect(result.stderr).toContain("status=INVALID_INPUT"); + expect(result.stdout).not.toMatch(/NOT_APPLICABLE|HEALTHY|CLOUD_PYTHON_RUNTIME/); + expect(result.stderr).not.toContain("invalid step"); + }); + + it("refuses selected runtime diagnostics at an inherited provider boundary", () => { + const result = spawnSync(process.execPath, ["scripts/diagnose-codex-cloud.mjs", "--legacy-profile"], { + cwd: process.cwd(), + encoding: "utf8", + env: { PATH: process.env.PATH, NODE_ENV: "test", OPENAI_API_KEY: "synthetic-provider-value" }, + }); + expect(result.status, result.stderr).toBe(1); + expect(result.stderr).toContain("status=BLOCKED_ENVIRONMENT"); + expect(result.stderr).toContain("OPENAI_API_KEY"); + expect(result.stderr).not.toContain("synthetic-provider-value"); + expect(result.stdout).not.toMatch(/HEALTHY|CLOUD_PYTHON_RUNTIME/); + }); + + it("accepts the setup failure trap's flags without depending on order", () => { + expect( + parseDiagnosticArguments( + [ + "--exit-code", + "1", + "--python-bin", + process.execPath, + "--legacy-profile", + "--setup-step", + "python-worker-requirements", + ], + {}, + ), + ).toEqual({ + options: { + "--exit-code": "1", + "--python-bin": process.execPath, + "--legacy-profile": true, + "--setup-step": "python-worker-requirements", + }, + errors: [], + }); + }); + + it("reads the repository lock independently of the caller's directory", () => { + const script = path.resolve("scripts/diagnose-codex-cloud.mjs"); + const result = spawnSync(process.execPath, [script, "--legacy-profile", "--python-bin", process.execPath], { + cwd: os.tmpdir(), + encoding: "utf8", + env: { PATH: process.env.PATH, NODE_ENV: "test" }, + }); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(1); + expect(result.stdout).toContain("CLOUD_PYTHON_RUNTIME"); + expect(result.stdout).not.toContain("CLOUD_PYTHON_LOCK_MISSING"); + }); + + it("retains failing runtime diagnostics when the legacy profile is explicitly requested", () => { + const result = spawnSync( + process.execPath, + ["scripts/diagnose-codex-cloud.mjs", "--legacy-profile", "--python-bin", process.execPath], + { + cwd: process.cwd(), + encoding: "utf8", + env: { PATH: process.env.PATH, NODE_ENV: "test" }, + }, + ); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(1); + expect(result.stdout).toContain("status=DEGRADED"); + expect(result.stdout).toContain("CLOUD_PYTHON_RUNTIME"); + expect(result.stdout).not.toContain("NOT_APPLICABLE"); + }); }); diff --git a/tests/codex-cloud-github-lifecycle.test.ts b/tests/codex-cloud-github-lifecycle.test.ts index 24264ca2e1..5544cb8003 100644 --- a/tests/codex-cloud-github-lifecycle.test.ts +++ b/tests/codex-cloud-github-lifecycle.test.ts @@ -1,5 +1,5 @@ import { spawnSync } from "node:child_process"; -import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; @@ -20,6 +20,7 @@ function fixture( options: { mode?: string; profile?: string; + environment?: Record; secret?: boolean; cached?: boolean; authFailure?: boolean; @@ -33,6 +34,10 @@ function fixture( roots.push(root); mkdirSync(path.join(root, "bin")); mkdirSync(path.join(root, "scripts")); + copyFileSync( + path.resolve("scripts/check-codex-cloud-raw-env.sh"), + path.join(root, "scripts/check-codex-cloud-raw-env.sh"), + ); const executable = (file: string, lines: string[]) => { const target = path.join(root, file); writeFileSync(target, ["#!/usr/bin/env bash", "set -eu", ...lines, ""].join("\n")); @@ -53,7 +58,7 @@ function fixture( ]); executable("bin/node", [ 'case "$1" in', - ' --version) [[ "$NODE_MISSING" != 1 || -f "$FIXTURE_ROOT/node-restored" ]] ;;', + ' --version) printf "node-version\\n" >> "$FIXTURE_ROOT/runtime-probes"; [[ "$NODE_MISSING" != 1 || -f "$FIXTURE_ROOT/node-restored" ]] ;;', ' *ensure-codex-cloud-git-remote.mjs) printf "origin\\n" >> "$FIXTURE_ROOT/events" ;;', " *check-github-shell-access.mjs)", ' printf "auth-preflight\\n" >> "$FIXTURE_ROOT/events"', @@ -87,7 +92,11 @@ function fixture( ]); if (options.cached) writeFileSync(path.join(root, "authenticated"), ""); const environment: NodeJS.ProcessEnv = { - ...process.env, + NODE_ENV: "test", + SystemRoot: process.env.SystemRoot, + WINDIR: process.env.WINDIR, + TEMP: process.env.TEMP, + TMP: process.env.TMP, HOME: bashPath(root), NVM_DIR: bashPath(path.join(root, ".nvm")), FIXTURE_ROOT: bashPath(root), @@ -96,8 +105,6 @@ function fixture( CODEX_CLOUD: "1", CODEX_CLOUD_ACCESS_PROFILE: options.profile ?? "connected", RAG_PROVIDER_MODE: "offline", - GH_TOKEN: "synthetic-shadow-token", - GITHUB_TOKEN: "synthetic-shadow-token", npm_config_http_proxy: "synthetic-obsolete-proxy", AUTH_FAILURE: options.authFailure ? "1" : "0", LOGIN_FAILURE: options.loginFailure ? "1" : "0", @@ -107,6 +114,7 @@ function fixture( }; delete environment.CODEX_CLOUD_GITHUB_PAT; if (options.secret) environment.CODEX_CLOUD_GITHUB_PAT = secret; + Object.assign(environment, options.environment); // Git for Windows prepends its real Git to PATH while starting bash.exe. // Reset the fixture PATH inside the shell so no mocked call reaches a real service. const result = spawnSync( @@ -126,19 +134,66 @@ function fixture( windowsHide: true, }, ); - const events = (() => { + const readEvents = (name: string) => { try { - return readFileSync(path.join(root, "events"), "utf8").trim().split("\n"); + return readFileSync(path.join(root, name), "utf8").trim().split("\n"); } catch { return []; } - })(); + }; expect(result.error).toBeUndefined(); expect(result.stdout + result.stderr).not.toContain(secret); - return { ...result, events }; + return { ...result, events: readEvents("events"), runtimeProbes: readEvents("runtime-probes") }; } describe("authorized Cloud GitHub lifecycle", () => { + it.each(["", "live", "synthetic-invalid-rag-selection"])( + "rejects RAG selection %j before login or runtime restoration", + (mode) => { + const result = fixture({ secret: true, nodeMissing: true, environment: { RAG_PROVIDER_MODE: mode } }); + expect(result.status).toBe(1); + expect(result.stderr).toContain("GH_RAG_MODE_INVALID"); + if (mode) expect(result.stderr).not.toContain(mode); + expect(result.events).toEqual([]); + expect(result.runtimeProbes).toEqual([]); + }, + ); + + it.each([ + "OPENAI_API_KEY", + "SUPABASE_SERVICE_ROLE_KEY", + "DATABASE_URL", + "RAILWAY_API_TOKEN", + "GH_TOKEN", + "GITHUB_TOKEN", + "GITLAB_TOKEN", + ])("rejects unexpected inherited %s before the setup PAT can authenticate", (name) => { + const value = "synthetic-unexpected-provider-value"; + const result = fixture({ secret: true, nodeMissing: true, environment: { [name]: value } }); + expect(result.status).toBe(1); + expect(result.stderr).toContain(name); + expect(result.stdout + result.stderr).not.toContain(value); + expect(result.events).toEqual([]); + expect(result.runtimeProbes).toEqual([]); + }); + + it("preserves the failed known-launcher boundary before provider lifecycle actions", () => { + const value = "synthetic-known-launcher-url"; + const result = fixture({ secret: true, nodeMissing: true, environment: { OPENAI_BASE_URL: value } }); + expect(result.status).toBe(2); + expect(result.stderr).toContain("FAIL-KNOWN"); + expect(result.stdout + result.stderr).not.toContain(value); + expect(result.events).toEqual([]); + expect(result.runtimeProbes).toEqual([]); + }); + + it("defaults an absent RAG mode to offline before the authorized PAT lifecycle", () => { + const result = fixture({ secret: true, environment: { RAG_PROVIDER_MODE: undefined } }); + expect(result.status, result.stderr).toBe(0); + expect(result.events).toEqual(["login", "origin", "auth-preflight", "setup", "shims", "helper", "live-gate"]); + expect(result.stdout).not.toContain("Raw Env] PASS"); + }); + it("authenticates before setup and removes secrets before child commands, even with caller tracing", () => { const result = fixture({ secret: true }); expect(result.status, result.stderr).toBe(0); diff --git a/tests/codex-cloud-policy-harness.test.ts b/tests/codex-cloud-policy-harness.test.ts index 5464d1342e..17ae0cfea6 100644 --- a/tests/codex-cloud-policy-harness.test.ts +++ b/tests/codex-cloud-policy-harness.test.ts @@ -1,6 +1,7 @@ import { spawnSync } from "node:child_process"; import { chmodSync, + copyFileSync, existsSync, mkdirSync, mkdtempSync, @@ -66,37 +67,249 @@ function fixture(runtime: "missing" | "mismatched") { config, configPath, runtimeCalls, - run(extraEnvironment: Record = {}, explicitHarness = true) { + run( + extraEnvironment: Record = {}, + explicitHarness = true, + script = "scripts/setup-codex-cloud.sh", + ) { // A minimal environment keeps inherited provider credentials and user // setup flags out of this isolated shell. Runtime shims precede system // utilities and log every invocation, including failure diagnostics. - return spawnSync( - bashCommand, - ["scripts/setup-codex-cloud.sh", ...(explicitHarness ? ["--policy-only-test-harness"] : [])], - { - cwd: repoRoot, - encoding: "utf8", - env: { - NODE_ENV: "test", - SystemRoot: process.env.SystemRoot, - WINDIR: process.env.WINDIR, - TEMP: process.env.TEMP, - TMP: process.env.TMP, - HOME: bashPath(home), - NVM_DIR: bashPath(nvmDirectory), - PATH: `${bashPath(bin)}:${process.platform === "win32" ? "/mingw64/bin:" : ""}/usr/bin:/bin`, - CLOUD_POLICY_RUNTIME_CALLS: bashPath(runtimeCalls), - CODEX_CLOUD_ACCESS_PROFILE: "offline", - CODEX_CLOUD_SETUP_STOP_AFTER_POLICY: "1", - ...extraEnvironment, - }, + return spawnSync(bashCommand, [script, ...(explicitHarness ? ["--policy-only-test-harness"] : [])], { + cwd: repoRoot, + encoding: "utf8", + env: { + NODE_ENV: "test", + SystemRoot: process.env.SystemRoot, + WINDIR: process.env.WINDIR, + TEMP: process.env.TEMP, + TMP: process.env.TMP, + HOME: bashPath(home), + NVM_DIR: bashPath(nvmDirectory), + PATH: `${bashPath(bin)}:${process.platform === "win32" ? "/mingw64/bin:" : ""}/usr/bin:/bin`, + CLOUD_POLICY_RUNTIME_CALLS: bashPath(runtimeCalls), + CODEX_CLOUD_ACCESS_PROFILE: "offline", + CODEX_CLOUD_SETUP_STOP_AFTER_POLICY: "1", + ...extraEnvironment, }, - ); + }); }, }; } describe("Codex Cloud policy-only harness runtime isolation", () => { + it.each(["", "invalid-profile", "synthetic-invalid-profile-value"])( + "rejects invalid explicit profile %j before ordinary bootstrap runtime work", + (profile) => { + const test = fixture("mismatched"); + const result = test.run({ CODEX_CLOUD_ACCESS_PROFILE: profile, CODEX_CLOUD_SETUP_STOP_AFTER_POLICY: "0" }, false); + + expect(result.status, result.stderr || result.stdout).toBe(1); + expect(result.stderr).toContain("Unsupported CODEX_CLOUD_ACCESS_PROFILE; expected offline or connected."); + if (profile) expect(result.stderr).not.toContain(profile); + expect(result.stderr).not.toContain("Running local diagnostics"); + expect(existsSync(test.runtimeCalls)).toBe(false); + expect(readFileSync(test.configPath, "utf8")).toBe(test.config); + expect(existsSync(path.join(test.home, ".clinical-kb-codex-cloud.sh"))).toBe(false); + }, + ); + + it("rejects connected live retrieval selection before ordinary bootstrap runtime work", () => { + const test = fixture("mismatched"); + const result = test.run( + { + CODEX_CLOUD_ACCESS_PROFILE: "connected", + RAG_PROVIDER_MODE: "live", + CODEX_CLOUD_SETUP_STOP_AFTER_POLICY: "0", + }, + false, + ); + + expect(result.status, result.stderr || result.stdout).toBe(1); + expect(result.stderr).toContain("must keep RAG_PROVIDER_MODE=offline"); + expect(existsSync(test.runtimeCalls)).toBe(false); + expect(existsSync(path.join(test.home, ".clinical-kb-codex-cloud.sh"))).toBe(false); + }); + + it.each([ + { CODEX_CLOUD_ACCESS_PROFILE: "" }, + { CODEX_CLOUD_ACCESS_PROFILE: "synthetic-invalid-maintenance-profile" }, + { CODEX_CLOUD_ACCESS_PROFILE: "connected", RAG_PROVIDER_MODE: "live" }, + { CODEX_CLOUD_ACCESS_PROFILE: "connected", RAG_PROVIDER_MODE: "" }, + { CODEX_CLOUD_ACCESS_PROFILE: undefined, RAG_PROVIDER_MODE: "live" }, + ])("rejects inherited maintenance selectors before a cached profile can replace them: %j", (selectors) => { + const test = fixture("missing"); + const profilePath = path.join(test.home, ".clinical-kb-codex-cloud.sh"); + const profile = [ + 'printf "%s\\n" profile >> "$CLOUD_POLICY_RUNTIME_CALLS"', + "export CODEX_CLOUD_ACCESS_PROFILE=offline", + "export RAG_PROVIDER_MODE=offline", + "", + ].join("\n"); + writeFileSync(profilePath, profile); + + const result = test.run(selectors, false, "scripts/maintain-codex-cloud.sh"); + + expect(result.status, result.stderr || result.stdout).toBe(1); + expect(result.stderr).toContain( + selectors.RAG_PROVIDER_MODE !== undefined + ? "RAG_PROVIDER_MODE=offline" + : "Unsupported CODEX_CLOUD_ACCESS_PROFILE", + ); + expect(result.stderr).not.toContain("synthetic-invalid-maintenance-profile"); + expect(existsSync(test.runtimeCalls)).toBe(false); + expect(readFileSync(profilePath, "utf8")).toBe(profile); + expect(readFileSync(test.configPath, "utf8")).toBe(test.config); + }); + + it.each(["offline", "connected"])("rejects a cached profile that replaces an explicit %s selection", (requested) => { + const test = fixture("missing"); + const cached = requested === "offline" ? "connected" : "offline"; + writeFileSync( + path.join(test.home, ".clinical-kb-codex-cloud.sh"), + `export CODEX_CLOUD_ACCESS_PROFILE=${cached}\nexport RAG_PROVIDER_MODE=offline\n`, + ); + + const result = test.run({ CODEX_CLOUD_ACCESS_PROFILE: requested }, false, "scripts/maintain-codex-cloud.sh"); + + expect(result.status, result.stderr || result.stdout).toBe(1); + expect(result.stderr).toContain("differs from the requested profile"); + expect(existsSync(test.runtimeCalls)).toBe(false); + }); + + it.each(["offline", "connected"])("reuses a valid cached %s profile when no profile was inherited", (cached) => { + const test = fixture("missing"); + writeFileSync( + path.join(test.home, ".clinical-kb-codex-cloud.sh"), + `export CODEX_CLOUD_ACCESS_PROFILE=${cached}\nexport RAG_PROVIDER_MODE=offline\n`, + ); + + const result = test.run({ CODEX_CLOUD_ACCESS_PROFILE: undefined }, false, "scripts/maintain-codex-cloud.sh"); + + // The isolated Node stub fails before any repository helper can execute. + expect(result.status, result.stderr || result.stdout).toBe(127); + expect(result.stderr).not.toContain("ERROR"); + expect(readFileSync(test.runtimeCalls, "utf8").trim()).toBe("node"); + }); + + it.each([ + { profile: "", rag: "offline" }, + { profile: "invalid", rag: "offline" }, + { profile: "connected", rag: "live" }, + ])("rejects invalid cached maintenance selection %j before repository helpers", ({ profile, rag }) => { + const test = fixture("missing"); + writeFileSync( + path.join(test.home, ".clinical-kb-codex-cloud.sh"), + `export CODEX_CLOUD_ACCESS_PROFILE="${profile}"\nexport RAG_PROVIDER_MODE="${rag}"\n`, + ); + + const result = test.run({ CODEX_CLOUD_ACCESS_PROFILE: undefined }, false, "scripts/maintain-codex-cloud.sh"); + + expect(result.status, result.stderr || result.stdout).toBe(1); + expect(result.stderr).toContain( + profile === "connected" ? "RAG_PROVIDER_MODE=offline" : "Cached CODEX_CLOUD_ACCESS_PROFILE is invalid", + ); + expect(existsSync(test.runtimeCalls)).toBe(false); + }); + + it.each(["scripts/setup-codex-cloud.sh", "scripts/maintain-codex-cloud.sh"])( + "%s preserves raw-boundary failures before runtime/profile execution", + (script) => { + for (const [variable, status] of [ + ["SUPABASE_SERVICE_ROLE_KEY", 1], + ["OPENAI_BASE_URL", 2], + ] as const) { + const test = fixture("missing"); + const profilePath = path.join(test.home, ".clinical-kb-codex-cloud.sh"); + const profile = 'printf "%s\\n" profile >> "$CLOUD_POLICY_RUNTIME_CALLS"\n'; + writeFileSync(profilePath, profile); + const value = "synthetic-boundary-value-never-print"; + const result = test.run({ [variable]: value }, true, script); + + expect(result.status, result.stderr || result.stdout).toBe(status); + expect(result.stderr).toContain(variable); + expect(result.stdout + result.stderr).not.toContain(value); + expect(result.stderr).not.toContain("Running local diagnostics"); + expect(existsSync(test.runtimeCalls)).toBe(false); + expect(readFileSync(test.configPath, "utf8")).toBe(test.config); + expect(readFileSync(profilePath, "utf8")).toBe(profile); + } + }, + ); + + it("blocks unexpected names before the direct shim installer can scrub them", () => { + const test = fixture("missing"); + writeFileSync( + path.join(test.home, ".clinical-kb-codex-cloud.sh"), + 'printf "%s\\n" profile >> "$CLOUD_POLICY_RUNTIME_CALLS"\nunset SUPABASE_SERVICE_ROLE_KEY\n', + ); + + const result = test.run( + { SUPABASE_SERVICE_ROLE_KEY: "synthetic-shim-boundary-value" }, + false, + "scripts/install-codex-cloud-command-shims.sh", + ); + + expect(result.status, result.stderr || result.stdout).toBe(1); + expect(result.stderr).toContain("SUPABASE_SERVICE_ROLE_KEY"); + expect(result.stderr).not.toContain("synthetic-shim-boundary-value"); + expect(existsSync(test.runtimeCalls)).toBe(false); + expect(existsSync(path.join(test.home, ".local/bin/node"))).toBe(false); + }); + + it("retains the documented restricted shim path for the known launcher name", () => { + const test = fixture("missing"); + const nodeBin = path.join(test.home, ".nvm/versions/node/v24.0.0/bin"); + mkdirSync(nodeBin, { recursive: true }); + for (const command of ["node", "npm", "npx"]) { + const executable = path.join(nodeBin, command); + writeFileSync(executable, "#!/bin/bash\nexit 0\n"); + chmodSync(executable, 0o755); + } + writeFileSync( + path.join(test.home, ".clinical-kb-codex-cloud.sh"), + 'export NVM_DIR="$HOME/.nvm"\nunset OPENAI_BASE_URL\nnvm() { printf "%s\\n" v24.0.0; }\n', + ); + + const result = test.run( + { OPENAI_BASE_URL: "synthetic-known-launcher-value" }, + false, + "scripts/install-codex-cloud-command-shims.sh", + ); + + expect(result.status, result.stderr || result.stdout).toBe(0); + expect(result.stderr).toContain("FAIL-KNOWN"); + expect(result.stderr).toContain("raw boundary remains failed"); + expect(result.stdout + result.stderr).not.toContain("synthetic-known-launcher-value"); + expect(existsSync(test.runtimeCalls)).toBe(false); + expect(readFileSync(path.join(test.home, ".local/bin/node"), "utf8")).toContain('.clinical-kb-codex-cloud.sh"'); + }); + + it("restores a missing maintenance profile before repository or runtime work", () => { + const test = fixture("missing"); + const checkout = path.join(test.home, "checkout"); + const scripts = path.join(checkout, "scripts"); + mkdirSync(scripts, { recursive: true }); + for (const script of ["maintain-codex-cloud.sh", "check-codex-cloud-raw-env.sh"]) { + copyFileSync(path.join(repoRoot, "scripts", script), path.join(scripts, script)); + } + writeFileSync(path.join(scripts, "setup-codex-cloud.sh"), 'printf "%s\\n" fixture-setup\n'); + const git = path.join(test.home, "bin/git"); + writeFileSync(git, '#!/bin/bash\nprintf "%s\\n" "$CLOUD_POLICY_FIXTURE_CHECKOUT"\n'); + chmodSync(git, 0o755); + + const result = test.run( + { CLOUD_POLICY_FIXTURE_CHECKOUT: bashPath(checkout) }, + false, + path.join(scripts, "maintain-codex-cloud.sh"), + ); + + expect(result.status, result.stderr || result.stdout).toBe(0); + expect(result.stdout).toContain("Generated runtime profile unavailable; rerunning full setup."); + expect(result.stdout).toContain("fixture-setup"); + expect(existsSync(test.runtimeCalls)).toBe(false); + }); + it.each(["inherited-flag", "ordinary-runtime"] as const)("rejects a policy-only shortcut in %s setup", (scenario) => { const test = fixture("missing"); const result = scenario === "inherited-flag" ? test.run({}, false) : test.run({ NODE_ENV: "production" }); @@ -147,4 +360,61 @@ describe("Codex Cloud policy-only harness runtime isolation", () => { expect(readdirSync(path.dirname(test.configPath)).filter((name) => name.startsWith(".config.toml."))).toEqual([]); }, ); + + it.each(["reversed", "duplicate", "orphan-end"] as const)( + "preserves unrelated configuration when managed policy markers are %s", + (scenario) => { + const test = fixture("missing"); + const begin = "# BEGIN clinical-kb-codex-cloud shell policy (managed by setup-codex-cloud.sh)"; + const end = "# END clinical-kb-codex-cloud shell policy (managed by setup-codex-cloud.sh)"; + const markers = + scenario === "reversed" ? [end, begin] : scenario === "duplicate" ? [begin, end, begin, end] : [end]; + const config = `${markers.join("\n")}\n[mcp_servers.preserved]\ncommand = "echo"\n`; + writeFileSync(test.configPath, config); + + const result = test.run(); + + expect(result.status, result.stderr || result.stdout).toBe(1); + expect(result.stderr).toContain("invalid marker order"); + expect(readFileSync(test.configPath, "utf8")).toBe(config); + expect(existsSync(test.runtimeCalls)).toBe(false); + }, + ); + + it.each(["a".repeat(40), "a".repeat(39), "G".repeat(40)])( + "loads generated login profile with a POSIX shell and validates base %s", + (base) => { + const test = fixture("missing"); + const result = test.run(); + expect(result.status, result.stderr || result.stdout).toBe(0); + rmSync(path.join(test.home, ".nvm/nvm.sh")); + const cache = path.join(test.home, ".cache/clinical-kb-codex"); + mkdirSync(cache, { recursive: true }); + writeFileSync(path.join(cache, "cloud-expected-base-sha"), `${base}\n`); + + const loaded = spawnSync( + bashCommand, + [ + "--noprofile", + "--norc", + "-c", + 'exec sh -eu -c \'. "$HOME/.clinical-kb-codex-cloud.sh"; printf "%s" "${CODEX_CLOUD_EXPECTED_BASE_SHA:-unset}"\'', + ], + { + encoding: "utf8", + env: { + NODE_ENV: "test", + HOME: bashPath(test.home), + PATH: process.platform === "win32" ? "/mingw64/bin:/usr/bin:/bin" : "/usr/bin:/bin", + SystemRoot: process.env.SystemRoot, + WINDIR: process.env.WINDIR, + }, + }, + ); + + expect(loaded.status, loaded.stderr).toBe(0); + expect(loaded.stderr).toBe(""); + expect(loaded.stdout).toBe(base === "a".repeat(40) ? base : "unset"); + }, + ); }); diff --git a/tests/codex-cloud-remote.test.ts b/tests/codex-cloud-remote.test.ts new file mode 100644 index 0000000000..a3f05bcd93 --- /dev/null +++ b/tests/codex-cloud-remote.test.ts @@ -0,0 +1,55 @@ +import { spawnSync } from "node:child_process"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; +import { + CODEX_CLOUD_REPOSITORY, + ensureOriginRemote, + inspectOriginRemote, + remoteRepositoryIdentity, +} from "../scripts/ensure-codex-cloud-git-remote.mjs"; + +const roots: string[] = []; +afterEach(() => { + for (const root of roots.splice(0)) { + rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); + } +}); + +describe("Cloud remote provider identity", () => { + it.each([ + "BigSimmo/PsychSift", + "/BigSimmo/PsychSift.git", + "file:///BigSimmo/PsychSift.git", + "https://example.com/BigSimmo/PsychSift.git", + "https://github.com.example.com/BigSimmo/PsychSift.git", + "https://synthetic-token@github.com/BigSimmo/PsychSift.git", + ])("does not identify a local, foreign or credential-bearing remote as GitHub: %s", (remote) => { + expect(remoteRepositoryIdentity(remote)).toBeNull(); + }); + + it.each([ + "https://github.com/BigSimmo/PsychSift.git", + "https://github.com/BigSimmo/PsychSift", + "git@github.com:BigSimmo/PsychSift.git", + "ssh://git@github.com/BigSimmo/PsychSift.git", + ])("preserves supported credential-free GitHub transport identity: %s", (remote) => { + expect(remoteRepositoryIdentity(remote)).toBe(CODEX_CLOUD_REPOSITORY); + }); + + it("refuses to preserve a local-path origin and leaves the configured remote unchanged", () => { + const root = mkdtempSync(join(tmpdir(), "cloud-remote-")); + roots.push(root); + const git = (...args: string[]) => spawnSync("git", args, { cwd: root, encoding: "utf8" }); + expect(git("init", "--quiet").status).toBe(0); + expect(git("remote", "add", "origin", "BigSimmo/PsychSift").status).toBe(0); + expect(inspectOriginRemote(root)).toEqual({ + configured: true, + repositoryMatch: false, + credentialsEmbedded: false, + }); + expect(() => ensureOriginRemote(root)).toThrow("refusing to overwrite"); + expect(git("config", "--get", "remote.origin.url").stdout.trim()).toBe("BigSimmo/PsychSift"); + }); +}); diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 552dddf1a0..27d61d79d0 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -1,4 +1,5 @@ import { spawnSync } from "node:child_process"; +import { createHash } from "node:crypto"; import { chmodSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -7,6 +8,7 @@ import { afterEach, describe, expect, it } from "vitest"; import { codexCloudValidationScope, + codexCloudValidationSelection, cloudSetupContainsMcpRegistration, configuredProviderCredentialNames, expectedMcpConfiguration, @@ -20,10 +22,13 @@ import { playwrightBrowserErrors, providerCredentialVariables, pythonWorkerImportError, + pythonWorkerDependencyErrors, pythonWorkerVersionLine, pythonWorkerImports, sanitizedCloudCapabilityLines, validateCodexCloudEnvironment, + validateCodexCloudRuntime, + validateCodexCloudCheckout, validateCloudNpmInstallContract, validateCodexProjectMcpConfiguration, validateHostedAppInventory, @@ -40,6 +45,15 @@ import { const temporaryDirectories: string[] = []; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const setupScript = "scripts/setup-codex-cloud.sh"; +// Fixtures must never inherit provider values, startup hooks, or host config paths. +const fixtureEnvironment: NodeJS.ProcessEnv = { + PATH: process.env.PATH, + SystemRoot: process.env.SystemRoot, + ComSpec: process.env.ComSpec, + NODE_ENV: "test", + GIT_CONFIG_NOSYSTEM: "1", + GIT_CONFIG_GLOBAL: os.devNull, +}; const bashCommand = process.platform === "win32" ? path.join(process.env.ProgramFiles || "C:\\Program Files", "Git/bin/bash.exe") @@ -95,12 +109,14 @@ function temporaryDirectory(prefix: string) { function temporaryGitRepository() { const directory = temporaryDirectory("codex-cloud-git-"); - expect(spawnSync("git", ["init", "--quiet", "--initial-branch=task", directory]).status).toBe(0); + expect( + spawnSync("git", ["init", "--quiet", "--initial-branch=task", directory], { env: fixtureEnvironment }).status, + ).toBe(0); return directory; } function git(directory: string, ...args: string[]) { - return spawnSync("git", ["-C", directory, ...args], { encoding: "utf8" }); + return spawnSync("git", ["-C", directory, ...args], { encoding: "utf8", env: fixtureEnvironment }); } function bashPathEntry(entry: string) { @@ -131,7 +147,7 @@ function runSetupPolicyOnly(home: string, env: Record { }); it("keeps the checked-in setup reproducible and provider-safe", () => { - const staticEnvironment = { ...process.env }; - delete staticEnvironment.CODEX_CLOUD; const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs"], { cwd: path.resolve(import.meta.dirname, ".."), encoding: "utf8", - env: staticEnvironment, + env: fixtureEnvironment, shell: false, }); @@ -183,6 +197,138 @@ describe("Codex Cloud environment contract", () => { expect(result.stdout).toContain("[Codex Cloud Check] PASS: static Cloud contracts match."); }); + it.each([ + ["unselected host", [], {}, { runtime: false, environment: false }], + ["generic Cloud marker", [], { CODEX_CLOUD: "1" }, { runtime: false, environment: false }], + [ + "configured offline profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "offline" }, + { runtime: false, environment: true }, + ], + [ + "configured connected profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "connected" }, + { runtime: false, environment: true }, + ], + [ + "invalid configured profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "invalid" }, + { runtime: false, environment: true }, + ], + ["empty configured profile", [], { CODEX_CLOUD_ACCESS_PROFILE: "" }, { runtime: false, environment: true }], + ["explicit environment acceptance", ["--environment"], {}, { runtime: false, environment: true }], + ["explicit runtime acceptance", ["--runtime"], {}, { runtime: true, environment: true }], + ])("selects legacy acceptance for %s", (_name, args, environment, expected) => { + expect(codexCloudValidationSelection(args, environment)).toEqual(expected); + }); + + it.each([ + ["generic Cloud marker", [], {}, 0, "PASS: static Cloud contracts match.", false], + [ + "configured offline profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "offline" }, + 0, + "PASS: static and environment Cloud contracts match.", + true, + ], + [ + "configured connected profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "connected" }, + 0, + "PASS: static and environment Cloud contracts match.", + true, + ], + [ + "invalid configured profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "invalid" }, + 1, + "CODEX_CLOUD_ACCESS_PROFILE must be offline or connected.", + false, + ], + [ + "empty configured profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "" }, + 1, + "CODEX_CLOUD_ACCESS_PROFILE must be offline or connected.", + false, + ], + [ + "explicit environment request", + ["--environment"], + {}, + 0, + "PASS: static and environment Cloud contracts match.", + true, + ], + ])("keeps CLI scope and failures honest for %s", (_name, args, profile, status, message, environmentSelected) => { + const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs", ...args], { + cwd: repoRoot, + encoding: "utf8", + shell: false, + env: { + PATH: process.env.PATH, + NODE_ENV: "test", + CODEX_CLOUD: "1", + RAG_PROVIDER_MODE: "offline", + NEXT_PUBLIC_DEMO_MODE: "true", + PLAYWRIGHT_OFFLINE_MODE: "true", + ...profile, + }, + }); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(status); + expect(`${result.stdout}\n${result.stderr}`).toContain(message); + expect(result.stdout.includes("[Codex Cloud Environment]")).toBe(environmentSelected); + }); + + it("stops runtime acceptance at a rejected legacy environment boundary", async () => { + const environment: NodeJS.ProcessEnv = { + NODE_ENV: "test", + CODEX_CLOUD: "1", + CODEX_CLOUD_ACCESS_PROFILE: "invalid", + }; + expect(await validateCodexCloudRuntime(environment)).toEqual(validateCodexCloudEnvironment(environment)); + }); + + it.each([ + [ + "invalid profile", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "invalid" }, + "CODEX_CLOUD_ACCESS_PROFILE must be offline or connected.", + ], + [ + "provider value", + [], + { OPENAI_API_KEY: "synthetic-provider-value" }, + "Offline mode exposes provider environment variables: OPENAI_API_KEY.", + ], + ["invalid flag", ["--unknown"], {}, "Unsupported Cloud-check argument"], + ])("refuses runtime probes for %s", (_label, args, environment, message) => { + const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs", "--runtime", ...args], { + cwd: repoRoot, + encoding: "utf8", + env: { + ...fixtureEnvironment, + CODEX_CLOUD: "1", + RAG_PROVIDER_MODE: "offline", + NEXT_PUBLIC_DEMO_MODE: "true", + PLAYWRIGHT_OFFLINE_MODE: "true", + ...environment, + }, + }); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(1); + expect(result.stderr).toContain(message); + expect(result.stdout).not.toContain("[Codex Cloud Environment]"); + expect(result.stderr).not.toMatch(/runtime check|Python OCR|Playwright|synthetic-provider-value/); + }); + it("keeps the Cloud npm install locked, cache-friendly, and lifecycle-capable", () => { const setup = readFileSync(new URL("../scripts/setup-codex-cloud.sh", import.meta.url), "utf8"); @@ -345,12 +491,13 @@ describe("Codex Cloud environment contract", () => { }, ); const report = lines.join("\n"); - expect(report).toContain("hosted_workspace.class_documented=personal-pro"); + expect(report).toContain("validation.contract=legacy-repository-managed-cloud-profile"); + expect(report).toContain("hosted_workspace.class=unverified"); expect(report).toContain("OPENAI_API_KEY.present=true"); expect(report).toContain("hosted_app.inventory=external-unverified-until-fresh-task"); - expect(report).toContain("provider_route.github=codex-native-connector"); - expect(report).toContain("provider_route.railway=chatgpt-official-app"); - expect(report).toContain("provider_route.supabase=chatgpt-project-scoped-read-only-app"); + expect(report).toContain("provider_routes=external-unverified"); + expect(report).not.toContain("personal-pro"); + expect(report).not.toContain("provider_route."); expect(report).toContain( "desktop_cli_mcp.template=railway type=http command=none endpoint=https://mcp.railway.com/", ); @@ -690,8 +837,8 @@ describe("Codex Cloud environment contract", () => { expect(setup.match(/unset npm_config_http_proxy npm_config_https_proxy npm_config_proxy/g)).toHaveLength(2); expect(setup).toContain("worker/python/requirements-cloud.txt"); expect(setup).toContain("diagnose-codex-cloud.mjs"); - expect(setup).toContain("CODEX_CLOUD=1 node scripts/diagnose-codex-cloud.mjs"); - expect(setup).toContain("CODEX_CLOUD=1 npm run diagnose:codex-cloud"); + expect(setup).toContain("CODEX_CLOUD=1 node scripts/diagnose-codex-cloud.mjs --legacy-profile"); + expect(setup).toContain("CODEX_CLOUD=1 npm run diagnose:codex-cloud -- --legacy-profile"); expect(setup).toContain("trap diagnose_setup_failure ERR"); expect(setup).toContain('setup_step="python-worker-requirements"'); expect(setup).toContain("--require-hashes -r worker/python/requirements-cloud.txt"); @@ -775,7 +922,7 @@ describe("Codex Cloud environment contract", () => { const install = spawnSync(bashCommand, ["scripts/install-codex-cloud-command-shims.sh"], { cwd: repoRoot, encoding: "utf8", - env: { ...process.env, HOME: bashHome, PATH: bashPathList(process.env.PATH || "") }, + env: { ...fixtureEnvironment, HOME: bashHome, PATH: bashPathList(process.env.PATH || "") }, }); expect(install.status, install.stderr || install.stdout).toBe(0); @@ -783,7 +930,7 @@ describe("Codex Cloud environment contract", () => { const result = spawnSync(bashCommand, [`${shimDir}/node`], { cwd: repoRoot, encoding: "utf8", - env: { ...process.env, HOME: bashHome, PATH: `${shimDir}:${shimDir}:/usr/bin:/bin` }, + env: { ...fixtureEnvironment, HOME: bashHome, PATH: `${shimDir}:${shimDir}:/usr/bin:/bin` }, }); expect(result.status, result.stderr || result.stdout).toBe(0); const resolvedPath = result.stdout.trim().split(":"); @@ -805,7 +952,7 @@ describe("Codex Cloud environment contract", () => { cwd: repoRoot, encoding: "utf8", env: { - ...process.env, + ...fixtureEnvironment, HOME: bashPathEntry(home), PATH: `${bashPathEntry(staleVenvBin)}:${bashPathEntry(bin)}:/usr/bin:/bin`, }, @@ -1047,6 +1194,89 @@ describe("Codex Cloud environment contract", () => { }); }); + it.each(["task-only", "local-main", "origin-main"])( + "rejects configured invalid bases in a %s checkout", + (reference) => { + const directory = temporaryGitRepository(); + const commit = git( + directory, + "-c", + "user.name=Codex", + "-c", + "user.email=codex@example.test", + "commit", + "--allow-empty", + "-m", + "task", + ); + expect(commit.status, commit.stderr).toBe(0); + const head = git(directory, "rev-parse", "HEAD").stdout.trim(); + if (reference !== "task-only") { + const ref = reference === "local-main" ? "refs/heads/main" : "refs/remotes/origin/main"; + expect(git(directory, "update-ref", ref, head).status).toBe(0); + } + for (const provisioning of ["0", "1"]) { + const environment = { CODEX_CLOUD: "1", CODEX_CLOUD_PROVISIONING: provisioning }; + const absentErrors = validateCodexCloudCheckout(directory, environment); + expect(absentErrors).toEqual( + reference === "task-only" && provisioning !== "1" + ? ["Checkout freshness is unverified: set CODEX_CLOUD_EXPECTED_BASE_SHA to the intended merge/base commit."] + : [], + ); + expect(validateCodexCloudCheckout(directory, { ...environment, CODEX_CLOUD_EXPECTED_BASE_SHA: head })).toEqual( + [], + ); + for (const configuredBase of ["", " ", "invalid", "0".repeat(40)]) { + const configuredEnvironment = { ...environment, CODEX_CLOUD_EXPECTED_BASE_SHA: configuredBase }; + expect(gitCheckoutFreshness(directory, configuredEnvironment)).toMatchObject({ + expectedBase: "invalid", + expectedBaseAncestor: "unverified", + freshness: "invalid", + }); + expect(validateCodexCloudCheckout(directory, configuredEnvironment)).toEqual([ + "CODEX_CLOUD_EXPECTED_BASE_SHA must identify an available 40-character commit SHA.", + ]); + } + } + }, + ); + + it.each([ + ["empty", "", "stale"], + ["whitespace", " \n\t", "stale"], + ["stale", "0".repeat(64), "stale"], + ["matching", "matching", null], + ["missing", null, "missing"], + ])("validates a %s Python requirements fingerprint and retains dependency checks", (_label, contents, problem) => { + const directory = temporaryDirectory("codex-cloud-python-fingerprint-"); + mkdirSync(path.join(directory, "bin")); + const python = path.join(directory, "bin/python"); + writeFileSync(python, "synthetic fixture; never executed"); + chmodSync(python, 0o755); + const expectedHash = createHash("sha256") + .update(readFileSync(path.join(repoRoot, "worker/python/requirements-cloud.txt"))) + .digest("hex"); + if (contents !== null) { + writeFileSync( + path.join(directory, ".requirements-cloud.sha256"), + contents === "matching" ? `${expectedHash}\n` : contents, + ); + } + const invocations: string[][] = []; + const errors = pythonWorkerDependencyErrors(python, (_command, args) => { + invocations.push(args); + return { status: 0 }; + }); + expect(errors).toEqual(problem ? [`Python worker requirements fingerprint is ${problem}; rerun Cloud setup.`] : []); + expect(invocations).toEqual([ + ["-m", "pip", "check"], + [ + "-c", + "from importlib.metadata import version; print('medspacy=%s spacy=%s' % (version('medspacy'), version('spacy')))", + ], + ]); + }); + it("normalizes Python package version output for capability reports", () => { const run = (() => ({ status: 0, diff --git a/tests/database-skills.test.ts b/tests/database-skills.test.ts index 4fc797c6ea..a8031aa6d1 100644 --- a/tests/database-skills.test.ts +++ b/tests/database-skills.test.ts @@ -161,6 +161,9 @@ describe("Database skill catalog", () => { expect(workflow).toContain("git worktree add"); expect(workflow).toContain("git rev-parse --show-toplevel"); expect(workflow).toContain("CODEX_CLOUD=1"); + expect(workflow).toContain("marker is neither required nor sufficient"); + expect(workflow).toContain("`--cloud` explicitly attests that workflow choice"); + expect(workflow).not.toContain("CODEX_CLOUD=1 is required."); expect(workflow).toContain("--expected-status-hash"); expect(workflow).toContain("--allow-dirty"); expect(workflow).toContain("does not provide an OS-level sandbox"); @@ -169,6 +172,7 @@ describe("Database skill catalog", () => { expect(verifier).toContain("dirty_override_requires_expected_state"); expect(verifier).toContain("expected_state_required"); expect(verifier).toContain("primary_worktree"); + expect(verifier).not.toContain("process.env.CODEX_CLOUD"); expect(verifier).not.toContain("node_modules"); const selfTest = spawnSync( @@ -181,7 +185,7 @@ describe("Database skill catalog", () => { }, ); expect(selfTest.status, `${selfTest.stdout}\n${selfTest.stderr}`).toBe(0); - expect(selfTest.stdout).toContain("prompt-perfector isolation self-test passed: 14/14"); + expect(selfTest.stdout).toContain("prompt-perfector isolation self-test passed: 24/24"); }); }); diff --git a/tests/organisation-instruction-guards.test.ts b/tests/organisation-instruction-guards.test.ts index ff17883a0e..c5992988af 100644 --- a/tests/organisation-instruction-guards.test.ts +++ b/tests/organisation-instruction-guards.test.ts @@ -182,6 +182,103 @@ describe("retired rules: the list itself", () => { expect(isInstructionFile("docs/AGENTS.md")).toBe(false); expect(isInstructionFile("docs/outstanding-issues.md")).toBe(false); }); + + it.each([ + "docs/codex-cloud.md", + "plugins/clinical-kb/skills/clinical-kb-workflow/SKILL.md", + "plugins/clinical-kb/skills/new-workflow/references/cloud.md", + "docs/prompts/codex-cloud-detailed-task.md", + "docs/prompts/new-codex-cloud-task.md", + "docs/prompts/mode-aware-clinical-ask-codex-cloud-handover.md", + "docs/prompts/rag-coverage-gate-extraction.md", + "docs/superpowers/plans/2026-08-22-mode-aware-clinical-ask-implementation.md", + "docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md", + "docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md", + "docs/superpowers/rag-upgrade/canonical/START-HERE.local.md", + "docs/superpowers/rag-upgrade/cloud/START-HERE.md", + "docs/superpowers/rag-upgrade/local/START-HERE.md", + ...["canonical", "cloud", "local"].flatMap((directory) => + [ + "approval-matrix.md", + "connected-execution.md", + "execution-order.md", + "programme-manifest.json", + "sdd-execution.md", + ].map((file) => `docs/superpowers/rag-upgrade/${directory}/${file}`), + ), + ])("includes active copied or programme instructions: %s", (file) => { + expect(isInstructionFile(file)).toBe(true); + expect(isInstructionFile(file.replaceAll("/", "\\"))).toBe(true); + }); + + it.each([ + "docs/prompts/codex-cloud-review/1-codex-full-stack-master-review-prompt.md", + "docs/prompts/codex-cloud-review/2-codex-full-stack-master-prompt-review-and-stress-test.md", + "docs/prompts/codex-cloud-review/3-codex-agents-md-companion.md", + "docs/archive/codex-cloud-legacy-profile-2026-10-16.md", + "docs/audit/codex-cloud-reliability-20260907.md", + "plugins/clinical-kb/skills-archive/clinical-kb-workflow/SKILL.md", + "plugins/clinical-kb/audit/2026-08-22-cloud.md", + "docs/superpowers/plans/2026-08-21-developer-hub-phase-1-HANDOFF.md", + "docs/superpowers/rag-upgrade/cloud/plans/2026-08-20-rag-adaptive-answer.md", + "docs/superpowers/rag-upgrade/local/plans/2026-08-20-rag-adaptive-answer.md", + "docs/superpowers/rag-upgrade/execution-receipts/programme/P00.json", + "docs/superpowers/rag-upgrade/execution-artifacts/programme/review.md", + ])("preserves source inputs and historical evidence outside the live scope: %s", (file) => { + expect(isInstructionFile(file)).toBe(false); + }); +}); + +describe("retired rules: Cloud instruction copies", () => { + it.each([ + { + file: "docs/prompts/new-codex-cloud-task.md", + text: "If the prompt does not contain the exact marker [xhigh-confirmed], stop.\n", + rule: "cloud-xhigh-marker-gate", + }, + { + file: "docs/superpowers/plans/2026-08-22-mode-aware-clinical-ask-implementation.md", + text: "Default to `CODEX_CLOUD_ACCESS_PROFILE=offline`.\n", + rule: "cloud-legacy-profile-default", + }, + { + file: "docs/superpowers/rag-upgrade/cloud/START-HERE.md", + text: "The first line is the repository's exact xhigh confirmation marker.\n", + rule: "cloud-variable-proves-host", + }, + ])("rejects a retired Cloud instruction in $file locally and in CI", ({ file, text, rule }) => { + const { root, base } = repo({ + "docs/organisation/retired-rules.json": fs.readFileSync(REPO_RULES, "utf8"), + }); + const head = commit(root, { [file]: text }); + + for (const args of [ + ["--root", root], + ["--root", root, "--base", base, "--head", head], + ]) { + const result = run(RETIRED, args); + expect(result.code).toBe(1); + expect(result.out).toContain(`${file}:1 [${rule}]`); + } + }); + + it("does not rewrite verbatim review inputs or dated evidence when checking Cloud retirements", () => { + const { root, base } = repo({ + "docs/organisation/retired-rules.json": fs.readFileSync(REPO_RULES, "utf8"), + }); + const historicalText = "Default to `CODEX_CLOUD_ACCESS_PROFILE=offline`.\n"; + const head = commit(root, { + "docs/prompts/codex-cloud-review/3-codex-agents-md-companion.md": historicalText, + "docs/archive/codex-cloud-legacy-profile-2026-10-16.md": historicalText, + "docs/audit/codex-cloud-reliability-20260907.md": historicalText, + "docs/superpowers/plans/2026-08-21-developer-hub-phase-1-HANDOFF.md": historicalText, + "docs/superpowers/rag-upgrade/cloud/plans/2026-08-20-rag-adaptive-answer.md": historicalText, + "docs/superpowers/rag-upgrade/execution-receipts/programme/P00.json": historicalText, + }); + + expect(run(RETIRED, ["--root", root]).code).toBe(0); + expect(run(RETIRED, ["--root", root, "--base", base, "--head", head]).code).toBe(0); + }); }); describe("retired rules: reading a diff", () => { diff --git a/tests/rag-plan-package-parity.test.ts b/tests/rag-plan-package-parity.test.ts index 3db30f9e79..5355215a89 100644 --- a/tests/rag-plan-package-parity.test.ts +++ b/tests/rag-plan-package-parity.test.ts @@ -186,6 +186,28 @@ describe("RAG plan execution packages", () => { ).not.toThrow(); }); + it("accepts xhigh user attestation without a literal prompt token and keeps phase effort floors", () => { + const launch = (...args: string[]) => + execFileSync(process.execPath, ["scripts/rag-phase-launch-check.mjs", ...args], { + cwd: process.cwd(), + encoding: "utf8", + stdio: "pipe", + }); + + // --xhigh-confirmed attests a user's confirmation, which may be ordinary language. + // Neither the old prompt token nor any prompt text is an input to the helper. + expect(launch("--target", "P01", "--effort", "xhigh", "--xhigh-confirmed")).toContain("PASS"); + expect(launch("--target", "P00", "--effort", "high")).toContain("PASS"); + expect(() => launch("--target", "P00", "--effort", "high", "--xhigh-confirmed")).toThrow(); + try { + launch("--target", "P01", "--effort", "xhigh"); + throw new Error("expected missing xhigh attestation to fail"); + } catch (error) { + expect(error).toMatchObject({ status: 1 }); + expect(String((error as { stderr?: string }).stderr)).toContain("ordinary language is sufficient"); + } + }); + it("ships a valid resumable phase-receipt contract", () => { expect(() => execFileSync(process.execPath, ["scripts/check-rag-phase-receipts.mjs"], { From 4187cf6309006e17092debde308fd681c9ab20ae Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Fri, 16 Oct 2026 17:28:28 +0000 Subject: [PATCH 02/10] Place installed Cloud guidance regression in its owning area --- docs/organisation/systems/knowledge.json | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/organisation/systems/knowledge.json b/docs/organisation/systems/knowledge.json index 2fa1177e89..ec031b760a 100644 --- a/docs/organisation/systems/knowledge.json +++ b/docs/organisation/systems/knowledge.json @@ -120,6 +120,7 @@ "tests/branch-review-index.test.ts", "tests/check-docs-links.test.ts", "tests/claude-*", + "tests/cloud-installed-guidance.test.ts", "tests/codebase-index-coverage.test.ts", "tests/codex-*", "tests/cursor-mcp-contract.test.ts", From 2d86e86fa2fbb5436a5bc7514d68546ccef477f0 Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Fri, 16 Oct 2026 17:32:40 +0000 Subject: [PATCH 03/10] Keep documented programme bootstrap behind the current raw-boundary policy --- .../rag-upgrade/CLOUD-EXECUTION-PROMPT.md | 2 +- .../rag-upgrade/canonical/START-HERE.cloud.md | 27 +++++++++------ .../rag-upgrade/cloud/START-HERE.md | 27 +++++++++------ tests/rag-plan-package-parity.test.ts | 34 ++++++++++++++++++- 4 files changed, 68 insertions(+), 22 deletions(-) diff --git a/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md b/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md index 5b916ae710..6739eacf7e 100644 --- a/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md +++ b/docs/superpowers/rag-upgrade/CLOUD-EXECUTION-PROMPT.md @@ -16,7 +16,7 @@ This prompt authorizes read-only repository inspection, offline/provider-free wo ### Current host readiness and programme acceptance -Follow the current-host policy in `docs/agents/codex-cloud-environment.md` and the readiness order in `cloud/START-HERE.md`. Establish repository identity, branch and status, host capabilities, runtime and lock parity. Run the legacy raw probe, setup, shims and Cloud acceptance checks only if the owner or selected environment configuration explicitly adopts that repository-managed setup. In that setup, preserve its raw exit codes, recovery, restrictions and base-pinned acceptance; its bounded policy-repair exception does not authorize programme execution or establish acceptance. Other host-managed environments use their supported readiness checks. Keep programme execution provider-free on every host. +Follow the current-host policy in `docs/agents/codex-cloud-environment.md` and the readiness order in `cloud/START-HERE.md`. Establish repository identity, branch and status, host capabilities, runtime and lock parity. Run the legacy raw probe only if the owner or selected environment configuration explicitly adopts that repository-managed setup. Only its exit 0 permits setup, maintenance, installation and full runtime acceptance. For either failed status, follow the active policy's restricted repair/offline path; it does not authorize programme execution, authentication or phase acceptance, and a scrubbed child is not a raw-parent pass. Preserve the names-only result and base-pinned acceptance. Other host-managed environments use their supported readiness checks. Keep programme execution provider-free on every host. For P00, fetch `PACKAGE_SOURCE_REF`, verify exact `PACKAGE_HEAD_SHA`, create `PROGRAMME_BRANCH` from it and run the package publish check. For P01–P17, fetch the exact remote programme tip with no rebase. Run the tracked launch helper with the declared target/effort, package parity and `npm run plans:rag:receipts:check -- --before TARGET_PHASE`. The declared target must be exactly next; never select a different phase after launch. diff --git a/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md b/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md index 630cb3ffd2..849e1a70c1 100644 --- a/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md +++ b/docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md @@ -34,18 +34,25 @@ raw_status=$? set -e ``` -For that legacy setup, exit 0 continues. Exit 1 blocks programme execution and -requires the documented recovery and fresh task; the runbook's bounded local -policy-repair exception remains separate from acceptance. Exit 2 continues only -for the documented `OPENAI_BASE_URL`-alone restricted path: never run OpenAI clients -from the raw parent or bypass the generated profile and `node`/`npm`/`npx` shims. -No other inherited name may use that path. After its accepted raw boundary, run: +For that selected setup, follow the [active raw-boundary and repair policy](../../../agents/codex-cloud-environment.md#raw-boundary-failure-and-local-policy-repair). +Only exit 0 permits setup, maintenance, package installation and full runtime +acceptance. Either failed status stops this programme lane. Exit 2 covers only +the policy's known names (`GH_TOKEN` and `OPENAI_BASE_URL`, alone or together); +it permits only the existing generated profile/shim path and separately authorised +provider-free offline checks. Do not authenticate or invoke raw OpenAI/GitHub +clients, or describe a scrubbed child as a raw-parent pass. Any permitted repair +after exit 1 or 2 follows the active policy and does not establish phase acceptance. ```bash -bash scripts/setup-codex-cloud.sh -bash scripts/install-codex-cloud-command-shims.sh -npm run check:codex-cloud -CODEX_CLOUD_EXPECTED_BASE_SHA="$PACKAGE_BASE_SHA" npm run check:codex-cloud -- --runtime +if [ "$raw_status" -eq 0 ]; then + bash scripts/setup-codex-cloud.sh + bash scripts/install-codex-cloud-command-shims.sh + npm run check:codex-cloud + CODEX_CLOUD_EXPECTED_BASE_SHA="$PACKAGE_BASE_SHA" npm run check:codex-cloud -- --runtime +else + printf 'Raw boundary failed; follow the active repair policy before programme execution.\n' >&2 + exit "$raw_status" +fi ``` For other host-managed environments, use the current host's supported readiness diff --git a/docs/superpowers/rag-upgrade/cloud/START-HERE.md b/docs/superpowers/rag-upgrade/cloud/START-HERE.md index 630cb3ffd2..849e1a70c1 100644 --- a/docs/superpowers/rag-upgrade/cloud/START-HERE.md +++ b/docs/superpowers/rag-upgrade/cloud/START-HERE.md @@ -34,18 +34,25 @@ raw_status=$? set -e ``` -For that legacy setup, exit 0 continues. Exit 1 blocks programme execution and -requires the documented recovery and fresh task; the runbook's bounded local -policy-repair exception remains separate from acceptance. Exit 2 continues only -for the documented `OPENAI_BASE_URL`-alone restricted path: never run OpenAI clients -from the raw parent or bypass the generated profile and `node`/`npm`/`npx` shims. -No other inherited name may use that path. After its accepted raw boundary, run: +For that selected setup, follow the [active raw-boundary and repair policy](../../../agents/codex-cloud-environment.md#raw-boundary-failure-and-local-policy-repair). +Only exit 0 permits setup, maintenance, package installation and full runtime +acceptance. Either failed status stops this programme lane. Exit 2 covers only +the policy's known names (`GH_TOKEN` and `OPENAI_BASE_URL`, alone or together); +it permits only the existing generated profile/shim path and separately authorised +provider-free offline checks. Do not authenticate or invoke raw OpenAI/GitHub +clients, or describe a scrubbed child as a raw-parent pass. Any permitted repair +after exit 1 or 2 follows the active policy and does not establish phase acceptance. ```bash -bash scripts/setup-codex-cloud.sh -bash scripts/install-codex-cloud-command-shims.sh -npm run check:codex-cloud -CODEX_CLOUD_EXPECTED_BASE_SHA="$PACKAGE_BASE_SHA" npm run check:codex-cloud -- --runtime +if [ "$raw_status" -eq 0 ]; then + bash scripts/setup-codex-cloud.sh + bash scripts/install-codex-cloud-command-shims.sh + npm run check:codex-cloud + CODEX_CLOUD_EXPECTED_BASE_SHA="$PACKAGE_BASE_SHA" npm run check:codex-cloud -- --runtime +else + printf 'Raw boundary failed; follow the active repair policy before programme execution.\n' >&2 + exit "$raw_status" +fi ``` For other host-managed environments, use the current host's supported readiness diff --git a/tests/rag-plan-package-parity.test.ts b/tests/rag-plan-package-parity.test.ts index 7d65c63f9b..155e6d2ea4 100644 --- a/tests/rag-plan-package-parity.test.ts +++ b/tests/rag-plan-package-parity.test.ts @@ -1,4 +1,4 @@ -import { execFileSync } from "node:child_process"; +import { execFileSync, spawnSync } from "node:child_process"; import { readFileSync, unlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -111,6 +111,38 @@ function ensureHistoryDeepened(depth = 2000): void { } describe("RAG plan execution packages", () => { + it.each([0, 1, 2])("admits the documented legacy bootstrap only after raw status 0 (status %s)", (status) => { + const guide = readFileSync("docs/superpowers/rag-upgrade/canonical/START-HERE.cloud.md", "utf8"); + const snippet = Array.from(guide.matchAll(/```bash\n([\s\S]*?)```/g), (match) => match[1]).find((block) => + block.includes('if [ "$raw_status" -eq 0 ]; then'), + ); + expect(snippet).toBeDefined(); + const shell = + process.platform === "win32" ? join(process.env.ProgramFiles || "C:/Program Files", "Git/bin/bash.exe") : "bash"; + const result = spawnSync( + shell, + [ + "--noprofile", + "--norc", + "-c", + `set -eu\nraw_status=${status}\nPACKAGE_BASE_SHA=synthetic-base\nbash() { printf 'SETUP:%s\\n' "$*"; }\nnpm() { printf 'CHECK:%s\\n' "$*"; }\n${snippet}`, + ], + { + encoding: "utf8", + env: { NODE_ENV: "test", PATH: process.env.PATH, SystemRoot: process.env.SystemRoot }, + }, + ); + expect(result.error).toBeUndefined(); + expect(result.status, result.stderr).toBe(status); + if (status === 0) { + expect(result.stdout).toContain("SETUP:scripts/setup-codex-cloud.sh"); + expect(result.stdout).toContain("CHECK:run check:codex-cloud -- --runtime"); + } else { + expect(result.stdout).toBe(""); + expect(result.stderr).toContain("Raw boundary failed"); + } + }); + it("keeps Local and Cloud task bodies identical and executable", () => { if (!isCommitAvailable(manifest.reconciledBase)) { ensureHistoryDeepened(2000); From d770b5949346e7a4855a7d906f4637a3250c147d Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Fri, 16 Oct 2026 17:43:49 +0000 Subject: [PATCH 04/10] Preserve explicitly selected Cloud profile during automatic loading --- scripts/check-codex-cloud-setup.mjs | 3 ++- tests/codex-cloud-setup.test.ts | 40 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index 6acb79491d..1a8874b853 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -1431,11 +1431,12 @@ if (isDirectEntrypoint(import.meta.url)) { bashExecutable, [ "-c", - 'profile_path="$1"; node_exec="$2"; script_path="$3"; shift 3; saved_args=("$@"); set --; . "$profile_path" && export CODEX_CLOUD_PROFILE_LOADED=1 && exec "$node_exec" "$script_path" "${saved_args[@]}"', + 'profile_path="$1"; node_exec="$2"; script_path="$3"; requested_cloud_check_profile="$4"; shift 4; saved_args=("$@"); set --; . "$profile_path" && { if [[ -n "$requested_cloud_check_profile" && "${CODEX_CLOUD_ACCESS_PROFILE-}" != "$requested_cloud_check_profile" ]]; then printf "%s\\n" "[Codex Cloud Check] FAIL: Cached CODEX_CLOUD_ACCESS_PROFILE differs from the requested profile; rerun setup with the intended profile." >&2; exit 1; fi; export CODEX_CLOUD_PROFILE_LOADED=1; exec "$node_exec" "$script_path" "${saved_args[@]}"; }', "codex-cloud-check", runtimeProfilePath.replaceAll("\\", "/"), process.execPath, fileURLToPath(import.meta.url), + process.env.CODEX_CLOUD_ACCESS_PROFILE ?? "", ...process.argv.slice(2), ], { cwd: repoRoot, stdio: "inherit" }, diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 37c1557d8a..9b800b279c 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -413,6 +413,46 @@ describe("Codex Cloud environment contract", () => { expect(readdirSync(home)).toContain("profile-loaded"); }); + it.each(["offline", "connected"])( + "rejects an automatic profile that replaces the explicit %s selection before child runtime execution", + (requested) => { + const home = temporaryDirectory("codex-cloud-auto-profile-mismatch-"); + const cached = requested === "offline" ? "connected" : "offline"; + const runtime = path.join(home, "reexec-node"); + writeFileSync(runtime, '#!/usr/bin/env bash\nprintf "called\\n" > "$HOME/runtime-called"\n'); + chmodSync(runtime, 0o755); + writeFileSync( + path.join(home, ".clinical-kb-codex-cloud.sh"), + [ + 'printf "sourced\\n" > "$HOME/profile-loaded"', + `export CODEX_CLOUD_ACCESS_PROFILE=${cached} RAG_PROVIDER_MODE=offline`, + "export NEXT_PUBLIC_DEMO_MODE=true PLAYWRIGHT_OFFLINE_MODE=true", + // Keep a broken implementation entirely inside the fixture: any + // attempted child runtime goes to this harmless logging executable. + 'node_exec="$HOME/reexec-node"', + "", + ].join("\n"), + ); + const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs", "--runtime"], { + cwd: repoRoot, + encoding: "utf8", + env: { + ...fixtureEnvironment, + NODE_ENV: "production", + HOME: home, + CODEX_CLOUD: "1", + CODEX_CLOUD_ACCESS_PROFILE: requested, + }, + }); + + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(1); + expect(result.stderr).toContain("Cached CODEX_CLOUD_ACCESS_PROFILE differs from the requested profile"); + expect(result.stdout).not.toContain("PASS:"); + expect(readdirSync(home)).toContain("profile-loaded"); + expect(readdirSync(home)).not.toContain("runtime-called"); + }, + ); + it("preserves an automatically sourced profile failure without reporting acceptance", () => { const home = temporaryDirectory("codex-cloud-auto-profile-failure-"); writeFileSync(path.join(home, ".clinical-kb-codex-cloud.sh"), "return 43\n"); From c06852ded956a0f15f26cc0477bb6bbfb83b9f39 Mon Sep 17 00:00:00 2001 From: "qodo-code-review[bot]" <151058649+qodo-code-review[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:44:56 +0000 Subject: [PATCH 05/10] fix: Scope maintenance RAG mode validation to connected profiles --- scripts/maintain-codex-cloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/maintain-codex-cloud.sh b/scripts/maintain-codex-cloud.sh index fb716d0125..cc6a778fd5 100644 --- a/scripts/maintain-codex-cloud.sh +++ b/scripts/maintain-codex-cloud.sh @@ -30,7 +30,7 @@ if [[ "$requested_cloud_profile_set" = "yes" ]]; then *) fail "Unsupported CODEX_CLOUD_ACCESS_PROFILE; expected offline or connected." ;; esac fi -if [[ "${RAG_PROVIDER_MODE+yes}" = "yes" && "$RAG_PROVIDER_MODE" != "offline" ]]; then +if [[ "$requested_cloud_profile" = "connected" && "${RAG_PROVIDER_MODE+yes}" = "yes" && "$RAG_PROVIDER_MODE" != "offline" ]]; then fail "The selected legacy Codex Cloud profile must keep RAG_PROVIDER_MODE=offline." fi From 55ae0789bb1d99dc26e5a47807b3dfadba9d6881 Mon Sep 17 00:00:00 2001 From: "qodo-code-review[bot]" <151058649+qodo-code-review[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:45:00 +0000 Subject: [PATCH 06/10] fix: Require a verified Cloud host signal for primary checkouts --- .../scripts/verify-repository-isolation.mjs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs b/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs index be695316af..91f1bf7a5e 100644 --- a/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs +++ b/.agents/skills/prompt-perfector/scripts/verify-repository-isolation.mjs @@ -65,12 +65,18 @@ export function evaluateRepositoryState(state, options = {}) { const currentIndex = state.worktrees.findIndex( (worktree) => normalizePath(String(worktree.worktree)) === currentPath, ); + const cloudEnvironment = + options.cloudEnvironment ?? + (process.env.CODEX_CLOUD === "1" || + process.env.CLAUDE_CODE_REMOTE === "true" || + process.env.CODEX_CLOUD_HOST === "1"); if (!path.isAbsolute(state.root)) reasons.push("repository_path_not_absolute"); if (!state.branch) reasons.push("detached_head"); else if (protectedBranch(state.branch)) reasons.push("protected_branch"); if (currentIndex < 0) reasons.push("unregistered_worktree"); else if (currentIndex === 0 && !options.cloud) reasons.push("primary_worktree"); + else if (currentIndex === 0 && options.cloud && !cloudEnvironment) reasons.push("cloud_environment_required"); else if (currentIndex === 0 && options.cloud && state.worktrees.length !== 1) reasons.push("cloud_primary_requires_single_worktree"); if (state.operations.length) reasons.push("git_operation_in_progress"); @@ -190,10 +196,18 @@ function runSelfTest() { expectedBranch: cloudBase.branch, expectedHead: head, cloud: true, + cloudEnvironment: true, }; const cases = [ ["safe secondary worktree", base, expected, true, ""], ["safe Cloud primary", cloudBase, cloudExpected, true, ""], + [ + "Cloud primary without host signal", + cloudBase, + { ...cloudExpected, cloudEnvironment: false }, + false, + "cloud_environment_required", + ], ["Cloud primary without explicit mode", cloudBase, { ...cloudExpected, cloud: false }, false, "primary_worktree"], ...["expectedRepo", "expectedBranch", "expectedHead"].map((key) => [ `Cloud missing ${key}`, From 07e39e3de3a4c747fe40af78ca5e985ecfe7b314 Mon Sep 17 00:00:00 2001 From: "qodo-code-review[bot]" <151058649+qodo-code-review[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:45:04 +0000 Subject: [PATCH 07/10] fix: Restore explicit confirmation for provider-backed actions --- .claude/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/settings.json b/.claude/settings.json index 07648bbab9..7ff121f569 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -482,7 +482,7 @@ "**Primary use of Claude Code**: software development on this personal clinical-reference RAG prototype (Next.js 16 / TypeScript / Supabase / OpenAI), with heavy use of repo verification gates, PR handoff, and review subagents", "**Trusted repo**: the currently verified checkout of `github.com/BigSimmo/PsychSift` and its task-owned worktrees, resolved from current Git identity rather than a fixed drive path. This remote is PUBLIC, so only that repo's own work may be committed or pushed there — content ported, repointed, or first read from outside this session's repo is not its own work, whoever directed the port. Visibility scopes confidential material only: secrets, clinical documents, and patient-identifiable data are never cleared into it by its visibility", "**Org-specific CLIs**: `supabase` (Supabase CLI), `gh` (GitHub CLI), repo `npm run` gates (`ensure`, `verify:cheap`, `verify:pr-local`, `verify:ui`, `test:focused`, `ledger:lookup`, `ledger:append`, `skills`), `sentry-cli`, `fnm`, `codex`", - "**Routine under this repo's prefix**: read-only and offline gate work inside that verified checkout and its task-owned worktrees is routine; anything provider-backed (OpenAI, live Supabase, GitHub API writes, hosted CI, `verify:release`, non-offline `eval:*`) remains subject to the repo's provider policy and existing task authorisation" + "**Routine under this repo's prefix**: read-only and offline gate work inside that verified checkout and its task-owned worktrees is routine; anything provider-backed (OpenAI, live Supabase, GitHub API writes, hosted CI, `verify:release`, non-offline `eval:*`) remains confirmation-required by the repo's own policy" ] }, "hooks": { From 80152f60ec80b5e97a6f4fcee1e10d46b5d00f05 Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Fri, 16 Oct 2026 17:57:24 +0000 Subject: [PATCH 08/10] Refresh repository awareness after Cloud documentation archival --- data/repo-awareness-snapshot.json | 88 +++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json index 056c671321..c466529d5e 100644 --- a/data/repo-awareness-snapshot.json +++ b/data/repo-awareness-snapshot.json @@ -3289,11 +3289,21 @@ "section": "redesign", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/ingestion-review-remediation-state.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/rag-improvement/gate-b-decision-record.md", "section": "rag-improvement", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/README.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/claude-cloud.md", "section": "root", @@ -3329,6 +3339,11 @@ "section": "decisions", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/db-remediation-coordination-state.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/superpowers/rag-upgrade/cloud/plans/2026-08-20-rag-ingestion-reindex.md", "section": "superpowers", @@ -3374,6 +3389,11 @@ "section": "superpowers", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/rag-programme-coordination-state.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/superpowers/plans/2026-10-13-phase-2-hardening.md", "section": "superpowers", @@ -4069,6 +4089,11 @@ "section": "superpowers", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/local-test-failures-windows.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/ledger-id-scheme-proposal.md", "section": "root", @@ -4139,6 +4164,11 @@ "section": "decisions", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/dev-drive-project-location.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/db-maintenance.md", "section": "root", @@ -4239,6 +4269,11 @@ "section": "root", "catalogued": true }, + { + "path": "docs/archive/codex-cloud-design-status-semantics-2026-10-16.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/decisions/2026-10-16-provider-agreements-verified-for-first-release.md", "section": "decisions", @@ -4254,6 +4289,11 @@ "section": "archive", "catalogued": true }, + { + "path": "docs/archive/rag-coverage-gate-extraction-2026-10-16.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/decisions/2026-08-28-logo-tail-tips-clean-cut.md", "section": "decisions", @@ -4374,6 +4414,11 @@ "section": "root", "catalogued": true }, + { + "path": "docs/decisions/2026-10-16-cloud-policy-repair-boundary.md", + "section": "decisions", + "catalogued": true + }, { "path": "docs/care-plan/HANDOFF-START-HERE.md", "section": "care-plan", @@ -4654,6 +4699,11 @@ "section": "plans", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/codex-chat-import-tooling.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/ingestion-concurrency-fix-workorder.md", "section": "root", @@ -4699,6 +4749,11 @@ "section": "decisions", "catalogued": true }, + { + "path": "docs/archive/cloud-session-state-2026-10-16/lighthouse-mobile-root-cls-flake.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/superpowers/plans/2026-09-27-roster-mode-build-brief.md", "section": "superpowers", @@ -4824,6 +4879,11 @@ "section": "root", "catalogued": true }, + { + "path": "docs/archive/codex-cloud-legacy-profile-2026-10-16.md", + "section": "archive", + "catalogued": true + }, { "path": "docs/design-system/sweep-fix-tap-floors-round-2.md", "section": "design-system", @@ -4939,6 +4999,11 @@ "section": "root", "catalogued": true }, + { + "path": "docs/decisions/2026-10-16-user-controlled-reasoning-effort.md", + "section": "decisions", + "catalogued": true + }, { "path": "docs/care-plan/session-handoff-2026-08-21.md", "section": "care-plan", @@ -5134,6 +5199,11 @@ "section": "superpowers", "catalogued": true }, + { + "path": "docs/decisions/2026-10-16-remove-obsolete-cloud-startup-instructions.md", + "section": "decisions", + "catalogued": true + }, { "path": "docs/superpowers/rag-upgrade/execution-artifacts/rag-answer-quality-and-repository-coverage-v1/README.md", "section": "superpowers", @@ -5284,6 +5354,11 @@ "section": "root", "catalogued": true }, + { + "path": "docs/decisions/2026-10-16-cloud-legacy-profile-scope.md", + "section": "decisions", + "catalogued": true + }, { "path": "docs/wa-mha-forms-handover-reconciliation-2026-09-16.md", "section": "root", @@ -5309,6 +5384,11 @@ "section": "decisions", "catalogued": true }, + { + "path": "docs/decisions/2026-10-16-cloud-operating-policy-overhaul.md", + "section": "decisions", + "catalogued": true + }, { "path": "docs/decisions/2026-09-25-calculator-governance-reference-not-linked.md", "section": "decisions", @@ -10628,6 +10708,14 @@ "outcome": "Authored and handed off as PR #1771; closes #285; operationalRisk true, clinicalRisk/ragRanking false", "checks": "test 5800 passed/1 pre-existing root-uid failure (pr-handoff-stop, confirmed on stashed clean tree); lint 0; typecheck 0; prettier --check . pass; check:runtime pass; check:codex-cloud pass; check:outstanding-issues pass; preinstall boundary proof 24.13/24.14.9 reject, 24.15/24.19 accept, 25.0.0 reject; contract test mutation-checked red" }, + { + "date": "2026-10-16", + "ref": "codex/cloud-policy-clarity", + "head": "3e0876f6e81d765f6d3695c74385d5b5a91b7951", + "scope": "Codex Cloud environment script and shell-profile hardening", + "outcome": "PASS (0 blocking findings; hardened .bashrc prepend, command-shim fallback, check-codex-cloud auto-profile load, Node 24.15.0 diagnostic floor, offline provider env scrubbing, and receipt schema cleanup)", + "checks": "check:codex-cloud, vitest (tests/codex-cloud-diagnose.test.ts, tests/codex-cloud-setup.test.ts: 50 passed), docs:maintain, check-format-changed, typecheck" + }, { "date": "2026-08-04", "ref": "codex/fix-mode-switching-and-loading-issues", From 788624f4cc2d0f4923eb70eabf57ae379bc45276 Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Fri, 16 Oct 2026 18:05:27 +0000 Subject: [PATCH 09/10] Align automatic Cloud checker RAG normalization with setup and maintenance --- scripts/check-codex-cloud-setup.mjs | 16 +++-- tests/codex-cloud-setup.test.ts | 95 ++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index 1a8874b853..2c6559ec53 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -1381,14 +1381,22 @@ export function shouldAutoSourceCloudProfile(env = process.env) { } /** Validate explicit inherited selectors before a cached profile can replace them. */ -function validateInheritedCloudProfileSelection(env = process.env) { +function validateInheritedCloudProfileSelection(env = process.env, autoSourceProfile = false) { const profile = env.CODEX_CLOUD_ACCESS_PROFILE; if (profile !== undefined && !["offline", "connected"].includes(profile)) { return ["CODEX_CLOUD_ACCESS_PROFILE must be offline or connected."]; } const errors = []; + // Only a sourced child can pin an offline selection or normalize an empty + // connected mode. An unsourced effective environment must already be offline. + if ( + env.RAG_PROVIDER_MODE !== undefined && + env.RAG_PROVIDER_MODE !== "offline" && + (!autoSourceProfile || (profile === "connected" && env.RAG_PROVIDER_MODE !== "")) + ) { + errors.push("RAG_PROVIDER_MODE must be an approved value for the selected legacy profile."); + } for (const [name, allowed] of Object.entries({ - RAG_PROVIDER_MODE: ["offline"], NEXT_PUBLIC_DEMO_MODE: profile === "offline" ? ["true"] : ["true", "false"], PLAYWRIGHT_OFFLINE_MODE: profile === "offline" ? ["true"] : ["true", "false"], })) { @@ -1407,7 +1415,7 @@ if (isDirectEntrypoint(import.meta.url)) { const inputErrors = [ ...validateHostedAppInventoryArguments(commandArguments), ...validateHostedAppInventory(hostedAppInventory), - ...(environment || autoSourceProfile ? validateInheritedCloudProfileSelection() : []), + ...(environment || autoSourceProfile ? validateInheritedCloudProfileSelection(process.env, autoSourceProfile) : []), ]; // A profile may populate missing modes and scrub an authorized offline child, // but a failed inherited boundary cannot become full runtime acceptance. @@ -1431,7 +1439,7 @@ if (isDirectEntrypoint(import.meta.url)) { bashExecutable, [ "-c", - 'profile_path="$1"; node_exec="$2"; script_path="$3"; requested_cloud_check_profile="$4"; shift 4; saved_args=("$@"); set --; . "$profile_path" && { if [[ -n "$requested_cloud_check_profile" && "${CODEX_CLOUD_ACCESS_PROFILE-}" != "$requested_cloud_check_profile" ]]; then printf "%s\\n" "[Codex Cloud Check] FAIL: Cached CODEX_CLOUD_ACCESS_PROFILE differs from the requested profile; rerun setup with the intended profile." >&2; exit 1; fi; export CODEX_CLOUD_PROFILE_LOADED=1; exec "$node_exec" "$script_path" "${saved_args[@]}"; }', + 'profile_path="$1"; node_exec="$2"; script_path="$3"; requested_cloud_check_profile="$4"; requested_cloud_check_rag_mode="${RAG_PROVIDER_MODE:-offline}"; shift 4; saved_args=("$@"); set --; export RAG_PROVIDER_MODE=offline; . "$profile_path" && { if [[ -n "$requested_cloud_check_profile" && "${CODEX_CLOUD_ACCESS_PROFILE-}" != "$requested_cloud_check_profile" ]]; then printf "%s\\n" "[Codex Cloud Check] FAIL: Cached CODEX_CLOUD_ACCESS_PROFILE differs from the requested profile; rerun setup with the intended profile." >&2; exit 1; fi; if [[ "${CODEX_CLOUD_ACCESS_PROFILE-}" = connected && "$requested_cloud_check_rag_mode" != offline ]]; then printf "%s\\n" "[Codex Cloud Check] FAIL: RAG_PROVIDER_MODE must be offline for the selected connected legacy profile." >&2; exit 1; fi; export CODEX_CLOUD_PROFILE_LOADED=1; exec "$node_exec" "$script_path" "${saved_args[@]}"; }', "codex-cloud-check", runtimeProfilePath.replaceAll("\\", "/"), process.execPath, diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 9b800b279c..bc651dc482 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -350,7 +350,18 @@ describe("Codex Cloud environment contract", () => { { CODEX_CLOUD_ACCESS_PROFILE: "" }, "CODEX_CLOUD_ACCESS_PROFILE must be offline or connected", ], - ["invalid RAG mode", [], { RAG_PROVIDER_MODE: "live" }, "RAG_PROVIDER_MODE must be an approved value"], + [ + "connected live RAG mode", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "connected", RAG_PROVIDER_MODE: "live" }, + "RAG_PROVIDER_MODE must be an approved value", + ], + [ + "connected auto RAG mode", + [], + { CODEX_CLOUD_ACCESS_PROFILE: "connected", RAG_PROVIDER_MODE: "auto" }, + "RAG_PROVIDER_MODE must be an approved value", + ], [ "runtime provider boundary", ["--runtime"], @@ -413,6 +424,88 @@ describe("Codex Cloud environment contract", () => { expect(readdirSync(home)).toContain("profile-loaded"); }); + it.each([ + { requested: "offline", mode: "auto" }, + { requested: "connected", mode: "" }, + ])("pins automatic child RAG mode offline for %j", ({ requested, mode }) => { + const home = temporaryDirectory("codex-cloud-auto-profile-rag-pin-"); + writeFileSync( + path.join(home, ".clinical-kb-codex-cloud.sh"), + [ + '[[ "$RAG_PROVIDER_MODE" = offline ]] || return 94', + `export CODEX_CLOUD_ACCESS_PROFILE=${requested} RAG_PROVIDER_MODE=offline`, + "export NEXT_PUBLIC_DEMO_MODE=true PLAYWRIGHT_OFFLINE_MODE=true", + "", + ].join("\n"), + ); + const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs", "--environment"], { + cwd: repoRoot, + encoding: "utf8", + env: { + ...fixtureEnvironment, + NODE_ENV: "production", + HOME: home, + CODEX_CLOUD: "1", + CODEX_CLOUD_ACCESS_PROFILE: requested, + RAG_PROVIDER_MODE: mode, + }, + }); + + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); + expect(result.stdout).toContain("PASS: static and environment Cloud contracts match."); + expect(result.stdout).toContain("RAG_PROVIDER_MODE=offline"); + }); + + it.each(["auto", "live"])("rejects inherited RAG %s when the cached profile selects connected", (mode) => { + const home = temporaryDirectory("codex-cloud-auto-profile-cached-rag-"); + const runtime = path.join(home, "reexec-node"); + writeFileSync(runtime, '#!/usr/bin/env bash\nprintf "called\\n" > "$HOME/runtime-called"\n'); + chmodSync(runtime, 0o755); + writeFileSync( + path.join(home, ".clinical-kb-codex-cloud.sh"), + [ + 'printf "sourced\\n" > "$HOME/profile-loaded"', + "export CODEX_CLOUD_ACCESS_PROFILE=connected RAG_PROVIDER_MODE=offline", + "export NEXT_PUBLIC_DEMO_MODE=true PLAYWRIGHT_OFFLINE_MODE=true", + 'node_exec="$HOME/reexec-node"', + "", + ].join("\n"), + ); + const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs", "--runtime"], { + cwd: repoRoot, + encoding: "utf8", + env: { ...fixtureEnvironment, NODE_ENV: "production", HOME: home, CODEX_CLOUD: "1", RAG_PROVIDER_MODE: mode }, + }); + + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(1); + expect(result.stderr).toContain("RAG_PROVIDER_MODE must be offline for the selected connected legacy profile"); + expect(result.stdout).not.toContain("PASS:"); + expect(readdirSync(home)).toContain("profile-loaded"); + expect(readdirSync(home)).not.toContain("runtime-called"); + }); + + it.each(["--environment", "--runtime"])( + "requires effective offline RAG without automatic sourcing for %s", + (flag) => { + const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs", flag], { + cwd: repoRoot, + encoding: "utf8", + env: { + ...fixtureEnvironment, + CODEX_CLOUD: "1", + CODEX_CLOUD_ACCESS_PROFILE: "offline", + RAG_PROVIDER_MODE: "auto", + NEXT_PUBLIC_DEMO_MODE: "true", + PLAYWRIGHT_OFFLINE_MODE: "true", + }, + }); + + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(1); + expect(result.stderr).toContain("RAG_PROVIDER_MODE must be an approved value"); + expect(result.stdout).not.toContain("PASS:"); + }, + ); + it.each(["offline", "connected"])( "rejects an automatic profile that replaces the explicit %s selection before child runtime execution", (requested) => { From d3815741222f1ec579d285971f8e8c6bd48ed351 Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Fri, 16 Oct 2026 18:08:44 +0000 Subject: [PATCH 10/10] Keep upstream shim path regression isolated and refresh expanded test count --- scripts/install-codex-cloud-command-shims.sh | 4 +-- tests/codex-cloud-setup.test.ts | 31 ++++++++++++++++++++ tests/diff-integrity.test.ts | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/scripts/install-codex-cloud-command-shims.sh b/scripts/install-codex-cloud-command-shims.sh index 73bfe34cee..01b61dcfc4 100644 --- a/scripts/install-codex-cloud-command-shims.sh +++ b/scripts/install-codex-cloud-command-shims.sh @@ -68,11 +68,11 @@ if [[ -n "$resolved_node_version" && "$resolved_node_version" != "N/A" ]]; then else fallback_node="$(PATH="$fallback_path" command -v node 2>/dev/null || true)" [[ -n "$fallback_node" ]] || fail "Node ${expected_node_major} is unavailable through nvm or PATH." - fallback_node_version="$($fallback_node -p 'process.versions.node' 2>/dev/null || true)" + fallback_node_version="$("$fallback_node" -p 'process.versions.node' 2>/dev/null || true)" node_version_supported "$fallback_node_version" || fail "Node ${expected_node_major}.x >=24.15.0 and <25 is required; detected ${fallback_node_version:-unavailable}." node_bin="$(cd "$(dirname "$fallback_node")" && pwd)" fi -node_version="$($node_bin/node -p 'process.versions.node' 2>/dev/null || true)" +node_version="$("$node_bin/node" -p 'process.versions.node' 2>/dev/null || true)" node_version_supported "$node_version" || fail "Node ${expected_node_major}.x >=24.15.0 and <25 is required; detected ${node_version:-unavailable}." mkdir -p "$HOME/.local/bin" for command_name in node npm npx; do diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index bc651dc482..4b98f688c5 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -1190,6 +1190,37 @@ describe("Codex Cloud environment contract", () => { expect(resolvedPath).toEqual(expect.arrayContaining(["/usr/bin", "/bin"])); }, 120_000); + it("probes a supported Node installed under a directory containing spaces", () => { + const homeRoot = temporaryDirectory("codex-cloud-shim-space-"); + const home = path.join(homeRoot, "home with spaces"); + mkdirSync(home, { recursive: true }); + const bashHome = bashPathEntry(home); + const nodeVersion = "v24.19.0"; + const nodeBin = path.join(home, ".nvm", "versions", "node", nodeVersion, "bin"); + mkdirSync(nodeBin, { recursive: true }); + for (const command of ["node", "npm", "npx"]) { + const executable = path.join(nodeBin, command); + writeFileSync( + executable, + command === "node" + ? `#!/usr/bin/env bash\nif [[ "\${1:-}" = "-p" ]]; then printf "%s\\n" "${nodeVersion.slice(1)}"; fi\n` + : '#!/usr/bin/env bash\nprintf "%s\\n" "$PATH"\n', + ); + chmodSync(executable, 0o755); + } + writeFileSync( + path.join(home, ".clinical-kb-codex-cloud.sh"), + [`export NVM_DIR="${bashHome}/.nvm"`, `nvm() { printf '${nodeVersion}\\n'; }`, ""].join("\n"), + ); + + const install = spawnSync(bashCommand, ["scripts/install-codex-cloud-command-shims.sh"], { + cwd: repoRoot, + encoding: "utf8", + env: { ...fixtureEnvironment, HOME: bashHome, PATH: bashPathList(process.env.PATH || "") }, + }); + expect(install.status, install.stderr || install.stdout).toBe(0); + }, 120_000); + it("only accepts a PATH fallback npm and npx that meet the repository npm range", () => { const home = temporaryDirectory("codex-cloud-shim-npm-home-"); const bashHome = bashPathEntry(home); diff --git a/tests/diff-integrity.test.ts b/tests/diff-integrity.test.ts index 5803608cba..48072b8859 100644 --- a/tests/diff-integrity.test.ts +++ b/tests/diff-integrity.test.ts @@ -116,7 +116,7 @@ describe("countTestCases", () => { const counts: Record = { "tests/guard-push-no-merge-base.test.ts": 1, "tests/pdf-extractor.test.ts": 6, - "tests/claude-cloud-profile.test.ts": 26, + "tests/claude-cloud-profile.test.ts": 59, }; for (const [path, expected] of Object.entries(counts)) { const source = readFileSync(resolve(REPOSITORY_ROOT, path), "utf8");