forgedock 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/bin/engine/admission.mjs +57 -1
  2. package/bin/engine/admission.test.mjs +60 -0
  3. package/commands/analytics.md +3 -1
  4. package/commands/audit-agents.md +3 -1
  5. package/commands/audit.md +10 -8
  6. package/commands/autopilot.md +16 -14
  7. package/commands/ci-audit.md +4 -2
  8. package/commands/cleanup.md +1 -1
  9. package/commands/deploy-pr.md +31 -11
  10. package/commands/geo-audit.md +3 -1
  11. package/commands/incident-response.md +3 -1
  12. package/commands/issue.md +4 -2
  13. package/commands/milestone.md +8 -6
  14. package/commands/orchestrate/config.md +2 -2
  15. package/commands/orchestrate/phase-1-resolve.md +56 -12
  16. package/commands/orchestrate/phase-3-dependency.md +15 -5
  17. package/commands/orchestrate/phase-4-execution.md +593 -78
  18. package/commands/orchestrate/phase-5-cleanup.md +4 -2
  19. package/commands/orchestrate/phase-6-report.md +7 -3
  20. package/commands/orchestrate.md +13 -9
  21. package/commands/pipeline-health/phase-4-proposals.md +6 -4
  22. package/commands/pipeline-health/phase-5-report.md +4 -2
  23. package/commands/pipeline-health.md +9 -7
  24. package/commands/pipeline-resume.md +4 -2
  25. package/commands/qa-sweep.md +3 -1
  26. package/commands/quality-gate.md +4 -3
  27. package/commands/recover-orphans.md +172 -13
  28. package/commands/review-pr-agents.md +6 -4
  29. package/commands/review-pr-staging.md +13 -5
  30. package/commands/review-pr.md +187 -19
  31. package/commands/rollback.md +3 -1
  32. package/commands/security-audit.md +3 -1
  33. package/commands/signal-planner.md +6 -4
  34. package/commands/sync-ecosystem.md +4 -2
  35. package/commands/test-gate.md +4 -2
  36. package/commands/upgrade-deps.md +4 -2
  37. package/commands/validate.md +3 -1
  38. package/commands/work-on/build/architect.md +81 -19
  39. package/commands/work-on/build/context.md +61 -18
  40. package/commands/work-on/build/implement.md +62 -18
  41. package/commands/work-on/build/validate.md +240 -60
  42. package/commands/work-on/build.md +344 -146
  43. package/commands/work-on/close.md +617 -246
  44. package/commands/work-on/decompose.md +239 -41
  45. package/commands/work-on/investigate.md +377 -61
  46. package/commands/work-on/remediate.md +42 -9
  47. package/commands/work-on/review.md +240 -29
  48. package/commands/work-on-monolithic.md +48 -7
  49. package/commands/work-on.md +180 -2014
  50. package/docs/CODEX.md +2 -0
  51. package/docs/CONFIG.md +11 -0
  52. package/package.json +1 -1
  53. package/scripts/build-spec-graph.mjs +11 -4
  54. package/scripts/check-spec-markers.sh +11 -1
  55. package/scripts/checkbox-sections.test.sh +8 -23
  56. package/scripts/extract-affected-files.sh +5 -2
  57. package/scripts/extract-affected-files.test.sh +27 -0
  58. package/scripts/forge-root.test.sh +176 -0
  59. package/scripts/is-docs-only.sh +31 -0
  60. package/scripts/is-docs-only.test.sh +64 -0
  61. package/scripts/lint-dispatch-prompt.sh +217 -0
  62. package/scripts/lint-dispatch-prompt.test.sh +307 -0
  63. package/scripts/validate-spec-graph.sh +3 -2
  64. package/scripts/verify-phase-trail.sh +282 -0
  65. package/scripts/verify-phase-trail.test.sh +298 -0
  66. package/scripts/wait-ci-green.sh +98 -0
  67. package/scripts/wait-ci-green.test.sh +99 -0
@@ -1,15 +1,18 @@
1
1
  ---
2
2
  description: Pre-implementation architecture planning — traces ALL affected code paths and produces an ordered implementation plan before any code is written
3
- argument-hint: "[issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--files AFFECTED_FILES]"
3
+ argument-hint: "{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE} --files \"<space-separated>\""
4
+ context: fork
4
5
  ---
5
6
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
7
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
7
8
 
8
9
  # work-on/build/architect — Multi-Path Implementation Planning
9
10
 
10
- **Invoked by**: `work-on.md` Step 3C.6, between Context Gathering and Implement.
11
+ > **Skill Name Resolution (forked phase)**: `{FORGE_SKILL_PREFIX}` is the namespace this skill itself was invoked under — invoked as `forgedock:work-on:…` → `forgedock:` (nesting `:`); as `work-on:…` → empty (`install.sh`); as `forge-work-on-…` → `forge-` (Codex, nesting `-`); OpenCode → empty with `-` nesting. Confirm the target name in the available-skills list before calling it. A forked phase receives no resolved value from its caller; never guess, and if the target skill is not listed return BLOCKED "skill not found: <name>".
12
+
13
+ **Invoked by**: `work-on:build` (phase B4), between Context Gathering and Implement, via `Skill()`. Runs in its own forked context: you see only this spec and your args — re-read all other state from GitHub/git.
11
14
  **Time budget**: Max 3 minutes. Skip any file read that times out.
12
- **Output**: Post `<!-- FORGE:ARCHITECT -->` comment on the issue, then return structured plan to caller.
15
+ **Output**: Post `<!-- FORGE:ARCHITECT -->` comment on the issue, then print exactly one `ARCHITECT_RESULT:` block (see Result Block) as your final reply. Every exit path — success, partial, skip, resume-complete, blocked — prints it.
13
16
 
14
17
  **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet", `effort: xhigh` (deep tier — full code-path tracing, multi-file architecture planning). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` only on Claude Code >= 2.1.154.
15
18
  **NEVER use plan mode (EnterPlanMode).**
@@ -29,7 +32,7 @@ COMPLEXITY_BAND=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
29
32
  COMPLEXITY_BAND="${COMPLEXITY_BAND:-STANDARD}"
30
33
  ```
31
34
 
32
- **If COMPLEXITY_BAND: TRIVIAL** → skip all phases (A0 through A5), **post the minimal skip marker comment described in "Skip Marker" under Skip Conditions below** (reason: `TRIVIAL complexity band`), then return an empty plan to caller immediately. Do not read any files. This is not an error — trivial single-file changes have no cross-path consistency risk. The skip marker (ending in `<!-- FORGE:ARCHITECT:COMPLETE -->`) is what makes a legitimate skip observable to the headless engine's marker-only gate, so the issue advances to build instead of being stranded at `needs-human`. <!-- Added: forge#679; skip-marker on skip: forge#2689 -->
35
+ **If COMPLEXITY_BAND: TRIVIAL or INVESTIGATION** → skip all phases (A0 through A5), **post the minimal skip marker comment described in "Skip Marker" under Skip Conditions below** (reason: `TRIVIAL complexity band` or `INVESTIGATION complexity band`), then print `ARCHITECT_RESULT:` with `status: SKIPPED` immediately. Do not read any files. This is not an error — trivial single-file changes have no cross-path consistency risk. The skip marker (ending in `<!-- FORGE:ARCHITECT:COMPLETE -->`) is what makes a legitimate skip observable to the headless engine's marker-only gate, so the issue advances to build instead of being stranded at `needs-human`. <!-- Added: forge#679; skip-marker on skip: forge#2689 -->
33
36
 
34
37
  **If COMPLEXITY_BAND: STANDARD or COMPLEX** → proceed to Phase A0 below.
35
38
 
@@ -50,14 +53,16 @@ Parse from $ARGUMENTS:
50
53
  - `--repo {GH_REPO}` — GitHub repo (e.g. `{owner}/{repo}` — resolved from `forge.yaml → project`)
51
54
  - `--gh-flag {GH_FLAG}` — gh CLI repo flag (e.g. `-R {owner}/{repo}`)
52
55
  - `--repo-path {REPO_PATH}` — local filesystem path to the worktree (e.g. `/path/to/.claude/worktrees/fix/issue-121`); used by Phase A1 grep commands
53
- - `--files {AFFECTED_FILES}` — space-separated list of files from investigation report (passed by `build.md` Phase B4)
56
+ - `--files "{AFFECTED_FILES}"` — space-separated list of files from investigation report (quoted as one value; passed by `work-on:build` phase B4; empty triggers the empty-list skip)
57
+
58
+ **Fail closed**: if `{NUMBER}`, `--repo`, `--gh-flag` or `--repo-path` is missing, print `ARCHITECT_RESULT:` with `status: BLOCKED`, `comment_url:` empty and `blocker: missing arg <name>`, and stop.
54
59
 
55
- Also read from the calling context (in-memory, passed by parent agent):
60
+ You receive no state from the caller. Re-derive everything from GitHub below:
56
61
  - `{INVESTIGATION_REPORT}` — full text of FORGE:INVESTIGATOR comment
57
62
  - `{CONTEXT_BRIEFING}` — full text of FORGE:CONTEXT comment (empty string if context step was skipped)
58
63
  - `{MEMORY_PRIORS}` — structured prior run blocks emitted by investigate Phase 0.5 (empty string if no priors found or investigate predates memory). When non-empty, treat each `[MEMORY PRIOR]` block as a high-confidence prior: if any prior's affected files overlap with the current plan's affected files, explicitly note the prior's root cause and key lesson in the FORGE:ARCHITECT comment's **Prior Run Priors** section (add this section after **Context Briefing** when priors exist). <!-- Added: forge#1316 -->
59
64
 
60
- If any of these are not passed directly, read them from GitHub (fallback/recovery path):
65
+ Read them from GitHub:
61
66
 
62
67
  ```bash
63
68
  # Read investigation report
@@ -80,7 +85,7 @@ gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
80
85
  --jq '.[] | select(.body | contains("FORGE:ARCHITECT")) | .body'
81
86
  ```
82
87
 
83
- - If `<!-- FORGE:ARCHITECT -->` comment exists AND `<!-- FORGE:ARCHITECT:COMPLETE -->` is present in the SAME comment → plan already complete, return existing plan to caller, EXIT.
88
+ - If `<!-- FORGE:ARCHITECT -->` comment exists AND `<!-- FORGE:ARCHITECT:COMPLETE -->` is present in the SAME comment → plan already complete: print `ARCHITECT_RESULT:` with `status: COMPLETE` and the existing comment's `comment_url`, EXIT.
84
89
  - If `<!-- FORGE:ARCHITECT -->` comment exists BUT `<!-- FORGE:ARCHITECT:COMPLETE -->` is ABSENT → plan was interrupted, delete the partial comment and restart:
85
90
  ```bash
86
91
  COMMENT_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
@@ -435,6 +440,15 @@ Read the 3–5 most relevant files identified. Do NOT read more than 8 files tot
435
440
 
436
441
  ---
437
442
 
443
+ ## Phase A1.6: Route-Tree Classification for Shared Components *(conditional — skip if no files under `components/` are affected, except `components/ui/primitives/`)* <!-- Added: forge#381 -->
444
+
445
+ When a change adds a new hook call or context dependency to a shared component, classify ALL call sites found in A1 by route context:
446
+ 1. **Authenticated routes**: Callers under `app/dashboard/`, `app/(authenticated)/`, or any layout that wraps children with an auth provider (e.g., `UserProvider`, `SessionProvider`).
447
+ 2. **Public routes**: Callers under `app/(public)/`, `app/playground/`, `app/(marketing)/`, or any layout without the relevant provider.
448
+ 3. **If both categories have callers AND the new hook throws when its provider is absent**: add an explicit implementation step to either (a) guard the hook call with a null-context check, (b) make the hook return a safe default when called outside its provider, or (c) remove the hook from the shared component and move it to the authenticated-route-only caller. Document the split in the FORGE:ARCHITECT affected paths table. Do NOT leave a shared component that crashes public routes to be discovered by the FE review agent.
449
+
450
+ ---
451
+
438
452
  ## Phase A2: Trace the Data Flow
439
453
 
440
454
  From each entry point identified in A1, trace what happens to the data being changed:
@@ -449,10 +463,40 @@ For each step in the flow, check:
449
463
  - Is there a validation or coercion that must be updated consistently?
450
464
  - Is there a test that covers this path?
451
465
 
466
+ **For every field or key read by the changed code, enumerate ALL write paths first** — search across all services for assignments to that field. If multiple code paths write different types to the same field (e.g. dict in the standard path, string in the auth-gated path), the implementation must handle all variants. Do not assume the type you see on the primary code path is the only possible type.
467
+
452
468
  Produce a table of ALL files/functions that must change to maintain consistency.
453
469
 
454
470
  ---
455
471
 
472
+ ## Phase A2.1: Runtime UID × Volume Ownership Check *(conditional — skip if no Dockerfile or entrypoint is affected)* <!-- Added: forge#323 -->
473
+
474
+ When the PR changes the container's runtime user (Dockerfile `USER` directive, `su-exec`, `gosu`, `setuid`), the architect MUST trace the full write-path chain before writing the implementation plan:
475
+ 1. **Enumerate volume mounts**: Read all `docker-compose*.yml` files for the affected service. List every named volume and its container mount point (e.g. `storage_shared:/app/storage`). Docker named volumes are created as root-owned by default — any UID change without a corresponding ownership fix will silently break writes.
476
+ 2. **Grep for filesystem writes**: Search the affected service's codebase for all filesystem write operations (`mkdir`, `Path.mkdir`, `write_bytes`, `open(`, `os.makedirs`, `shutil.copy`, `shutil.move`). For each write operation, identify the target path.
477
+ 3. **Cross-reference**: For each write path that falls under a named volume mount point, add an explicit implementation step to ensure ownership compatibility before the privilege drop — typically `chown -R <user>:<group> <mount_point>` in the entrypoint script before the `exec su-exec` / `exec gosu` call.
478
+ 4. **Add to FORGE:ARCHITECT deliverables table**: List the entrypoint or docker-compose change as an explicit deliverable. Do NOT leave volume ownership to be discovered by the builder or reviewer.
479
+
480
+ ---
481
+
482
+ ## Phase A2.2: Gate-Condition Caller Sweep *(conditional — when the fix changes a gate condition that guards a function call or restricts a field)* <!-- Added: forge#383 -->
483
+
484
+ Before finalizing the affected-paths table, grep for all callers of the gated function across sibling files in the same service directory. For each caller, verify that the gate condition applied at the call site is semantically correct.
485
+
486
+ ```bash
487
+ # Identify the gated function from the issue/contract
488
+ GATED_FUNCTION="{function_being_called_inside_the_gate}"
489
+ SERVICE_DIR=$(dirname {PRIMARY_AFFECTED_FILE})
490
+ # Find all call sites
491
+ grep -rn "$GATED_FUNCTION" "$SERVICE_DIR" --include="*.py" | grep -v "#"
492
+ ```
493
+
494
+ For each call site found: read the surrounding gate condition (±10 lines). If the condition includes fields that do NOT require the gated resource (e.g., `extraction_schema` gated behind an LLM key check when `extraction_schema` is processed without an LLM), add that caller file to the FORGE:ARCHITECT affected-paths table with a note explaining the incorrect gate condition.
495
+
496
+ **Do NOT** omit sibling callers from the affected-paths table simply because they were not listed in the issue spec. The architect's scope is determined by code correctness, not by the issue spec's file list. A gate-condition bug that exists identically in 3 router files must be fixed in all 3 — even if the issue only named 1.
497
+
498
+ ---
499
+
456
500
  ## Phase A2.5: Pipeline Phase-Dependency Check
457
501
 
458
502
  **Skip if**: None of the `{AFFECTED_FILES}` are Forge pipeline command files (i.e., no file matches `commands/**/*.md`). When skipped, proceed directly to A3.
@@ -471,7 +515,7 @@ For each affected `commands/*.md` file, identify what it produces that other pha
471
515
 
472
516
  1. **Comment markers** — HTML comment markers the phase writes to GitHub (e.g., `<!-- FORGE:ARCHITECT -->`, `<!-- FORGE:ARCHITECT:COMPLETE -->`). Search for `<!-- FORGE:` patterns in the affected file.
473
517
  2. **Structured output blocks** — named result blocks returned to the caller (e.g., `BUILD_RESULT:`, `VALIDATE_RESULT:`, `IMPLEMENT_RESULT:`). Search for lines ending in `:` that start a return block.
474
- 3. **Skill() invocation signatures** — parameters the file passes when invoking subcommands (e.g., `Skill("work-on:build:architect", args="... --files ...")`). Search for `Skill(` calls.
518
+ 3. **Skill() invocation signatures** — parameters the file passes when invoking subcommands (e.g., `Skill("{FORGE_SKILL_PREFIX}work-on:build:architect", args="... --files ...")`). Search for `Skill(` calls.
475
519
  4. **Label state machine transitions** — `gh issue edit ... --add-label ... --remove-label ...` calls that define which label is set at the end of this phase.
476
520
 
477
521
  **P2: Find downstream phases that consume these artefacts**
@@ -668,12 +712,13 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ARCHITECT -->
668
712
 
669
713
  ## Skip Conditions
670
714
 
671
- Skip the planning work (Phases A0–A5) and return an empty plan to caller — **but always post the Skip Marker below first** — if:
715
+ Skip the planning work (Phases A0–A5) and print `ARCHITECT_RESULT:` with `status: SKIPPED` — **but always post the Skip Marker below first** — if:
672
716
  - **COMPLEXITY_BAND: TRIVIAL** — checked via FORGE:FAST_PATH comment at entry (see guard above) <!-- Primary skip path: forge#679 -->
673
717
  - Issue creates only **new files** with no callers to find (e.g. a new command file with no existing integration point yet)
674
718
  - Issue is a 1-file config or docs edit with no code logic
675
719
  - Issue title starts with "docs:" or "chore:"
676
720
  - `{AFFECTED_FILES}` is empty
721
+ - **COMPLEXITY_BAND: INVESTIGATION** — investigation tasks produce issues, not code
677
722
 
678
723
  When skipped, the builder proceeds with investigation report + context briefing only (see Integration Point below — the builder falls back gracefully on an empty plan).
679
724
 
@@ -681,7 +726,7 @@ When skipped, the builder proceeds with investigation report + context briefing
681
726
 
682
727
  **Do not post nothing.** The headless engine gate (`bin/engine/phases.mjs` → `architect.detectOutcome`) treats a **missing** `FORGE:ARCHITECT:COMPLETE` sentinel as a phase **failure**, retries to `maxAttempts` (3), then strands the issue at `needs-human`. It reads GitHub comment markers only — it cannot see this subcommand's in-process "empty plan" return value. So a skip that posts no marker is indistinguishable from a crashed architect run and deterministically strands **every** `chore:`/`docs:`/trivial issue.
683
728
 
684
- On **every** skip branch above, post exactly this minimal comment (substitute the concrete `{SKIP_REASON}`, e.g. `chore:/docs: title`, `TRIVIAL complexity band`, `new files only — no callers`, or `AFFECTED_FILES empty`) **before** returning the empty plan:
729
+ On **every** skip branch above, post exactly this minimal comment (substitute the concrete `{SKIP_REASON}`, e.g. `chore:/docs: title`, `TRIVIAL complexity band`, `new files only — no callers`, or `AFFECTED_FILES empty`) **before** printing `ARCHITECT_RESULT:`:
685
730
 
686
731
  ```bash
687
732
  # The skip marker is mandatory and un-gated by design — posting it IS the point
@@ -704,22 +749,39 @@ The empty-plan-with-marker changes no downstream behavior — the builder alread
704
749
 
705
750
  ---
706
751
 
707
- ## Integration Point in work-on.md
752
+ ## Result Block (MANDATORY on every path)
753
+
754
+ Your final reply MUST end with exactly one block, printed after the plan, partial, or skip-marker comment has been posted. `work-on:build` reads only this block.
755
+
756
+ ```
757
+ ARCHITECT_RESULT:
758
+ status: COMPLETE | PARTIAL | SKIPPED | BLOCKED
759
+ comment_url: {URL of the FORGE:ARCHITECT comment, or empty}
760
+ blocker: {description if status=BLOCKED, else empty}
761
+ ```
762
+
763
+ - `COMPLETE` — plan posted with `FORGE:ARCHITECT:COMPLETE` (also the result when the Resume Check finds a complete plan).
764
+ - `PARTIAL` — budget exceeded; `FORGE:ARCHITECT:PARTIAL` posted.
765
+ - `SKIPPED` — a skip condition applied and the Skip Marker was posted (give its URL).
766
+ - `BLOCKED` — conflicting constraints that cannot be resolved, or a missing required arg. Post a comment describing the conflict and add `needs-human` (guard with `DRY_RUN`) before printing the block.
767
+
768
+ ---
769
+
770
+ ## Integration Point
708
771
 
709
- This module runs at **Step 3C.6** — after Context Gathering, before Implement:
772
+ This module runs as build phase B4 — after Context Gathering, before Implement:
710
773
 
711
774
  ```
712
- 3C → Builder Contract posted
713
- 3C.5 → Context Gathering (FORGE:CONTEXT comment)
714
- 3C.6 → [THIS MODULE] Architecture Planning (FORGE:ARCHITECT comment)
775
+ B3 → Context Gathering (FORGE:CONTEXT comment)
776
+ B4 → [THIS MODULE] Architecture Planning (FORGE:ARCHITECT comment)
715
777
  Phase A0: Read Custom Instructions and Project Conventions (highest precedence)
716
- Phase A1: Read Entry Points
717
- Phase A2: Trace the Data Flow
778
+ Phase A1: Read Entry Points (A1.5 Prior Decision Injection, A1.6 Route-Tree Classification)
779
+ Phase A2: Trace the Data Flow (A2.1 Runtime UID x Volume Ownership, A2.2 Gate-Condition Caller Sweep)
718
780
  Phase A2.5: Pipeline Phase-Dependency Check
719
781
  Phase A3: Consistency Rules
720
782
  Phase A4: Sequence the Implementation
721
783
  Phase A5: Risk Assessment
722
- 3F → Implement (builder reads plan, implements ALL affected paths)
784
+ B5 → Implement (builder reads plan, implements ALL affected paths)
723
785
  ```
724
786
 
725
787
  The builder agent reads the `<!-- FORGE:ARCHITECT -->` comment as its **primary input** before writing any code. The raw issue body is secondary context. If the architect step was skipped, the builder falls back to investigation report + contract.
@@ -1,15 +1,16 @@
1
1
  ---
2
2
  description: Pre-implementation context gathering — surfaces historical findings, bug patterns, and related code paths before the builder writes any code
3
- argument-hint: "[issue number] [affected_files...] [--functions function_names...]"
3
+ argument-hint: "{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE} --files \"<space-separated>\" [--functions \"<names>\"]"
4
+ context: fork
4
5
  ---
5
6
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
7
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
7
8
 
8
9
  # work-on/build/context — Pre-Implementation Context Gathering
9
10
 
10
- **Invoked by**: `work-on.md` Step 3C.5, between Builder Contract and Implement.
11
+ **Invoked by**: `work-on:build` (phase B3), between Builder Contract and Architect, via `Skill()`. Runs in its own forked context: you see only this spec and your args — re-read all other state from GitHub/git.
11
12
  **Time budget**: Max 2 minutes of queries. Skip any query that times out.
12
- **Output**: Post `<!-- FORGE:CONTEXT -->` comment on the issue, then return structured briefing to caller.
13
+ **Output**: Post `<!-- FORGE:CONTEXT -->` comment on the issue, then print exactly one `CONTEXT_RESULT:` block (see Result Block) as your final reply. Every exit path — success, partial, skip, failure — prints it.
13
14
 
14
15
  <!-- FORGE:SPEC_LOADED — work-on/build/context.md loaded and active. Agent is bound by this spec. -->
15
16
 
@@ -26,7 +27,7 @@ COMPLEXITY_BAND=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
26
27
  COMPLEXITY_BAND="${COMPLEXITY_BAND:-STANDARD}"
27
28
  ```
28
29
 
29
- **If COMPLEXITY_BAND: TRIVIAL** → skip all phases (C-1 through C4), post NO comment, return empty briefing to caller immediately. Do not query GitHub, do not read files. This is not an error — trivial single-file changes have no institutional memory to surface. <!-- Added: forge#679 -->
30
+ **If COMPLEXITY_BAND: TRIVIAL or INVESTIGATION** → skip all phases (C-1 through C4), post NO comment, print `CONTEXT_RESULT:` with `status: SKIPPED` and `comment_url:` empty immediately. Do not query GitHub, do not read files. This is not an error — trivial single-file changes (and investigation tasks, which produce issues rather than code) have no institutional memory to surface. <!-- Added: forge#679 -->
30
31
 
31
32
  **If COMPLEXITY_BAND: STANDARD or COMPLEX** → proceed to Phase C-1 below.
32
33
 
@@ -47,8 +48,10 @@ Parse from `$ARGUMENTS`:
47
48
  - `--repo {GH_REPO}` — GitHub repo (e.g. `{owner}/{repo}` — resolved from `forge.yaml → project`)
48
49
  - `--gh-flag {GH_FLAG}` — gh CLI repo flag (e.g. `-R {owner}/{repo}`)
49
50
  - `--repo-path {REPO_PATH}` — local filesystem path to the worktree (e.g. `/path/to/.claude/worktrees/fix/issue-121`); used by Phase C3 grep commands
50
- - `{AFFECTED_FILES}` — space-separated file paths (positional, after `{NUMBER}`, before any `--functions` flag)
51
- - `--functions {FUNCTION_NAMES}` — space-separated function/class names extracted from the Builder Contract deliverables table (optional)
51
+ - `--files "{AFFECTED_FILES}"` — space-separated file paths (quoted as one value; an empty value is allowed and triggers the empty-list skip)
52
+ - `--functions "{FUNCTION_NAMES}"` — space-separated function/class names extracted from the Builder Contract deliverables table (optional)
53
+
54
+ **Fail closed**: if `{NUMBER}`, `--repo`, `--gh-flag` or `--repo-path` is missing, print `CONTEXT_RESULT:` with `status: SKIPPED` and `comment_url:` empty plus a one-line `note: missing arg <name>` and stop (context is advisory — a missing arg must never block the build, but it must be visible).
52
55
 
53
56
  **Graceful skip for empty FUNCTION_NAMES**: If `--functions` is absent or `{FUNCTION_NAMES}` is empty, Phase C3 produces zero for-loop iterations and is effectively skipped — no error, no output for that phase. This is expected behavior when the contract does not name specific functions.
54
57
 
@@ -674,6 +677,12 @@ If `GIST_SUMMARIES` is non-empty, it will be included in the `### Prior Investig
674
677
 
675
678
  ---
676
679
 
680
+ ## Batch Execution of C1–C4 (MANDATORY) <!-- Added: forge#1826 -->
681
+
682
+ C1, C2 (including the direct commit-body read and the pickaxe pass), C3, and C4 below are mutually independent — none consumes another's output. Issue ALL of their underlying `gh`/`git`/`grep` calls as a single batch of parallel tool calls in one message, not as four sequential steps. This is the same rule as the "independent tool calls → same message" convention used elsewhere in the pipeline; it turns a ~4x serial round-trip chain (each `gh api` call is 0.5-2s) into one wall-clock round-trip. The per-file loop inside C1 and the per-function loop inside C3 are themselves independent across iterations — include every iteration's call in the same batch rather than looping turn-by-turn. Total budget is still 20s per query / 2 min overall; batching only removes serialization, it does not change the timeout.
683
+
684
+ ---
685
+
677
686
  ## Phase C1: Past Review Findings on These Files
678
687
 
679
688
  **Primary path — Forge Ledger** (O(1) local index lookup, zero API calls): <!-- Added: forge#1732 -->
@@ -922,36 +931,70 @@ gh issue comment {NUMBER} -R {GH_REPO} --body "<!-- FORGE:CONTEXT -->
922
931
  - Each `gh issue list` call: timeout after 20s, skip if exceeded
923
932
  - Each `gh pr list` call: timeout after 20s, skip if exceeded
924
933
  - Each `grep -r` call: timeout after 10s, skip if exceeded
925
- - Total wall time budget: **2 minutes** (C-1 through C4 combined). If budget exceeded, post partial results with `<!-- FORGE:CONTEXT:PARTIAL -->` marker instead of `COMPLETE`.
934
+ - Total wall time budget: **2 minutes** (C-1 through C4 combined). If budget exceeded, post partial results with `<!-- FORGE:CONTEXT:PARTIAL -->` marker instead of `COMPLETE`, and report `status: PARTIAL` in `CONTEXT_RESULT:`.
935
+ - C1–C4 are issued as one parallel batch (see Batch Execution above) — the 2-minute budget is wall time for the batch, not per step.
926
936
 
927
937
  ---
928
938
 
929
939
  ## Skip Conditions
930
940
 
931
- Skip this entire step (post nothing, return empty briefing) if:
932
- - **COMPLEXITY_BAND: TRIVIAL** — checked via FORGE:FAST_PATH comment at entry (see guard above) <!-- Primary skip path: forge#679 -->
941
+ Skip the context gathering (print `CONTEXT_RESULT:` with `status: SKIPPED`) if:
942
+ - **COMPLEXITY_BAND: TRIVIAL or INVESTIGATION** — checked via FORGE:FAST_PATH comment at entry (see guard above); post NO comment <!-- Primary skip path: forge#679 -->
933
943
  - Issue is a 1-file config or docs edit with no code logic
934
944
  - The affected files have zero git history (new files being created)
935
945
  - `{AFFECTED_FILES}` is empty (investigation produced no file list)
936
946
 
947
+ **Minimal marker on every non-TRIVIAL skip (forge#3123)**: for STANDARD/COMPLEX the phase-trail gate (`scripts/verify-phase-trail.sh`) requires a `FORGE:CONTEXT` marker, so a skip must still post an empty marker comment (idempotent: skip if a `FORGE:CONTEXT` comment already exists), otherwise a re-run can never satisfy the gate:
948
+
949
+ ```bash
950
+ EXISTING_CONTEXT=$(gh api "repos/{GH_REPO}/issues/{NUMBER}/comments" --paginate \
951
+ --jq '[.[] | select(.body | contains("<!-- FORGE:CONTEXT -->"))] | length' \
952
+ | awk '{s+=$1} END {print s+0}')
953
+ if [ "${DRY_RUN:-false}" = "true" ]; then
954
+ echo "[DRY_RUN] would post minimal FORGE:CONTEXT marker on #{NUMBER}"
955
+ elif [ "$EXISTING_CONTEXT" -eq 0 ]; then
956
+ gh issue comment "{NUMBER}" -R "{GH_REPO}" --body "<!-- FORGE:CONTEXT -->
957
+ ## Implementation Context for #{NUMBER}
958
+
959
+ Context gathering skipped: {REASON — config/docs-only edit | new files, no git history | empty affected-file list}. No institutional memory to surface.
960
+
961
+ <!-- FORGE:CONTEXT:COMPLETE -->"
962
+ fi
963
+ ```
964
+
965
+ ---
966
+
967
+ ## Result Block (MANDATORY on every path)
968
+
969
+ Your final reply MUST end with exactly one block, printed after any marker comment has been posted. `work-on:build` reads only this block.
970
+
971
+ ```
972
+ CONTEXT_RESULT:
973
+ status: COMPLETE | PARTIAL | SKIPPED
974
+ comment_url: {URL of the FORGE:CONTEXT comment, or empty when no comment was posted}
975
+ ```
976
+
977
+ - `COMPLETE` — full `FORGE:CONTEXT:COMPLETE` comment posted.
978
+ - `PARTIAL` — budget exceeded or a source errored; `FORGE:CONTEXT:PARTIAL` posted.
979
+ - `SKIPPED` — any skip condition (TRIVIAL/INVESTIGATION band: no comment; other skips: minimal marker comment, give its URL if you posted or found one).
980
+ - An error, timeout or missing arg is never a reason to omit the block and never blocks the build.
981
+
937
982
  ---
938
983
 
939
- ## Integration Point in work-on.md
984
+ ## Integration Point
940
985
 
941
- This module runs at **Step 3C.5** — after Builder Contract is posted, before Implement:
986
+ This module runs as build phase B3 — after the Builder Contract is posted, before Architect:
942
987
 
943
988
  ```
944
- 3C → Builder Contract posted
945
- 3C.5 → [THIS MODULE] Context gathering (max 2 min)
989
+ B2 → Builder Contract posted
990
+ B3 → [THIS MODULE] Context gathering (max 2 min)
946
991
  Phase C-1: Authoritative Devdocs (project-resident knowledge — highest precedence)
947
992
  Phase C-0.5: Active Peer Claims Reader (conditional — orchestration only)
948
993
  Phase C0.5: Danger-Zone Rule Cards (fixed 400-token slot — forge#1744)
949
994
  Phase C0: Prior Investigation Findings (from Gists)
950
- Phase C1: Past Review Findings on These Files
951
- Phase C2: Past Bugs in the Same Module
952
- Phase C3: Related Code Paths
953
- Phase C4: Successful Similar Implementations
954
- 3F → Implement (builder now has context briefing)
995
+ Phases C1–C4: batched in parallel (Past Review Findings, Past Bugs, Related Code Paths, Similar Implementations)
996
+ B4 → Architect
997
+ B5 → Implement (builder now has context briefing)
955
998
  ```
956
999
 
957
1000
  The builder agent reads the `<!-- FORGE:CONTEXT -->` comment before writing any code. If the context step was skipped, the builder proceeds with investigation report + contract only.
@@ -1,16 +1,21 @@
1
1
  ---
2
2
  description: Implementation agent — writes code, makes commits, posts builder comment
3
- argument-hint: "[issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--worktree PATH] [--branch BRANCH]"
3
+ argument-hint: "{NUMBER} --repo {GH_REPO} --gh-flag \"{GH_FLAG}\" --worktree {WORKTREE} --branch {BRANCH} --base {PR_BASE} [--fix-acceptance \"<failed checks>\"]"
4
+ context: fork
4
5
  ---
5
6
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
7
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
7
8
 
8
9
  # work-on/build/implement — Implementation Subcommand
9
10
 
11
+ > **Skill Name Resolution (forked phase)**: `{FORGE_SKILL_PREFIX}` is the namespace this skill itself was invoked under — invoked as `forgedock:work-on:…` → `forgedock:` (nesting `:`); as `work-on:…` → empty (`install.sh`); as `forge-work-on-…` → `forge-` (Codex, nesting `-`); OpenCode → empty with `-` nesting. Confirm the target name in the available-skills list before calling it. A forked phase receives no resolved value from its caller; never guess, and if the target skill is not listed return BLOCKED "skill not found: <name>".
12
+
10
13
  **Input**: $ARGUMENTS
11
14
 
12
- **Invoked by**: `work-on.md` Step 3F, after worktree is created and context is gathered.
13
- **Output**: Write code, commit(s), post `<!-- FORGE:BUILDER -->` comment, return result to caller.
15
+ **Invoked by**: `work-on:build`, after the worktree is created and context is gathered. This skill runs in an isolated forked context: it sees only this text and its args, so it re-reads all other state from GitHub/git.
16
+ **Output**: Write code, stage changes, post `<!-- FORGE:BUILDER -->` comment, and end with exactly one `IMPLEMENT_RESULT:` block as the final reply.
17
+
18
+ **Result-on-every-exit rule**: every exit path — success, `ALREADY_DONE`, `INVESTIGATION_COMPLETE`, every guard, every failure — MUST print the `IMPLEMENT_RESULT:` block (see Output) as the final reply. Never exit with a bare `exit 1` or free text. Failures use `status: BLOCKED` with a `blocker` line.
14
19
 
15
20
  **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
16
21
  **NEVER use plan mode (EnterPlanMode).**
@@ -25,11 +30,18 @@ Parse from $ARGUMENTS:
25
30
  - `{NUMBER}` — issue number (required)
26
31
  - `--repo {GH_REPO}` — GitHub repo (e.g. `{owner}/{repo}` — resolved from `forge.yaml → project`)
27
32
  - `--gh-flag {GH_FLAG}` — gh CLI repo flag (e.g. `-R {owner}/{repo}`)
28
- - `--worktree {WORKTREE_PATH}` — absolute path to the git worktree (set by caller)
29
- - `--branch {BRANCH}` — feature branch name (e.g. `feat/my-feature`)
33
+ - `--worktree {WORKTREE_PATH}` — absolute path to the git worktree (required)
34
+ - `--branch {BRANCH}` — feature branch name (e.g. `feat/my-feature`) (required)
35
+ - `--base {PR_BASE}` — PR base branch name without the `origin/` prefix (required; computed by the router and passed down; used by the cross-lane import guard and migration collision check)
36
+
37
+ **Fail closed**: if `{NUMBER}`, `--repo`, `--worktree`, `--branch`, or `--base` is missing or empty, or `{WORKTREE_PATH}` is not an existing directory, print `IMPLEMENT_RESULT:` with `status: BLOCKED` and `blocker: missing or invalid arg: <name>` and STOP. `{GH_FLAG}` defaults to `-R {GH_REPO}` when absent.
38
+
39
+ **Derived values**: `{CHANGED_FILES}` (used below) is the space-separated list of files you changed in the worktree — derive it with `git -C {WORKTREE_PATH} diff --name-only HEAD` plus any untracked files from `git -C {WORKTREE_PATH} ls-files --others --exclude-standard`. `{GH_REPO}` and `{GH_FLAG}` as parsed above.
30
40
 
31
41
  ---
32
42
 
43
+ - `--fix-acceptance "<failed checks>"` — optional. Set only by build B6.5 after a failed acceptance gate. In this mode, skip I1–I2 planning: read the listed failing `ACCEPTANCE_CHECK` ids from the FORGE:INVESTIGATOR comment, change the code in `{WORKTREE_PATH}` until each check's target/matcher holds (do not edit the checks), update the BUILDER comment's `### Changes` list, and return `IMPLEMENT_RESULT: status: COMPLETE`. If a check is impossible as written (contradicts the contract), return `status: BLOCKED` with that reason.
44
+
33
45
  ## Phase I1: Load Context from GitHub
34
46
 
35
47
  Read the full context chain before writing a single line of code:
@@ -56,7 +68,7 @@ gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
56
68
  ```
57
69
 
58
70
  **Resume check**:
59
- - If `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment → implementation is done — EXIT and return `IMPLEMENT_RESULT: status: ALREADY_DONE` to caller.
71
+ - If `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment → implementation is done — EXIT and print `IMPLEMENT_RESULT: status: ALREADY_DONE` as the final reply.
60
72
  - If `<!-- FORGE:BUILDER -->` exists BUT `<!-- FORGE:BUILDER:COMPLETE -->` is ABSENT → implementation was interrupted after the comment was posted but before the commit (validate.md V5). Delete the partial comment and restart from Phase I2:
61
73
  ```bash
62
74
  PARTIAL_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
@@ -102,7 +114,7 @@ Extract from contract:
102
114
  | Refactor / Maintenance | Implement directly following contract deliverables |
103
115
  | Investigation | Spawn research agents, create GitHub issues for findings, skip to I5 |
104
116
 
105
- **Investigation task special case**: Research deeply, create GitHub issues for each finding using the Pipeline Issue Template (see `commands/issue.md` § "Pipeline Issue Template"). Each issue MUST include `## Problem`, `## Affected Files`, and `## Acceptance Criteria`. Create each issue via the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` § "Programmatic Invocation Contract") — `Skill(skill="issue", args="--title \"...\" --body-file <path> --label ...")` — instead of calling the raw issue-creation command directly; this gets dedup and body validation for free. Post a deliverables comment listing the created issues, close the original issue, return `IMPLEMENT_RESULT: status: INVESTIGATION_COMPLETE`.
117
+ **Investigation task special case**: Research deeply, create GitHub issues for each finding using the Pipeline Issue Template (see `commands/issue.md` § "Pipeline Issue Template"). Each issue MUST include `## Problem`, `## Affected Files`, and `## Acceptance Criteria`. Create each issue via the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` § "Programmatic Invocation Contract") — `Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"...\" --body-file <path> --label ...")` — instead of calling the raw issue-creation command directly; this gets dedup and body validation for free. Post a deliverables comment listing the created issues, close the original issue, and print `IMPLEMENT_RESULT: status: INVESTIGATION_COMPLETE` as the final reply (the build skill passes this up and the router skips review). <!-- Added: forge#2090 -->
106
118
 
107
119
  ---
108
120
 
@@ -118,8 +130,12 @@ Work in `{WORKTREE_PATH}`. Follow the contract deliverables table exactly — im
118
130
  - **Library callback verification**: When writing a lambda or callable that will be passed to a library/framework parameter (e.g., `prepared_statement_name_func=lambda: ""`, `key=lambda x: ...`), you MUST verify the expected calling convention BEFORE writing it. Check the library's default value for that parameter, its documentation, or its source code. A lambda with wrong arity causes `TypeError` at runtime — this is invisible to static analysis and linting. The P0 incident from PR #14391 was caused by `lambda _: ""` (1 arg) passed where SQLAlchemy expects 0 args.
119
131
  - **Worktree-aware path derivation**: When writing shell code that derives repository paths, ALWAYS use `git rev-parse --show-toplevel` for the repo root in regular checkouts, or `git rev-parse --git-common-dir` (then `dirname`) to get the shared `.git` directory when the context may be a linked worktree. For worktree cleanup code specifically, ALWAYS use `--git-common-dir` — `--show-toplevel` returns the worktree path itself, NOT the main repo root. NEVER use `pwd`, relative paths, or `dirname` chains on `--show-toplevel` output for repo root derivation. Test path logic for both regular checkouts and linked worktrees. (Ref: review-findings #104, #105 — 4 PATH_DERIVATION defects, 15% of all review findings)
120
132
  - **State machine completeness verification**: When implementing routing logic, state transition tables, or phase dispatch code (e.g., adding a `Skill()` call in a routing loop, adding a new phase to a state machine, creating a new subcommand file), you MUST run three checks BEFORE staging: (1) **Routing target existence** — for each `Skill("subcommand", ...)` call or file reference in routing logic, verify the target file exists at `commands/{subcommand-path}.md`; (2) **State reachability** — for each declared state or phase, verify at least one prior state or entry condition in the router routes to it; (3) **Subcommand invocation wiring** — for each `commands/work-on/*.md` file that declares its invocation condition (e.g., `Invoked by work-on.md routing loop, when X`), verify that condition is actually handled in the router. A missing target file, an unreachable state, or a declared-but-unwired subcommand will not surface until review. (Ref: review-findings #85, #116, #137 — 4 ROUTING defects, 15% of all review findings)
121
- - **Migration safety checklist** *(trigger: diff includes `*.sql` files or files under a `migrations/` path)*: When any migration file is in the diff, verify ALL of the following BEFORE staging. Fix each violation inline — do not defer to review or the quality gate: (a) **Rollback file**: a corresponding down/rollback migration file exists (e.g. `0042_down_*.sql`, `rollback_*.sql`) or the migration is explicitly self-reversing (DROP of a previously-added column); (b) **NOT NULL safety**: any `ADD COLUMN ... NOT NULL` either includes a `DEFAULT` clause or is preceded by a backfill step — a NOT NULL column without DEFAULT locks the table and fails on existing rows; (c) **Constraint name consistency**: constraint names in the migration match ORM model declarations — a mismatch causes `alembic stamp` and FK introspection to silently diverge; (d) **CREATE TRIGGER idempotency**: every `CREATE TRIGGER` uses `CREATE OR REPLACE TRIGGER` or is preceded by `DROP TRIGGER IF EXISTS` — a bare `CREATE TRIGGER` fails on re-run in test and CI environments; (e) **Migration prefix uniqueness**: confirm the new file's numeric prefix does not already exist in the `infra/migrations/` tree (`ls infra/migrations/*.sql | grep -oP '^\d+' | sort | uniq -d` prints duplicates) — a duplicate prefix hard-fails the deploy gate regardless of file content. <!-- Added: forge#373 -->
122
- - If you discover the contract is wrong (e.g. a file doesn't exist, a function has a different signature): STOP, post a comment on the issue explaining the discrepancy, add label `needs-human`, and EXIT
133
+ - **Migration safety checklist** *(trigger: diff includes `*.sql` files or files under a `migrations/` path)*: When any migration file is in the diff, verify ALL of the following BEFORE staging. Fix each violation inline — do not defer to review or the quality gate: (a) **Rollback file**: a corresponding down/rollback migration file exists (e.g. `0042_down_*.sql`, `rollback_*.sql`) or the migration is explicitly self-reversing (DROP of a previously-added column); (b) **NOT NULL safety**: any `ADD COLUMN ... NOT NULL` either includes a `DEFAULT` clause or is preceded by a backfill step — a NOT NULL column without DEFAULT locks the table and fails on existing rows; (c) **Constraint name consistency**: constraint names in the migration match ORM model declarations — a mismatch causes `alembic stamp` and FK introspection to silently diverge; (d) **CREATE TRIGGER idempotency**: every `CREATE TRIGGER` uses `CREATE OR REPLACE TRIGGER` or is preceded by `DROP TRIGGER IF EXISTS` — a bare `CREATE TRIGGER` fails on re-run in test and CI environments; (e) **Migration prefix uniqueness**: confirm the new file's numeric prefix does not already exist in the `infra/migrations/` tree (`ls infra/migrations/*.sql | xargs -n1 basename | grep -oE '^[0-9]+' | sort | uniq -d` prints duplicates) — a duplicate prefix hard-fails the deploy gate regardless of file content. <!-- Added: forge#373 -->
134
+ - **Cross-lane import guard**: Before adding any `import` or `from X import Y` statement for a service-internal module (`app.*`), verify the module exists on the PR's base branch (`{PR_BASE}`) — NOT just on your local disk or a milestone branch. Run `git show origin/{PR_BASE}:{module_path}.py` (replacing dots with slashes) to confirm. If the module only exists on a milestone branch, do NOT import it — find an alternative implementation or make the import conditional with a `try/except ImportError` fallback. A milestone-only import on a fast-lane PR will crash production on every request with `ModuleNotFoundError`. <!-- Added: forge#277 -->
135
+ - **Deliverable-type consistency check**: Before committing, compare the actual output against the CONTRACT's deliverable list. If the CONTRACT explicitly states "no code changes required", "docs only", or "configuration update only" AND the diff introduces new executable files (`.py`, `.js`, `.ts`, `.sh` — not test, config, or documentation files), STOP. Do NOT commit. Re-read the CONTRACT, the investigation recommendation, and the ARCHITECT plan. If all three agree that code is needed, update the CONTRACT comment to reflect the new deliverable type before proceeding. If only the builder decided to add code without contract support, discard the code change and implement the contracted deliverable instead. <!-- Added: forge#279 -->
136
+ - **Pipeline check documentation — generalization rule**: When writing or updating pipeline check documentation (in `commands/*.md`), describe the **bug class**, not a specific incident. Do NOT embed: PR numbers, issue numbers, run IDs, timestamps, function names, dollar amounts, or multi-sentence incident timelines in check prose or `**Evidence**:` blocks. One brief HTML comment `<!-- Added: forge#NNN -->` is acceptable per check for traceability. `**Evidence**:` blocks must describe the vulnerability pattern (what the class of bug looks like, why it's dangerous) — not narrate a single historical occurrence. CHANGELOG entries may reference originating issues; command prompt text must not.
137
+ - **Endpoint response contract consumer tracing**: When changing an endpoint's response body shape or status field values (e.g., changing `"status": "healthy"` to `"status": "ok"`, renaming response keys, removing fields), grep the full repo for ALL consumers of that response body before committing. Consumers are not limited to the service being changed — they include deploy scripts, CI health checks, monitoring configs, docker-compose healthcheck definitions, Traefik probes, and any script that parses or pattern-matches on the response body. Run: `grep -rn "{old_value}\|{endpoint_path}" scripts/ infra/ .github/ docker-compose*.yml traefik/ 2>/dev/null`. All consumers whose behavior depends on the old response format MUST be updated in the same PR — a response contract change that updates only one consumer while leaving others on the old format is a deploy-time breakage. <!-- Added: forge#321 -->
138
+ - If you discover the contract is wrong (e.g. a file doesn't exist, a function has a different signature): STOP, post a comment on the issue explaining the discrepancy, add label `needs-human`, and EXIT with `IMPLEMENT_RESULT: status: BLOCKED` and a `blocker` describing the discrepancy
123
139
 
124
140
  **Worktree working directory**:
125
141
  ```bash
@@ -143,7 +159,7 @@ Scan changed files for newly introduced env var references:
143
159
  cd {WORKTREE_PATH}
144
160
  # Collect all env var names referenced in changed files
145
161
  NEW_ENV_VARS=$(grep -rnE "os\.getenv\(|process\.env\." {CHANGED_FILES} 2>/dev/null \
146
- | grep -oP "(os\.getenv\(['\"]|process\.env\.)(\w+)" \
162
+ | grep -oE "(os\.getenv\(['\"]|process\.env\.)[A-Za-z0-9_]+" \
147
163
  | sed "s/os\.getenv\(['\"]//; s/process\.env\.//" \
148
164
  | sort -u)
149
165
 
@@ -203,6 +219,33 @@ grep -rnE "(api_key|secret|password|token|credential)\s*=\s*(f?['\"]|\`)[^{'\"\`
203
219
 
204
220
  **If HARDCODED IP or HARDCODED CREDENTIAL is printed**: replace the literal value with a config reference before proceeding to Phase I4. This is a hard blocker — do not stage hardcoded secrets or IPs.
205
221
 
222
+ ### Check 4 — SDK/API Literal sync advisory
223
+
224
+ **Trigger**: the diff contains `Literal[` in a schema file.
225
+
226
+ ```bash
227
+ # Detect Literal type changes in API schema files
228
+ cd {WORKTREE_PATH}
229
+ LITERAL_CHANGES=$(git diff HEAD -- | grep -E '^\+.*Literal\[' | grep -v '^\+\+\+')
230
+ if [ -n "$LITERAL_CHANGES" ]; then
231
+ echo "SDK SYNC ADVISORY: Literal type changed in schema."
232
+ echo "Changed Literal lines:"
233
+ echo "$LITERAL_CHANGES"
234
+ echo ""
235
+ echo "ACTION REQUIRED — verify SDK method/type lists match new API schema:"
236
+ echo " - sdk/python/*/client.py: check _valid_methods or equivalent list"
237
+ echo " - sdk/node/src/index.ts: check JSDoc @param Literal type annotation"
238
+ echo " - web/public/openapi*.json: check enum arrays for affected field"
239
+ echo " - web/public/openapi-versions/*.json: check all versioned specs"
240
+ echo ""
241
+ echo "Inconsistency example: API schema narrows Literal['GET','POST','PUT','PATCH','DELETE']"
242
+ echo "to Literal['GET','POST'] but SDK JSDoc still lists all 5 methods — API returns 422"
243
+ echo "for callers following SDK docs. This produces silent user-facing failures."
244
+ fi
245
+ ```
246
+
247
+ This advisory is informational — it does NOT block the commit. But the implementer MUST check each listed file and add to the implementation scope if any SDK/spec file still documents the removed/changed Literal values. If SDK files need changes, add them to the current PR rather than leaving the inconsistency for review to catch.
248
+
206
249
  ---
207
250
 
208
251
  ## Phase I4: Stage Changes
@@ -214,7 +257,7 @@ Migration collision check (if applicable):
214
257
  git fetch origin
215
258
  git log --oneline origin/{PR_BASE}..HEAD -- {MIGRATION_PATHS}
216
259
  ```
217
- If a collision is detected, post a comment and add `needs-human` label before staging.
260
+ If a collision is detected, post a comment, add `needs-human` label before staging, and EXIT with `IMPLEMENT_RESULT: status: BLOCKED` (`blocker: migration collision`).
218
261
 
219
262
  Stage the changed files:
220
263
  ```bash
@@ -272,7 +315,7 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
272
315
 
273
316
  ## Output
274
317
 
275
- The subcommand writes its results to GitHub (FORGE:BUILDER comment). Return structured output to the caller:
318
+ The subcommand writes its results to GitHub (FORGE:BUILDER comment). The final reply is exactly one `IMPLEMENT_RESULT:` block — on every exit path (use `status: BLOCKED` + `blocker` for failures; fields not applicable may be empty):
276
319
 
277
320
  ```
278
321
  IMPLEMENT_RESULT:
@@ -286,15 +329,16 @@ IMPLEMENT_RESULT:
286
329
 
287
330
  ---
288
331
 
289
- ## Integration Point in work-on.md
332
+ ## Integration Point
290
333
 
291
- This module runs at **Step 3F** — after worktree creation and context gathering, before validate:
334
+ This module is invoked by `work-on:build` after the worktree exists and context/architect are posted, and before validate:
292
335
 
293
336
  ```
294
- 3E → Worktree created (by router)
295
- 3C.5 → Context gathered (by context.md)
296
- 3F → [THIS MODULE] Implement — code written, staged (not committed)
297
- 3F.5 → Validate (by validate.md) — gate loop runs; V5 commit happens here after GATE_PASSED=true
337
+ build (worktree + contract)
338
+ → context (work-on:build:context)
339
+ → architect (work-on:build:architect)
340
+ → [THIS MODULE] implement — Phases I1–I6: code written, staged (not committed), FORGE:BUILDER posted
341
+ → validate (work-on:build:validate) — gate loop; the single commit happens in validate Phase V5 after the gate passes
298
342
  ```
299
343
 
300
344
  The validate subcommand reads the staged diff produced by this module, runs the gate, and commits only after the gate passes.