forgedock 1.1.7 → 1.1.9

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 (97) hide show
  1. package/README.md +10 -33
  2. package/bin/cinema.mjs +23 -5
  3. package/bin/engine/invariants.mjs +361 -0
  4. package/bin/engine-cli.mjs +137 -11
  5. package/bin/env-detect.mjs +249 -0
  6. package/bin/forge-utils.mjs +364 -4
  7. package/bin/forgedock.mjs +1064 -135
  8. package/bin/hooks/pre-tool-use.mjs +182 -0
  9. package/bin/hooks/session-start.mjs +68 -3
  10. package/bin/init-detect.mjs +101 -12
  11. package/bin/journey.mjs +917 -16
  12. package/bin/labels.json +10 -0
  13. package/bin/recall.mjs +557 -0
  14. package/bin/registry.mjs +91 -3
  15. package/bin/runner.mjs +38 -2
  16. package/bin/tui.mjs +49 -3
  17. package/commands/adopt.md +1 -1
  18. package/commands/analytics.md +8 -8
  19. package/commands/audit.md +1 -1
  20. package/commands/autopilot.md +958 -77
  21. package/commands/ci-audit.md +1 -1
  22. package/commands/cleanup.md +139 -55
  23. package/commands/compat-audit.md +1 -1
  24. package/commands/deploy-info.md +1 -1
  25. package/commands/deploy-pr.md +1 -1
  26. package/commands/diagnose.md +1 -1
  27. package/commands/explain.md +1 -1
  28. package/commands/fix-ci.md +1 -1
  29. package/commands/forgedock-init.md +1 -1
  30. package/commands/incident-response.md +3 -3
  31. package/commands/issue.md +1 -1
  32. package/commands/optimize.md +586 -1
  33. package/commands/orchestrate/config.md +13 -3
  34. package/commands/orchestrate/phase-1-resolve.md +36 -26
  35. package/commands/orchestrate/phase-2-triage.md +1 -1
  36. package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
  37. package/commands/orchestrate/phase-3-dependency.md +587 -48
  38. package/commands/orchestrate/phase-4-execution.md +1031 -57
  39. package/commands/orchestrate/phase-5-cleanup.md +1 -1
  40. package/commands/orchestrate/phase-6-report.md +248 -11
  41. package/commands/orchestrate/safety.md +2 -2
  42. package/commands/pipeline-health/phase-2-metrics.md +305 -1
  43. package/commands/pipeline-health/phase-4-proposals.md +127 -0
  44. package/commands/qa-sweep.md +1 -1
  45. package/commands/quality-gate.md +418 -1
  46. package/commands/recover-orphans.md +5 -5
  47. package/commands/replay.md +1 -1
  48. package/commands/review-pr-agents/api.md +2 -2
  49. package/commands/review-pr-agents/auth.md +2 -2
  50. package/commands/review-pr-agents/billing.md +2 -2
  51. package/commands/review-pr-agents/concurrency.md +2 -2
  52. package/commands/review-pr-agents/database.md +2 -2
  53. package/commands/review-pr-agents/frontend.md +2 -2
  54. package/commands/review-pr-agents/infra.md +2 -2
  55. package/commands/review-pr-agents/protocols.md +1 -1
  56. package/commands/review-pr-agents/scraper.md +2 -2
  57. package/commands/review-pr-agents/security.md +2 -2
  58. package/commands/review-pr-staging.md +41 -3
  59. package/commands/review-pr.md +462 -19
  60. package/commands/rollback.md +1 -1
  61. package/commands/scope.md +1 -1
  62. package/commands/security-audit.md +1 -1
  63. package/commands/spec-doctor.md +429 -0
  64. package/commands/test-gate.md +1 -1
  65. package/commands/validate.md +53 -1
  66. package/commands/work-on/build/architect.md +78 -1
  67. package/commands/work-on/build/context.md +360 -22
  68. package/commands/work-on/build/implement.md +3 -1
  69. package/commands/work-on/build/validate.md +29 -1
  70. package/commands/work-on/build.md +75 -6
  71. package/commands/work-on/close.md +624 -10
  72. package/commands/work-on/decompose.md +1 -1
  73. package/commands/work-on/investigate.md +235 -5
  74. package/commands/work-on/remediate.md +331 -0
  75. package/commands/work-on/review.md +1 -1
  76. package/commands/work-on-monolithic.md +1 -1
  77. package/commands/work-on.md +205 -66
  78. package/docs/CI.md +2 -2
  79. package/docs/CODEX.md +1 -1
  80. package/docs/CONFIG.md +186 -0
  81. package/docs/FORGE-PROTOCOL.md +101 -0
  82. package/docs/site/command-reference.md +31 -1
  83. package/docs/site/getting-started.md +9 -34
  84. package/docs/site/index.md +1 -1
  85. package/docs/spec/forge-protocol-v1.md +97 -5
  86. package/docs/spec/label-state-machine.md +6 -1
  87. package/package.json +3 -2
  88. package/packages/protocol/src/cli.js +374 -84
  89. package/packages/protocol/src/types.js +75 -1
  90. package/scripts/build-knowledge-index.mjs +1761 -0
  91. package/scripts/calibration.mjs +996 -0
  92. package/scripts/check-command-docs-drift.sh +4 -2
  93. package/scripts/danger-zones.mjs +789 -0
  94. package/scripts/doctor-pipeline-state.sh +88 -2
  95. package/scripts/select-fix-targets.sh +234 -0
  96. package/scripts/transition-label.sh +245 -6
  97. package/scripts/refresh-bot-token.sh +0 -97
package/docs/CONFIG.md CHANGED
@@ -42,6 +42,7 @@ The default (no flags) is the annotated review screen: detection runs, AI enrich
42
42
  | [`project`](#project-required) | **Yes** | GitHub identity (owner, repo, name) |
43
43
  | [`paths`](#paths-required) | **Yes** | Local filesystem locations |
44
44
  | [`branches`](#branches-required) | **Yes** | Branch naming conventions |
45
+ | [`agents`](#agents-optional) | No | Default model for pipeline agents |
45
46
  | [`repos`](#repos-optional) | No | Multi-repository routing |
46
47
  | [`project_board`](#project_board-optional) | No | GitHub Projects v2 integration |
47
48
  | [`services`](#services-optional) | No | External service URLs and IDs |
@@ -125,6 +126,40 @@ branches:
125
126
 
126
127
  ---
127
128
 
129
+ ## `agents` (OPTIONAL)
130
+
131
+ Model configuration for pipeline agents. `default_model` governs the main agent — the one you invoke directly (`work-on`, `orchestrate`, `review-pr`, etc.), referenced by every command spec's "Agent model policy" line. `subagent_model` governs the **child** sub-agents that a top-level command spawns internally — e.g. `/orchestrate`'s per-issue `/work-on` agents, `/review-pr`'s domain persona reviewers, `/analytics`'s parallel data-collection agents, `/incident-response`'s validation/root-cause agents.
132
+
133
+ ```yaml
134
+ agents:
135
+ default_model: "sonnet"
136
+ subagent_model: "sonnet"
137
+ ```
138
+
139
+ | Field | Type | Required | Default | Description |
140
+ |-------|------|----------|---------|-------------|
141
+ | `default_model` | string | No | `"sonnet"` | Short alias — `"sonnet"`, `"opus"`, or `"haiku"` — the same values the Agent/Task tool's `model` parameter accepts. This is the only form that works for interactive command-spec sub-agent spawning. |
142
+ | `subagent_model` | string | No | `default_model`, else `"sonnet"` | Short alias, same accepted values as `default_model`. Overrides the model used for child agents spawned internally by a top-level command (see above). Omit to inherit `default_model`. |
143
+
144
+ **Resolution order** (highest precedence first):
145
+ 1. `--model <id>` — CLI flag, `npx forgedock run` only (headless; main agent only).
146
+ 2. `FORGEDOCK_MODEL` env var — headless runner only (main agent only).
147
+ 3. `agents.default_model` — this field. Governs the main agent.
148
+ 4. Hardcoded default — `"sonnet"` for interactive agents, `"claude-sonnet-5"` for the headless runner (`bin/runner.mjs`'s `DEFAULT_MODEL`).
149
+
150
+ **Sub-agent resolution order** (highest precedence first):
151
+ 1. `agents.subagent_model` — this field. Governs child agents spawned by a top-level command.
152
+ 2. `agents.default_model` — falls back here when `subagent_model` is unset.
153
+ 3. Hardcoded default — `"sonnet"`.
154
+
155
+ **Headless runner note**: `bin/runner.mjs` (`npx forgedock run`, used for CI/headless invocations) calls the Anthropic SDK directly and also accepts a full Anthropic model ID here as a pass-through escape hatch (e.g. `"claude-opus-4-6"`). A full model ID does **not** work for interactive Agent/Task tool calls in command specs — those only accept the short-alias enum (`sonnet`/`opus`/`haiku`/`fable`). Prefer the short alias unless you specifically need a headless-only override. `subagent_model` is interactive-only — it has no headless-runner equivalent, since the headless runner does not spawn child sub-agents.
156
+
157
+ Projects without this section see no behavior change — everything defaults exactly as it did before these fields existed.
158
+
159
+ **Commands that use this section**: all commands with an "Agent model policy" line (`default_model`, nearly every command spec) and all commands that spawn internal sub-agents — `orchestrate`, `review-pr`, `analytics`, `incident-response` (`subagent_model`).
160
+
161
+ ---
162
+
128
163
  ## `repos` (OPTIONAL)
129
164
 
130
165
  Multi-repository routing. Use this when your project spans multiple GitHub repositories and you want to route issues to satellite repos by prefix.
@@ -571,6 +606,26 @@ Colors are grouped by semantic meaning:
571
606
 
572
607
  ---
573
608
 
609
+ ## GitHub App Install
610
+
611
+ During `npx forgedock`, the installer prompts you to install the ForgeDock GitHub App (`rapiercraft-forgedock`) on your account or org.
612
+
613
+ **What installing it does today**: registers the app against the account/org you choose. That's it.
614
+
615
+ **What it does NOT do (yet)**:
616
+ - It does **not** create a bot token. GitHub App *installation* tokens can only be minted with the app's private key, and that key is held solely by RapierCraft Studios (the app's owner) — your installation grants *your* installation ID against *RapierCraft's* app, not a key you can use yourself.
617
+ - It does **not** auto-refresh anything, and there is no background process managing token expiry.
618
+ - It does **not** change which `gh` auth context pipeline commands (`/work-on`, `/orchestrate`, etc.) use. Every pipeline `gh` call always uses whatever auth is currently active in your shell — your personal token unless you've manually configured something else.
619
+ - It does **not** wire up webhook-driven "automatic pipeline triggers" — no webhook receiver exists yet.
620
+
621
+ **Why this matters**: GitHub App installation tokens get materially higher API rate limits than a personal access token, and actions taken with them are attributed to the bot (`rapiercraft-forgedock[bot]`) rather than your personal account. Those benefits are real, but delivering them to arbitrary installers requires a hosted token-minting backend service that doesn't exist yet (tracked as forge#1890). Installing the app today is safe and free, but on its own changes nothing about how the pipeline authenticates.
622
+
623
+ `npx forgedock doctor` reports this status explicitly (Check 12: "GitHub App / bot token") so you aren't left guessing whether a bot token is active.
624
+
625
+ **If you want bot-token behavior today**: you'd need to register your own GitHub App (with your own private key), and drive your own token-refresh loop (mint an installation token via the app's `/app/installations/{id}/access_tokens` API, then `gh auth login --with-token`, repeating before the ~1-hour expiry). This is an advanced, self-hosted setup — ForgeDock does not package a generic version of this for you.
626
+
627
+ ---
628
+
574
629
  ## `marketing` (OPTIONAL)
575
630
 
576
631
  Controls opt-in growth features such as 'Powered by ForgeDock' footers on PR descriptions created by the pipeline.
@@ -730,6 +785,53 @@ See [Per-Directory State Registry](#per-directory-state-registry) below for how
730
785
 
731
786
  ---
732
787
 
788
+ ## Persisted Toolset Home (`~/.forge/`)
789
+
790
+ `npx`/`npm` never installs `forgedock` to a stable location on your machine — it resolves to wherever `npx`'s cache (or a global npm install, or a git clone) happens to physically live. Historically, `~/.claude/commands/` and the SessionStart hook's script path were symlinked **directly** from that ephemeral location. When npm/npx (or the OS) later evicted its cache, those symlinks silently dangled — Claude Code sessions stopped getting ForgeDock context with no error shown.
791
+
792
+ As of this feature, every `npx forgedock` (or `npx forgedock update`) run copies ForgeDock's own installable payload into a stable, version-independent home:
793
+
794
+ ```
795
+ ~/.forge/
796
+ bin/ — the same bin/ tree bundled with the resolved package (hooks, CLI entry points)
797
+ commands/ — the slash-command specs (.md)
798
+ scripts/ — the universal pipeline-agent scripts (classify-lane.sh, etc.)
799
+ templates/ — scaffold templates (e.g. the demo repo)
800
+ version — plain-text file containing the source package's version (e.g. "1.7.2")
801
+ ```
802
+
803
+ `~/.claude/commands/` symlinks and the SessionStart hook's baked-in script path are then built from **this** copy, not the original ephemeral source — so they keep working even after the npm/npx cache that originally served them is cleared.
804
+
805
+ ### When It Runs
806
+
807
+ | Command | Behavior |
808
+ |---------|----------|
809
+ | `npx forgedock` / `npx forgedock install` | Copies the resolved package's payload into `~/.forge/` (content-compared — unchanged files are never rewritten) before linking commands, then links from `~/.forge/`, not the original source |
810
+ | `npx forgedock init` | Does not touch `~/.forge/` — `init` only writes `forge.yaml`, it never installs commands |
811
+ | `npx forgedock update` (npm/npx install) | Refreshes `~/.forge/` from whatever the currently-resolved package looks like, then relinks |
812
+ | `npx forgedock update` (git-clone install) | **Does not** touch `~/.forge/` at all — see the exemption below |
813
+ | `npx forgedock doctor` | Reports `~/.forge/version` and confirms `~/.forge/{bin,commands,scripts,templates}` exist |
814
+
815
+ The copy is idempotent: files whose content is already byte-identical are never rewritten, so steady-state re-runs are fast and don't generate false "updated" noise.
816
+
817
+ ### The Git-Clone Exemption
818
+
819
+ If you run ForgeDock from a git clone (or a git worktree) of the repo itself — the development/self-hosted install mode — nothing is copied into `~/.forge/`. A git clone is already a stable, user-owned location; copying it into `~/.forge/` and linking commands from the copy instead of the clone would silently disconnect `git pull` (or `npx forgedock update`'s git branch) from what's actually linked into `~/.claude/commands/`. So:
820
+
821
+ - `~/.claude/commands/` continues to symlink directly from the clone, exactly as before this feature.
822
+ - `~/.forge/{bin,commands,scripts,templates}` correctly does **not** exist for this install mode — `npx forgedock doctor` treats that absence as healthy, not a warning.
823
+
824
+ ### Not the Same as `~/.forge/{runs,index}`
825
+
826
+ `~/.forge/` also hosts two **unrelated, pre-existing** directories used by other parts of ForgeDock:
827
+
828
+ - `~/.forge/runs/` — durable engine run state for `npx forgedock run-issue` / `resume-stalled` (see `bin/engine.mjs`).
829
+ - `~/.forge/index/` — the `recall` knowledge index (see `bin/recall.mjs`, `docs/FORGE-PROTOCOL.md`).
830
+
831
+ These are **engine data**, not the toolset itself, and this feature does not read, write, or otherwise change them. `~/.forge/{bin,commands,scripts,templates,version}` (this section) and `~/.forge/{runs,index}` (engine data) are unrelated siblings that merely happen to live under the same `~/.forge/` root — don't conflate the two when reading `~/.forge/`'s contents.
832
+
833
+ ---
834
+
733
835
  ## Per-Directory State Registry
734
836
 
735
837
  ForgeDock tracks per-directory opt-out state in a central registry file on the local machine.
@@ -810,6 +912,90 @@ npx forgedock status [dir] # Show a directory's resolved state (default: cwd)
810
912
 
811
913
  ---
812
914
 
915
+ ## Install Receipt
916
+
917
+ ForgeDock writes a machine-readable receipt of what the last install or update actually did, so debugging drift (wrong commands installed, wrong tier, stale hooks) can start from a recorded fact instead of re-deriving state from scratch.
918
+
919
+ ### Receipt File Location
920
+
921
+ ```
922
+ ~/.forge/install-receipt.json
923
+ ```
924
+
925
+ The directory (`~/.forge/`) is created automatically on first write with `mkdir(..., { recursive: true })`. The file is never committed to version control — it is per-user, per-machine state.
926
+
927
+ ### When It's Written
928
+
929
+ - After every successful `npx forgedock install` (end of the onboarding journey, right after the "Forged." celebration screen).
930
+ - After every `npx forgedock update` — both the git-clone branch (fast-forward pull) and the npm branch (version-check advisory) route through the same repair step (`relinkAndHint()`), so the receipt is refreshed either way.
931
+
932
+ The write is best-effort and never blocks or fails the install/update it records: any error (permission denied, disk full, unusual `FORGE_HOME` layout) degrades silently to a no-op, matching the fail-open contract already used by the command-manifest and hook-registration steps.
933
+
934
+ ### Receipt Schema
935
+
936
+ ```json
937
+ {
938
+ "schemaVersion": 1,
939
+ "timestamp": "2026-07-10T12:00:00.000Z",
940
+ "forgedockVersion": "1.1.6",
941
+ "installMode": "npm",
942
+ "forgeHome": "/home/user/.npm/_npx/abc123/node_modules/forgedock",
943
+ "cwd": "/home/user/projects/my-repo",
944
+ "platform": {
945
+ "platform": "linux",
946
+ "platformLabel": "Linux",
947
+ "isWSL": false,
948
+ "wslDistro": null,
949
+ "shell": "bash"
950
+ },
951
+ "tier": "core",
952
+ "commands": {
953
+ "count": 42,
954
+ "list": ["work-on", "review-pr", "quality-gate", "orchestrate/phase-1-resolve"]
955
+ },
956
+ "hooks": {
957
+ "sessionStart": "registered",
958
+ "preToolUse": "registered",
959
+ "subagentStopEnforce": null
960
+ },
961
+ "forgeYaml": {
962
+ "present": true,
963
+ "validShape": true
964
+ }
965
+ }
966
+ ```
967
+
968
+ | Field | Type | Description |
969
+ |-------|------|--------------|
970
+ | `schemaVersion` | number | Schema version — currently `1` |
971
+ | `timestamp` | string | ISO-8601 timestamp of this install/update |
972
+ | `forgedockVersion` | string | `version` field from `{forgeHome}/package.json` (empty string if unreadable) |
973
+ | `installMode` | `"npm"` \| `"git-clone"` | Detected from whether `{forgeHome}/.git` exists — same predicate `update()` already uses to choose its update strategy |
974
+ | `forgeHome` | string | Absolute path to the ForgeDock package install (`FORGE_HOME`) |
975
+ | `cwd` | string | Absolute path to the project directory the command was run in |
976
+ | `platform` | object | `detectEnvironment()`'s output — platform, human label, WSL status/distro, detected shell |
977
+ | `tier` | `"core"` \| `"extras"` | Whether `--extras` was passed (opt-in command tier, see `install: extras` frontmatter) |
978
+ | `commands` | object | `count` and `list` (relative slash-command names) of everything installed for the resolved tier, sourced from `findMarkdownFiles()` — never a separately maintained list |
979
+ | `hooks.sessionStart` | string \| null | SessionStart hook registration status (`"registered"`, `"already"`, `"skipped-malformed"`) |
980
+ | `hooks.preToolUse` | string \| null | PreToolUse enforcement hook status, or `null` when the installed Claude Code version doesn't support it |
981
+ | `hooks.subagentStopEnforce` | string \| null | Cleanup status of the (retired) SubagentStop enforce hook |
982
+ | `forgeYaml.present` | boolean | Whether `forge.yaml` exists in `cwd` at receipt-write time |
983
+ | `forgeYaml.validShape` | boolean | Whether `forge.yaml` contains the 3 required top-level sections (`project:`, `paths:`, `branches:`) — a lightweight regex check, not a full YAML parse |
984
+
985
+ ### No PII or Secrets
986
+
987
+ The receipt never contains `process.env` values, GitHub tokens, API keys, or the contents of `forge.yaml` — only the `present`/`validShape` booleans above. Absolute filesystem paths (`forgeHome`, `cwd`) are included for drift debugging; they typically contain the OS username but nothing more sensitive, consistent with other per-machine ForgeDock state (e.g. `registry.json`'s path-keyed entries, see above).
988
+
989
+ ### Reading the Receipt
990
+
991
+ ```bash
992
+ cat ~/.forge/install-receipt.json
993
+ ```
994
+
995
+ There is no CLI subcommand to read it yet — `doctor()` reading the receipt to detect drift is a deliberately deferred stretch goal, not part of this feature.
996
+
997
+ ---
998
+
813
999
  ## Complete Example
814
1000
 
815
1001
  See [`forge.yaml.example`](../forge.yaml.example) at the repository root for a fully annotated example covering all sections.
@@ -13,3 +13,104 @@
13
13
  > and usable in any product with attribution. This older draft was mistakenly
14
14
  > marked AGPL-3.0; the protocol description itself has always been intended for
15
15
  > open, license-compatible adoption.
16
+
17
+ ---
18
+
19
+ ## Forge Ledger — Derived Artifact of the Protocol
20
+
21
+ The **Forge Ledger** is the persistent, file/symbol-keyed knowledge index built from FORGE
22
+ annotations. It is a _derived artifact_ of the protocol: the ledger is rebuilt by re-parsing
23
+ the annotation corpus on GitHub (source of truth), not by maintaining separate authoritative
24
+ state.
25
+
26
+ ### Architecture
27
+
28
+ ```
29
+ GitHub Issues/PRs (annotation corpus, source of truth)
30
+ │
31
+ ▼
32
+ scripts/build-knowledge-index.mjs ← incremental indexer (watermarked by issue.updated_at)
33
+ │ uses packages/protocol/src/parse.js (FORGE annotation parser)
34
+ ▼
35
+ ~/.forge/index/
36
+ knowledge.jsonl ← card records (one JSON per line, keyed by issue/kind/seq)
37
+ postings.json ← BM25-lite inverted index (term → card IDs with per-field tf)
38
+ manifest.json ← issue → content-hash map + watermark + card count
39
+ renames.jsonl ← git rename log (old path → new path for query-time resolution)
40
+ │
41
+ ▼ (mirrored to orphan branch)
42
+ forge-knowledge branch ← versioned, diffable, hydratable by any clone or CI
43
+ ```
44
+
45
+ ### Card Schema
46
+
47
+ Cards are keyed by provenance (`{issue}/{kind}/{seq}`), never by file path. Paths are
48
+ attributes maintained by the indexer; renames are metadata updates, not re-keys.
49
+
50
+ ```json
51
+ {
52
+ "schemaVersion": 1,
53
+ "id": "1370/pattern/0",
54
+ "kind": "pattern",
55
+ "issue": 1370,
56
+ "pattern": "...",
57
+ "rootCause": "...",
58
+ "prevention": "...",
59
+ "paths": ["bin/runner.mjs"],
60
+ "symbols": ["scrubEnv"],
61
+ "anchor": { "path": "bin/runner.mjs", "symbol": "scrubEnv" },
62
+ "status": "fresh",
63
+ "createdAt": "2026-07-04"
64
+ }
65
+ ```
66
+
67
+ ### CLI
68
+
69
+ ```
70
+ # Query by file path (exact lookup):
71
+ forge recall --file bin/runner.mjs --json
72
+
73
+ # Query by symbol:
74
+ forge recall --symbol scrubEnv --json
75
+
76
+ # Free-text BM25 search:
77
+ forge recall "env scrub proc environ" --k 5
78
+
79
+ # Index health:
80
+ forge recall --doctor
81
+
82
+ # Rebuild index from scratch:
83
+ node scripts/build-knowledge-index.mjs --full-rebuild
84
+
85
+ # Incremental update (used by close phase):
86
+ node scripts/build-knowledge-index.mjs --issue {NUMBER}
87
+ ```
88
+
89
+ ### Invariants
90
+
91
+ - **Full rebuild = incremental parity**: running a full rebuild produces byte-identical
92
+ card content to a sequence of incremental runs over the same issue range.
93
+ - **Idempotency**: re-indexing the same issue twice produces the same card content (card IDs
94
+ are deterministic; content is a pure function of current annotation state).
95
+ - **GitHub wins**: the index is always rebuildable from the annotation corpus alone. Deleting
96
+ `~/.forge/index/` loses nothing — a rebuild restores the same state.
97
+ - **Non-blocking**: ledger indexing at close time never stalls the pipeline. Failure is logged
98
+ and the close phase continues.
99
+
100
+ ### Staleness Detection (tiered, cheapest first)
101
+
102
+ 1. Current blob sha of `anchor.path` matches stored blob sha → **FRESH**
103
+ 2. `anchor.symbol` is still defined in the current file → **FRESH**
104
+ 3. Rolling-hash scan finds `snippetHash` elsewhere in file → **MOVED** (re-anchor)
105
+ 4. `snippetHash` found in a file touched by rename log → **MOVED** (re-anchor path)
106
+ 5. Otherwise → **STALE** (excluded from binding injection; recallable with `--include-stale`)
107
+
108
+ ### Integration Points
109
+
110
+ - **`commands/work-on/build/context.md` Phase C1**: uses `forge recall --file` for exact
111
+ file-path lookup; falls back to live `gh issue list --search` when index is absent.
112
+ - **`commands/work-on/close.md` Phase C5.3**: calls `build-knowledge-index.mjs --issue`
113
+ after merge to index the just-closed issue; advances watermark.
114
+
115
+ See issue [#1732](https://github.com/RapierCraftStudios/ForgeDock/issues/1732) for the
116
+ implementation history and design decisions.
@@ -592,7 +592,37 @@ These are invoked automatically by `/work-on` but can also be run standalone.
592
592
  | `/work-on/review` | Phase 5 | Review only |
593
593
  | `/work-on/close` | Phase 6 | Close & cleanup only |
594
594
  | `/work-on/decompose` | Phase 2 | Decompose into sub-issues |
595
- | `/review-pr-agents` | PR review | Agent catalog read by `/review-pr` during dispatch (not user-invokable directly) |
595
+ | `/work-on/remediate` | Phase 0A.1 | Remediate a `needs-human` PR: fix findings, re-review, re-gate (`/work-on <pr> --remediate`) |
596
+ | `/review-pr-agents` | PR review | Agent catalog router read by `/review-pr` during dispatch (not user-invokable directly) |
597
+
598
+ `/review-pr` (core) reads the following per-persona and shared-protocol templates from `commands/review-pr-agents/` at runtime during Phase 3C dispatch. They ship as part of every install alongside `/review-pr` — not user-invokable directly.
599
+
600
+ | Command | Phase | Purpose |
601
+ |---------|-------|---------|
602
+ | `/review-pr-agents/protocols` | Phase 3C | Shared Evidence-Based/Structured-Findings/Input-Scoping protocols read before every persona template |
603
+ | `/review-pr-agents/security` | Phase 3C | General security & quality scan (always runs) |
604
+ | `/review-pr-agents/auth` | Phase 3C | Auth conventions auditor (AUTH domain) |
605
+ | `/review-pr-agents/billing` | Phase 3C | Billing/payments auditor (BILLING domain) |
606
+ | `/review-pr-agents/concurrency` | Phase 3C | Concurrency/locking auditor (CONCURRENCY domain) |
607
+ | `/review-pr-agents/database` | Phase 3C | Database/migration auditor (DATABASE domain) |
608
+ | `/review-pr-agents/frontend` | Phase 3C | Frontend conventions auditor (FRONTEND domain) |
609
+ | `/review-pr-agents/infra` | Phase 3C | Infra/deploy auditor (INFRA domain) |
610
+ | `/review-pr-agents/api` | Phase 3C | API/SDK contract auditor (API domain) |
611
+ | `/review-pr-agents/scraper` | Phase 3C | Scraping/anti-bot auditor (SCRAPING domain) |
612
+
613
+ `/orchestrate` (core) reads the following phase files from `commands/orchestrate/` at runtime. They ship as part of every install alongside `/orchestrate` — not user-invokable directly.
614
+
615
+ | Command | Phase | Purpose |
616
+ |---------|-------|---------|
617
+ | `/orchestrate/config` | Phase 0 | Hard rules, config resolution, multi-repo support — read first |
618
+ | `/orchestrate/phase-1-resolve` | Phase 1 | Resolve the issue set from input |
619
+ | `/orchestrate/phase-2-triage` | Phase 2 | Investigation-first triage, Wave 0 |
620
+ | `/orchestrate/phase-2.5-synthesis` | Phase 2.5 | Investigation synthesis and deconfliction |
621
+ | `/orchestrate/phase-3-dependency` | Phase 3 | Dependency analysis, DAG construction, execution plan |
622
+ | `/orchestrate/phase-4-execution` | Phase 4 | Streaming DAG execution, agent dispatch, stall detection |
623
+ | `/orchestrate/phase-5-cleanup` | Phase 5 | Post-batch cleanup sweep and agent audit |
624
+ | `/orchestrate/phase-6-report` | Phase 6 | Consolidated report and pipeline summary |
625
+ | `/orchestrate/safety` | Reference | Safety rules and examples |
596
626
 
597
627
  ---
598
628
 
@@ -49,9 +49,9 @@ Run the installer with `npx` from your project directory:
49
49
  npx forgedock
50
50
  ```
51
51
 
52
- This installs all 25+ ForgeDock command specs into the current project (project-scoped by default) — making them available as slash commands in Claude Code sessions for this repo.
52
+ This installs all 25+ ForgeDock command specs into `~/.claude/commands/` — making them available as slash commands in every Claude Code session on this machine, not just this repo.
53
53
 
54
- > **Want commands available across all projects on this machine?** Use `npx forgedock --global` to install into `~/.claude/commands/` instead. Project-scoped is the default for new installs; use `--global` as an explicit opt-in.
54
+ > Install is always global. `--global` is still accepted on the command line for backward compatibility, but it's a no-op — `npx forgedock` and `npx forgedock --global` do exactly the same thing.
55
55
 
56
56
  **Verify the install:**
57
57
 
@@ -75,7 +75,7 @@ ForgeDock Doctor — Installation Health Check
75
75
 
76
76
  It exits `0` when everything passes and `1` if any check fails, so you can also use it in CI.
77
77
 
78
- > Prefer a quick spot-check? `ls .claude/commands/ | grep -E "work-on|review-pr|quality-gate"` should list `work-on.md`, `review-pr.md`, and `quality-gate.md`. (For a `--global` install, use `ls ~/.claude/commands/` instead.)
78
+ > Prefer a quick spot-check? `ls ~/.claude/commands/ | grep -E "work-on|review-pr|quality-gate"` should list `work-on.md`, `review-pr.md`, and `quality-gate.md`.
79
79
 
80
80
  ---
81
81
 
@@ -205,7 +205,7 @@ The quick fixes below cover the most common first-run issues. For the full list
205
205
 
206
206
  **`/work-on` not found in Claude Code**
207
207
 
208
- The symlink wasn't created. Re-run `npx forgedock` and check `ls .claude/commands/` (project-scoped, default) or `ls ~/.claude/commands/` (if you used `--global`).
208
+ The symlink wasn't created. Re-run `npx forgedock` and check `ls ~/.claude/commands/`.
209
209
 
210
210
  **`forge.yaml` not found**
211
211
 
@@ -221,37 +221,12 @@ The issue may have been marked `workflow:invalid`. Check the issue comments for
221
221
 
222
222
  ---
223
223
 
224
- ## Migrating from a global install
224
+ ## A note on install location
225
225
 
226
- If you installed ForgeDock before project-scoped became the default (prior to v1.0.22), your commands live in `~/.claude/commands/`. You have two options:
227
-
228
- **Option A: Keep the global install (no action required)**
229
-
230
- Add `--global` to all future `npx forgedock install/update/uninstall/doctor` calls. Your existing setup is unchanged.
231
-
232
- ```bash
233
- npx forgedock update --global # re-link after a version bump
234
- ```
235
-
236
- **Option B: Switch to project-scoped (recommended for per-repo version control)**
237
-
238
- 1. Remove the global install:
239
- ```bash
240
- npx forgedock uninstall --global
241
- ```
242
- 2. In each repo you want ForgeDock in, run a fresh project-scoped install:
243
- ```bash
244
- cd /path/to/your-repo
245
- npx forgedock install
246
- ```
247
- 3. Verify:
248
- ```bash
249
- npx forgedock doctor # auto-detects project-scoped install
250
- ```
251
-
252
- **Detect your current mode:**
226
+ ForgeDock briefly shipped a project-scoped-by-default install mode. It was backed out after causing a "split-brain" bug (`doctor`/`status` assumed project-scoped while the installer still wrote globally — [#1589](https://github.com/RapierCraftStudios/ForgeDock/issues/1589)), so install has always been global (`~/.claude/commands/`) since. If you have scripts or CI that still pass `--global`, there's nothing to change — it's accepted for backward compatibility and does exactly what a plain `npx forgedock` does.
253
227
 
254
228
  ```bash
255
- npx forgedock doctor # prints "Mode: project-scoped" or "Mode: global"
256
- npx forgedock status # shows install path
229
+ npx forgedock update # re-link after a version bump
230
+ npx forgedock doctor # confirm the install is healthy
231
+ npx forgedock status # shows the install path (~/.claude/commands/)
257
232
  ```
@@ -56,7 +56,7 @@ No more coordinating between agent sessions. No more lost context. No more start
56
56
  npx forgedock
57
57
  ```
58
58
 
59
- Installs commands into the current project (project-scoped by default). Add `--global` to install into `~/.claude/commands/` for all projects on this machine.
59
+ Installs commands into `~/.claude/commands/`, available in every Claude Code session on this machine. `--global` is still accepted as a flag for backward compatibility, but install is always global — there's nothing to opt into.
60
60
 
61
61
  **Requirements**: Claude Code, Node.js 18+, `gh` CLI authenticated with GitHub.
62
62
 
@@ -449,6 +449,43 @@ artifact so a sub-issue builder can fetch upstream context directly.
449
449
  <!-- FORGE:PRIOR_GIST: {https://example.com/artifact} -->
450
450
  ```
451
451
 
452
+ #### Security: Gist Visibility — Normative Requirement <!-- Added: forge#1729 -->
453
+
454
+ **All FORGE gist types (`FORGE:KNOWLEDGE_GIST`, `FORGE:MILESTONE_INDEX`, `FORGE:PRIOR_GIST`,
455
+ and the memory-index Gist referenced by `FORGE:MEMORY_INDEX` in `close.md`) MUST be created
456
+ secret. Publishing them as public Gists is a protocol violation.**
457
+
458
+ Rationale: these artifacts embed investigation findings — root causes, file paths, affected
459
+ service names, and security details. A public Gist exposes this content to a world-readable
460
+ URL with no access control. `gh gist create` is secret by default; the requirement is that
461
+ no pipeline spec or operator script must pass `--public` when creating any of these artifacts.
462
+
463
+ **Enforcement**: The `pre-tool-use.mjs` hook (Rule 3) hard-blocks `gh gist create --public`
464
+ in pipeline sessions. The `doctor-pipeline-state.sh` check I8 audits existing annotation-referenced
465
+ gists and reports a CRITICAL finding if any are public.
466
+
467
+ **Migration** (if a public gist is found): GitHub does not support changing a Gist's visibility
468
+ in place. To replace a public pipeline gist with a secret one:
469
+
470
+ ```bash
471
+ # 1. Save current content
472
+ gh gist view {GIST_ID} > /tmp/gist_migrate.md
473
+ [ -s /tmp/gist_migrate.md ] || { echo "ABORT: empty save"; exit 1; }
474
+
475
+ # 2. Delete the public gist
476
+ gh gist delete {GIST_ID} --yes
477
+
478
+ # 3. Recreate secret (no --public) with same description
479
+ gh gist create /tmp/gist_migrate.md --desc "{ORIGINAL_DESCRIPTION}"
480
+ # gh gist create without --public creates a secret gist.
481
+
482
+ # 4. Update the FORGE annotation on the issue to point to the new URL
483
+ ```
484
+
485
+ **Override**: set `FORGE_ALLOW_PUBLIC_GIST=1` in the operator's shell environment before
486
+ starting Claude Code only if a public gist is genuinely required (e.g., sharing a demo
487
+ artifact). This must not be used for pipeline memory gists.
488
+
452
489
  ### 4.3 Control and error markers
453
490
 
454
491
  These markers carry no Markdown body; their presence is the signal.
@@ -1143,16 +1180,71 @@ separate, MIT-licensed npm package:
1143
1180
  **`@forgedock/protocol`** — available at `packages/protocol/` in the ForgeDock
1144
1181
  repository.
1145
1182
 
1146
- It provides:
1183
+ ### 13.1 Normative codec path
1184
+
1185
+ **`packages/protocol/` is the normative write and read path for all FORGE
1186
+ annotations.** Hand-rolled annotation construction (embedding raw
1187
+ `<!-- FORGE:TYPE -->` strings in inline heredocs or `--body` blocks) is
1188
+ **non-conformant** because it bypasses the codec's escaping and round-trip
1189
+ guarantees. Every producer MUST route annotation construction through one of:
1190
+
1191
+ 1. **`forge-annotation.sh write <TYPE> --field KEY=VALUE ...`** (AGPL-licensed,
1192
+ ships with the npm package at `scripts/forge-annotation.sh`), or
1193
+ 2. **`node packages/protocol/src/cli.js emit <TYPE> --field KEY=VALUE ...`**
1194
+ (MIT-licensed, part of the `@forgedock/protocol` package)
1195
+
1196
+ Both paths delegate to `emit()` from `packages/protocol/src/emit.js`, which
1197
+ applies the complete escaping and sanitization policy (§3.5 injection hardening).
1198
+
1199
+ ### 13.2 JavaScript API
1200
+
1201
+ ```js
1202
+ import { parse, validate, emit, emitPartial } from '@forgedock/protocol';
1203
+ ```
1147
1204
 
1148
1205
  - `parse(commentBody)` — extracts all annotations from a comment body string
1149
1206
  - `validate(annotation)` — checks conformance per §3–4; returns `{ valid, errors, warnings }`
1150
- - `emit(type, fields)` — produces well-formed annotation strings
1207
+ - `emit(type, fields)` — produces a well-formed annotation string; applies all §3.5 escaping
1151
1208
  - `emitPartial(type)` — produces the partial sentinel for a type (§3.3)
1152
1209
 
1153
- A conformance suite in `packages/protocol/fixtures/` covers all 13 reserved
1154
- types with valid and invalid examples. The CLI runner can be invoked against any
1155
- fixtures directory:
1210
+ ### 13.3 CLI subcommands
1211
+
1212
+ The CLI at `packages/protocol/src/cli.js` exposes three modes:
1213
+
1214
+ ```bash
1215
+ # Emit — print a well-formed annotation body to stdout
1216
+ node packages/protocol/src/cli.js emit INVESTIGATOR \
1217
+ --field Verdict=CONFIRMED --field Confidence=HIGH \
1218
+ --field Severity=MEDIUM --field "Task Type=Bug Fix" \
1219
+ --field "Decomposition Assessment=NO — single fix."
1220
+
1221
+ # Parse — read a comment body from stdin, extract a field value
1222
+ echo '<!-- FORGE:INVESTIGATOR -->...' | \
1223
+ node packages/protocol/src/cli.js parse --type INVESTIGATOR --field Verdict
1224
+ # → CONFIRMED
1225
+
1226
+ # Conformance runner (legacy — no subcommand keyword)
1227
+ node packages/protocol/src/cli.js fixtures/
1228
+ ```
1229
+
1230
+ The `emit` subcommand also supports the Base64url machine-surface form for
1231
+ `FORGE:CARD` annotations (design decision 2026-07-08: encoding beats escaping):
1232
+
1233
+ ```bash
1234
+ node packages/protocol/src/cli.js emit CARD \
1235
+ --field issue=1370 --field status=merged --b64
1236
+ # → <!-- FORGE:CARD: v1 sha:7234c2d8 b64:eyJ... -->
1237
+ ```
1238
+
1239
+ The Base64url alphabet `[A-Za-z0-9_-]` cannot contain `>`, `<`, or `!`, making
1240
+ `-->`, `--!>`, and `<!--` structurally unrepresentable in the machine surface.
1241
+
1242
+ ### 13.4 Conformance suite
1243
+
1244
+ A conformance suite in `packages/protocol/fixtures/` covers all reserved types
1245
+ with valid and invalid examples, including adversarial payloads that reproduce
1246
+ historical escaping vulnerabilities. The suite can be run against any fixtures
1247
+ directory:
1156
1248
 
1157
1249
  ```bash
1158
1250
  npx @forgedock/protocol fixtures/
@@ -14,11 +14,14 @@ See also: [FORGE Annotation Protocol §6](forge-protocol-v1.md#6-label-state-mac
14
14
  | `workflow:ready-to-build` | Investigation complete, build not started | Investigator agent |
15
15
  | `workflow:building` | Build phase active | Builder agent |
16
16
  | `workflow:in-review` | PR created, review active | Orchestrator agent |
17
+ | `workflow:awaiting-merge` | Remediated + re-reviewed, awaiting a human merge decision | Review-pr Phase 8 (auto-merge guard) |
17
18
  | `workflow:merged` | PR merged, issue closed | Close phase agent |
18
19
  | `workflow:invalid` | Issue closed as invalid | Investigator agent |
19
20
  | `workflow:decomposed` | Issue decomposed into sub-issues | Decomposer agent |
20
21
  | `needs-human` | Pipeline blocked, human intervention required | Any agent on error |
21
22
 
23
+ `workflow:awaiting-merge` vs `needs-human`: both are terminal (the pipeline stops advancing the issue automatically), but they mean different things. `needs-human` means the pipeline hit a condition it cannot resolve on its own (genuinely blocked — conflicting PR, failed verdict, calibration/trust escalation, etc.) and a human must diagnose and act. `workflow:awaiting-merge` means the opposite: the PR was previously escalated to `needs-human`, has since been remediated and re-reviewed to a clean `APPROVED` verdict with no mergeability blockers, but does not yet meet the automated auto-land bar (see forge#1809 Q1) — a human only needs to click merge, not diagnose a problem. `scripts/transition-label.sh` only clears `needs-human` (best-effort) when the target state is `workflow:awaiting-merge` — every other forward transition leaves a pre-existing `needs-human` label untouched, preserving its sticky/terminal semantics.
24
+
22
25
  ## Transitions
23
26
 
24
27
  ```
@@ -27,6 +30,7 @@ See also: [FORGE Annotation Protocol §6](forge-protocol-v1.md#6-label-state-mac
27
30
  → workflow:ready-to-build [Phase 2: Investigation complete]
28
31
  → workflow:building [Phase 3: Build started]
29
32
  → workflow:in-review [Phase 4: PR created]
33
+ → workflow:awaiting-merge [TERMINAL] [Phase 5/review-pr: re-reviewed after needs-human, awaiting human merge]
30
34
  → workflow:merged [TERMINAL] [Phase 5: PR merged]
31
35
  → workflow:invalid [TERMINAL] [Any phase: closed as invalid]
32
36
  → workflow:decomposed [TERMINAL] [Phase 2: split into sub-issues]
@@ -40,6 +44,7 @@ Processing stops when any of these labels is set:
40
44
  - `workflow:merged`
41
45
  - `workflow:invalid`
42
46
  - `workflow:decomposed`
47
+ - `workflow:awaiting-merge`
43
48
  - `needs-human`
44
49
 
45
50
  ## Label Exclusivity
@@ -49,7 +54,7 @@ At most one `workflow:*` label should be active on an issue at any time. When tr
49
54
  ```bash
50
55
  gh issue edit {NUMBER} -R {REPO} \
51
56
  --add-label "workflow:building" \
52
- --remove-label "workflow:investigating,workflow:ready-to-build,workflow:in-review,workflow:merged,workflow:invalid,workflow:decomposed"
57
+ --remove-label "workflow:investigating,workflow:ready-to-build,workflow:in-review,workflow:awaiting-merge,workflow:merged,workflow:invalid,workflow:decomposed"
53
58
  ```
54
59
 
55
60
  This is enforced by `scripts/transition-label.sh` — use `resolve_script 'transition-label'` rather than calling `gh issue edit` directly when possible.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgedock",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "Autonomous dev pipeline for Claude Code. GitHub as institutional memory — every issue, PR, and annotation survives context resets and makes the next agent smarter.",
5
5
  "bin": {
6
6
  "forgedock": "bin/forgedock.mjs"
@@ -40,12 +40,13 @@
40
40
  "examples/",
41
41
  "packages/protocol/src/",
42
42
  "scripts/",
43
+ "!scripts/refresh-bot-token.sh",
43
44
  "templates/",
44
45
  "LICENSE",
45
46
  "README.md"
46
47
  ],
47
48
  "scripts": {
48
- "test": "node --test \"bin/tests/**/*.test.mjs\"",
49
+ "test": "node --test \"bin/tests/**/*.test.mjs\" \"packages/protocol/test/**/*.test.mjs\"",
49
50
  "docs:dev": "vitepress dev docs/site",
50
51
  "docs:build": "vitepress build docs/site",
51
52
  "docs:preview": "vitepress preview docs/site"