From 926d5f5da41ff1c08df0b2d59611c20a649fcce3 Mon Sep 17 00:00:00 2001 From: Joshua Simpson Date: Sat, 17 Oct 2026 05:39:16 +0000 Subject: [PATCH 01/16] fix: reconcile Cloud setup ownership and agent rules --- .agents/skills/prompt-perfector/SKILL.md | 6 +- .claude/agents/frontend-ui-reviewer.md | 2 +- .claude/agents/ingestion-worker-reviewer.md | 2 +- .claude/agents/verification-router.md | 6 +- .claude/cloud-profile/skills/prompt/SKILL.md | 2 + .claude/cloud-profile/skills/semgrep/SKILL.md | 61 +- .../skills/semgrep/references/rulesets.md | 10 +- .../skills/semgrep/references/scan-modes.md | 4 +- .../semgrep/references/scanner-task-prompt.md | 59 +- .../skills/semgrep/scripts/scan-status.sh | 85 +++ .../skills/semgrep/workflows/scan-workflow.md | 52 +- .claude/hooks/pr-handoff-stop.sh | 12 +- .claude/settings.json | 29 +- .claude/skills/gates/SKILL.md | 29 +- .claude/skills/prompt/SKILL.md | 2 + .../skills/release-readiness-review/SKILL.md | 4 +- .github/pull_request_template.md | 8 +- .github/workflows/ci.yml | 4 + CLAUDE.md | 9 +- README.md | 2 +- data/repo-awareness-snapshot.json | 33 +- docs/README.md | 2 + docs/agents/cloud-instruction-activation.md | 56 ++ docs/agents/codex-cloud-environment.md | 5 + docs/agents/codex-cloud-legacy-bootstrap.md | 78 +++ docs/agents/pull-request-workflow.md | 4 +- docs/agents/verification-gates.md | 2 +- docs/claude-cloud.md | 87 +-- ...0-17-cloud-installed-guidance-ownership.md | 12 + docs/organisation/README.md | 2 +- docs/organisation/retired-rules.json | 8 + docs/organisation/systems/knowledge.json | 4 + docs/process-hardening.md | 4 +- docs/scripts-index.md | 2 +- docs/task-receipts.md | 2 +- package.json | 3 +- scripts/apply-claude-cloud-profile.mjs | 522 +++++++++++------- scripts/audit-codex-cloud-artifacts.sh | 49 ++ scripts/check-codex-cloud-setup.mjs | 36 +- scripts/claude-session-start.mjs | 45 ++ scripts/claude-session-start.sh | 24 + scripts/install-codex-cloud-command-shims.sh | 102 +++- scripts/lib/codex-cloud-paths.sh | 12 + scripts/maintain-codex-cloud.sh | 11 + scripts/organisation/check-retired-rules.mjs | 3 + .../organisation/check-rule-consistency.mjs | 301 ++++++++++ scripts/refresh-codex-cloud-base.sh | 6 +- scripts/setup-claude-cloud.sh | 106 +++- scripts/setup-codex-cloud.sh | 59 +- tests/browser-test-plan.test.ts | 5 +- tests/claude-cloud-lifecycle.test.ts | 435 +++++++++++++++ tests/claude-cloud-profile.test.ts | 87 ++- tests/claude-cloud-write-safety.test.ts | 82 +++ tests/codex-cloud-policy-harness.test.ts | 326 ++++++++++- tests/codex-cloud-setup.test.ts | 141 ++++- tests/organisation-hooks.test.ts | 7 +- tests/organisation-instruction-guards.test.ts | 22 + tests/organisation-rule-consistency.test.ts | 274 +++++++++ 58 files changed, 2865 insertions(+), 482 deletions(-) create mode 100644 .claude/cloud-profile/skills/semgrep/scripts/scan-status.sh create mode 100644 docs/agents/cloud-instruction-activation.md create mode 100644 docs/agents/codex-cloud-legacy-bootstrap.md create mode 100644 docs/decisions/2026-10-17-cloud-installed-guidance-ownership.md create mode 100644 scripts/audit-codex-cloud-artifacts.sh create mode 100644 scripts/claude-session-start.mjs create mode 100644 scripts/claude-session-start.sh create mode 100644 scripts/lib/codex-cloud-paths.sh create mode 100644 scripts/organisation/check-rule-consistency.mjs create mode 100644 tests/claude-cloud-lifecycle.test.ts create mode 100644 tests/claude-cloud-write-safety.test.ts create mode 100644 tests/organisation-rule-consistency.test.ts diff --git a/.agents/skills/prompt-perfector/SKILL.md b/.agents/skills/prompt-perfector/SKILL.md index 16c87c91c4..bd0b4b3ac9 100644 --- a/.agents/skills/prompt-perfector/SKILL.md +++ b/.agents/skills/prompt-perfector/SKILL.md @@ -1,11 +1,13 @@ --- name: prompt-perfector -description: Refine or evaluate LLM and agent prompts while preserving intent with explicit output and action controls. Use when asked to polish, perfect, rewrite, structure, optimize, or assess a prompt. +description: Refine or evaluate prompts, preserving intent, output controls and authority. Use for prompt refinement or assessment. --- # Prompt Perfector -Produce a ready-to-use prompt that preserves intent. Refine only unless evaluation or execution is explicit. +Refine only unless evaluation or execution is explicit. + +Claude’s separately named `prompt` skill refines then executes within existing authority. These are distinct defaults; explicit user controls win. ## Workflow diff --git a/.claude/agents/frontend-ui-reviewer.md b/.claude/agents/frontend-ui-reviewer.md index 4a5762efc1..bf5ac0e904 100644 --- a/.claude/agents/frontend-ui-reviewer.md +++ b/.claude/agents/frontend-ui-reviewer.md @@ -22,7 +22,7 @@ Follow `AGENTS.md` review throttling and `docs/codex-review-protocol.md` before ## Gate -For UI/browser/styling/routing/reduced-motion/forced-colors changes, run `npm run ensure` before browser QA (never assume port 3000/3001/3002), then use `npm run verify:ui` (Chromium) as the gate. These are local/offline-safe (demo mode). +Follow `docs/agents/verification-gates.md#gate-selection`. For UI/browser/styling/routing/reduced-motion/forced-colors changes, run `npm run ensure` before browser QA, use its printed URL and verify `/api/local-project-id`, then prove the changed owner or journey first. Use `npm run verify:ui` for shared UI foundations or when PR/handoff policy requires the complete Chromium gate. Report focused proof as focused proof, retaining required broad gates and any physical Safari or installed-PWA acceptance gaps. ## Review Checklist diff --git a/.claude/agents/ingestion-worker-reviewer.md b/.claude/agents/ingestion-worker-reviewer.md index 831943f8a9..e1da070993 100644 --- a/.claude/agents/ingestion-worker-reviewer.md +++ b/.claude/agents/ingestion-worker-reviewer.md @@ -23,7 +23,7 @@ Follow `AGENTS.md` review throttling and `docs/codex-review-protocol.md` before ## Provider boundary -Ingestion checks against live services and any reindex that spends OpenAI budget are confirmation-required (`AGENTS.md`). Report the command and ask. Note the worker stack (Deno edge functions, Python OCR in `worker/python/requirements.txt`) does not run in this VM — do not attempt to execute it. +Ingestion checks against live services and any reindex that spends OpenAI budget are confirmation-required (`AGENTS.md`). Report the command and ask. Inspect current-host capabilities before selecting offline worker checks: confirm the required Deno/Python binaries and the specific command's local dependencies, fixtures and service needs. Run supported offline checks when available; report missing capabilities and unrun evidence precisely. A provisioned runtime does not authorize live ingestion, reindexing, downloads or provider spend. ## Review Checklist diff --git a/.claude/agents/verification-router.md b/.claude/agents/verification-router.md index 7aef791785..fd940be32b 100644 --- a/.claude/agents/verification-router.md +++ b/.claude/agents/verification-router.md @@ -23,7 +23,7 @@ Default to running **only** offline-safe gates. For any provider-touching comman - `lint`, `typecheck`, `test` - `verify:cheap` (lock parity + diff-integrity + lint + typecheck + unit tests — nothing else) -- `verify:full` (41 static/consistency gates, then lint + typecheck + test; does not invoke cheap) +- `verify:full` (static/consistency gates, then lint + typecheck + test; does not invoke cheap) - `verify:pr-local` (risk-routed PR mirror: focused docs/workflow contracts for light scope; fail-closed lint/typecheck/full unit plus applicable build/domain checks for executable or unknown scope); inspect selection with `verify:pr-local -- --dry-run --files ` - `eval:rag:offline`, `ensure`, `verify:ui` (Chromium, local dev server / demo mode) @@ -37,9 +37,9 @@ Default to running **only** offline-safe gates. For any provider-touching comman ## Routing by touched surface - **Retrieval / ranking / selection / chunking / scoring:** offline gate is `verify:cheap`; note that the merge gate `eval:retrieval:quality` (36/36) is provider-touching → report and ask. -- **Ingestion / answer-gen / source-governance / privacy / production env:** smallest relevant domain check + `check:production-readiness` (runs fail-closed offline). +- **Ingestion / answer-gen / source-governance / privacy / production env:** follow `docs/agents/verification-gates.md#gate-selection`: smallest relevant offline domain check; add `check:production-readiness:ci` when readiness or configuration structure is in scope. The bare `check:production-readiness` is release-mode proof for production release or activation, retaining all applicable approvals. - **UI / frontend / a11y / routing / styling:** `npm run ensure`, then the affected journey; - add `verify:ui` only for shared UI foundations or a distinct unclosed failure class. + follow `docs/agents/verification-gates.md#gate-selection` and add `verify:ui` for shared UI foundations or when PR/handoff policy requires the complete Chromium gate. - **Supabase env/config change:** `check:supabase-project` (provider — report and ask). - **Default source/config/test change:** `verify:pr-local` at PR handoff; do not pre-run `verify:cheap` because the routed handoff gate already owns that coverage. diff --git a/.claude/cloud-profile/skills/prompt/SKILL.md b/.claude/cloud-profile/skills/prompt/SKILL.md index c49de1c4e1..099db56d7e 100644 --- a/.claude/cloud-profile/skills/prompt/SKILL.md +++ b/.claude/cloud-profile/skills/prompt/SKILL.md @@ -7,6 +7,8 @@ description: Perfect a rough request or draft prompt using relevant context, att Turn the user's request and relevant supplied context into a precise, ready-to-use prompt, then carry it out. Preserve intent, avoid scope creep, and use only the detail, context, tools, and validation that change the result. +**Default for this skill:** refine, show the perfected prompt, then execute within existing authority. The separately named repository `prompt-perfector` skill refines only unless execution is explicit; it is not an alias. Preserve the invoked skill's default and the user's latest explicit controls. + ## Operating principles - Put the outcome, constraints, and success criteria ahead of process detail. diff --git a/.claude/cloud-profile/skills/semgrep/SKILL.md b/.claude/cloud-profile/skills/semgrep/SKILL.md index f53af43d7a..f2f15e9bc7 100644 --- a/.claude/cloud-profile/skills/semgrep/SKILL.md +++ b/.claude/cloud-profile/skills/semgrep/SKILL.md @@ -1,27 +1,26 @@ --- name: semgrep description: >- - Run Semgrep static analysis scan on a codebase using parallel subagents. + Run Semgrep static analysis on a codebase with capability-checked workers. Supports two scan modes — "run all" (full ruleset coverage) and "important - only" (high-confidence security vulnerabilities). Automatically detects and - uses Semgrep Pro for cross-file taint analysis when available. Use when asked + only" (high-confidence security vulnerabilities). Records local Semgrep Pro + evidence and uses the approved engine. Use when asked to scan code for vulnerabilities, run a security audit with Semgrep, find - bugs, or perform static analysis. Spawns parallel workers for multi-language - codebases. -allowed-tools: Bash Read Glob Task AskUserQuestion TaskCreate TaskList TaskUpdate + bugs, or perform static analysis. Uses parallel workers when authorized and + supported, with bounded direct execution as a fallback. --- # Semgrep Security Scan -Run a Semgrep scan with automatic language detection, parallel execution via Task subagents, and merged SARIF output. +Run a Semgrep scan with local language detection, supported execution tools, and merged SARIF output. Inspect current-host capabilities; this seed does not install a scanner agent or prove hosted tool availability. ## Essential Principles -1. **Always use `--metrics=off`** — Semgrep sends telemetry by default; `--config auto` also phones home. Every `semgrep` command must include `--metrics=off` to prevent data leakage during security audits. -2. **User must approve the scan plan (Step 3 is a hard gate)** — The original "scan this codebase" request is NOT approval. Present exact rulesets, target, engine, and mode; wait for explicit "yes"/"proceed" before spawning scanners. +1. **Always use `--metrics=off`** — Semgrep sends telemetry by default; `--config auto` also phones home. Every `semgrep` command must include `--metrics=off`. This disables telemetry, not registry or provider access; those actions still need authorization. +2. **User must approve the scan plan (Step 3 is a hard gate)** — A generic "scan this codebase" request does not approve an unspecified plan. Present exact rulesets, target, engine, mode and network/provider actions before execution. Preserve existing explicit authorization for that exact plan; ask only for missing authority. 3. **Third-party rulesets are required, not optional** — Trail of Bits, 0xdea, and Decurity rules catch vulnerabilities absent from the official registry. Include them whenever the detected language matches. -4. **Spawn all scan Tasks in a single message** — Parallel execution is the core performance advantage. Never spawn Tasks sequentially; always emit all Task tool calls in one response. -5. **Always check for Semgrep Pro before scanning** — Pro enables cross-file taint tracking and catches ~250% more true positives. Skipping the check means silently missing critical inter-file vulnerabilities. +4. **Use supported execution capabilities** — When delegation is authorized and workers are available, dispatch independent language tasks together using the bounded scanner prompt. Otherwise execute the same approved tasks directly within current-host limits. Do not invent an agent type, tool or delegation authority. +5. **Record engine evidence honestly** — Inspect only local binary/version/installation metadata before approval. Pro supports cross-file analysis; installed metadata does not prove a usable license. Any Pro, registry or network validation belongs after Step 3 authorization. Stop on an unavailable approved engine; do not silently substitute OSS. ## When to Use @@ -79,15 +78,12 @@ $OUTPUT_DIR/ ## Prerequisites -**Required:** Semgrep CLI (`semgrep --version`). If not installed, see [Semgrep installation docs](https://semgrep.dev/docs/getting-started/). +**Required:** Semgrep CLI. Discover it locally with `command -v semgrep` and `SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --metrics=off --version`. If absent, report the missing capability; installation needs applicable authority. See [Semgrep installation docs](https://semgrep.dev/docs/getting-started/). -**Optional:** Semgrep Pro — enables cross-file taint tracking, inter-procedural analysis, and additional languages (Apex, C#, Elixir). Check with: - -```bash -semgrep --pro --validate --config p/default 2>/dev/null && echo "Pro available" || echo "OSS only" -``` +**Optional:** Semgrep Pro — enables cross-file taint tracking, inter-procedural analysis, and additional languages (Apex, C#, Elixir). Read local installation metadata if available and record Pro as installed, absent or unverified. Do not use a registry ruleset or a Pro/license/network probe as an availability check before approval. Pro usability is established only by authorized execution; a failed probe is not evidence of OSS-only availability. **Limitations:** OSS mode cannot track data flow across files. Pro mode uses `-j 1` for cross-file analysis (slower per ruleset, but parallel rulesets compensate). +The bundled scan-status helper requires a supported Bash shell and local Python 3 to validate each fresh JSON/SARIF pair. Check these local capabilities before execution; missing tooling does not authorize installation. ## Scan Modes @@ -109,10 +105,10 @@ See [scan-modes.md](references/scan-modes.md) for metadata criteria and jq filte ``` ┌──────────────────────────────────────────────────────────────────┐ │ MAIN AGENT (this skill) │ -│ Step 1: Detect languages + check Pro availability │ +│ Step 1: Detect languages + inspect local engine evidence │ │ Step 2: Select scan mode + rulesets (ref: rulesets.md) │ │ Step 3: Present plan + rulesets, get approval [⛔ HARD GATE] │ -│ Step 4: Spawn parallel scan Tasks (approved rulesets + mode) │ +│ Step 4: Execute approved scans via workers or directly │ │ Step 5: Merge results and report │ └──────────────────────────────────────────────────────────────────┘ │ Step 4 @@ -134,13 +130,13 @@ See [scan-modes.md](references/scan-modes.md) for metadata criteria and jq filte | Step | Action | Gate | Key Reference | |------|--------|------|---------------| -| 1 | Resolve output dir, detect languages + Pro availability | — | Use Glob, not Bash | +| 1 | Resolve output dir, detect languages + local engine evidence | Local only | Supported file search and local metadata | | 2 | Select scan mode + rulesets | — | [rulesets.md](references/rulesets.md) | -| 3 | Present plan, get explicit approval | ⛔ HARD | AskUserQuestion | -| 4 | Spawn parallel scan Tasks | — | [scanner-task-prompt.md](references/scanner-task-prompt.md) | +| 3 | Present plan, get explicit approval | ⛔ HARD | Supported user-input tool or ordinary chat | +| 4 | Execute approved scans via supported workers or directly | Approved actions only | [scanner-task-prompt.md](references/scanner-task-prompt.md) | | 5 | Merge results and report | — | Merge script (below) | -**Task enforcement:** On invocation, create 5 tasks with blockedBy dependencies (each step blocks the previous). Step 3 is a HARD GATE — mark complete ONLY after user explicitly approves. +**Task tracking:** Use the host's supported task tracker for the five ordered steps when available, with each step depending on the previous. Otherwise keep a short checklist. Step 3 is a HARD GATE regardless of tracking tools; mark it complete only with explicit authorization for the exact plan. **Merge command (Step 5):** @@ -148,27 +144,23 @@ See [scan-modes.md](references/scan-modes.md) for metadata criteria and jq filte uv run {baseDir}/scripts/merge_sarif.py $OUTPUT_DIR/raw $OUTPUT_DIR/results/results.sarif ``` -## Agents - -| Agent | Tools | Purpose | -|-------|-------|---------| -| `static-analysis:semgrep-scanner` | Bash | Executes parallel semgrep scans for a language category | +## Execution capability -Use `subagent_type: static-analysis:semgrep-scanner` in Step 4 when spawning Task subagents. +Use an available general-purpose worker with shell/file access and the bounded [scanner task prompt](references/scanner-task-prompt.md) when delegation is authorized. A specialist scanner is optional only when the current host actually exposes it; the repository bundles no exact scanner-agent dependency. If workers are unavailable or delegation is not authorized, use direct execution of the same approved task with bounded concurrency. Report the execution path actually used. ## Rationalizations to Reject | Shortcut | Why It's Wrong | |----------|----------------| -| "User asked for scan, that's approval" | Original request ≠ plan approval. Present plan, use AskUserQuestion, await explicit "yes" | +| "User asked for scan, that's approval" | A generic scan request does not specify a plan. Obtain missing exact-plan authority; preserve an existing explicit approval | | "Step 3 task is blocking, just mark complete" | Lying about task status defeats enforcement. Only mark complete after real approval | | "I already know what they want" | Assumptions cause scanning wrong directories/rulesets. Present plan for verification | | "Just use default rulesets" | User must see and approve exact rulesets before scan | | "Add extra rulesets without asking" | Modifying approved list without consent breaks trust | | "Third-party rulesets are optional" | Trail of Bits, 0xdea, Decurity catch vulnerabilities not in official registry — REQUIRED | | "Use --config auto" | Sends metrics; less control over rulesets | -| "One Task at a time" | Defeats parallelism; spawn all Tasks together | -| "Pro is too slow, skip --pro" | Cross-file analysis catches 250% more true positives; worth the time | +| "A missing scanner agent blocks every scan" | Use a supported general worker when authorized, or execute the bounded approved tasks directly | +| "Change the engine without asking" | Pro and OSS have different coverage; preserve the approved engine or obtain a changed plan | | "Semgrep handles GitHub URLs natively" | URL handling fails on repos with non-standard YAML; always clone first | | "Cleanup is optional" | Cloned repos pollute the user's workspace and accumulate across runs | | "Use `.` or relative path as target" | Subagents need absolute paths to avoid ambiguity | @@ -190,11 +182,12 @@ Use `subagent_type: static-analysis:semgrep-scanner` in Step 4 when spawning Tas - [ ] Output directory resolved (user-specified or auto-incremented default) - [ ] All generated files stored inside `$OUTPUT_DIR` -- [ ] Languages detected with file counts; Pro status checked +- [ ] Languages detected with file counts; local engine evidence and Pro uncertainty recorded - [ ] Scan mode selected by user (run all / important only) - [ ] Rulesets include third-party rules for all detected languages - [ ] User explicitly approved the scan plan (Step 3 gate passed) -- [ ] All scan Tasks spawned in a single message and completed +- [ ] Approved scans completed via supported authorized workers or bounded direct execution +- [ ] Every approved scan's PID/status and fresh JSON/SARIF pair passed `wait_semgrep_scans`; no incomplete batch or successful subset is reported as complete - [ ] Every `semgrep` command used `--metrics=off` - [ ] Approved rulesets logged to `$OUTPUT_DIR/rulesets.txt` - [ ] Raw per-scan outputs stored in `$OUTPUT_DIR/raw/` diff --git a/.claude/cloud-profile/skills/semgrep/references/rulesets.md b/.claude/cloud-profile/skills/semgrep/references/rulesets.md index b4ae92d1a5..0bb636fd4c 100644 --- a/.claude/cloud-profile/skills/semgrep/references/rulesets.md +++ b/.claude/cloud-profile/skills/semgrep/references/rulesets.md @@ -138,16 +138,16 @@ These are **NOT optional**. Include automatically when language matches: | Java | [Atlassian Labs](https://github.com/atlassian-labs/atlassian-sast-ruleset) | Atlassian-maintained Java rules | | Python, JS/TS, Java, Ruby, Go, PHP | [Apiiro](https://github.com/apiiro/malicious-code-ruleset) | Malicious code detection, supply chain | -### Step 5: Verify Rulesets +### Step 5: Propose Ruleset Verification -Before finalizing, verify official rulesets load: +Before approval, record proposed ruleset identifiers without contacting registries. Registry validation and browsing are network actions: list the exact lookups in the Step 3 plan and perform them only after authorization. With that authority, verify approved rulesets load and retain the command's actual status: ```bash -# Quick validation (exits 0 if valid) -semgrep --config p/python --validate --metrics=off 2>&1 | head -3 +# After Step 3 authorizes this registry lookup (do not pipe away its status): +SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --config p/python --validate --metrics=off ``` -Or browse the [Semgrep Registry](https://semgrep.dev/explore). +The [Semgrep Registry](https://semgrep.dev/explore) is an alternative after the same network authorization. For an approved local-only scan, validate only the approved local rule paths and report registry coverage as unverified. ### Output Format diff --git a/.claude/cloud-profile/skills/semgrep/references/scan-modes.md b/.claude/cloud-profile/skills/semgrep/references/scan-modes.md index 2d9de702d2..d36d5d73b8 100644 --- a/.claude/cloud-profile/skills/semgrep/references/scan-modes.md +++ b/.claude/cloud-profile/skills/semgrep/references/scan-modes.md @@ -102,9 +102,11 @@ done In important-only mode, add `[SEVERITY_FLAGS]` to the scanner template: ```bash -semgrep [--pro if available] --metrics=off [SEVERITY_FLAGS] --config [RULESET] --json -o [OUTPUT_DIR]/raw/[lang]-[ruleset].json --sarif-output=[OUTPUT_DIR]/raw/[lang]-[ruleset].sarif [TARGET] & +start_semgrep_scan [OUTPUT_DIR]/raw/[lang]-[ruleset].json [OUTPUT_DIR]/raw/[lang]-[ruleset].sarif env SEMGREP_ENABLE_VERSION_CHECK=0 semgrep [--pro if approved] --metrics=off [SEVERITY_FLAGS] --config [RULESET] --json -o [OUTPUT_DIR]/raw/[lang]-[ruleset].json --sarif-output=[OUTPUT_DIR]/raw/[lang]-[ruleset].sarif [TARGET] || SEMGREP_SCAN_LAUNCH_FAILED=1 ``` +Source the scan-status helper and require `wait_semgrep_scans` to pass as described in [scanner-task-prompt.md](scanner-task-prompt.md). Severity filtering never waives process-status or expected-artifact checks. + Where `[SEVERITY_FLAGS]` is: - **Run all**: *(empty)* - **Important only**: `--severity MEDIUM --severity HIGH --severity CRITICAL` diff --git a/.claude/cloud-profile/skills/semgrep/references/scanner-task-prompt.md b/.claude/cloud-profile/skills/semgrep/references/scanner-task-prompt.md index e4cf37d4a3..3b2ab8bbc1 100644 --- a/.claude/cloud-profile/skills/semgrep/references/scanner-task-prompt.md +++ b/.claude/cloud-profile/skills/semgrep/references/scanner-task-prompt.md @@ -1,6 +1,6 @@ # Scanner Subagent Task Prompt -Use this prompt template when spawning scanner Tasks in Step 4. Use `subagent_type: static-analysis:semgrep-scanner`. +Use this bounded prompt in Step 4 with an available general-purpose worker when delegation is authorized, or for direct execution when workers are unavailable or delegation is not authorized. Inspect current-host tools; no exact scanner-agent type is required. ## Template @@ -10,13 +10,17 @@ You are a Semgrep scanner for [LANGUAGE_CATEGORY]. ## Task Run Semgrep scans for [LANGUAGE] files and save results to [OUTPUT_DIR]/raw. -## Pro Engine Status: [PRO_AVAILABLE: true/false] +## Approved Engine: [ENGINE: Pro/OSS]; local availability evidence: [EVIDENCE] ## Scan Mode: [SCAN_MODE: run-all/important-only] ## APPROVED RULESETS (from user-confirmed plan) [LIST EXACT RULESETS USER APPROVED - DO NOT SUBSTITUTE] +## APPROVED NETWORK/PROVIDER ACTIONS +[EXACT AUTHORIZED REGISTRY LOOKUPS, CLONES, PRO/LICENSE VALIDATION, OR LOCAL-ONLY] +Do not install tools, contact a registry or use Pro/provider access beyond this authority. Stop on an unavailable approved engine or ruleset; do not silently substitute. + Example: - p/python - p/django @@ -26,6 +30,13 @@ Example: ## Commands to Run (in parallel) +### Track every approved scan and its expected artifacts: +```bash +source {baseDir}/scripts/scan-status.sh +``` + +Use a supported Bash shell and local Python 3 for result validation; if either is unavailable, report the missing capability before execution. Do not install implicitly. Use unique, fresh absolute JSON/SARIF paths for each approved ruleset and keep the batch within current-host concurrency limits. + ### Clone GitHub URL rulesets first: ```bash mkdir -p [OUTPUT_DIR]/repos @@ -35,14 +46,16 @@ git clone --depth 1 https://github.com/org/repo [OUTPUT_DIR]/repos/repo-name ### Generate commands for EACH approved ruleset: ```bash -semgrep [--pro if available] --metrics=off [SEVERITY_FLAGS] [INCLUDE_FLAGS] --config [RULESET] --json -o [OUTPUT_DIR]/raw/[lang]-[ruleset].json --sarif-output=[OUTPUT_DIR]/raw/[lang]-[ruleset].sarif [TARGET] & +start_semgrep_scan [OUTPUT_DIR]/raw/[lang]-[ruleset].json [OUTPUT_DIR]/raw/[lang]-[ruleset].sarif env SEMGREP_ENABLE_VERSION_CHECK=0 semgrep [--pro if approved] --metrics=off [SEVERITY_FLAGS] [INCLUDE_FLAGS] --config [RULESET] --json -o [OUTPUT_DIR]/raw/[lang]-[ruleset].json --sarif-output=[OUTPUT_DIR]/raw/[lang]-[ruleset].sarif [TARGET] || SEMGREP_SCAN_LAUNCH_FAILED=1 ``` -Wait for all to complete: +Wait for every recorded PID and validate every expected fresh JSON/SARIF pair. The helper remembers launch failures and rejects nonzero exits, missing/invalid outputs and JSON-reported scan errors: ```bash -wait +wait_semgrep_scans [APPROVED_SCAN_COUNT] || exit 1 ``` +Do not merge or report successful completion after a failed batch. Preserve raw diagnostics and report the incomplete coverage, even if some result files exist. + ### Clean up cloned repos: ```bash [ -n "[OUTPUT_DIR]" ] && rm -rf [OUTPUT_DIR]/repos @@ -51,10 +64,11 @@ wait ## Critical Rules - Use ONLY the rulesets listed above - do not add or remove any - Always use --metrics=off (prevents sending telemetry to Semgrep servers) -- Use --pro when Pro is available (enables cross-file taint tracking) +- Use --pro only for the approved Pro engine; report failure instead of silently falling back - If scan mode is **important-only**, add `--severity MEDIUM --severity HIGH --severity CRITICAL` to every command - If scan mode is **run-all**, do NOT add severity flags -- Run all rulesets in parallel with & and wait +- Bound parallel rulesets to current-host resource and repository coordinator limits +- Require `wait_semgrep_scans` to pass before reporting the scanner task complete; bare `wait` does not preserve every child status - For GitHub URL rulesets, always clone into [OUTPUT_DIR]/repos/ and use the local path as --config (do NOT pass URLs directly to semgrep — its URL handling is unreliable for repos with non-standard YAML) - Add `--include` flags for language-specific rulesets (e.g., `--include="*.py"` for p/python). Do NOT add `--include` to cross-language rulesets like p/security-audit, p/secrets, or third-party repos - After all scans complete, delete [OUTPUT_DIR]/repos/ to avoid leaving cloned repos behind @@ -74,7 +88,8 @@ Report: | `[LANGUAGE_CATEGORY]` | Language group being scanned | Python, JavaScript, Docker | | `[LANGUAGE]` | Specific language | Python, TypeScript, Go | | `[OUTPUT_DIR]` | Output directory (absolute path, resolved in Step 1) | /path/to/static_analysis_semgrep_1 | -| `[PRO_AVAILABLE]` | Whether Pro engine is available | true, false | +| `[ENGINE]` / `[EVIDENCE]` | Approved engine and observed local capability evidence | Pro / installed metadata, usability unverified | +| `[APPROVED_SCAN_COUNT]` | Number of exact approved scan jobs assigned to this worker, fixed before launch | 5 | | `[SEVERITY_FLAGS]` | Severity pre-filter flags | *(empty)* for run-all, `--severity MEDIUM --severity HIGH --severity CRITICAL` for important-only | | `[INCLUDE_FLAGS]` | File extension filter for language-specific rulesets | `--include="*.py"` for Python rulesets, *(empty)* for cross-language rulesets like p/security-audit, p/secrets, or third-party repos | | `[RULESET]` | Semgrep ruleset identifier or local clone path | p/python, [OUTPUT_DIR]/repos/semgrep-rules | @@ -88,7 +103,7 @@ You are a Semgrep scanner for Python. ## Task Run Semgrep scans for Python files and save results to /path/to/static_analysis_semgrep_1/raw. -## Pro Engine Status: true +## Approved Engine: Pro; usability established only by authorized execution ## Scan Mode: run-all @@ -99,8 +114,17 @@ Run Semgrep scans for Python files and save results to /path/to/static_analysis_ - p/secrets - https://github.com/trailofbits/semgrep-rules +## APPROVED NETWORK/PROVIDER ACTIONS +- Fetch the listed registry rulesets and clone the listed Trail of Bits repository +- Use the approved Pro engine; no installation or additional provider actions + ## Commands to Run (in parallel) +### Track expected result pairs: +```bash +source {baseDir}/scripts/scan-status.sh +``` + ### Clone GitHub URL rulesets first: ```bash mkdir -p /path/to/static_analysis_semgrep_1/repos @@ -109,12 +133,12 @@ git clone --depth 1 https://github.com/trailofbits/semgrep-rules /path/to/static ### Run scans: ```bash -semgrep --pro --metrics=off --include="*.py" --config p/python --json -o /path/to/static_analysis_semgrep_1/raw/python-python.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-python.sarif /path/to/codebase & -semgrep --pro --metrics=off --include="*.py" --config p/django --json -o /path/to/static_analysis_semgrep_1/raw/python-django.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-django.sarif /path/to/codebase & -semgrep --pro --metrics=off --config p/security-audit --json -o /path/to/static_analysis_semgrep_1/raw/python-security-audit.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-security-audit.sarif /path/to/codebase & -semgrep --pro --metrics=off --config p/secrets --json -o /path/to/static_analysis_semgrep_1/raw/python-secrets.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-secrets.sarif /path/to/codebase & -semgrep --pro --metrics=off --config /path/to/static_analysis_semgrep_1/repos/trailofbits --json -o /path/to/static_analysis_semgrep_1/raw/python-trailofbits.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-trailofbits.sarif /path/to/codebase & -wait +start_semgrep_scan /path/to/static_analysis_semgrep_1/raw/python-python.json /path/to/static_analysis_semgrep_1/raw/python-python.sarif env SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --pro --metrics=off --include="*.py" --config p/python --json -o /path/to/static_analysis_semgrep_1/raw/python-python.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-python.sarif /path/to/codebase || SEMGREP_SCAN_LAUNCH_FAILED=1 +start_semgrep_scan /path/to/static_analysis_semgrep_1/raw/python-django.json /path/to/static_analysis_semgrep_1/raw/python-django.sarif env SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --pro --metrics=off --include="*.py" --config p/django --json -o /path/to/static_analysis_semgrep_1/raw/python-django.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-django.sarif /path/to/codebase || SEMGREP_SCAN_LAUNCH_FAILED=1 +start_semgrep_scan /path/to/static_analysis_semgrep_1/raw/python-security-audit.json /path/to/static_analysis_semgrep_1/raw/python-security-audit.sarif env SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --pro --metrics=off --config p/security-audit --json -o /path/to/static_analysis_semgrep_1/raw/python-security-audit.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-security-audit.sarif /path/to/codebase || SEMGREP_SCAN_LAUNCH_FAILED=1 +start_semgrep_scan /path/to/static_analysis_semgrep_1/raw/python-secrets.json /path/to/static_analysis_semgrep_1/raw/python-secrets.sarif env SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --pro --metrics=off --config p/secrets --json -o /path/to/static_analysis_semgrep_1/raw/python-secrets.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-secrets.sarif /path/to/codebase || SEMGREP_SCAN_LAUNCH_FAILED=1 +start_semgrep_scan /path/to/static_analysis_semgrep_1/raw/python-trailofbits.json /path/to/static_analysis_semgrep_1/raw/python-trailofbits.sarif env SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --pro --metrics=off --config /path/to/static_analysis_semgrep_1/repos/trailofbits --json -o /path/to/static_analysis_semgrep_1/raw/python-trailofbits.json --sarif-output=/path/to/static_analysis_semgrep_1/raw/python-trailofbits.sarif /path/to/codebase || SEMGREP_SCAN_LAUNCH_FAILED=1 +wait_semgrep_scans 5 || exit 1 ``` ### Clean up cloned repos: @@ -125,8 +149,9 @@ rm -rf /path/to/static_analysis_semgrep_1/repos ## Critical Rules - Use ONLY the rulesets listed above - do not add or remove any - Always use --metrics=off -- Use --pro when Pro is available -- Run all rulesets in parallel with & and wait +- Use --pro only for the approved Pro engine; stop on failure rather than substituting +- Bound parallel rulesets to current-host resource and repository coordinator limits +- Every recorded PID and expected JSON/SARIF pair must pass the helper before completion; never merge a successful subset - Clone GitHub URL rulesets into the output dir repos/ subfolder, use local path as --config - Add --include="*.py" to language-specific rulesets (p/python, p/django) but NOT to p/security-audit, p/secrets, or third-party repos - Delete repos/ after scanning diff --git a/.claude/cloud-profile/skills/semgrep/scripts/scan-status.sh b/.claude/cloud-profile/skills/semgrep/scripts/scan-status.sh new file mode 100644 index 0000000000..a9c60dbe37 --- /dev/null +++ b/.claude/cloud-profile/skills/semgrep/scripts/scan-status.sh @@ -0,0 +1,85 @@ +# Source from a supported Bash shell after exact-plan approval. No scanners run on source. +# The caller owns approved commands, absolute output paths and concurrency limits. +SEMGREP_SCAN_PIDS=() +SEMGREP_SCAN_JSON=() +SEMGREP_SCAN_SARIF=() +SEMGREP_SCAN_LAUNCH_FAILED=0 + +start_semgrep_scan() { + local json="$1" sarif="$2" existing + shift 2 + if [ "$#" -eq 0 ] || [ "$json" = "$sarif" ]; then + printf '%s\n' 'scan-status: command and distinct JSON/SARIF outputs are required' >&2 + SEMGREP_SCAN_LAUNCH_FAILED=1 + return 1 + fi + for existing in "${SEMGREP_SCAN_JSON[@]}" "${SEMGREP_SCAN_SARIF[@]}"; do + if [ "$existing" = "$json" ] || [ "$existing" = "$sarif" ]; then + printf '%s\n' 'scan-status: duplicate result path; use unique outputs for each approved scan' >&2 + SEMGREP_SCAN_LAUNCH_FAILED=1 + return 1 + fi + done + if [ -e "$json" ] || [ -L "$json" ] || [ -e "$sarif" ] || [ -L "$sarif" ]; then + printf '%s\n' 'scan-status: result already exists; preserve it and select fresh output paths' >&2 + SEMGREP_SCAN_LAUNCH_FAILED=1 + return 1 + fi + "$@" & + SEMGREP_SCAN_PIDS+=("$!") + SEMGREP_SCAN_JSON+=("$json") + SEMGREP_SCAN_SARIF+=("$sarif") +} + +wait_semgrep_scans() { + local failed="$SEMGREP_SCAN_LAUNCH_FAILED" index status expected="${1:-}" + if [ "${#SEMGREP_SCAN_PIDS[@]}" -eq 0 ]; then + printf '%s\n' 'scan-status: no approved scans were launched' >&2 + return 1 + fi + if [[ ! "$expected" =~ ^[1-9][0-9]*$ ]] || [ "$expected" != "${#SEMGREP_SCAN_PIDS[@]}" ]; then + failed=1 + printf '%s\n' 'scan-status: launched scan count differs from the exact approved plan' >&2 + fi + # Await every PID even after a failure; bare `wait` can discard a failed child's status. + for ((index = 0; index < ${#SEMGREP_SCAN_PIDS[@]}; index++)); do + if wait "${SEMGREP_SCAN_PIDS[$index]}"; then + status=0 + else + status=$? + failed=1 + printf 'scan-status: scan %s failed with exit %s\n' "$((index + 1))" "$status" >&2 + fi + if ! python3 - "${SEMGREP_SCAN_JSON[$index]}" "${SEMGREP_SCAN_SARIF[$index]}" <<'PY' +import json +import os +import sys + +try: + documents = [] + for filename in sys.argv[1:]: + if os.path.islink(filename) or not os.path.isfile(filename): + raise ValueError("missing or linked output") + with open(filename, encoding="utf-8") as stream: + documents.append(json.load(stream)) + result, sarif = documents + if not isinstance(result, dict) or not isinstance(result.get("results"), list): + raise ValueError("invalid scan JSON") + if result.get("errors"): + raise ValueError("scan JSON reports incomplete analysis") + if not isinstance(sarif, dict) or not isinstance(sarif.get("runs"), list) or not sarif["runs"]: + raise ValueError("invalid or empty SARIF runs") +except (OSError, ValueError, TypeError): + print("scan-status: expected fresh JSON/SARIF pair is missing, invalid or incomplete", file=sys.stderr) + sys.exit(1) +PY + then + failed=1 + fi + done + if [ "$failed" -ne 0 ]; then + printf '%s\n' 'scan-status: incomplete scan batch; preserve diagnostics and do not report or merge a successful subset' >&2 + return 1 + fi + printf 'scan-status: %s approved scans completed with valid JSON/SARIF pairs\n' "${#SEMGREP_SCAN_PIDS[@]}" +} diff --git a/.claude/cloud-profile/skills/semgrep/workflows/scan-workflow.md b/.claude/cloud-profile/skills/semgrep/workflows/scan-workflow.md index fc8ab1bce2..d8852735bd 100644 --- a/.claude/cloud-profile/skills/semgrep/workflows/scan-workflow.md +++ b/.claude/cloud-profile/skills/semgrep/workflows/scan-workflow.md @@ -4,14 +4,14 @@ Complete 5-step scan execution process. Read from start to finish and follow eac ## Task System Enforcement -On invocation, create these tasks with dependencies: +Use the current host's supported task tracker if available; otherwise record these steps as a short checklist with the same dependencies. Tool names below describe tracking actions, not required APIs: ``` -TaskCreate: "Detect languages and Pro availability" (Step 1) -TaskCreate: "Select scan mode and rulesets" (Step 2) - blockedBy: Step 1 -TaskCreate: "Present plan with rulesets, get approval" (Step 3) - blockedBy: Step 2 -TaskCreate: "Execute scans with approved rulesets and mode" (Step 4) - blockedBy: Step 3 -TaskCreate: "Merge results and report" (Step 5) - blockedBy: Step 4 +1. Detect languages and local engine evidence +2. Select scan mode and rulesets — depends on Step 1 +3. Present exact plan, obtain missing approval — depends on Step 2 +4. Execute approved scans — depends on Step 3 +5. Merge results and report — depends on Step 4 ``` ### Mandatory Gate @@ -27,7 +27,7 @@ Mark Step 3 as `completed` ONLY after user says "yes", "proceed", "approved", or ## Step 1: Resolve Output Directory, Detect Languages and Pro Availability > **Entry:** User has specified or confirmed the target directory. -> **Exit:** `OUTPUT_DIR` resolved and created; language list with file counts produced; Pro availability determined. +> **Exit:** `OUTPUT_DIR` resolved and created; language list with file counts produced; local engine evidence recorded, with Pro usability unverified until authorized execution. ### Resolve Output Directory @@ -50,22 +50,23 @@ echo "Output directory: $OUTPUT_DIR" `$OUTPUT_DIR` is used by all subsequent steps. Pass its **absolute path** to scanner subagents. Scanners write raw output to `$OUTPUT_DIR/raw/`; merged/filtered results go to `$OUTPUT_DIR/results/`. -**Detect Pro availability** (requires Bash): +**Discover the local CLI** (use a supported shell): ```bash if ! command -v semgrep >/dev/null 2>&1; then echo "ERROR: semgrep is not installed. Install from https://semgrep.dev/docs/getting-started/" exit 1 fi -semgrep --version -semgrep --pro --validate --config p/default 2>/dev/null && echo "Pro: AVAILABLE" || echo "Pro: NOT AVAILABLE" +SEMGREP_ENABLE_VERSION_CHECK=0 semgrep --metrics=off --version ``` -**Detect languages** using Glob (not Bash). Run these patterns against the target directory and count matches: +Inspect existing local installation metadata for Pro if useful; do not read or print credentials. Record installed, absent or unverified evidence without claiming license usability. **Before Step 3 approval, do not resolve registry configs, browse registries, clone rulesets, install tools or run Pro/license/network probes.** A version check is not scan or engine validation. Include any required registry/Pro/network validation in the plan for authorization, then run it only after Step 3. + +**Detect languages** using supported file-search tools (a glob tool when available, or `rg --files` with equivalent filters). Apply these patterns to the target directory and count matches: `**/*.py`, `**/*.js`, `**/*.ts`, `**/*.tsx`, `**/*.jsx`, `**/*.go`, `**/*.rb`, `**/*.java`, `**/*.php`, `**/*.c`, `**/*.cpp`, `**/*.rs`, `**/Dockerfile`, `**/*.tf` -Also check for framework markers: `package.json`, `pyproject.toml`, `Gemfile`, `go.mod`, `Cargo.toml`, `pom.xml`. Use Read to inspect these files for framework dependencies (e.g., read `package.json` to detect React, Express, Next.js; read `pyproject.toml` for Django, Flask, FastAPI). +Also check for framework markers: `package.json`, `pyproject.toml`, `Gemfile`, `go.mod`, `Cargo.toml`, `pom.xml`. Use supported file-reading tools to inspect framework dependencies (e.g., read `package.json` to detect React, Express, Next.js; read `pyproject.toml` for Django, Flask, FastAPI). Map findings to categories: @@ -87,10 +88,10 @@ Map findings to categories: ## Step 2: Select Scan Mode and Rulesets -> **Entry:** Step 1 complete — languages detected, Pro status known. +> **Entry:** Step 1 complete — languages detected, local CLI evidence and Pro uncertainty recorded. > **Exit:** Scan mode selected; structured rulesets JSON compiled for all detected languages. -**First, select scan mode** using `AskUserQuestion`: +**First, select scan mode.** Preserve an explicit requested mode; otherwise ask using a supported user-input tool or ordinary chat: ``` header: "Scan Mode" @@ -113,7 +114,7 @@ The algorithm covers: 3. Framework rulesets (if detected) 4. Infrastructure rulesets 5. **Required** third-party rulesets (Trail of Bits, 0xdea, Decurity — NOT optional) -6. Registry verification +6. Propose registry verification for after Step 3 approval; do not contact a registry during selection **Output:** Structured JSON passed to Step 3 for user review: @@ -147,6 +148,9 @@ Present plan to user with **explicit ruleset listing**: **Target:** /path/to/codebase **Output directory:** $OUTPUT_DIR **Engine:** Semgrep Pro (cross-file analysis) | Semgrep OSS (single-file) +**Engine evidence:** Local version/metadata; license usability unverified where applicable +**Network/provider actions:** Exact registry lookups, ruleset downloads/clones and any Pro/license validation; or explicitly local-only +**Execution:** Available authorized general worker(s), or bounded direct execution if workers are unavailable or delegation is not authorized **Scan mode:** Run all | Important only (security vulns, medium-high confidence/impact) ### Detected Languages/Technologies: @@ -182,13 +186,13 @@ Present plan to user with **explicit ruleset listing**: **⛔ STOP: Await explicit user approval.** 1. **If user wants to modify rulesets:** Add/remove as requested, re-present the updated plan, return to waiting. -2. **Use AskUserQuestion** if user hasn't responded: +2. **Use a supported user-input tool or ordinary chat** when exact-plan authorization is missing: ``` "I've prepared the scan plan with N rulesets (including Trail of Bits). Proceed with scanning?" Options: ["Yes, run scan", "Modify rulesets first"] ``` 3. **Valid approval:** "yes", "proceed", "approved", "go ahead", "looks good", "run it" -4. **NOT approval:** User's original request ("scan this codebase"), silence, questions about the plan +4. **NOT approval:** A generic request ("scan this codebase"), silence, questions about the plan. Reuse an existing explicit approval of the exact target, rulesets, engine, mode and network/provider actions; do not ask for it again. ### Pre-Scan Checklist @@ -200,7 +204,8 @@ Before marking Step 3 complete: - [ ] User given opportunity to modify rulesets - [ ] User explicitly approved (quote their confirmation) - [ ] **Final ruleset list captured for Step 4** -- [ ] Agent type listed: `static-analysis:semgrep-scanner` +- [ ] Registry/Pro/network validation, downloads and provider actions explicitly listed and authorized +- [ ] Supported execution path listed; any delegation separately authorized ### Log Approved Rulesets @@ -231,9 +236,10 @@ RULESETS **Use `$OUTPUT_DIR` resolved in Step 1.** It already exists; no need to create it again. Scanners write all output to `$OUTPUT_DIR/raw/`. -**Spawn N Tasks in a SINGLE message** (one per language category) using `subagent_type: static-analysis:semgrep-scanner`. +Inspect current-host worker capabilities. When delegation is authorized, use available general-purpose workers with shell/file access, dispatching independent language tasks together. A specialist scanner may be used only if actually exposed by the host; this seed requires no exact agent type. If workers are unavailable or delegation is not authorized, execute the bounded approved tasks directly. Report that fallback and retain host concurrency limits. Use the scanner task prompt template from [scanner-task-prompt.md](../references/scanner-task-prompt.md). +Use its [scan-status helper](../scripts/scan-status.sh) for every approved scan. It records each PID and expected fresh JSON/SARIF pair. A nonzero exit, launch failure, missing artifact, invalid output or JSON-reported scan error makes the whole batch incomplete; bare `wait` and a successful subset are not completion evidence. Preserve all diagnostics and report the uncompleted rulesets instead of merging partial success. **Mode-dependent scanner flags:** - **Run all**: No additional flags @@ -241,7 +247,7 @@ Use the scanner task prompt template from [scanner-task-prompt.md](../references **Example — 3 Language Scan (with approved rulesets):** -Spawn these 3 Tasks in a SINGLE message: +When supported and authorized, dispatch these 3 independent tasks together; otherwise run the same approved tasks directly: 1. **Task: Python Scanner** — Rulesets: p/python, p/django, p/security-audit, p/secrets, trailofbits → `$OUTPUT_DIR/raw/python-*.json` 2. **Task: JavaScript Scanner** — Rulesets: p/javascript, p/react, p/nodejs, p/security-audit, p/secrets, trailofbits → `$OUTPUT_DIR/raw/js-*.json` @@ -250,16 +256,18 @@ Spawn these 3 Tasks in a SINGLE message: ### Operational Notes - Always use **absolute paths** for `[TARGET]` — subagents can't resolve relative paths +- Run registry/Pro/network validation and ruleset downloads only when included in Step 3 authorization; `--metrics=off` disables telemetry, not registry access. Keep version checking disabled with `SEMGREP_ENABLE_VERSION_CHECK=0` during execution too. +- If the approved engine or ruleset is unavailable, stop and report it. Obtain approval for a changed plan rather than silently substituting engines, rulesets or network access. - Clone GitHub URL rulesets into `$OUTPUT_DIR/repos/` — never pass URLs directly to `--config` (semgrep's URL handling fails on repos with non-standard YAML) - Delete `$OUTPUT_DIR/repos/` after all scans complete -- Run rulesets in parallel with `&` and `wait`, not sequentially +- Bound parallel ruleset execution to current-host resource and repository coordinator limits - Use `--include="*.py"` for language-specific rulesets, but NOT for cross-language rulesets (p/security-audit, p/secrets, third-party repos) --- ## Step 5: Merge Results and Report -> **Entry:** Step 4 complete — all scan Tasks finished. +> **Entry:** Step 4 complete — every scanner task reports `wait_semgrep_scans` passed for all approved rulesets and expected JSON/SARIF pairs. An incomplete task blocks success and merging even when other tasks produced results. > **Exit:** `results.sarif` exists in `$OUTPUT_DIR/results/` and is valid JSON. **Important-only mode: Post-filter before merge.** Apply the filter from [scan-modes.md](../references/scan-modes.md) ("Filter All Result Files in a Directory" section) to each result JSON in `$OUTPUT_DIR/raw/`. The filter creates `*-important.json` files alongside the originals — the originals are preserved unmodified. diff --git a/.claude/hooks/pr-handoff-stop.sh b/.claude/hooks/pr-handoff-stop.sh index e0e9d0b3b3..082cf262c9 100755 --- a/.claude/hooks/pr-handoff-stop.sh +++ b/.claude/hooks/pr-handoff-stop.sh @@ -4,10 +4,10 @@ # Why: a cron entry parked on a PR outlives the session, so nothing later can # stop it. Everything else about following a PR — reading checks and run logs, # re-running a failed job, syncing the branch, pushing fixes, waiting between -# looks — is ordinary work and is not restricted by this hook. AGENTS.md -# "Babysit the pull request, then stop" states the plain policy: follow a -# PR's CI while it is useful, fix only this change's breakage, stop when CI -# settles, and never park a cron job on it. This hook enforces only the one +# looks — is governed by docs/agents/pull-request-workflow.md#follow-ci and +# the applicable authorization. Bare publication stops at the URL. Where CI +# follow is in scope, stop when CI settles and never park a cron job on it. +# This hook enforces only the one # clause prose cannot: cron survives the session that created it. # # Two modes, both registered in .claude/settings.json: @@ -272,7 +272,7 @@ post) fi [ -f "$marker" ] || exit 0 - context="The pull request is open: hand over its URL. Follow its CI while that is useful, fix only this change's breakage, and stop as soon as CI settles — see AGENTS.md \"Babysit the pull request, then stop\". Never park a cron job on this PR; it would outlive the session." + context="The pull request is open: hand over its URL. Follow docs/agents/pull-request-workflow.md#follow-ci within the requested scope: bare publication stops at the URL; where CI observation is in scope, fix only this change's breakage and stop as soon as CI settles. Never park a cron job on this PR; it would outlive the session." printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}\n' "$(json_escape "$context")" exit 0 ;; @@ -281,7 +281,7 @@ pre) [ -f "$marker" ] || exit 0 [ "$tool_name" = "CronCreate" ] || exit 0 - reason="Blocked: a cron entry parked on this pull request outlives the session, so nothing later could stop it. Use ScheduleWakeup or Monitor to wait between checks instead, then report where CI stands and stop. See AGENTS.md \"Babysit the pull request, then stop\". Unlock only on an explicit user ask, by deleting the marker: CLAUDE_ALLOW_PR_FOLLOW=1 rm \"$marker\"." + reason="Blocked: a cron entry parked on this pull request outlives the session, so nothing later could stop it. Where CI observation is authorized, inspect the current host's supported wait or terminal-event tools and use a bounded wait; if unavailable, report pending CI and stop. Follow docs/agents/pull-request-workflow.md#follow-ci. Unlock only on an explicit user ask, by deleting the marker: CLAUDE_ALLOW_PR_FOLLOW=1 rm \"$marker\"." printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"%s"}}\n' "$(json_escape "$reason")" exit 0 ;; diff --git a/.claude/settings.json b/.claude/settings.json index e35865e23e..4854985c89 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -491,33 +491,8 @@ "hooks": [ { "type": "command", - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh\"", - "timeout": 900 - }, - { - "type": "command", - "command": "bash \"$CLAUDE_PROJECT_DIR/scripts/setup-claude-cloud.sh\" --session", - "timeout": 300 - }, - { - "type": "command", - "command": "node \"$CLAUDE_PROJECT_DIR/scripts/check-base-freshness.mjs\" --hook", - "timeout": 30 - }, - { - "type": "command", - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/issues-surface.sh\"", - "timeout": 30 - }, - { - "type": "command", - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/testing-policy.sh\"", - "timeout": 15 - }, - { - "type": "command", - "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/organisation-session-line.sh\"", - "timeout": 20 + "command": "bash \"$CLAUDE_PROJECT_DIR/scripts/claude-session-start.sh\"", + "timeout": 1300 } ] } diff --git a/.claude/skills/gates/SKILL.md b/.claude/skills/gates/SKILL.md index 4fb7081d0c..7567abd846 100644 --- a/.claude/skills/gates/SKILL.md +++ b/.claude/skills/gates/SKILL.md @@ -27,7 +27,7 @@ Check these before believing any result. for exactly this reason — if installed packages do not match `package-lock.json`, treat any test, lint, or typecheck result as void until `npm ci` has run. Its own failure message says as much. - **`verify:full` stops at the first failing check.** Everything after that point never ran. Do not - describe the change as broadly verified when the gate died at check 2 of 45. + describe the change as broadly verified when the gate died at check 2 of 46. - **Changed-file formatting is required in CI but is part of neither gate.** A locally green `verify:cheap` can still fail CI on formatting. During iteration, format only task-owned files. Before a push, follow `AGENTS.md`: from an isolated or otherwise fully owned worktree run @@ -42,16 +42,16 @@ Match the gate to what actually changed. Running a broader gate is not more rigo observe the change; running a narrower one is not sloppy if it can. Add a second gate only when it covers a distinct plausible regression and the incremental confidence justifies its cost. -| Change | Gate that can actually fail | -| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| Markdown / docs only | `prettier --check`, `docs:check-links`, `docs:check-index` | -| Localised source behavior | `test:focused -- --files ` | -| Ordinary pre-PR check | `verify:cheap` — `check:installed-lock-parity` + `check:diff-integrity` + `lint` + `typecheck` + `test` | -| Cross-module/unknown scope | `verify:full` — 42 static gates (`check:runtime` through `check:offline-pack` terminal static gate) then `lint` + `typecheck` + unit `test` | -| Before PR handoff | `verify:pr-local` (risk-routed; inspect with `--dry-run`) | -| UI, styling, routing, a11y | `npm run ensure`, affected journey, broad UI only when shared | -| Phone chrome | `verify:phone-chrome` (narrower than `verify:ui`; run it first) | -| Explicit release confidence | `verify:release` (includes `build` + full Playwright + governance/eval; provider approval required) | +| Change | Gate that can actually fail | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Markdown / docs only | `prettier --check`, `docs:check-links`, `docs:check-index` | +| Localised source behavior | `test:focused -- --files ` | +| Ordinary pre-PR check | `verify:cheap` — `check:installed-lock-parity` + `check:diff-integrity` + `lint` + `typecheck` + `test` | +| Cross-module/unknown scope | `verify:full` — static gates (`check:runtime` through `check:offline-pack` terminal static gate) then `lint` + `typecheck` + unit `test` | +| Before PR handoff | `verify:pr-local` (risk-routed; inspect with `--dry-run`) | +| UI, styling, routing, a11y | `npm run ensure`, affected journey, broad UI only when shared | +| Phone chrome | `verify:phone-chrome` (narrower than `verify:ui`; run it first) | +| Explicit release confidence | `verify:release` (includes `build` + full Playwright + governance/eval; provider approval required) | `lint`, `typecheck`, and `test` cannot observe a markdown-only change. Say so rather than running them for appearance. @@ -76,8 +76,11 @@ Never run provider-backed gates without explicit user confirmation: `eval:rag`, `eval:retrieval:quality`, `verify:release`, `check:supabase-project`, `test:live`, and any GitHub, Supabase, OpenAI, or hosted-CI call. Report the command and ask instead of running it. -The only standing exception is the user typing `Run PR`, and that authorises only the GitHub actions -enumerated in the `run-pr` skill, for that sweep alone. +Use the canonical [API and provider confirmation boundary](../../../AGENTS.md#api-and-provider-confirmation-boundary) +for standing authorization, including the distinct `Run PR` and `Clear PRs` shortcut scopes. +Preserve each shortcut's duration, actions and exclusions in +[the PR workflow](../../../docs/agents/pull-request-workflow.md); do not ask again for an already +authorized action or extend that authorization to another action. ## Third-party claims are not evidence diff --git a/.claude/skills/prompt/SKILL.md b/.claude/skills/prompt/SKILL.md index c49de1c4e1..099db56d7e 100644 --- a/.claude/skills/prompt/SKILL.md +++ b/.claude/skills/prompt/SKILL.md @@ -7,6 +7,8 @@ description: Perfect a rough request or draft prompt using relevant context, att Turn the user's request and relevant supplied context into a precise, ready-to-use prompt, then carry it out. Preserve intent, avoid scope creep, and use only the detail, context, tools, and validation that change the result. +**Default for this skill:** refine, show the perfected prompt, then execute within existing authority. The separately named repository `prompt-perfector` skill refines only unless execution is explicit; it is not an alias. Preserve the invoked skill's default and the user's latest explicit controls. + ## Operating principles - Put the outcome, constraints, and success criteria ahead of process detail. diff --git a/.cursor/skills/release-readiness-review/SKILL.md b/.cursor/skills/release-readiness-review/SKILL.md index 80e5fded8f..9d106280dc 100644 --- a/.cursor/skills/release-readiness-review/SKILL.md +++ b/.cursor/skills/release-readiness-review/SKILL.md @@ -5,7 +5,7 @@ description: Reviews build validation, linting/type-checking, target database ch # Release Readiness Review Skill -Use this skill when preparing a branch for merging, pull request submission, or production deployment. +Use this skill for explicitly requested staging or production release readiness. For routine development or PR handoff, use [the canonical gate selector](../../../docs/agents/verification-gates.md#gate-selection). Bare PR publication follows the separate publication route in `AGENTS.md` and does not invoke readiness work. ## Repository Review Protocol @@ -23,4 +23,4 @@ Follow `AGENTS.md` review throttling and `docs/codex-review-protocol.md` before - **Preflight Verification:** Complete all checklist items in `.github/pull_request_template.md`. - **Secret Scan:** Confirm no private configuration files (`.env.local`), local debug logs, or keys are present in the git stage. -- **Production Readiness Check:** Run `npm run check:production-readiness` for privacy, ingestion, or clinical behavior changes. +- **Production Readiness Check:** Follow [the canonical gate selector](../../../docs/agents/verification-gates.md#gate-selection): routine work uses the smallest relevant offline domain check, with `npm run check:production-readiness:ci` when readiness or configuration structure is in scope. Before production release or activation, run the bare `npm run check:production-readiness` in release mode with all applicable clinical, privacy, legal, security and provider approvals. Pending release approvals block release or activation. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d8923dcaa8..49972baee0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,10 +10,10 @@ Paste the decisive line from whatever you ran — exit code 0 alone is not proof - [ ] Checks run: -Use `npm run test:focused -- --files ` while iterating, `npm run verify:cheap` (lint + -typecheck + unit tests) before opening a PR, and `npm run verify:full` only when a change is -broad enough to warrant the whole static suite. `npm run verify:ui` covers UI, routing, styling, -reduced-motion and forced-colors changes. +Select checks using [the canonical verification policy](../docs/agents/verification-gates.md). +Use focused proof for the changed behavior and the applicable PR-local gate. Shared UI foundations +or an applicable handoff requirement can require the complete browser gate; routine bounded work +does not automatically require a full unit, browser or release suite. Nothing in this template blocks a merge. The gates that fail closed are migration history, required-check forgery, and CI itself. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2d57aa14f..250fb2abee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -626,6 +626,10 @@ jobs: HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} run: npm run check:instructions + - name: Active agent rule consistency + if: ${{ !cancelled() }} + run: npm run check:rule-consistency + - name: Offline pack if: ${{ !cancelled() }} run: npm run check:offline-pack diff --git a/CLAUDE.md b/CLAUDE.md index 659097f309..13b28125c8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -101,7 +101,7 @@ completed passing evidence; skipped or pending checks are not passes. | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `npm run test:focused -- --files ` | Source-only iteration. Fails closed for deleted files and test infrastructure — then run `npm run test`. | | `npm run verify:cheap` | **Ordinary offline gate:** `check:installed-lock-parity` then `check:diff-integrity` + `lint` + `typecheck` + full offline unit suite. Nothing else. Prefer `test:focused` while iterating; run cheap before an ordinary PR push when the diff needs those five checks. CI selects the heavy static set when classified scope requires it. | -| `npm run verify:full` | **Broad offline gate:** the 42 static/consistency gates (docs index, ledgers, knip, design tokens, owner-scope…; `npm run check:gate-manifest` lists them), then `lint` + `typecheck` + `test`. Does not invoke the `verify:cheap` script. For cross-module risk or before a release — not routinely. | +| `npm run verify:full` | **Broad offline gate:** the 43 static/consistency gates (docs index, ledgers, knip, design tokens, owner-scope…; `npm run check:gate-manifest` lists them), then `lint` + `typecheck` + `test`. Does not invoke the `verify:cheap` script. For cross-module risk or before a release — not routinely. | | `npm run verify:pr-local` | Risk-routed PR mirror: focused docs/workflow contracts for recognised light scope, fail-closed heavy checks for executable or unknown scope. `-- --dry-run --files ` shows selection. | | `npm run verify:ui` | Chromium production journeys. Run `npm run ensure` first. | | `npm run verify:phone-chrome` | Phone-chrome changes; selects affected owners/journeys before escalating to `verify:ui` | @@ -114,8 +114,11 @@ static gate (lint + source typecheck; override `SKIP_STATIC_GUARD=1`). The auto- automation override; the other guards retain their documented override env vars. Domain changes (auth, Supabase, ingestion, answer generation, search/ranking, clinical -output, source governance) additionally want the smallest relevant domain check plus -`npm run check:production-readiness`. +output, source governance) follow the [canonical gate selector](docs/agents/verification-gates.md#gate-selection): +use the smallest relevant offline domain check, adding `npm run check:production-readiness:ci` +when readiness or configuration structure is in scope. The bare +`npm run check:production-readiness` is the release-mode gate for production release or +activation; retain all applicable clinical, privacy, legal, security and provider approvals. CI (`.github/workflows/ci.yml`) is risk-scoped: a `changes` job classifies paths, `static-pr` always runs a small baseline and conditionally selects docs/workflow or heavy static checks, diff --git a/README.md b/README.md index 2dbdcb185c..53d09be9f9 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,7 @@ Verification gates (see `package.json` for the full chain): ```bash npm run verify:cheap # ordinary offline gate: check:installed-lock-parity # + check:diff-integrity + lint + typecheck + test -npm run verify:full # 42 static/consistency gates (check:runtime through +npm run verify:full # 43 static/consistency gates (check:runtime through # check:offline-pack; `npm run check:gate-manifest` # lists them from verify:full:internal) then lint + # typecheck + test — does not invoke the cheap script diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json index 4f835e9e43..04387dfcca 100644 --- a/data/repo-awareness-snapshot.json +++ b/data/repo-awareness-snapshot.json @@ -1,7 +1,7 @@ { "version": "repo-awareness-snapshot-v3", "captured_revision": { - "committed_at": "2026-10-16" + "committed_at": "2026-10-17" }, "routes": { "modes": [ @@ -2529,6 +2529,11 @@ "section": "decisions", "catalogued": true }, + { + "path": "docs/agents/cloud-instruction-activation.md", + "section": "agents", + "catalogued": true + }, { "path": "docs/product/clinical-trust-direction.md", "section": "product", @@ -3044,6 +3049,11 @@ "section": "superpowers", "catalogued": true }, + { + "path": "docs/agents/codex-cloud-legacy-bootstrap.md", + "section": "agents", + "catalogued": true + }, { "path": "docs/design-system/sweep-2026-08-29-structure.md", "section": "design-system", @@ -4329,6 +4339,11 @@ "section": "superpowers", "catalogued": true }, + { + "path": "docs/decisions/2026-10-17-cloud-installed-guidance-ownership.md", + "section": "decisions", + "catalogued": true + }, { "path": "docs/decisions/2026-10-15-database-changes-need-owner-permission.md", "section": "decisions", @@ -9169,6 +9184,14 @@ "outcome": "MERGE-READY for product scope once exact-head required checks finish after final main sync. Fixed: Bugbot pathname searchMode (Compiler-safe previous-state sync); docs index; PR_POLICY_BODY/governance; maintainability extract; prescribing Safety filters (no fields=index); Sheet teardown document guard. Unresolved threads: none. Residual: human approving review; title still stale (gh/ManagePullRequest cannot edit); main may move again.", "checks": "Hosted on prior tip 973d5d88: PR policy/Static/Unit/Build/Production UI/PR required all SUCCESS. Final sync pushed; awaiting exact-head recheck. No OpenAI/live Supabase writes." }, + { + "date": "2026-10-17", + "ref": "claude/rag-ingestion-hardening", + "head": "2a9d44e0015358e4e7d197e4cf15f83df0a9ce35", + "scope": "RAG retrieval/summary/cache generation isolation and ingestion worker/edge hardening", + "outcome": "resolved", + "checks": "typecheck:internal, eslint, check:diff-integrity, check:migration-immutability, check:owner-scope, check:rag:fixtures, eval:rag:offline (26 files/979 tests), eval:rag:adversarial:offline (25 tests), vitest (8 suites/254 tests)" + }, { "date": "2026-07-28", "ref": "PR #1297 / `motion-audit-fixes-clean`", @@ -21281,6 +21304,14 @@ "outcome": "Already MERGED to main before code changes needed; tip had correct sidebar absence assertion; 0 unresolved threads at close.", "checks": "Hosted PR required + Production UI SUCCESS on merged tip. No provider-backed checks." }, + { + "date": "2026-10-17", + "ref": "claude/rag-ingestion-hardening", + "head": "b8278363b9e55ca218133ac52f6191a2edf60502", + "scope": "RAG and Ingestion review follow-ups", + "outcome": "Resolved PR review findings, repaired bot-applied regressions, and restored rag.ts maintainability budget", + "checks": "npm run typecheck:internal; node scripts/check-maintainability-budgets.mjs; npx eslint; npx vitest run (11 suites)" + }, { "date": "2026-08-10", "ref": "cursor/same-mode-focus-no-steal-6df8", diff --git a/docs/README.md b/docs/README.md index 2151960498..acd6024ef5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -238,6 +238,8 @@ Every remaining tracked document in this category (process, plus the `agents/` r - [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) — Canonical Cloud operating policy: current-host evidence, task continuation, tools, credentials and explicitly selected legacy setup. +- [agents/cloud-instruction-activation.md](agents/cloud-instruction-activation.md) — Verify repository, installed-host and fresh-session instruction states separately. +- [agents/codex-cloud-legacy-bootstrap.md](agents/codex-cloud-legacy-bootstrap.md) — Explicit legacy bootstrap containment, scoped cache and read-only artifact inspection. - [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 diff --git a/docs/agents/cloud-instruction-activation.md b/docs/agents/cloud-instruction-activation.md new file mode 100644 index 0000000000..32f06216e4 --- /dev/null +++ b/docs/agents/cloud-instruction-activation.md @@ -0,0 +1,56 @@ +# Cloud instruction activation + +Use this guide after changing setup, agent rules or installed guidance, or when a fresh task still +behaves like an older session. Source publication, host installation and loaded chat context are +separate evidence states. A passing repository check proves none of the other two. + +## Identify the selected source + +Record the repository, checkout, branch and full commit using local Git. Confirm any working-tree +changes before treating the commit as the complete source. Follow the current host's actual tools, +network policy and supported configuration controls. Inspect credential names/readiness only; +never print values, open authentication files or copy credentials to another environment. + +Use the current repository's setup only when that environment explicitly selects it. The retained +Codex legacy bootstrap needs its own isolated home/configuration when sharing a machine with +unrelated tasks; see [the Cloud policy](codex-cloud-environment.md). Claude profile installation +must respect its ownership manifest and preserve host modifications; see +[Claude container setup](../claude-cloud.md). A repository revision is not permission to provision +a host or activate a provider integration. + +## Verify the three states + +| State | Evidence | What remains unproved | +| -------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| Repository | Exact checkout/revision and relevant source diff; applicable static and fixture checks | Host installation and loaded prompts | +| Installed host | Supported setup result, selected configuration directory, source ownership and managed-artifact status | What an already-running chat loaded | +| Active session | Supported context inspection or a fresh session reporting its loaded entry files and relevant continuation/topic rules | Future sessions, other environments and permission to run providers | + +When a managed environment stores setup outside Git, inspect its supported configuration view and +record its revision separately. Do not edit a generated policy snapshot or infer that changing a +repository updates every saved environment. If the supported configuration view is unavailable, +report that layer as unverified and continue independent repository work. + +## Client loading matrix + +Verify only clients actually supported by the selected host. Do this at the next useful task; +dummy agent dispatches and provider requests are not activation tests. + +| Client | Expected source | Verification in a fresh session | +| ----------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| Codex | Applicable AGENTS.md plus required continuation and topic pointers | Inspect loaded context if supported; identify exact checkout and relevant safeguards | +| Claude Code | CLAUDE.md importing AGENTS.md; scoped nested files; selected owned user profile | Inspect native context and setup status; check that nested guidance preserves root boundaries | +| Cursor | Applicable AGENTS.md and selected skills/agents | Inspect actual loaded rules and relevant canonical verification guidance | +| Gemini CLI | GEMINI.md directing it to applicable AGENTS.md | Confirm native import/reference loading and required continuation | +| Antigravity | Supported native adapters and their expanded targets | Inspect expanded context and aggregate/per-file limits; file existence alone is insufficient | + +After an update, start a fresh session or use a supported reload and inspect the result. Existing +chats can retain previous prompts. Do not overwrite personal memory or delete historical records +to simulate a context refresh. Use the managed installer’s ownership-aware reconciliation for +retired installed files; preserve modified/unowned content for explicit review. + +## Completion evidence + +Report the source revision, checks run, installation status and session-loading status separately. +State unavailable controls and retained modifications. An old instruction is resolved only at the +layers actually observed; a local edit is never a claim that every environment has been purged. diff --git a/docs/agents/codex-cloud-environment.md b/docs/agents/codex-cloud-environment.md index 39c018e2ad..71d5d65e33 100644 --- a/docs/agents/codex-cloud-environment.md +++ b/docs/agents/codex-cloud-environment.md @@ -61,6 +61,8 @@ The [archived legacy runbook](../archive/codex-cloud-legacy-profile-2026-10-16.m 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. +For the retained bootstrap's checkout containment, config-home selection, cache identity and +read-only retirement inventory, use [the current legacy bootstrap notes](codex-cloud-legacy-bootstrap.md). 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 @@ -96,6 +98,9 @@ by a running process. Do not claim which component injected a value without evid ### Activation and retained context +Use [Cloud instruction activation](cloud-instruction-activation.md) to verify the selected source, +installed host and fresh native session separately across supported clients. + 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 diff --git a/docs/agents/codex-cloud-legacy-bootstrap.md b/docs/agents/codex-cloud-legacy-bootstrap.md new file mode 100644 index 0000000000..2b3beaf515 --- /dev/null +++ b/docs/agents/codex-cloud-legacy-bootstrap.md @@ -0,0 +1,78 @@ +# Retained Codex Cloud bootstrap + +These notes apply only to an explicitly selected legacy bootstrap. The +[Cloud operating policy](codex-cloud-environment.md) remains canonical; the +[archived profile runbook](../archive/codex-cloud-legacy-profile-2026-10-16.md) +records the older disposable-container setup. Current host-managed tasks do not +need this bootstrap merely because they run on Linux or in Codex Cloud. + +The setup command remains `bash scripts/setup-codex-cloud.sh`; maintenance is +`bash scripts/maintain-codex-cloud.sh`. Both preserve the raw-shell admission +policy. Unexpected provider names stop setup, maintenance and the direct shim +installer. The direct installer alone permits the documented inherited +`GH_TOKEN`/`OPENAI_BASE_URL` exit `2` path; that remains a failed raw boundary. +No cache, scoped profile or child scrub makes the raw parent clean. + +## Select one checkout and config home + +Setup binds its generated `~/.clinical-kb-codex-cloud.sh` and command shims to +the selected physical Git checkout root. The profile returns before changing +environment or loading nvm in any other checkout. The shims remove their own +PATH entries there and dispatch the command already available on the remaining +PATH; they fail with exit `127` when no external command exists. Nested Git +repositories and other linked worktrees have different checkout identities. + +Rerunning setup or maintenance against a generated profile bound to another +checkout fails before runtime work. Setup also refuses unmanaged runtime profiles +and symbolic-link shell startup destinations. Shim installation validates every +target and destination before staging all three wrappers. It refuses unmanaged +regular destination files and runtime targets inside the shim directory. Atomic +replacement replaces a destination symlink itself, preserving its runtime target. + +The active shell policy is written to `${CODEX_HOME:-$HOME/.codex}/config.toml`. +Unrelated settings are preserved and the managed block is replaced atomically. +An existing config destination must be a regular file; directories and symbolic +links fail before runtime or profile writes. +The shell policy applies to that entire Codex config home. Use an isolated +`HOME` **and** `CODEX_HOME` for separate legacy installations or when other tasks +need a different shell policy. Do not point setup at a shared personal home to +provision an isolated environment. + +Checkout guards protect fresh shells. A shell that already activated the legacy +profile retains its exports after changing directories; the bootstrap cannot +recover unknown earlier environment values. Use a fresh shell or a separate +scoped process when switching projects. Host proxy and CA variables are preserved +by the legacy profile; provider scrubbing keeps the existing inventory and policy. + +## Checkout base cache + +`refresh-codex-cloud-base.sh` writes the fetched merge base under +`~/.cache/clinical-kb-codex/checkouts//cloud-expected-base-sha`. +Separate repositories and linked worktrees cannot replace one another's cached +base. The generated profile ignores the old home-wide `cloud-expected-base-sha`. +Moving a checkout requires a fresh setup/base refresh. + +An explicitly supplied `CODEX_CLOUD_EXPECTED_BASE_SHA` always takes precedence, +including an empty or invalid value. Existing checkout validation must reject +invalid pins; setup must not silently repair them using a cached value. + +## Inspect historical installed artifacts + +Run `bash scripts/audit-codex-cloud-artifacts.sh --dry-run` from the selected +checkout for a bounded, provider-free inventory. It reports only known runtime +profile, command-shim, shell-startup, active Codex-config and base-cache paths. +It does not source profiles or execute Node/npm/npx. Generated markers, checkout +ownership and SHA-256 hashes help review existing files; the output never prints +their contents or credential values. + +The inventory always stays read-only. Historical installs have no trusted +hash manifest, so a generated marker alone cannot prove that a file or managed +block still matches repository output. It cannot safely authorize automatic +uninstall or reconcile. Preserve host edits and unrelated config, review exact +managed blocks before retirement, and record any separately authorized cleanup. +This command does not remove installed CLIs, dependencies, Python environments, +browser caches, host memories or files belonging to other tools. + +Source changes take effect only after the selected environment actually reruns +setup and a fresh session verifies its active config/profile. Local tests and +edited files do not establish activation on other hosts or in existing chats. diff --git a/docs/agents/pull-request-workflow.md b/docs/agents/pull-request-workflow.md index 4907c5fd6d..43b537301b 100644 --- a/docs/agents/pull-request-workflow.md +++ b/docs/agents/pull-request-workflow.md @@ -106,7 +106,7 @@ Following the PR is ordinary work: to every fix before it is pushed. Never weaken workflows or delete required checks to force green. - Look on a **slow cadence** — roughly five minutes between checks, and wait with - `ScheduleWakeup` or `Monitor` (or the tool's own terminal-event wait) rather than polling + the current host's supported event/wait mechanism rather than polling tightly. Prefer a terminal-event wait over repeated log reads; never stream logs minute-by-minute. - **Stop as soon as CI settles.** A green run ends the follow; so does a failure that is not this @@ -143,7 +143,7 @@ about sweep pacing, not about how long a session may follow its own just-opened `.claude/settings.json`) drops a session-scoped marker when a PR-creating call — `gh pr create` or any `create_pull_request` MCP tool — returns a real PR URL, and denies `CronCreate` for the rest of that session. Nothing else is restricted: `gh pr` reads, GitHub MCP PR/CI tools, -`Monitor`, `ScheduleWakeup`, committing, pushing, ledger appends, and PR create/merge are all +supported event waits, committing, pushing, ledger appends, and PR create/merge are all ordinary work. Sessions that never create a PR are untouched, so `Run PR` sweeps, `pr-ci-fix` work, and review sessions on someone else's PR still function normally. diff --git a/docs/agents/verification-gates.md b/docs/agents/verification-gates.md index 22c69cb6b9..ff9c964aa5 100644 --- a/docs/agents/verification-gates.md +++ b/docs/agents/verification-gates.md @@ -25,7 +25,7 @@ _Updated 2026-10-01 - distinguish scoped CI selection from completed verificatio | 3 — Domain gate | Shared UI/routing, dependencies, security, privacy, RAG, clinical output, production configuration, or another cross-cutting domain | The smallest applicable repository/domain selector, focused journey, or contract gate | | 4 — Broad handoff | The diff crosses multiple subsystems, cannot be bounded reliably, or the task explicitly requires PR/release confidence | One appropriate broad gate, selected rather than stacked by default | -- Do not run a broad baseline routinely before localized work, and do not select `verify:cheap` merely because a change is described as “non-trivial.” `npm run verify:cheap` is the ordinary offline gate (`check:installed-lock-parity` + `check:diff-integrity` + lint + typecheck + test). Use `npm run verify:full` when cross-module risk warrants the broad static set (42 static gates ending with `check:offline-pack`, then lint + typecheck + test). Use `npm run verify:pr-local` when a change is ready for PR handoff: it classifies the changed paths, runs focused documentation/workflow contracts for recognised low-risk scopes, and fails closed to lint, typecheck, the full unit suite, RAG fixture validation, and relevant build/domain gates for executable or unknown scope. If the diff has not changed, do not run `verify:cheap` first merely to repeat the same coverage. +- Do not run a broad baseline routinely before localized work, and do not select `verify:cheap` merely because a change is described as “non-trivial.” `npm run verify:cheap` is the ordinary offline gate (`check:installed-lock-parity` + `check:diff-integrity` + lint + typecheck + test). Use `npm run verify:full` when cross-module risk warrants the broad static set (43 static gates ending with `check:offline-pack`, then lint + typecheck + test). Use `npm run verify:pr-local` when a change is ready for PR handoff: it classifies the changed paths, runs focused documentation/workflow contracts for recognised low-risk scopes, and fails closed to lint, typecheck, the full unit suite, RAG fixture validation, and relevant build/domain gates for executable or unknown scope. If the diff has not changed, do not run `verify:cheap` first merely to repeat the same coverage. - Do not stack focused tests, full tests, typecheck, lint, build, and browser checks unless each catches a distinct plausible regression. Do not rerun an unchanged successful gate. Since 2026-08-21 that last rule is enforced rather than remembered: `scripts/gate-receipts.mjs` memoises `lint`, `typecheck` and non-coverage Vitest runs against a content signature, so an identical re-run on unchanged content exits 0 immediately instead of repeating the work. A reused receipt must be reported as "reused receipt from