@garygentry/feature-forge 0.2.6 → 0.2.7

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 (102) hide show
  1. package/adapters/claude/.feature-forge-bundle.json +1 -1
  2. package/adapters/claude/references/portable-root.md +18 -9
  3. package/adapters/claude/references/shared-conventions.md +21 -4
  4. package/adapters/claude/references/stage-exit-protocol.md +1 -1
  5. package/adapters/claude/references/vendor-construct-inventory.md +1 -0
  6. package/adapters/claude/scripts/forge-session.py +248 -5
  7. package/adapters/claude/skills/forge/SKILL.md +9 -9
  8. package/adapters/claude/skills/forge-0-epic/SKILL.md +5 -5
  9. package/adapters/claude/skills/forge-0-epic/references/edit-mode.md +2 -2
  10. package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
  11. package/adapters/claude/skills/forge-1-prd/SKILL.md +1 -1
  12. package/adapters/claude/skills/forge-2-tech/SKILL.md +1 -1
  13. package/adapters/claude/skills/forge-3-specs/SKILL.md +1 -1
  14. package/adapters/claude/skills/forge-4-backlog/SKILL.md +1 -1
  15. package/adapters/claude/skills/forge-5-loop/SKILL.md +2 -2
  16. package/adapters/claude/skills/forge-6-docs/SKILL.md +1 -1
  17. package/adapters/claude/skills/forge-bootstrap/SKILL.md +4 -4
  18. package/adapters/claude/skills/forge-init/SKILL.md +1 -1
  19. package/adapters/claude/skills/forge-verify/SKILL.md +1 -1
  20. package/adapters/claude/skills/forge-verify/references/verification-checklists.md +1 -1
  21. package/adapters/codex/.feature-forge-bundle.json +1 -1
  22. package/adapters/codex/references/portable-root.md +18 -9
  23. package/adapters/codex/references/shared-conventions.md +21 -4
  24. package/adapters/codex/references/stage-exit-protocol.md +1 -1
  25. package/adapters/codex/references/vendor-construct-inventory.md +1 -0
  26. package/adapters/codex/scripts/forge-session.py +248 -5
  27. package/adapters/codex/skills/forge/SKILL.md +9 -9
  28. package/adapters/codex/skills/forge-0-epic/SKILL.md +5 -5
  29. package/adapters/codex/skills/forge-0-epic/references/edit-mode.md +2 -2
  30. package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
  31. package/adapters/codex/skills/forge-1-prd/SKILL.md +1 -1
  32. package/adapters/codex/skills/forge-2-tech/SKILL.md +1 -1
  33. package/adapters/codex/skills/forge-3-specs/SKILL.md +1 -1
  34. package/adapters/codex/skills/forge-4-backlog/SKILL.md +1 -1
  35. package/adapters/codex/skills/forge-5-loop/SKILL.md +2 -2
  36. package/adapters/codex/skills/forge-6-docs/SKILL.md +1 -1
  37. package/adapters/codex/skills/forge-bootstrap/SKILL.md +4 -4
  38. package/adapters/codex/skills/forge-init/SKILL.md +1 -1
  39. package/adapters/codex/skills/forge-verify/SKILL.md +1 -1
  40. package/adapters/codex/skills/forge-verify/references/verification-checklists.md +1 -1
  41. package/adapters/copilot/.feature-forge-bundle.json +1 -1
  42. package/adapters/copilot/references/portable-root.md +18 -9
  43. package/adapters/copilot/references/shared-conventions.md +21 -4
  44. package/adapters/copilot/references/stage-exit-protocol.md +1 -1
  45. package/adapters/copilot/references/vendor-construct-inventory.md +1 -0
  46. package/adapters/copilot/scripts/forge-session.py +248 -5
  47. package/adapters/copilot/skills/forge/forge.md +9 -9
  48. package/adapters/copilot/skills/forge-0-epic/forge-0-epic.md +5 -5
  49. package/adapters/copilot/skills/forge-0-epic/references/edit-mode.md +2 -2
  50. package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
  51. package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +1 -1
  52. package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +1 -1
  53. package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +1 -1
  54. package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +1 -1
  55. package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +2 -2
  56. package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +1 -1
  57. package/adapters/copilot/skills/forge-bootstrap/forge-bootstrap.md +4 -4
  58. package/adapters/copilot/skills/forge-init/forge-init.md +1 -1
  59. package/adapters/copilot/skills/forge-verify/forge-verify.md +1 -1
  60. package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +1 -1
  61. package/adapters/cursor/.feature-forge-bundle.json +1 -1
  62. package/adapters/cursor/references/portable-root.md +18 -9
  63. package/adapters/cursor/references/shared-conventions.md +21 -4
  64. package/adapters/cursor/references/stage-exit-protocol.md +1 -1
  65. package/adapters/cursor/references/vendor-construct-inventory.md +1 -0
  66. package/adapters/cursor/scripts/forge-session.py +248 -5
  67. package/adapters/cursor/skills/forge/forge.mdc +9 -9
  68. package/adapters/cursor/skills/forge-0-epic/forge-0-epic.mdc +5 -5
  69. package/adapters/cursor/skills/forge-0-epic/references/edit-mode.md +2 -2
  70. package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
  71. package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +1 -1
  72. package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +1 -1
  73. package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +1 -1
  74. package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +1 -1
  75. package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +2 -2
  76. package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +1 -1
  77. package/adapters/cursor/skills/forge-bootstrap/forge-bootstrap.mdc +4 -4
  78. package/adapters/cursor/skills/forge-init/forge-init.mdc +1 -1
  79. package/adapters/cursor/skills/forge-verify/forge-verify.mdc +1 -1
  80. package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +1 -1
  81. package/adapters/gemini/.feature-forge-bundle.json +1 -1
  82. package/adapters/gemini/gemini-extension.json +1 -1
  83. package/adapters/gemini/references/portable-root.md +18 -9
  84. package/adapters/gemini/references/shared-conventions.md +21 -4
  85. package/adapters/gemini/references/stage-exit-protocol.md +1 -1
  86. package/adapters/gemini/references/vendor-construct-inventory.md +1 -0
  87. package/adapters/gemini/scripts/forge-session.py +248 -5
  88. package/adapters/gemini/skills/forge/forge.md +9 -9
  89. package/adapters/gemini/skills/forge-0-epic/forge-0-epic.md +5 -5
  90. package/adapters/gemini/skills/forge-0-epic/references/edit-mode.md +2 -2
  91. package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
  92. package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +1 -1
  93. package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +1 -1
  94. package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +1 -1
  95. package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +1 -1
  96. package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +2 -2
  97. package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +1 -1
  98. package/adapters/gemini/skills/forge-bootstrap/forge-bootstrap.md +4 -4
  99. package/adapters/gemini/skills/forge-init/forge-init.md +1 -1
  100. package/adapters/gemini/skills/forge-verify/forge-verify.md +1 -1
  101. package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +1 -1
  102. package/package.json +1 -1
@@ -16,7 +16,7 @@ question goes through `AskUserQuestion`.
16
16
  Before offering any edit, validate the existing manifest:
17
17
 
18
18
  ```bash
19
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
19
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
20
20
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
21
21
  python3 "$R/scripts/epic-manifest.py" validate "{epic}" --specs-dir "{specsDir}" --json
22
22
  ```
@@ -77,7 +77,7 @@ status is **not** `not-started`, warn the user. Read the **live** status (never
77
77
  completion in prose):
78
78
 
79
79
  ```bash
80
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
80
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
81
81
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
82
82
  python3 "$R/scripts/epic-manifest.py" render-status "{epic}" --specs-dir "{specsDir}" --json
83
83
  ```
@@ -12,7 +12,7 @@ the write if it would introduce a cycle, dangling ref, duplicate, or schema viol
12
12
  flag surface (owned by 02 §7):
13
13
 
14
14
  ```bash
15
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
15
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
16
16
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
17
17
  # Add a feature — seeds EMPTY exposes/consumes; contracts are populated below.
18
18
  python3 "$R/scripts/epic-manifest.py" add-feature "{epic}" "{feature}" \
@@ -117,7 +117,7 @@ Write pipeline state conforming to `references/pipeline-state-schema.json`.
117
117
  **Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
118
118
 
119
119
  ```bash
120
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
120
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
121
121
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
122
122
  python3 "$R/scripts/forge-session.py" stage-exit --feature "{feature}" --stage forge-1-prd --specs-dir "{specsDir}" --host claude
123
123
  ```
@@ -194,7 +194,7 @@ Write pipeline state conforming to `references/pipeline-state-schema.json`.
194
194
  **Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
195
195
 
196
196
  ```bash
197
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
197
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
198
198
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
199
199
  python3 "$R/scripts/forge-session.py" stage-exit --feature "{feature}" --stage forge-2-tech --specs-dir "{specsDir}" --host claude
200
200
  ```
@@ -148,7 +148,7 @@ Write pipeline state conforming to `references/pipeline-state-schema.json`.
148
148
  **Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
149
149
 
150
150
  ```bash
151
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
151
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
152
152
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
153
153
  python3 "$R/scripts/forge-session.py" stage-exit --feature "{feature}" --stage forge-3-specs --specs-dir "{specsDir}" --host claude
154
154
  ```
@@ -142,7 +142,7 @@ Write pipeline state conforming to `references/pipeline-state-schema.json`. Foll
142
142
  **Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
143
143
 
144
144
  ```bash
145
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
145
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
146
146
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
147
147
  python3 "$R/scripts/forge-session.py" stage-exit --feature "{feature}" --stage forge-4-backlog --specs-dir "{specsDir}" --host claude
148
148
  ```
@@ -61,7 +61,7 @@ Read the resolved feature's `.pipeline-state.json`. **If it has no `epic` key, s
61
61
  1. Run `render-status "{epic}" --specs-dir "{specsDir}" --json` via the helper:
62
62
 
63
63
  ```bash
64
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
64
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
65
65
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
66
66
  python3 "$R/scripts/epic-manifest.py" \
67
67
  render-status "{epic}" --specs-dir "{specsDir}" --json
@@ -114,7 +114,7 @@ Verify the file exists on disk. If not, STOP and tell the user: "No backlog.json
114
114
 
115
115
  ### 1f. Branch Pre-flight (if using git)
116
116
 
117
- The runner commits each completed item straight onto the current branch, so guard against committing onto the default branch. Skip if not a git repo or `branchPerFeature` is false. Read the current branch (`git rev-parse --abbrev-ref HEAD`) and default branch (`git symbolic-ref --quiet refs/remotes/origin/HEAD`, else `main`/`master`). If `.pipeline-state.json` records a `branch` that differs from the current one, warn via `AskUserQuestion` (offer **switch back** or **proceed here**). Otherwise, if the current branch **is** the default, strongly recommend via `AskUserQuestion` creating/switching to `{branchPrefix}{feature}` (`git switch -c`, then record it to the state `branch` field) before the loop commits — still allowing **proceed on `{defaultBranch}`**. Never hard-stop.
117
+ The runner commits each item onto the current branch. Skip if not a git repo or `branchPerFeature` is false. Otherwise run the **Branch Reconciliation** block in `references/shared-conventions.md` (it runs `reconcile-branch` and, on `warn-drift` — you are on the default branch — strongly recommends creating `{branchPrefix}{feature}` via `AskUserQuestion` before the loop commits; on `adopt-current` it updates the recorded branch to the current one, never pushing you back to a stale/imposed branch). Never hard-stop.
118
118
 
119
119
  ## Step 2: Construct the Loop Command
120
120
 
@@ -44,7 +44,7 @@ Check `.pipeline-state.json` for `stages.forge-verify-impl`. If it is **absent**
44
44
  If the resolved feature has an `epic` back-pointer in its `.pipeline-state.json`, run:
45
45
 
46
46
  ```bash
47
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
47
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
48
48
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
49
49
  python3 "$R/scripts/epic-manifest.py" render-status "{epic}" --specs-dir "{specsDir}" --json
50
50
  ```
@@ -59,7 +59,7 @@ Every bash invocation begins with the byte-identical portable-root prelude, then
59
59
  helper. Pass `--specs-dir ./specs` (the default) so the gate allow-lists the specs directory.
60
60
 
61
61
  ```bash
62
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
62
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
63
63
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
64
64
  python3 "$R/scripts/forge-bootstrap.py" check "<target-dir>" --json --specs-dir ./specs
65
65
  ```
@@ -136,7 +136,7 @@ when running under a Claude host (e.g. `AskUserQuestion` is available), else `"c
136
136
  `CLAUDE.md` only when `host == "claude"`.
137
137
 
138
138
  ```bash
139
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
139
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
140
140
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
141
141
  python3 "$R/scripts/forge-bootstrap.py" scaffold "<target-dir>" --json --answers '<Answers JSON>'
142
142
  ```
@@ -144,7 +144,7 @@ python3 "$R/scripts/forge-bootstrap.py" scaffold "<target-dir>" --json --answers
144
144
  ### Step 5 — verify
145
145
 
146
146
  ```bash
147
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
147
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
148
148
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
149
149
  python3 "$R/scripts/forge-bootstrap.py" verify "<target-dir>" --json --answers '<Answers JSON>'
150
150
  ```
@@ -170,7 +170,7 @@ and removes the sentinel before staging so it never enters history. Read
170
170
  leave the sentinel in-progress (resumable) — do **not** declare success.
171
171
 
172
172
  ```bash
173
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
173
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
174
174
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
175
175
  python3 "$R/scripts/forge-bootstrap.py" commit "<target-dir>" --json --answers '<Answers JSON>' [--stage-only]
176
176
  ```
@@ -9,7 +9,7 @@ description: Initialize feature-forge configuration in the current project. Use
9
9
  Run the initialization script to create `forge.config.json` with default settings:
10
10
 
11
11
  ```bash
12
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
12
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
13
13
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
14
14
  bash "$R/scripts/forge-init.sh"
15
15
  ```
@@ -256,7 +256,7 @@ Do NOT mark as `findings-applied` — that happens after the fix pass.
256
256
  - For specs verification, also run the deterministic traceability validator to supplement agent-driven traceability checks. Include any uncovered requirements or orphaned references as findings:
257
257
 
258
258
  ```bash
259
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
259
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
260
260
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
261
261
  python3 "$R/scripts/validate-traceability.py" {resolvedFeatureDir}/PRD.md {resolvedFeatureDir}/ --json
262
262
  ```
@@ -192,7 +192,7 @@ findings to E01/E02/E03/E08. Then perform the judgment checks E04–E07 by readi
192
192
  manifest, EPIC.md, and completed members' specs.
193
193
 
194
194
  ```bash
195
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
195
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
196
196
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
197
197
  python3 "$R/scripts/epic-manifest.py" validate "{epic}" --specs-dir "{specsDir}" --json
198
198
  ```
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feature-forge",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "agent": "codex",
5
5
  "generatedBy": "python3 scripts/build-adapters.py"
6
6
  }
@@ -12,7 +12,7 @@ against the fenced block here, byte-for-byte.
12
12
  ## Canonical bootstrap prelude
13
13
 
14
14
  ```bash
15
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
15
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
16
16
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
17
17
  ```
18
18
 
@@ -24,25 +24,34 @@ makes several calls, add the prelude once and reuse `$R` for each. A fresh block
24
24
  prelude (per-block re-resolution). Worked example:
25
25
 
26
26
  ```bash
27
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
27
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
28
28
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
29
29
  python3 "$R/scripts/epic-manifest.py" render-status "{epic}" --specs-dir "{specsDir}" --json
30
30
  ```
31
31
 
32
32
  ## Invariants (do NOT "fix" these)
33
33
 
34
- 1. **Probes paths, not the env var.** The prelude's `for d in …` enumerates directory paths to
35
- locate an executable `forge-root.sh`; it contains no plugin-root environment variable. That is what lets
36
- a prelude occurrence satisfy the "zero residual var in canonical surfaces" rule while staying
37
- portable.
34
+ 1. **First hint is the Claude plugin-root env var; the rest are paths.** The prelude's
35
+ `for d in …` list leads with the `CLAUDE_PLUGIN_ROOT` env var (in default-empty `:-` form)
36
+ — the exact bundle dir Claude exports in every marketplace/plugin session — followed by the
37
+ directory-path candidates. The hint gives exact, glob-free resolution on any current/future
38
+ Claude layout (no version-skew window); when unset it expands to empty and is harmlessly
39
+ skipped, so other hosts fall through to the path candidates unchanged. This is the **one
40
+ sanctioned** appearance of that variable in canonical surfaces: spec-purity rule 3 allows it
41
+ by stripping the byte-pinned prelude before its residual-var scan (so a stray var anywhere
42
+ else — including the default-empty form — still fails), and `forge-agent-adapters-build`
43
+ translates it to `FEATURE_FORGE_ROOT` for non-Claude bundles (which `forge-root.sh` already
44
+ prefers). The exact literal is shown only in the fenced prelude above and audited in
45
+ `vendor-construct-inventory.md`.
38
46
  2. **First-discoverable-resolver-wins.** The `exec` inside the `$(…)` command substitution means
39
47
  the loop stops at the first directory holding an executable `forge-root.sh` and delegates ALL
40
48
  final root resolution to that script. The `for` list is a discovery order for `forge-root.sh`
41
49
  itself, not a fallback chain for the plugin root. Removing the `exec` to "keep looping" is a
42
50
  regression — once `exec`'d, the loop is replaced by the resolver process and never advances.
43
- 3. **Prelude candidate set is an agent-neutral bootstrap subset.** The prelude's `for d` list
44
- exists only to bootstrap-discover `forge-root.sh`; the authoritative multi-root probe lives in
45
- `forge-root.sh` step 2. The list enumerates install roots across agents — the Claude
51
+ 3. **Prelude candidate set is an agent-neutral bootstrap subset (after the env hint).** The
52
+ prelude's `for d` list exists only to bootstrap-discover `forge-root.sh`; the authoritative
53
+ multi-root probe lives in `forge-root.sh` step 2. After the leading env-var hint (invariant 1),
54
+ the list enumerates install roots across agents — the Claude
46
55
  skill/plugin dirs — including the marketplace-cache layout
47
56
  `~/.claude/plugins/cache/<marketplace>/feature-forge/<version>/`, listed before the
48
57
  single-star plugins glob so a versioned cache install always beats the marketplace clone —
@@ -81,7 +81,7 @@ Extract these config values (use defaults if not present):
81
81
  Before any file I/O against a feature's artifacts, resolve its directory through the deterministic helper rather than hardcoding `{specsDir}/{feature}/`. This makes flat (`{specsDir}/{feature}/`) and nested (`{specsDir}/{epic}/{feature}/`) layouts both resolve from a bare feature name (REQ-DIR-03), with standalone features behaving exactly as today.
82
82
 
83
83
  ```bash
84
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
84
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
85
85
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
86
86
  resolvedFeatureDir=$(python3 "$R/scripts/epic-manifest.py" \
87
87
  resolve "<feature>" --specs-dir "<specsDir>")
@@ -96,7 +96,7 @@ In both failure cases, do not fall back to a guessed path.
96
96
  **On `not-found`, check other branches before stopping.** With `branchPerFeature`, the feature's directory (and its `.pipeline-state.json`) may exist only on its topic branch — invisible from the default branch of a fresh clone. Before concluding the pipeline does not exist, run the read-only cross-branch discovery:
97
97
 
98
98
  ```bash
99
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
99
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
100
100
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
101
101
  python3 "$R/scripts/forge-session.py" discover-feature "<feature>" --specs-dir "<specsDir>" --json
102
102
  ```
@@ -124,7 +124,7 @@ Whenever a stage creates the specs tree for the first time (the first PRD or epi
124
124
  Run this after creating the feature/epic directory, before the stage's git commit:
125
125
 
126
126
  ```bash
127
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
127
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
128
128
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
129
129
  mkdir -p "<specsDir>"
130
130
  [ -f "<specsDir>/AGENTS.md" ] || cp "$R/references/templates/specs-hygiene/AGENTS.md" "<specsDir>/AGENTS.md"
@@ -151,7 +151,7 @@ After resolving the feature directory, check the feature's `.pipeline-state.json
151
151
  To obtain the manifest contracts and the live completion status of each dependency in one deterministic call, run `render-status` and read the per-feature `status` and the `consumes`/`exposes` arrays rather than re-deriving them:
152
152
 
153
153
  ```bash
154
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
154
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
155
155
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
156
156
  python3 "$R/scripts/epic-manifest.py" \
157
157
  render-status "<epic>" --specs-dir "<specsDir>" --json
@@ -194,6 +194,23 @@ Invoke this block at the **very start** of a pipeline entry point — `forge-1-p
194
194
 
195
195
  **Record the branch.** After this block resolves, write the resulting branch name to the feature's `.pipeline-state.json` top-level `branch` field (create/update it when the state file is first written for this stage). Downstream stages and `forge-5-loop` read it to detect drift back onto the default branch.
196
196
 
197
+ ## Branch Reconciliation
198
+
199
+ The recorded `branch` is a **self-healing hint, not gospel.** A hosted environment (Claude.ai remote, cloud agents) can impose an arbitrary session branch (e.g. `claude/<slug>`) that Branch Setup silently records; the user may then move the work to the intended topic branch, leaving the recorded field stale. Every branch-aware mechanism (the `forge-5-loop` guard, `discover-feature`) keys off that field, so a stale value actively misleads — the loop would offer to switch you *back* to the imposed branch. Invoke this block from `forge-5-loop`'s pre-flight (and any stage that acts on the recorded branch) to reconcile deterministically. Skip if not a git repo or `branchPerFeature` is false.
200
+
201
+ ```bash
202
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
203
+ [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
204
+ python3 "$R/scripts/forge-session.py" reconcile-branch --feature "{feature}" --specs-dir "{specsDir}" --json
205
+ ```
206
+
207
+ Act on the emitted `action` (source of truth is where the state actually resolves, not the recorded field):
208
+ - **`adopt-current`** — you are on a non-default topic branch where the state resolves, and the recorded `branch` differs (a stale/imposed value). Write `newBranch` into the state `branch` field with a **visible one-line note** ("recorded branch was `{stateBranch}`; work is on `{currentBranch}` — updating to match") — never silently, and **never push the user back** to the recorded branch (offer that only as a plain alternative).
209
+ - **`warn-drift`** — you are on the **default** branch and the state records a topic branch. Via `AskUserQuestion`, strongly recommend creating/switching to `{branchPrefix}{feature}` (then record it), still allowing **proceed on the default branch**. Never hard-stop.
210
+ - **`none`** / **`not-resolved`** — nothing to do; proceed.
211
+
212
+ If the helper is unavailable (non-Claude host without the resolver), fall back to the manual check: current branch differs from recorded → adopt the current branch unless it is the default, in which case recommend creating `{branchPrefix}{feature}`.
213
+
197
214
  ## Git Commit Protocol
198
215
 
199
216
  When `gitCommitAfterStage` is true, follow this exact order to avoid state inconsistency.
@@ -57,7 +57,7 @@ placeholders the skill resolves before running the command, exactly as elsewhere
57
57
  **Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
58
58
 
59
59
  ```bash
60
- R="$(bash -c 'for d in "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
60
+ R="$(bash -c 'for d in "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
61
61
  [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
62
62
  python3 "$R/scripts/forge-session.py" stage-exit {stage-exit-args} --specs-dir "{specsDir}" --host claude
63
63
  ```
@@ -25,6 +25,7 @@ vocabulary defined in `00-core-definitions.md` §8. No free-form values are perm
25
25
  | `argument-hint` (top-level frontmatter key) | 10 `skills/*/SKILL.md` (all skills **except** `forge-init`) | `relocated` | Claude-specific vendor key (constraint C-2). Moved verbatim to `metadata.argument-hint` per REQ-VND-01 (see `02-frontmatter-purity-and-inventory.md` §2). Value byte-identical; `description` untouched. |
26
26
  | `${CLAUDE_PLUGIN_ROOT}` — canonical invocations + prose | 23 occurrences across 9 canonical surfaces: `skills/forge-0-epic/SKILL.md` (12), `skills/forge/SKILL.md` (3), `skills/forge-5-loop/SKILL.md` (1), `skills/forge-6-docs/SKILL.md` (1), `skills/forge-init/SKILL.md` (1), `skills/forge-verify/SKILL.md` (1), `skills/forge-verify/references/verification-checklists.md` (1), `references/shared-conventions.md` (2), `agents/forge-verifier.md` (1) | `routed-through-resolver` | Claude-only env var. Routed through the byte-identical bootstrap prelude + `scripts/forge-root.sh` per REQ-RES-03. Mechanics owned by `03-portable-root-resolver.md`; recorded here for audit completeness. |
27
27
  | `${CLAUDE_PLUGIN_ROOT}` — sanctioned residual | 1 occurrence in `scripts/forge-root.sh` (env-fallback, REQ-RES-02 step 3) | `preserved-as-spec-allowed` | The single sanctioned residual: the resolver's documented Claude-compat fallback (REQ-RES-03 / REQ-RES-05). Exempt from the residual-var scan (`00-core-definitions.md` §6 `RESIDUAL_VAR_EXEMPT`). |
28
+ | `${CLAUDE_PLUGIN_ROOT:-}` — bootstrap-prelude first-hint (Chunk 2b) | 1 occurrence per prelude across every canonical stamp site (the byte-pinned `BOOTSTRAP_PRELUDE`) | `preserved-as-spec-allowed` | The prelude's first resolver candidate — exact, glob-free root resolution on any Claude layout; expands to empty and is skipped when unset. Rule 3 allows it by stripping the byte-pinned prelude before its scan (detection is by `${CLAUDE_PLUGIN_ROOT` prefix, so the `:-}` default form is not an escape hatch elsewhere). `forge-agent-adapters-build` translates it to `${FEATURE_FORGE_ROOT:-}` in non-Claude bundles. |
28
29
  | `${CLAUDE_PLUGIN_ROOT}` — in `hooks/hooks.json` | 1 occurrence in `hooks/hooks.json` | `out-of-canon` | Non-canonical Claude artifact (REQ-VND-04). Not a canonical surface; exempt from the REQ-RES-03 scan. Left in place. |
29
30
  | `hooks/hooks.json` SessionStart wiring | 1 file (`hooks/hooks.json`) — Claude `SessionStart` → `bash ${CLAUDE_PLUGIN_ROOT}/scripts/session-check.sh` | `out-of-canon` | Claude-specific plugin hook wiring (REQ-VND-04, decision D3). Preserved + documented so `forge-agent-adapters-build` treats it as a Claude artifact, not portable canon. |
30
31
  | (contingency) any other vendor invocation directive | none found in the audit | — | REQ-VND-02 contingency did not fire (see Notes). If one is later surfaced, add a row with `removed` or `out-of-canon` per `02-frontmatter-purity-and-inventory.md` §3. |