forgedock 1.1.1 → 1.1.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 (171) hide show
  1. package/README.md +53 -8
  2. package/bin/batch-runner.mjs +339 -0
  3. package/bin/demo.mjs +92 -7
  4. package/bin/engine/phases.mjs +20 -0
  5. package/bin/engine/state.mjs +8 -3
  6. package/bin/engine-cli.mjs +177 -5
  7. package/bin/forgedock.mjs +409 -26
  8. package/bin/hooks/interactive-engine.mjs +509 -0
  9. package/bin/hooks/pre-tool-use.mjs +498 -0
  10. package/bin/hooks/subagent-stop-enforce.mjs +197 -0
  11. package/bin/journey.mjs +72 -14
  12. package/bin/registry.mjs +139 -0
  13. package/bin/report.mjs +447 -0
  14. package/bin/runner.mjs +68 -19
  15. package/bin/settings-hook.mjs +235 -1
  16. package/commands/adopt.md +4 -1
  17. package/commands/analytics.md +4 -1
  18. package/commands/audit-agents.md +110 -27
  19. package/commands/audit.md +20 -1
  20. package/commands/autopilot.md +80 -8
  21. package/commands/changelog.md +2 -1
  22. package/commands/ci-audit.md +2 -1
  23. package/commands/cleanup.md +161 -1
  24. package/commands/compat-audit.md +2 -1
  25. package/commands/deploy-info.md +58 -1
  26. package/commands/design-bench.md +5 -1
  27. package/commands/design-render-critique-loop.md +1 -1
  28. package/commands/diagnose.md +2 -1
  29. package/commands/explain.md +2 -1
  30. package/commands/forge-stats.md +121 -2
  31. package/commands/forgedock-init.md +1 -1
  32. package/commands/geo-audit.md +3 -0
  33. package/commands/incident-response.md +2 -1
  34. package/commands/issue.md +23 -2
  35. package/commands/milestone.md +77 -12
  36. package/commands/optimize.md +2 -1
  37. package/commands/orchestrate/config.md +67 -0
  38. package/commands/orchestrate/phase-1-resolve.md +159 -0
  39. package/commands/orchestrate/phase-2-triage.md +126 -0
  40. package/commands/orchestrate/phase-2.5-synthesis.md +156 -0
  41. package/commands/orchestrate/phase-3-dependency.md +591 -0
  42. package/commands/orchestrate/phase-4-execution.md +962 -0
  43. package/commands/orchestrate/phase-5-cleanup.md +47 -0
  44. package/commands/orchestrate/phase-6-report.md +198 -0
  45. package/commands/orchestrate/safety.md +62 -0
  46. package/commands/orchestrate.md +25 -2045
  47. package/commands/pipeline-health/config.md +34 -0
  48. package/commands/pipeline-health/phase-1-context.md +180 -0
  49. package/commands/pipeline-health/phase-2-metrics.md +1729 -0
  50. package/commands/pipeline-health/phase-3-analyze.md +204 -0
  51. package/commands/pipeline-health/phase-4-proposals.md +117 -0
  52. package/commands/pipeline-health/phase-5-report.md +284 -0
  53. package/commands/pipeline-health/phase-6-summary.md +52 -0
  54. package/commands/pipeline-health.md +25 -2401
  55. package/commands/pipeline-resume.md +24 -1
  56. package/commands/pipeline-status.md +65 -0
  57. package/commands/qa-sweep.md +4 -1
  58. package/commands/quality-gate.md +248 -10
  59. package/commands/replay.md +2 -1
  60. package/commands/review-pr-agents/api.md +135 -0
  61. package/commands/review-pr-agents/auth.md +137 -0
  62. package/commands/review-pr-agents/billing.md +101 -0
  63. package/commands/review-pr-agents/concurrency.md +145 -0
  64. package/commands/review-pr-agents/database.md +103 -0
  65. package/commands/review-pr-agents/frontend.md +89 -0
  66. package/commands/review-pr-agents/infra.md +385 -0
  67. package/commands/review-pr-agents/protocols.md +185 -0
  68. package/commands/review-pr-agents/scraper.md +185 -0
  69. package/commands/review-pr-agents/security.md +406 -0
  70. package/commands/review-pr-agents.md +45 -1722
  71. package/commands/review-pr-staging.md +196 -40
  72. package/commands/review-pr.md +591 -52
  73. package/commands/rollback.md +2 -1
  74. package/commands/scope.md +2 -1
  75. package/commands/security-audit.md +2 -1
  76. package/commands/signal-planner.md +623 -0
  77. package/commands/sync-ecosystem.md +1 -0
  78. package/commands/test-gate.md +17 -1
  79. package/commands/validate.md +2 -1
  80. package/commands/work-on/build/architect.md +28 -0
  81. package/commands/work-on/build/context.md +2 -0
  82. package/commands/work-on/build/implement.md +3 -1
  83. package/commands/work-on/build/validate.md +26 -7
  84. package/commands/work-on/build.md +163 -24
  85. package/commands/work-on/close.md +130 -3
  86. package/commands/work-on/decompose.md +19 -1
  87. package/commands/work-on/investigate.md +117 -2
  88. package/commands/work-on/review.md +24 -4
  89. package/commands/work-on-monolithic.md +7 -2
  90. package/commands/work-on.md +202 -4
  91. package/docs/BRAND.md +14 -7
  92. package/docs/CI.md +2 -2
  93. package/docs/CODEX.md +36 -1
  94. package/docs/CONFIG.md +5 -8
  95. package/docs/FORGE-PROTOCOL.md +15 -1064
  96. package/docs/anthropic-devrel-outreach.md +2 -2
  97. package/docs/articles/command-decomposition-token-savings.md +214 -0
  98. package/docs/articles/model-release-playbook.md +131 -0
  99. package/docs/articles/per-repo-adaptive-scripts-token-savings.md +27 -25
  100. package/docs/claude-breakpoints.json +75 -5
  101. package/docs/design/abc-benchmark.md +1 -1
  102. package/docs/design/design-spec-schema.md +1 -1
  103. package/docs/design/divergent-generation.md +1 -1
  104. package/docs/design/eval-gate-runbook.md +178 -0
  105. package/docs/design/show-hn-draft.md +26 -1
  106. package/docs/design/token-cost-baseline-1298.md +59 -0
  107. package/docs/eval/README.md +117 -0
  108. package/docs/eval/corpus/seed-v1.json +6 -0
  109. package/docs/eval/published/claude-sonnet-4-6-2026-07-04.json +33 -0
  110. package/docs/site/annotations-explained.md +24 -0
  111. package/docs/site/command-learning-path.md +51 -14
  112. package/docs/site/command-reference.md +339 -113
  113. package/docs/site/for-companies.md +95 -0
  114. package/docs/site/forge-annotation-protocol.md +12 -256
  115. package/docs/site/getting-started.md +43 -4
  116. package/docs/site/github-is-the-memory.md +113 -0
  117. package/docs/site/how-it-works.md +3 -13
  118. package/docs/site/index.md +9 -1
  119. package/docs/spec/eval-run-result.md +147 -0
  120. package/docs/spec/forge-protocol-v1.md +441 -18
  121. package/docs/spec/label-state-machine.md +55 -0
  122. package/docs/spec/review-protocol.md +158 -0
  123. package/docs/spec-graph-schema.md +28 -0
  124. package/docs/superpowers/specs/2026-07-04-economic-self-governance-design.md +181 -0
  125. package/docs/superpowers/specs/2026-07-04-provenance-trail-design.md +189 -0
  126. package/examples/forgedock-demo/README.md +41 -22
  127. package/examples/forgedock-demo/issues/06-bug-tags-type-validation.md +43 -0
  128. package/examples/forgedock-demo/issues/07-bug-negative-offset-pagination.md +42 -0
  129. package/examples/forgedock-demo/issues/08-bug-patch-mass-assignment.md +49 -0
  130. package/examples/forgedock-demo/issues/09-bug-count-injection.md +47 -0
  131. package/examples/forgedock-demo/issues/10-feature-sort-param.md +39 -0
  132. package/examples/forgedock-demo/issues/11-feature-patch-tags.md +37 -0
  133. package/examples/forgedock-demo/issues/12-feature-bulk-create.md +42 -0
  134. package/examples/forgedock-demo/issues/13-feature-archive-notes.md +46 -0
  135. package/examples/forgedock-demo/issues/14-refactor-extract-title-validation.md +41 -0
  136. package/examples/forgedock-demo/issues/15-refactor-protected-route-flag.md +43 -0
  137. package/examples/forgedock-demo/issues/16-refactor-extract-store.md +45 -0
  138. package/examples/forgedock-demo/issues/17-perf-tags-dedup-set.md +35 -0
  139. package/examples/forgedock-demo/issues/18-perf-remove-redundant-sort.md +37 -0
  140. package/examples/forgedock-demo/issues/19-docs-api-reference-full.md +53 -0
  141. package/examples/forgedock-demo/issues/20-docs-intentional-flaw-convention.md +46 -0
  142. package/examples/forgedock-demo/scripts/smoke.js +18 -1
  143. package/examples/forgedock-demo/src/db.js +36 -11
  144. package/examples/forgedock-demo/src/routes/notes.js +106 -9
  145. package/examples/forgedock-demo/src/server.js +4 -1
  146. package/package.json +3 -1
  147. package/packages/protocol/LICENSE +21 -0
  148. package/packages/protocol/src/cli.js +135 -0
  149. package/packages/protocol/src/emit.js +146 -0
  150. package/packages/protocol/src/index.js +43 -0
  151. package/packages/protocol/src/parse.js +229 -0
  152. package/packages/protocol/src/types.js +171 -0
  153. package/packages/protocol/src/validate.js +122 -0
  154. package/scripts/bench-topology-cost.mjs +372 -0
  155. package/scripts/check-command-docs-drift.sh +198 -0
  156. package/scripts/check-protocol-restatements.sh +176 -0
  157. package/scripts/check-registry/README.md +72 -0
  158. package/scripts/check-registry/manifest.json +6 -0
  159. package/scripts/code-index.sh +454 -0
  160. package/scripts/conformance-check.mjs +168 -0
  161. package/scripts/doctor-pipeline-state.sh +484 -0
  162. package/scripts/eval-gate-baseline.json +17 -0
  163. package/scripts/eval-gate-scorecard.mjs +242 -0
  164. package/scripts/eval-scorecard-aggregator.mjs +252 -0
  165. package/scripts/flaky-quarantine.sh +241 -0
  166. package/scripts/forge-annotation.sh +567 -0
  167. package/scripts/issue-dedup.sh +236 -0
  168. package/scripts/validate-annotation-node.mjs +233 -0
  169. package/scripts/worktree-lifecycle.sh +3 -3
  170. package/templates/devdocs/agent/using-forgedock.md +14 -0
  171. package/docs/site/.vitepress/config.mjs +0 -85
@@ -2,6 +2,7 @@
2
2
  description: Deterministic deploy-gate — verify a staging→main bundle's acceptance criteria against running code before deploy
3
3
  argument-hint: [--prs "<N1 N2 ...>"] [--base <branch>]
4
4
  allowed-tools: Task, Bash, Read, Grep, Glob
5
+ install: extras
5
6
  ---
6
7
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
7
8
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -12,7 +13,22 @@ allowed-tools: Task, Bash, Read, Grep, Glob
12
13
 
13
14
  Verifies a staging→main bundle's acceptance criteria against running code before deploy. Called by `/review-pr-staging` (Phase 6.5) with the bundle PRs, or run standalone when `--prs` is absent (computes the bundle itself). Returns a machine-readable BLOCK / PASS / SKIP verdict for the caller to consume.
14
15
 
15
- **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
16
+ **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
17
+ **NEVER use plan mode (EnterPlanMode).**
18
+ **NEVER use the Agent tool** — test-gate dispatches test clusters via `Task` only. The Agent tool bypasses the allowed-tools constraint and produces output that cannot be structured into the machine-readable BLOCK / PASS / SKIP verdict.
19
+
20
+ <!-- FORGE:SPEC_LOADED — test-gate.md loaded and active. Agent is bound by this spec. -->
21
+
22
+ ## Forbidden Tools Self-Check
23
+
24
+ **Before executing any phase**, verify you are NOT using any of these tools:
25
+
26
+ | Tool | Status | Reason |
27
+ |------|--------|--------|
28
+ | `Agent` | **FORBIDDEN** | Bypasses allowed-tools constraint; produces unstructured output that cannot be parsed into the machine-readable BLOCK / PASS / SKIP verdict |
29
+ | `EnterPlanMode` | **FORBIDDEN** | Breaks execution context; gate phases must execute, not be planned |
30
+
31
+ If you find yourself about to call `Agent(...)`, stop and use `Task(...)` instead.
16
32
 
17
33
  **Posture**: Blocking-with-override at the staging→main boundary (mirrors the Phase 0A open-finding gate in `/review-pr-staging`). Posture is config-driven via `verification.test_gate`.
18
34
 
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  description: Independently verify if a reported issue is actually a problem before making code changes.
3
3
  argument-hint: [issue description or #number]
4
+ install: extras
4
5
  ---
5
6
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
7
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +12,7 @@ argument-hint: [issue description or #number]
11
12
 
12
13
  Verify whether a reported issue is real before anyone writes code. This command exists for reports that come from outside the pipeline — user complaints, DevOps alerts, monitoring triggers, gut feelings. It is the checkpoint before creating a GitHub issue.
13
14
 
14
- **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
15
+ **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
15
16
 
16
17
  **Output**: A verdict (CONFIRMED / NOT A PROBLEM / NEEDS MORE DATA) with evidence.
17
18
 
@@ -11,6 +11,11 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--files AFFE
11
11
  **Time budget**: Max 3 minutes. Skip any file read that times out.
12
12
  **Output**: Post `<!-- FORGE:ARCHITECT -->` comment on the issue, then return structured plan to caller.
13
13
 
14
+ **Agent model policy**: `model: "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
+ **NEVER use plan mode (EnterPlanMode).**
16
+
17
+ <!-- FORGE:SPEC_LOADED — work-on/build/architect.md loaded and active. Agent is bound by this spec. -->
18
+
14
19
  ---
15
20
 
16
21
  ## COMPLEXITY_BAND Guard (check BEFORE all phases)
@@ -50,6 +55,7 @@ Parse from $ARGUMENTS:
50
55
  Also read from the calling context (in-memory, passed by parent agent):
51
56
  - `{INVESTIGATION_REPORT}` — full text of FORGE:INVESTIGATOR comment
52
57
  - `{CONTEXT_BRIEFING}` — full text of FORGE:CONTEXT comment (empty string if context step was skipped)
58
+ - `{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 -->
53
59
 
54
60
  If any of these are not passed directly, read them from GitHub (fallback/recovery path):
55
61
 
@@ -316,6 +322,28 @@ agent defaults ← Built-in behaviors
316
322
 
317
323
  Identify every entry point that initiates the affected logic. Start from the files named in the investigation report.
318
324
 
325
+ ### A1.0: Code Index Query (run FIRST — deterministic, one tool call)
326
+
327
+ If `scripts/code-index.sh` exists under `{REPO_PATH}`, query the pre-built index before any grep. The index provides caller lists and import graphs without re-scanning the repo:
328
+
329
+ ```bash
330
+ # Ensure index is current (cache-hit on unchanged HEAD — instant if already built)
331
+ bash {REPO_PATH}/scripts/code-index.sh --repo-path {REPO_PATH} 2>/dev/null || true
332
+
333
+ # Get callers of the primary function under change
334
+ bash {REPO_PATH}/scripts/code-index.sh query --callers {PRIMARY_FUNCTION} --repo-path {REPO_PATH} 2>/dev/null || true
335
+
336
+ # Get all files that import the affected module
337
+ bash {REPO_PATH}/scripts/code-index.sh query --importers {AFFECTED_FILE} --repo-path {REPO_PATH} 2>/dev/null || true
338
+
339
+ # Get all files in the same domain as affected files (for sibling path detection)
340
+ bash {REPO_PATH}/scripts/code-index.sh query --domain {DOMAIN} --repo-path {REPO_PATH} 2>/dev/null || true
341
+ ```
342
+
343
+ Use index results to populate the caller list and sibling candidates below. If the index returns results, **skip the grep fallback** — do not re-scan what the index already answered. If the index is absent or returns nothing, fall back to grep.
344
+
345
+ ### A1.1: Grep fallback (only when index absent or returned no results)
346
+
319
347
  For each affected file, read it and identify:
320
348
  1. The primary function/method being changed
321
349
  2. All callers of that function (grep for usages)
@@ -11,6 +11,8 @@ argument-hint: [issue number] [affected_files...] [--functions function_names...
11
11
  **Time budget**: Max 2 minutes of queries. Skip any query that times out.
12
12
  **Output**: Post `<!-- FORGE:CONTEXT -->` comment on the issue, then return structured briefing to caller.
13
13
 
14
+ <!-- FORGE:SPEC_LOADED — work-on/build/context.md loaded and active. Agent is bound by this spec. -->
15
+
14
16
  ---
15
17
 
16
18
  ## COMPLEXITY_BAND Guard (check BEFORE all phases)
@@ -12,9 +12,11 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--worktree P
12
12
  **Invoked by**: `work-on.md` Step 3F, after worktree is created and context is gathered.
13
13
  **Output**: Write code, commit(s), post `<!-- FORGE:BUILDER -->` comment, return result to caller.
14
14
 
15
- **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
15
+ **Agent model policy**: `model: "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
16
  **NEVER use plan mode (EnterPlanMode).**
17
17
 
18
+ <!-- FORGE:SPEC_LOADED — work-on/build/implement.md loaded and active. Agent is bound by this spec. -->
19
+
18
20
  ---
19
21
 
20
22
  ## Inputs
@@ -12,9 +12,11 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--worktree P
12
12
  **Invoked by**: `work-on.md` Step 3F.5, after `implement.md` has written and staged code (not committed).
13
13
  **Output**: Return `GATE_PASSED: true/false` to caller. On failure after max iterations, post comment and set `needs-human`.
14
14
 
15
- **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
15
+ **Agent model policy**: `model: "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
16
  **NEVER use plan mode (EnterPlanMode).**
17
17
 
18
+ <!-- FORGE:SPEC_LOADED — work-on/build/validate.md loaded and active. Agent is bound by this spec. -->
19
+
18
20
  ---
19
21
 
20
22
  ## Inputs
@@ -52,10 +54,22 @@ while iteration < max_iterations:
52
54
  GATE_PASSED = true
53
55
  break
54
56
  else:
55
- Fix each HIGH and MEDIUM finding in the code at {WORKTREE_PATH}
56
- (Do NOT commit yet — fixes are staged for the next gate run)
57
-
58
- if iteration == max_iterations AND result != PASS:
57
+ # Separate quarantined test findings from real blocker findings.
58
+ # quality-gate Step 2R classifies each failing test as PRE_BROKEN, FLAKY, or REAL.
59
+ # TEST-QUARANTINE findings (LOW) are advisory — do not fix, do not count as gate failures.
60
+ # TEST-REAL findings (HIGH) and all other HIGH/MEDIUM findings must be fixed.
61
+ quarantine_findings = findings where severity == LOW and code starts with "TEST-QUARANTINE"
62
+ blocker_findings = findings where code != "TEST-QUARANTINE"
63
+
64
+ if blocker_findings is empty:
65
+ # All remaining findings are quarantined tests — gate passes from the builder's perspective.
66
+ GATE_PASSED = true
67
+ break
68
+ else:
69
+ Fix each HIGH and MEDIUM finding in blocker_findings at {WORKTREE_PATH}
70
+ (Do NOT commit yet — fixes are staged for the next gate run)
71
+
72
+ if iteration == max_iterations AND result != PASS AND blocker_findings not empty:
59
73
  GATE_PASSED = false
60
74
  → post comment (see V1-FAIL below)
61
75
  → add label needs-human
@@ -71,6 +85,7 @@ Skill("quality-gate", args="{CHANGED_FILES} --worktree {WORKTREE_PATH}")
71
85
  - Re-run after EVERY fix pass — never trust that fixes resolved findings without verification
72
86
  - Each iteration re-scans ALL changed files — fixes can introduce new issues
73
87
  - Only HIGH and MEDIUM findings must be fixed; LOW findings are advisory only
88
+ - `TEST-QUARANTINE | LOW` findings (pre-broken or flaky tests classified by Step 2R) do **not** require fixing and do **not** count toward gate failure — include them in the V5 commit comment for reviewer visibility
74
89
 
75
90
  **V1-FAIL comment** (post when gate never passes):
76
91
  ```bash
@@ -305,9 +320,13 @@ BUILDER_COMMENT_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
305
320
  --jq '[.[] | select(.body | contains("FORGE:BUILDER") and (contains("FORGE:BUILDER:COMPLETE") | not))] | last | .id // ""')
306
321
 
307
322
  if [ -n "$BUILDER_COMMENT_ID" ]; then
308
- # Fetch current body and append the completion marker
323
+ # Fetch current body and append the completion marker plus best-effort cost signal
309
324
  CURRENT_BODY=$(gh api repos/{GH_REPO}/issues/comments/$BUILDER_COMMENT_ID --jq '.body')
310
- UPDATED_BODY="${CURRENT_BODY}
325
+ # Best-effort cost append: only include if session telemetry provides a value; never block
326
+ PHASE_COST_LINE=""
327
+ [ -n "${PHASE_COST_USD:-}" ] && PHASE_COST_LINE="
328
+ cost_usd: ${PHASE_COST_USD}"
329
+ UPDATED_BODY="${CURRENT_BODY}${PHASE_COST_LINE}
311
330
 
312
331
  <!-- FORGE:BUILDER:COMPLETE -->"
313
332
  gh api repos/{GH_REPO}/issues/comments/$BUILDER_COMMENT_ID \
@@ -10,12 +10,30 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--base PR_BA
10
10
  **Input**: $ARGUMENTS
11
11
 
12
12
  **Invoked by**: `work-on.md` Phase 3 — entered when the issue carries label `workflow:ready-to-build` or `workflow:building` (see Universal Phase Dispatcher in work-on.md).
13
- **Output**: Create worktree, post contract, sequence build subcommands, return result to work-on.md.
13
+ **Output**: Create worktree, post contract, run build phases, return result to work-on.md.
14
14
 
15
- **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
15
+ **Agent model policy**: `model: "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
16
  **NEVER use plan mode (EnterPlanMode).**
17
17
 
18
- **CRITICAL: You MUST execute ALL phases B0–B6 in order. After each Skill() call returns, you MUST continue to the next phase. Phases B3 (context) and B4 (architect) are skipped ONLY when COMPLEXITY_BAND: TRIVIAL (read from FORGE:FAST_PATH comment in Phase B0). For STANDARD and COMPLEX tasks they are NOT optional — skipping them degrades build quality.**
18
+ **CRITICAL: You MUST execute ALL phases B0–B6 in order. Phases B3 (context) and B4 (architect) are skipped ONLY when COMPLEXITY_BAND: TRIVIAL (read from FORGE:FAST_PATH comment in Phase B0). For STANDARD and COMPLEX tasks they are NOT optional — skipping them degrades build quality.**
19
+
20
+ ### Canonical Build Path (STANDARD/fast-lane) <!-- Added: forge#1276 -->
21
+
22
+ **Default execution model: inline.** For STANDARD and fast-lane issues, phases B3 (context gathering) and B4 (architecture planning) run **inline in the current context window** — not as separate `Skill()` sub-agent spawns. B5 (implement) and B6 (validate) also run inline.
23
+
24
+ `Skill()` invocations for context/architect sub-phases are only permitted when the Spawn-Decision Table (work-on.md `##Spawn-Decision Policy`) explicitly applies — specifically Row (c) (parent context near overflow: ≥20 Skill invocations or ≥10 files already changed before the build sub-phase). For most issues, the Skill() forms shown in B3 and B4 below are **reference documentation** describing the sub-phase contract, not mandatory sub-agent invocations.
25
+
26
+ **Build topology summary**:
27
+
28
+ | Path | When | Phases |
29
+ |------|------|--------|
30
+ | **STANDARD/fast-lane (default)** | All issues not matching exceptions below | B0 → B1 → B2 → B2.5 → [B3] → [B4] → B5 → B6 — all inline |
31
+ | **Spawn exception (Row c)** | ≥20 Skill invocations OR ≥10 files changed before build | Spawn B3/B4 as fresh sub-agents via `Skill()` |
32
+ | **TRIVIAL fast-path** | COMPLEXITY_BAND: TRIVIAL | Skip B3 and B4 entirely |
33
+
34
+ This resolves the three-topology conflict: `work-on.md` Phase 3 (inline 3A–3M), `work-on/build.md` (this file), and `work-on-monolithic.md` ([BENCHMARK]) all describe the **same canonical inline path**. `work-on/build.md` adds worktree lifecycle management (B1) and the FORGE:CONTRACT handoff (B2) that the monolithic variant omits for brevity. The `Skill()` forms in B3/B4 below document the sub-phase contract and serve as the exception path only. <!-- Added: forge#1276 -->
35
+
36
+ <!-- FORGE:SPEC_LOADED — work-on/build.md loaded and active. Agent is bound by this spec. -->
19
37
 
20
38
  ---
21
39
 
@@ -27,6 +45,8 @@ Parse from $ARGUMENTS:
27
45
  - `--gh-flag {GH_FLAG}` — gh CLI repo flag (e.g. `-R {owner}/{repo}`)
28
46
  - `--base {PR_BASE}` — PR target branch (e.g. `milestone/modular-pipeline-architecture` or `staging`)
29
47
 
48
+ **Phase notation**: This file uses **B0–B6** for its own phases. The calling orchestrator (`work-on.md`) uses **3A–3M** for its sub-phases. Mapping: work-on.md Phase 3A = B0 (load state), Phase 3B = complexity classification (posts `FORGE:FAST_PATH` before invoking build), Phase 3C onward maps to B1+ in this file. When cross-references mention "Phase 3B", they refer to work-on.md's Phase 3B, not a phase in this file. <!-- Added: forge#1380 -->
49
+
30
50
  ---
31
51
 
32
52
  ## Phase B0: Load State from GitHub (MANDATORY)
@@ -64,7 +84,7 @@ Extract from investigation report:
64
84
  - Recommendation
65
85
  - Task type (Bug Fix / Feature / Refactor / Maintenance / UI/UX / Full-Stack)
66
86
 
67
- **Read COMPLEXITY_BAND** (from FORGE:FAST_PATH comment posted by Phase 3B):
87
+ **Read COMPLEXITY_BAND** (from `FORGE:FAST_PATH` comment posted by Phase 3B of `work-on.md` — the complexity classification step that runs before invoking this file): <!-- Fixed: forge#1380 -->
68
88
  ```bash
69
89
  COMPLEXITY_BAND=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
70
90
  --jq '.[] | select(.body | contains("FORGE:FAST_PATH")) | .body' 2>/dev/null \
@@ -192,44 +212,46 @@ If `FUNCTION_NAMES` is non-empty, it will be passed via `--functions` to the con
192
212
 
193
213
  ## Phase B3: Context Gathering (MANDATORY for STANDARD/COMPLEX — skip for TRIVIAL)
194
214
 
195
- **Skip if COMPLEXITY_BAND: TRIVIAL** (read from FORGE:FAST_PATH in Phase B0) — do not invoke the context subcommand. Proceed directly to Phase B4.
215
+ **Skip if COMPLEXITY_BAND: TRIVIAL** (read from FORGE:FAST_PATH in Phase B0) — skip this phase entirely. Proceed directly to Phase B4.
196
216
 
197
- **For STANDARD and COMPLEX tasks**: Always invoke. Do NOT skip without a TRIVIAL COMPLEXITY_BAND.
217
+ **For STANDARD and COMPLEX tasks**: Always run. Do NOT skip without a TRIVIAL COMPLEXITY_BAND.
198
218
 
199
- Invoke the context subcommand to surface historical review findings and bug patterns:
219
+ **Execution model**: Run **inline** (see Canonical Build Path above). Read the `commands/work-on/build/context.md` spec and execute its steps directly in this context window. Only spawn a Skill() sub-agent when the Spawn-Decision Table Row (c) applies (≥20 prior Skill invocations or ≥10 files already changed). <!-- Added: forge#1276 -->
200
220
 
201
- ```
202
- Skill("work-on:build:context", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE_PATH} {AFFECTED_FILES} --functions {FUNCTION_NAMES}")
203
- ```
204
-
205
- If `FUNCTION_NAMES` is empty, omit the `--functions` flag entirely:
221
+ Surface historical review findings and bug patterns for the affected files. The full step-by-step logic is defined in `commands/work-on/build/context.md`. Key steps: search closed issues with `review-finding` label on the affected files; check git log for past bug patterns; synthesize a `FORGE:CONTEXT` annotation and post it as a GitHub comment.
206
222
 
223
+ **Spawn exception** (only when Row (c) applies):
207
224
  ```
208
- Skill("work-on:build:context", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE_PATH} {AFFECTED_FILES}")
225
+ Skill("work-on:build:context", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE_PATH} {AFFECTED_FILES} --functions {FUNCTION_NAMES}")
209
226
  ```
227
+ If `FUNCTION_NAMES` is empty, omit `--functions`. The Skill() form above is the exception path — not the default. <!-- Added: forge#1276 -->
210
228
 
211
- **After subcommand returns**:
212
- - Returns structured context briefing (or indicates no relevant history) → continue to B4
213
- - If subcommand times out or errors → log warning, continue to B4 with empty context (non-blocking)
229
+ **After context gathering**:
230
+ - Structured context briefing produced (or no relevant history found) → continue to B4
231
+ - Context gathering timed out or errored → log warning, continue to B4 with empty context (non-blocking)
214
232
  # MUST CONTINUE to Phase B4 — context result is intermediate, NOT terminal.
215
233
 
216
234
  ---
217
235
 
218
236
  ## Phase B4: Architecture Planning (MANDATORY for STANDARD/COMPLEX — skip for TRIVIAL)
219
237
 
220
- **Skip if COMPLEXITY_BAND: TRIVIAL** (read from FORGE:FAST_PATH in Phase B0) — do not invoke the architect subcommand. Proceed directly to Phase B5.
238
+ **Skip if COMPLEXITY_BAND: TRIVIAL** (read from FORGE:FAST_PATH in Phase B0) — skip this phase entirely. Proceed directly to Phase B5.
221
239
 
222
- **For STANDARD and COMPLEX tasks**: Always invoke. Even a 1-file STANDARD fix benefits from cross-path consistency checks. Do NOT skip without a TRIVIAL COMPLEXITY_BAND.
240
+ **For STANDARD and COMPLEX tasks**: Always run. Even a 1-file STANDARD fix benefits from cross-path consistency checks. Do NOT skip without a TRIVIAL COMPLEXITY_BAND.
223
241
 
224
- Invoke the architect subcommand to trace all affected code paths and produce an ordered implementation plan:
242
+ **Execution model**: Run **inline** (see Canonical Build Path above). Read the `commands/work-on/build/architect.md` spec and execute its steps directly in this context window. Only spawn a Skill() sub-agent when the Spawn-Decision Table Row (c) applies. <!-- Added: forge#1276 -->
225
243
 
244
+ Trace all affected code paths and produce an ordered implementation plan. The full step-by-step logic is defined in `commands/work-on/build/architect.md`. Key steps: map all callers and importers of changed functions; check consistency rules across paths; post a `FORGE:ARCHITECT` annotation with the ordered plan and a risk table.
245
+
246
+ **Spawn exception** (only when Row (c) applies):
226
247
  ```
227
248
  Skill("work-on:build:architect", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE_PATH} --files {AFFECTED_FILES}")
228
249
  ```
250
+ The Skill() form above is the exception path — not the default. <!-- Added: forge#1276 -->
229
251
 
230
- **After subcommand returns**:
252
+ **After architecture planning**:
231
253
  - Returns ordered implementation plan → continue to B5
232
- - If subcommand returns BLOCKED → post comment, add `needs-human`, return `BUILD_RESULT: status: BLOCKED`
254
+ - BLOCKED (conflicting constraints that cannot be resolved inline) → post comment, add `needs-human`, return `BUILD_RESULT: status: BLOCKED`
233
255
  # MUST CONTINUE to Phase B5 — architect result is intermediate, NOT terminal.
234
256
 
235
257
  ---
@@ -262,9 +284,126 @@ Skill("work-on:build:validate", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FL
262
284
  Where `{CHANGED_FILES}` is the space-separated list of files changed by the implement subcommand (read from `IMPLEMENT_RESULT` or from the `<!-- FORGE:BUILDER -->` comment).
263
285
 
264
286
  **After subcommand returns**:
265
- - `VALIDATE_RESULT: gate_passed: true` → write checkpoint, then return `BUILD_RESULT: status: COMPLETE`
287
+ - `VALIDATE_RESULT: gate_passed: true` → continue to Phase B6.5 (acceptance gate)
266
288
  - `VALIDATE_RESULT: gate_passed: false` → subcommand has already posted comment and added `needs-human` label; return `BUILD_RESULT: status: BLOCKED`
267
289
 
290
+ ---
291
+
292
+ ## Phase B6.5: Acceptance Gate (MANDATORY — cannot be silently skipped) <!-- Added: forge#1315 -->
293
+
294
+ **Goal**: Execute the machine-checkable acceptance spec emitted by investigate Phase 1C and block merge if any check fails. This is a hard gate — not advisory.
295
+
296
+ **Read acceptance spec from FORGE:INVESTIGATOR comment**:
297
+
298
+ ```bash
299
+ ACCEPTANCE_CHECKS=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
300
+ --jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body' \
301
+ | grep "^ACCEPTANCE_CHECK:" )
302
+ ```
303
+
304
+ **If `ACCEPTANCE_CHECKS` is empty** (investigation predates this feature or comment was deleted): post a warning comment and **block** — do not silently pass:
305
+
306
+ ```bash
307
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ACCEPTANCE_GATE -->
308
+ ## Acceptance Gate — No Spec Found
309
+
310
+ No \`ACCEPTANCE_CHECK:\` lines found in the FORGE:INVESTIGATOR comment. This may mean:
311
+ - The investigation was run before acceptance spec emission was added (re-run investigate to generate the spec), or
312
+ - The investigator comment was deleted.
313
+
314
+ **Gate result: BLOCKED** — re-run \`/work-on:investigate {NUMBER}\` to regenerate the acceptance spec, then retry the build.
315
+
316
+ <!-- FORGE:ACCEPTANCE_GATE:BLOCKED -->"
317
+ gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
318
+ ```
319
+ Return `BUILD_RESULT: status: BLOCKED`, blocker: "No acceptance spec — re-run investigate to emit ACCEPTANCE_CHECK lines".
320
+
321
+ **If all checks are `type=skipped`**: post a pass comment noting human review is required, then continue to the checkpoint (non-blocking — skip was deliberate):
322
+
323
+ ```bash
324
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ACCEPTANCE_GATE -->
325
+ ## Acceptance Gate — Skipped (No Machine-Checkable Criteria)
326
+
327
+ The acceptance spec contains only a skip sentinel (\`type=skipped\`). No automated checks were run. Human review is required before merge.
328
+
329
+ <!-- FORGE:ACCEPTANCE_GATE:PASSED -->"
330
+ ```
331
+
332
+ **Otherwise — execute each check**:
333
+
334
+ ```bash
335
+ GATE_PASS=true
336
+ FAILED_CHECKS=""
337
+
338
+ while IFS= read -r check_line; do
339
+ ID=$(echo "$check_line" | sed -n 's/.*id=\([^ ]*\).*/\1/p')
340
+ TYPE=$(echo "$check_line" | sed -n 's/.*type=\([^ ]*\).*/\1/p')
341
+ TARGET=$(echo "$check_line"| sed -n 's/.*target=\([^ ]*\).*/\1/p')
342
+ MATCHER=$(echo "$check_line"| sed -n 's/.*matcher=\([^ ]*\).*/\1/p')
343
+ DESC=$(echo "$check_line" | sed -n 's/.*description=\(.*\)/\1/p')
344
+
345
+ [ "$TYPE" = "skipped" ] && continue
346
+
347
+ RESULT="PASS"
348
+ DETAIL=""
349
+
350
+ case "$TYPE" in
351
+ exists)
352
+ [ -e "$TARGET" ] || { RESULT="FAIL"; DETAIL="path not found: $TARGET"; }
353
+ ;;
354
+ contains)
355
+ grep -qE "$MATCHER" "$TARGET" 2>/dev/null || { RESULT="FAIL"; DETAIL="'$MATCHER' not found in $TARGET"; }
356
+ ;;
357
+ command|behavior)
358
+ if [ "$MATCHER" = "exit_0" ]; then
359
+ eval "$TARGET" >/dev/null 2>&1 || { RESULT="FAIL"; DETAIL="command exited non-zero: $TARGET"; }
360
+ else
361
+ OUTPUT=$(eval "$TARGET" 2>&1)
362
+ echo "$OUTPUT" | grep -qE "$MATCHER" || { RESULT="FAIL"; DETAIL="output did not match '$MATCHER'. Got: $(echo "$OUTPUT" | head -3)"; }
363
+ fi
364
+ ;;
365
+ *)
366
+ RESULT="FAIL"; DETAIL="unknown check type: $TYPE"
367
+ ;;
368
+ esac
369
+
370
+ if [ "$RESULT" = "FAIL" ]; then
371
+ GATE_PASS=false
372
+ FAILED_CHECKS="${FAILED_CHECKS}\n- **$ID** ($DESC): $DETAIL"
373
+ fi
374
+ done <<< "$ACCEPTANCE_CHECKS"
375
+ ```
376
+
377
+ **Post gate result comment**:
378
+
379
+ ```bash
380
+ if [ "$GATE_PASS" = "true" ]; then
381
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ACCEPTANCE_GATE -->
382
+ ## Acceptance Gate — PASSED
383
+
384
+ All machine-checkable acceptance criteria verified against real behavior.
385
+
386
+ <!-- FORGE:ACCEPTANCE_GATE:PASSED -->"
387
+ else
388
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ACCEPTANCE_GATE -->
389
+ ## Acceptance Gate — FAILED
390
+
391
+ The following acceptance checks did not pass:
392
+
393
+ $(echo -e "$FAILED_CHECKS")
394
+
395
+ Merge is blocked. Fix the failing criteria and re-run the validate phase.
396
+
397
+ <!-- FORGE:ACCEPTANCE_GATE:FAILED -->"
398
+ gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
399
+ # Return BLOCKED — merge gate failed
400
+ fi
401
+ ```
402
+
403
+ If `GATE_PASS = false`: return `BUILD_RESULT: status: BLOCKED`, blocker: "Acceptance gate failed — see FORGE:ACCEPTANCE_GATE comment".
404
+
405
+ If `GATE_PASS = true`: continue to write the phase checkpoint below.
406
+
268
407
  **When gate_passed is true — write machine-readable phase checkpoint before returning (MANDATORY)**:
269
408
  ```bash
270
409
  CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
@@ -295,8 +434,8 @@ BUILD_RESULT:
295
434
  This module runs during **Phase 3** of the work-on.md pipeline (label: `workflow:ready-to-build` or `workflow:building`). The full sequence is defined by the Universal Phase Dispatcher in work-on.md:
296
435
 
297
436
  ```
298
- Phase 3 (Build) → [THIS MODULE] worktree + contract + context + architect + implement + validate
299
- → posts FORGE:BUILDER comment, writes FORGE:CHECKPOINT next_phase=REVIEW
437
+ Phase 3 (Build) → [THIS MODULE] worktree + contract + context + architect + implement + validate + acceptance-gate
438
+ → posts FORGE:BUILDER comment + FORGE:ACCEPTANCE_GATE comment, writes FORGE:CHECKPOINT next_phase=REVIEW
300
439
  Phase 4 (PR) → work-on:review — push branch, create PR, set workflow:in-review
301
440
  Phase 5 (Review) → work-on:review — invoke /review-pr --auto-merge
302
441
  Phase 6 (Close) → work-on:close — trajectory + parent tracker + summary + worktree cleanup
@@ -12,9 +12,11 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--pr PR_NUMB
12
12
  **Invoked by**: `work-on.md` Phase 6–7, after `review.md` returns `REVIEW_RESULT: status: COMPLETE`.
13
13
  **Output**: Update project board, close issue, update parent tracker, post trajectory log. Return final summary.
14
14
 
15
- **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
15
+ **Agent model policy**: `model: "haiku"`, `effort: low` (mechanical tier — label transitions, annotation posting, board updates). Fallback: `model: "sonnet"` if rate-limited. Feature gate: pass `effort` only on Claude Code >= 2.1.154.
16
16
  **NEVER use plan mode (EnterPlanMode).**
17
17
 
18
+ <!-- FORGE:SPEC_LOADED — work-on/close.md loaded and active. Agent is bound by this spec. -->
19
+
18
20
  ---
19
21
 
20
22
  ## Inputs
@@ -183,9 +185,11 @@ ISSUE_STATE=$(gh issue view {NUMBER} {GH_FLAG} --json state --jq '.state')
183
185
  gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:PHASE:COMPLETE -->
184
186
  Phase complete. PR #{PR_NUMBER} merged to \`{PR_BASE}\`. ${REMAINING_AFTER} phase item(s) remain — leaving issue open for next pipeline iteration."
185
187
 
186
- # Update labels to reflect phase complete (not yet fully merged)
188
+ # Update labels to reflect phase complete — remove current-phase labels and set next-phase label
189
+ # so the router has a signal for which phase comes next (fixes: issue #1381)
187
190
  gh issue edit {NUMBER} {GH_FLAG} \
188
- --remove-label "workflow:in-review,workflow:building,workflow:investigating" 2>/dev/null || true
191
+ --remove-label "workflow:in-review,workflow:building,workflow:investigating" \
192
+ --add-label "workflow:investigating" 2>/dev/null || true
189
193
 
190
194
  # EXIT — do not close, do not post trajectory, do not run C3–C6
191
195
  # Return CLOSE_RESULT: status: PHASE_COMPLETE to caller
@@ -444,6 +448,129 @@ Where:
444
448
 
445
449
  ---
446
450
 
451
+ ## Phase C5.2: Memory Index Update <!-- Added: forge#1316 -->
452
+
453
+ **Goal**: Append this run's learnings to the per-repo memory index so future `investigate` runs can retrieve relevant priors. This is the write side of the compounding intelligence loop.
454
+
455
+ **This phase is non-blocking** — if Gist creation or update fails, log the reason and continue to Phase C5.5. Never stall close for memory.
456
+
457
+ **Skip if**: Terminal state is `INVALID` (nothing useful to learn from an invalid issue) OR `<!-- FORGE:TRAJECTORY -->` was already posted AND `<!-- FORGE:MEMORY_INDEXED -->` comment exists on the issue (idempotency guard).
458
+
459
+ ### Step 1: Compose memory entry
460
+
461
+ Extract key fields from the pipeline:
462
+
463
+ ```bash
464
+ ROOT_CAUSE=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
465
+ --jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body' \
466
+ | sed -n '/### Root Cause/{n;p;q}' | head -1 | cut -c1-120)
467
+
468
+ AFFECTED_FILES_BRIEF=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
469
+ --jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body' \
470
+ | sed -n '/### Affected Files/{n;p;q}' | head -1 | cut -c1-120)
471
+
472
+ DOMAIN_TAGS=$(gh issue view {NUMBER} {GH_FLAG} --json labels \
473
+ --jq '[.labels[].name | select(test("^(auth|billing|database|security|payments|gdpr|perf|ui|api|config)"))] | join(",")' 2>/dev/null || echo "")
474
+
475
+ MEMORY_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
476
+
477
+ MEMORY_ENTRY="MEMORY_ENTRY: issue={NUMBER} title=\"{TITLE}\" domain=\"${DOMAIN_TAGS}\" root_cause=\"${ROOT_CAUSE}\" outcome=\"${CARD_STATUS}\" files=\"${AFFECTED_FILES_BRIEF}\" lesson=\"${ROOT_CAUSE}\" timestamp=${MEMORY_TIMESTAMP}"
478
+ ```
479
+
480
+ ### Step 2: Find or create the memory index Gist
481
+
482
+ ```bash
483
+ MEMORY_INDEX_ID=$(gh gist list --limit 100 \
484
+ --jq '.[] | select(.description | contains("FORGE:MEMORY_INDEX: {GH_REPO}")) | .id' 2>/dev/null | head -1)
485
+
486
+ if [ -z "$MEMORY_INDEX_ID" ]; then
487
+ # First run — create the index Gist
488
+ TMPFILE=$(mktemp --suffix=.md)
489
+ cat > "$TMPFILE" <<GIST_EOF
490
+ # ForgeDock Memory Index — {GH_REPO}
491
+ <!-- FORGE:MEMORY_INDEX: {GH_REPO} -->
492
+ Generated by ForgeDock close phase. Each line is a prior pipeline run.
493
+
494
+ ${MEMORY_ENTRY}
495
+ GIST_EOF
496
+ # Memory gists MUST be secret — never pass --public here (forge#1587).
497
+ # The entry content below embeds real issue titles, root causes, and file
498
+ # paths; for a private consumer repo, --public would publish that content
499
+ # to a world-readable Gist. `gh gist create` is secret by default, so
500
+ # simply omitting --public is sufficient — the read side (investigate.md
501
+ # Phase 0.5, via `gh gist view`/`gh gist list`) works unchanged against
502
+ # secret gists for the authenticated owner.
503
+ MEMORY_INDEX_URL=$(gh gist create "$TMPFILE" \
504
+ --desc "FORGE:MEMORY_INDEX: {GH_REPO} — per-codebase learning index" \
505
+ 2>/dev/null | head -1)
506
+ MEMORY_INDEX_ID=$(echo "$MEMORY_INDEX_URL" | sed 's|.*/||')
507
+ rm -f "$TMPFILE"
508
+ echo "[MEMORY] Created memory index Gist: ${MEMORY_INDEX_URL}"
509
+ else
510
+ # Append to existing index
511
+ EXISTING_CONTENT=$(gh gist view "$MEMORY_INDEX_ID" 2>/dev/null)
512
+ UPDATED_CONTENT="${EXISTING_CONTENT}
513
+ ${MEMORY_ENTRY}"
514
+ INDEX_FILENAME=$(gh api gists/${MEMORY_INDEX_ID} --jq '.files | keys[0]' 2>/dev/null)
515
+ INDEX_FILENAME="${INDEX_FILENAME:-memory_index.md}"
516
+ TMPFILE=$(mktemp --suffix=.md)
517
+ echo "$UPDATED_CONTENT" > "$TMPFILE"
518
+ gh gist edit "$MEMORY_INDEX_ID" -f "$INDEX_FILENAME" "$TMPFILE" 2>/dev/null
519
+ EDIT_EXIT=$?
520
+ rm -f "$TMPFILE"
521
+ MEMORY_INDEX_URL="https://gist.github.com/${MEMORY_INDEX_ID}"
522
+ if [ $EDIT_EXIT -eq 0 ]; then
523
+ echo "[MEMORY] Appended to memory index: ${MEMORY_INDEX_URL}"
524
+ else
525
+ echo "WARNING: Failed to update memory index — run will not be persisted"
526
+ MEMORY_INDEX_URL=""
527
+ fi
528
+ fi
529
+ ```
530
+
531
+ ### Migration: replacing an existing public memory-index Gist
532
+
533
+ GitHub does not support flipping a Gist's visibility from public to secret in place. If you find an existing `FORGE:MEMORY_INDEX: {GH_REPO}` Gist that is **public** (check with `gh api gists/${MEMORY_INDEX_ID} --jq '.public'`), replace it:
534
+
535
+ ```bash
536
+ # The gist ID here is the same one found by the FORGE:MEMORY_INDEX search above.
537
+ OLD_PUBLIC_GIST_ID="$MEMORY_INDEX_ID"
538
+
539
+ # 1. Save the existing content so no learnings are lost — and verify the save
540
+ # succeeded BEFORE deleting anything.
541
+ gh gist view "$OLD_PUBLIC_GIST_ID" > /tmp/memory_index_migrate.md
542
+ if [ ! -s /tmp/memory_index_migrate.md ]; then
543
+ echo "ABORT: failed to save gist content — not deleting the public gist." >&2
544
+ exit 1
545
+ fi
546
+
547
+ # 2. Delete the public gist (removes the world-readable copy)
548
+ gh gist delete "$OLD_PUBLIC_GIST_ID" --yes
549
+
550
+ # 3. Recreate it secret (no --public) with the same description tag so the
551
+ # next close/investigate run finds it via the FORGE:MEMORY_INDEX search
552
+ gh gist create /tmp/memory_index_migrate.md \
553
+ --desc "FORGE:MEMORY_INDEX: {GH_REPO} — per-codebase learning index"
554
+ ```
555
+
556
+ This is a one-time manual step per affected repo — Phase C5.2 itself only ever creates the index once (Step 2's `if [ -z "$MEMORY_INDEX_ID" ]` branch) and appends afterward, so a stale public gist is never auto-recreated secret on its own.
557
+
558
+ ### Step 3: Post audit annotation on the issue
559
+
560
+ ```bash
561
+ if [ -n "$MEMORY_INDEX_URL" ]; then
562
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:MEMORY_INDEXED -->
563
+ This run has been indexed in the per-repo memory: ${MEMORY_INDEX_URL}
564
+
565
+ Future \`investigate\` runs on \`{GH_REPO}\` will retrieve this entry as a prior when working on related issues.
566
+
567
+ <!-- FORGE:MEMORY_INDEXED:COMPLETE -->"
568
+ echo "[MEMORY] Indexed issue #{NUMBER} into memory at: ${MEMORY_INDEX_URL}"
569
+ fi
570
+ ```
571
+
572
+ ---
573
+
447
574
  ## Phase C5.5: Graph Decision Record (MANDATORY when PR exists)
448
575
 
449
576
  **Skip if**: `{PR_NUMBER}` is empty OR `<!-- FORGE:DECISION_RECORD -->` already posted on the PR.
@@ -12,7 +12,7 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG]
12
12
  **Invoked by**: `work-on.md` routing loop, when `INVESTIGATE_RESULT.decompose = YES`.
13
13
  **Output**: Create sub-issues, update parent tracker, post `<!-- FORGE:DECOMPOSED -->` comment, set labels. STOP — each sub-issue runs its own /work-on.
14
14
 
15
- **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
15
+ **Agent model policy**: `model: "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
16
  **NEVER use plan mode (EnterPlanMode).**
17
17
 
18
18
  ---
@@ -108,6 +108,24 @@ For each sub-issue, prepare:
108
108
 
109
109
  For each sub-issue (in dependency order):
110
110
 
111
+ **Before creating each sub-issue, run the deterministic dedup check:** <!-- Added: forge#1335 -->
112
+
113
+ ```bash
114
+ SUB_TITLE="{fix|feat|refactor}: {SUB_ISSUE_TITLE}"
115
+ DEDUP_RESULT=$(scripts/issue-dedup.sh "$SUB_TITLE" {GH_FLAG} 2>&1)
116
+ DEDUP_EXIT=$?
117
+ if [ "$DEDUP_EXIT" -eq 1 ]; then
118
+ echo "DEDUP: Sub-issue near-duplicate detected — $DEDUP_RESULT"
119
+ echo "Skipping creation. Comment on the existing issue instead."
120
+ # Do NOT call gh issue create for this sub-issue
121
+ elif [ "$DEDUP_EXIT" -eq 2 ]; then
122
+ echo "DEDUP: Usage error — $DEDUP_RESULT"
123
+ # Do NOT call gh issue create for this sub-issue — fix the invocation and retry
124
+ fi
125
+ ```
126
+
127
+ Only call `gh issue create` when the dedup script exits 0 (no match):
128
+
111
129
  ```bash
112
130
  gh issue create {GH_FLAG} \
113
131
  --title "{fix|feat|refactor}: {SUB_ISSUE_TITLE}" \