@entelligentsia/forgecli 1.0.14 → 1.0.20

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 (62) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/dist/CHANGELOG-forge-plugin.md +61 -0
  3. package/dist/extensions/forgecli/config-layer.d.ts +0 -16
  4. package/dist/extensions/forgecli/config-layer.js +0 -5
  5. package/dist/extensions/forgecli/config-layer.js.map +1 -1
  6. package/dist/extensions/forgecli/context-governor-compaction.d.ts +83 -0
  7. package/dist/extensions/forgecli/context-governor-compaction.js +302 -0
  8. package/dist/extensions/forgecli/context-governor-compaction.js.map +1 -0
  9. package/dist/extensions/forgecli/context-governor.d.ts +173 -0
  10. package/dist/extensions/forgecli/context-governor.js +618 -0
  11. package/dist/extensions/forgecli/context-governor.js.map +1 -0
  12. package/dist/extensions/forgecli/dashboard/component.d.ts +7 -4
  13. package/dist/extensions/forgecli/dashboard/component.js +80 -101
  14. package/dist/extensions/forgecli/dashboard/component.js.map +1 -1
  15. package/dist/extensions/forgecli/dashboard/register.js +7 -21
  16. package/dist/extensions/forgecli/dashboard/register.js.map +1 -1
  17. package/dist/extensions/forgecli/dashboard/theme.d.ts +27 -0
  18. package/dist/extensions/forgecli/dashboard/theme.js +91 -0
  19. package/dist/extensions/forgecli/dashboard/theme.js.map +1 -0
  20. package/dist/extensions/forgecli/fix-bug.js +59 -5
  21. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  22. package/dist/extensions/forgecli/forge-artifact-tool.js +2 -1
  23. package/dist/extensions/forgecli/forge-artifact-tool.js.map +1 -1
  24. package/dist/extensions/forgecli/forge-cli-schema.json +0 -4
  25. package/dist/extensions/forgecli/forge-subagent.d.ts +20 -1
  26. package/dist/extensions/forgecli/forge-subagent.js +17 -3
  27. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  28. package/dist/extensions/forgecli/forge-tools.js +3 -1
  29. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  30. package/dist/extensions/forgecli/hook-dispatcher.d.ts +3 -1
  31. package/dist/extensions/forgecli/hook-dispatcher.js +37 -3
  32. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  33. package/dist/extensions/forgecli/index.js +33 -1
  34. package/dist/extensions/forgecli/index.js.map +1 -1
  35. package/dist/extensions/forgecli/lib/halt-advisor.d.ts +19 -14
  36. package/dist/extensions/forgecli/lib/halt-advisor.js +36 -13
  37. package/dist/extensions/forgecli/lib/halt-advisor.js.map +1 -1
  38. package/dist/extensions/forgecli/orchestrator-status-bar.d.ts +3 -2
  39. package/dist/extensions/forgecli/orchestrator-status-bar.js +90 -60
  40. package/dist/extensions/forgecli/orchestrator-status-bar.js.map +1 -1
  41. package/dist/extensions/forgecli/run-sprint.d.ts +3 -1
  42. package/dist/extensions/forgecli/run-sprint.js +1 -0
  43. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  44. package/dist/extensions/forgecli/run-task.d.ts +34 -1
  45. package/dist/extensions/forgecli/run-task.js +144 -6
  46. package/dist/extensions/forgecli/run-task.js.map +1 -1
  47. package/dist/extensions/forgecli/thread-switcher.d.ts +4 -1
  48. package/dist/extensions/forgecli/thread-switcher.js +36 -21
  49. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  50. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +9 -0
  51. package/dist/forge-payload/.base-pack/workflows/plan_task.md +7 -0
  52. package/dist/forge-payload/.base-pack/workflows/review_code.md +4 -3
  53. package/dist/forge-payload/.base-pack/workflows/review_plan.md +4 -3
  54. package/dist/forge-payload/.base-pack/workflows/validate_task.md +4 -3
  55. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  56. package/dist/forge-payload/.schemas/migrations.json +132 -27
  57. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +4 -3
  58. package/dist/forge-payload/meta/workflows/meta-review-plan.md +4 -3
  59. package/dist/forge-payload/meta/workflows/meta-validate.md +4 -3
  60. package/dist/forge-payload/tools/collate.cjs +32 -0
  61. package/dist/forge-payload/tools/postflight-gate.cjs +56 -10
  62. package/package.json +2 -2
@@ -61,9 +61,10 @@ deps:
61
61
  - If present, extract:
62
62
  - `Iteration: N of M` — current attempt number and the configured limit
63
63
  - `Is final iteration: true/false`
64
- - If absent (user-invoked, not orchestrated): treat as iteration 1 of M, where M is
65
- read from `.forge/config.json` `maxReviewIterations` (default 3 if field absent).
66
- - Include `(iteration N of M)` in the opening line of the `PLAN_REVIEW.md` artifact.
64
+ - If absent (user-invoked, not orchestrated): treat as `iteration 1`, no limit do
65
+ NOT read any iteration cap from config. The orchestrator owns loop budgets; a human
66
+ standalone re-run is the escape hatch for stuck items (forge-engineering#34).
67
+ - Include `(iteration N of M)` (orchestrated) or `(standalone review)` in the opening line of the `PLAN_REVIEW.md` artifact.
67
68
  - If this is the final iteration (`N == M`) and the verdict is `Revision Required`,
68
69
  append a `### Next Steps` section to the artifact showing:
69
70
  ```
@@ -59,9 +59,10 @@ deps:
59
59
  - If present, extract:
60
60
  - `Iteration: N of M` — current attempt number and the configured limit
61
61
  - `Is final iteration: true/false`
62
- - If absent (user-invoked, not orchestrated): treat as iteration 1 of M, where M is
63
- read from `.forge/config.json` `maxReviewIterations` (default 3 if field absent).
64
- - Include `(iteration N of M)` in the opening line of the `VALIDATION_REPORT.md` artifact.
62
+ - If absent (user-invoked, not orchestrated): treat as `iteration 1`, no limit do
63
+ NOT read any iteration cap from config. The orchestrator owns loop budgets; a human
64
+ standalone re-run is the escape hatch for stuck items (forge-engineering#34).
65
+ - Include `(iteration N of M)` (orchestrated) or `(standalone review)` in the opening line of the `VALIDATION_REPORT.md` artifact.
65
66
  - If this is the final iteration (`N == M`) and the verdict is `Revision Required`,
66
67
  append a `### Next Steps` section to the artifact showing:
67
68
  ```
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge",
3
- "version": "1.2.14",
3
+ "version": "1.2.17",
4
4
  "description": "Self-enhancing AI software development lifecycle — generates project-specific SDLC instances from meta-definitions",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,4 +1,43 @@
1
1
  {
2
+ "1.2.16": {
3
+ "version": "1.2.17",
4
+ "date": "2026-06-04",
5
+ "notes": "Fix two deterministic false-halt bugs in the S26-T19 postflight output guard, exposed on its first live firing (the guards were dormant until the v1.2.16 base-pack recompile activated them; CART-S03-T01 plan halted despite a fully successful phase). (1) Require predicates: the materialized outputs blocks use bare record paths (summaries.plan.verdict) while the gate passed state={task:record}; every bare require evaluated undefined and failed unconditionally. readField now falls back to the entity record (state.task / state.bug) after the direct walk. (2) Artifact paths: {sprint} was substituted with the bare sprintId, dropping the sprints/ segment, so artifact checks probed <engineering>/<sprintId>/<taskId> instead of <engineering>/sprints/<sprintId>/<taskId> and always reported output-missing. New buildSubstitutions() resolves {sprint} to the path segment under engineering (sprints/<id> for tasks, bugs for bug records).",
6
+ "target": "tools:postflight-gate",
7
+ "regenerate": [
8
+ "tools"
9
+ ],
10
+ "breaking": false,
11
+ "manual": [
12
+ "Run /forge:update, then /forge:rebuild tools to re-vendor .forge/tools/postflight-gate.cjs. Without this, every phase with an outputs block halts with output-missing/require-failed despite succeeding."
13
+ ]
14
+ },
15
+ "1.2.15": {
16
+ "version": "1.2.16",
17
+ "date": "2026-06-04",
18
+ "notes": "Remove phase-side review-iteration self-limiting (forge-engineering#34). The standalone-invocation fallback in the review/validate workflows read maxReviewIterations from .forge/config.json — a redundant protection layer that produced a Key-not-found error on every review phase and, worse, worked against the recovery model: loop budgeting belongs solely to the orchestrator (run-task PHASE_PIPELINE maxIterations), exhaustion escalates to a human, and a deliberate human standalone re-run IS the escape hatch for stuck items — a phase consulting its own cap can refuse exactly that. The workflows now treat user-invoked runs as iteration 1 with no limit and never read an iteration cap from config; orchestrated runs keep taking N-of-M from the Review Loop Context block injected by the orchestrator.",
19
+ "target": "workflows:review_plan workflows:review_code workflows:validate_task",
20
+ "regenerate": [
21
+ "workflows"
22
+ ],
23
+ "breaking": false,
24
+ "manual": [
25
+ "Run /forge:update, then /forge:rebuild workflows to refresh .forge/workflows/ (review_plan.md, review_code.md, validate_task.md). Prompt-text only — no tool or schema change."
26
+ ]
27
+ },
28
+ "1.2.14": {
29
+ "version": "1.2.15",
30
+ "date": "2026-06-03",
31
+ "notes": "COST_REPORT now accounts incomplete (aborted/failed) phase attempts. forge-cli >=1.0.16 emits phase events with verdict aborted (user cancel) / failed (halt-on-failure) carrying the provider-billed tokens of incomplete attempts; previously those tokens were invisible (the CART-S02-T03 baseline under-counted by 259,950 tokens across two aborted plan passes — husk events with no token data). collate.cjs adds an Incomplete Passes section to COST_REPORT listing each aborted/failed attempt (task, phase, outcome, tokens); Per-Task/Per-Role/Model-Split totals already sum verdict-agnostically, so the previously-missing spend now lands in the totals automatically once events arrive.",
32
+ "target": "tools:collate",
33
+ "regenerate": [
34
+ "tools"
35
+ ],
36
+ "breaking": false,
37
+ "manual": [
38
+ "Run /forge:update, then /forge:rebuild tools to re-vendor .forge/tools/collate.cjs. Report-layer only — no schema change; older forge-cli versions simply produce no incomplete-pass events."
39
+ ]
40
+ },
2
41
  "1.2.13": {
3
42
  "version": "1.2.14",
4
43
  "date": "2026-06-03",
@@ -17,7 +56,9 @@
17
56
  "date": "2026-06-03",
18
57
  "notes": "Deterministic post-phase exit guard (FORGE-S26-T19). New parseOutputs() export in parse-gates.cjs handles ```outputs phase=X``` fence blocks with closed grammar (artifact + require directives; unknown throws). New postflight-gate.cjs evaluator runs post-phase; returns { phase, reasonCode, detail, remediation } with new output-missing and output-stub reasonCodes; no LLM in path. forge-cli run-task.ts now calls runPostflightGate() after runForgeSubagent returns and before FSM advance; on UNSATISFIED halts + calls existing runHaltAdvisor. wfl-run-task.js gets a #22 parity-seam comment. meta-plan-task.md and meta-implement.md: prose Tier-2 self-check replaced by declarative outputs blocks + thin pointer reminders.",
19
58
  "target": "tools:parse-gates tools:postflight-gate",
20
- "regenerate": ["workflows"],
59
+ "regenerate": [
60
+ "workflows"
61
+ ],
21
62
  "breaking": false,
22
63
  "manual": [
23
64
  "Run /forge:rebuild to pick up updated meta-workflow changes (meta-plan-task.md, meta-implement.md now include declarative outputs blocks). postflight-gate.cjs is additive — no-op for workflows without outputs blocks."
@@ -37,7 +78,9 @@
37
78
  "date": "2026-06-03",
38
79
  "notes": "Halt-recovery advisor (FORGE-S26-T18). preflight-gate.cjs now emits structured JSON on stdout on gate failure: { phase, reasonCode, detail, remediation }. reasonCode values: artifact-missing, predecessor-verdict-missing, illegal-status, tool-error. Both LLM orchestrators (meta-orchestrate.md, meta-fix-bug.md) parse and render the structured failure instead of raw stderr. forge-cli run-task and fix-bug upgraded to runPreflightGateWithData() which returns structured failure alongside the PreflightResult enum; on halt, the CLI spawns a read-only halt-recovery advisor subagent on the advisorModel config slot (or getAvailable()[0] fallback). New optional advisorModel field added to forge-cli layered config (L1 global / L2 project). Post-phase self-check guards added to meta-plan-task.md and meta-implement.md (Tier 2) to prevent false complete events when a phase subagent silently fails to write its primary output.",
39
80
  "target": "tools:preflight-gate",
40
- "regenerate": ["workflows"],
81
+ "regenerate": [
82
+ "workflows"
83
+ ],
41
84
  "breaking": false,
42
85
  "manual": [
43
86
  "Run /forge:rebuild to pick up updated meta-workflow changes (meta-orchestrate.md, meta-fix-bug.md, meta-plan-task.md, meta-implement.md). The structured JSON stdout from preflight-gate.cjs is additive — orchestrators that do not parse it still receive human-readable stderr and exit 1."
@@ -48,7 +91,9 @@
48
91
  "date": "2026-06-03",
49
92
  "notes": "Fix incomplete .forge/tools/ vendored closure. generate-tools.md and rebuild.md copied tools via `*.cjs` globs only, silently skipping the two `.js` lib helpers (lib/result.js, lib/validate.js) and the top-level list-skills.js. Because store-cli.cjs does a top-level require('./lib/validate.js') and collate.cjs requires './lib/result.js', a freshly vendored .forge/tools/ left store-cli.cjs dead-on-arrival (ENOENT) and broke KB collation. Latent since the vendored-tools model landed; S29 made it fatal by moving tool execution off $FORGE_ROOT/tools/ onto the vendored copy. Both vendoring sites now copy `.cjs` and `.js` (find -maxdepth 1, excludes __tests__/) and record both in the generation manifest. New regression guard tools/__tests__/vendored-tools-closure.test.cjs ties the documented copy extensions to the actual require()-closure.",
50
93
  "target": "tools:vendor-closure",
51
- "regenerate": ["tools"],
94
+ "regenerate": [
95
+ "tools"
96
+ ],
52
97
  "breaking": false,
53
98
  "manual": [
54
99
  "Run /forge:rebuild tools to re-vendor .forge/tools/ with the complete closure. Existing projects vendored by v1.2.4–v1.2.9 are missing .forge/tools/lib/result.js, .forge/tools/lib/validate.js, and .forge/tools/list-skills.js — store-cli and KB collation are broken until this re-vendor runs."
@@ -59,7 +104,10 @@
59
104
  "date": "2026-06-03",
60
105
  "notes": "Finish the FORGE_ROOT retirement in the workflow/persona layer (vendored-path scope). Remove the vestigial 'Resolve FORGE_ROOT (config.paths.forgeRoot)' preamble bullet from meta-approve/review-plan/review-implementation/validate/commit/plan-task/implement/bug-triage (the gate calls already use node .forge/tools/preflight-gate.cjs). Switch persona identity banners to node .forge/tools/banners.cjs <id> at the generator (build-base-pack.cjs identityBlock) and in the three base-pack-only personas (librarian/orchestrator/product-manager); drop the broken paths.forgeRoot resolution. meta-collate/collator now invoke node .forge/tools/collate.cjs directly. meta-migrate resolves the plugin root from CLAUDE_PLUGIN_ROOT (plugin-side reads of init/base-pack are not vendored). Stale paths.forgeRoot doc references removed from store-cli.spec.md, meta-enhance config_fields, and tool-discipline.md. paths.forgeRoot is intentionally retained as a maintained convenience field (written by check-update.cjs) for skills/commands that resolve the plugin root — those are out of scope for this entry.",
61
106
  "target": "workflows:forge-root-residual-cleanup",
62
- "regenerate": ["workflows", "personas"],
107
+ "regenerate": [
108
+ "workflows",
109
+ "personas"
110
+ ],
63
111
  "breaking": false,
64
112
  "manual": [
65
113
  "Run /forge:rebuild workflows personas to regenerate the updated workflow and persona files. The vendored banners/collate invocations require .forge/tools/ to be present — run /forge:rebuild tools first if it is absent (see the 1.2.7 migration)."
@@ -70,7 +118,9 @@
70
118
  "date": "2026-06-03",
71
119
  "notes": "Remove all $FORGE_ROOT references from init/smoke-test.md. Switch tool exec check to node .forge/tools/validate-store.cjs, generation-manifest stamp to node .forge/tools/generation-manifest.cjs, version read to .forge/tools/.forge-tools-version, distribution detection to the distribution field in .forge-tools-version, and remove the forgeRoot field from update-check-cache.json stamp. Smoke-test runs only at init time — existing projects are not affected.",
72
120
  "target": "init:smoke-test",
73
- "regenerate": ["init"],
121
+ "regenerate": [
122
+ "init"
123
+ ],
74
124
  "breaking": false,
75
125
  "manual": [
76
126
  "Re-run /forge:init on new projects to pick up the updated smoke-test. Existing projects are unaffected — smoke-test runs only once at init time."
@@ -81,7 +131,9 @@
81
131
  "date": "2026-06-02",
82
132
  "notes": "Remove paths.forgeRoot from config.schema.json required[] and properties. forge-preflight.cjs now resolves plugin root via forgeRef-based cache scan (mirrors manage-config.cjs Priority 2) instead of reading config.paths.forgeRoot. manage-config.cjs resolve-forge-root drops Priority 3 (forgeRoot fallback). generate-tools.md drops Step 1 (forgeRoot write). update.md removes forgeRoot refresh from Step 4 config section and fixes tools regenerate category to invoke /forge:rebuild tools instead of running schema refresh inline.",
83
133
  "target": "config:forgeRoot",
84
- "regenerate": ["tools"],
134
+ "regenerate": [
135
+ "tools"
136
+ ],
85
137
  "breaking": false,
86
138
  "manual": [
87
139
  "Run /forge:rebuild tools to re-vendor .forge/tools/ on existing projects. Projects that have never run /forge:rebuild tools will have .forge/tools/ absent until this step."
@@ -92,7 +144,9 @@
92
144
  "date": "2026-06-02",
93
145
  "notes": "Add vendored-tools integrity and staleness check to /forge:health. Exports checkToolsVersion(projectRoot) from check-structure.cjs: reads .forge/tools/.forge-tools-version marker and paths.forgeRef from config, returns { present, vendoredVersion, activeVersion, stale, reason }. generate-tools.md Step 2c now writes .forge/tools/.forge-tools-version after the tool copy loop. rebuild.md tools category now re-writes the marker after re-copy. health.md gains step 8b (vendored tools presence + staleness check, emitting 〇/△/× with /forge:rebuild tools recommendation). Closes the integrity gap for existing projects that ran /forge:rebuild tools in v1.2.4 without the marker.",
94
146
  "target": "tools:health",
95
- "regenerate": ["tools"],
147
+ "regenerate": [
148
+ "tools"
149
+ ],
96
150
  "breaking": false,
97
151
  "manual": [
98
152
  "Run /forge:update, then /forge:rebuild tools to write .forge/tools/.forge-tools-version. Until this runs, /forge:health will report 'stale (marker absent)' — non-breaking."
@@ -103,7 +157,10 @@
103
157
  "date": "2026-06-02",
104
158
  "notes": "Rewrite all ~159 node \"$FORGE_ROOT/tools/X.cjs\" call-sites in meta/workflows/*.md, meta/workflows/_fragments/*.md, and wfl-*.js drivers to use the fixed project-relative form node .forge/tools/X.cjs. Delete the FORGE_ROOT export preamble from all three JS drivers (wfl-run-task.js, wfl-run-sprint.js, wfl-fix-bug.js). Tools are now vendored in .forge/tools/ (added in v1.2.4), so no subagent needs to resolve a plugin root path at runtime. Drift test guard updated: asserts no $FORGE_ROOT remains in any driver source. Existing projects must regenerate workflows and workflows-js to pick up the new paths.",
105
159
  "target": "workflows:*",
106
- "regenerate": ["workflows", "workflows-js"],
160
+ "regenerate": [
161
+ "workflows",
162
+ "workflows-js"
163
+ ],
107
164
  "breaking": false,
108
165
  "manual": [
109
166
  "Run /forge:update, then run /forge:rebuild tools (if not done in v1.2.4 step), then /forge:rebuild workflows and /forge:rebuild workflows-js. Tools must be vendored in .forge/tools/ before running the rewritten workflows."
@@ -114,7 +171,9 @@
114
171
  "date": "2026-06-02",
115
172
  "notes": "Add tools vendor category: copy plugin tools/*.cjs + tools/lib/*.cjs into .forge/tools/ at init and rebuild, mirroring the existing schemas copy. Adds tools namespace to structure-manifest.json (via build-manifest.cjs TOOLS_FILES constant), Step 2b to generate-tools.md (init copy), and tools/tools:<filename> category to rebuild.md (verbatim re-copy from $FORGE_ROOT/tools/). check-structure.cjs validateManifest now maps the tools namespace to forgeRoot/tools/ with recursive lib/ enumeration. Foundation for FORGE-S29 call-site rewrite (T02+). No existing call-sites changed in this task.",
116
173
  "target": "tools",
117
- "regenerate": ["tools"],
174
+ "regenerate": [
175
+ "tools"
176
+ ],
118
177
  "breaking": false,
119
178
  "manual": [
120
179
  "Run /forge:update, then /forge:rebuild tools to materialise .forge/tools/."
@@ -240,70 +299,108 @@
240
299
  "date": "2026-05-31",
241
300
  "notes": "v1.0.10 — set-summary self-resolve hardening. When the JSON file is omitted, set-summary/set-bug-summary resolve the CANONICAL sidecar name (e.g. VALIDATION-SUMMARY.json) from the phase→kind map. If that file is missing but a near-name summary sidecar exists in the same dir (e.g. an agent wrote VALIDATE-SUMMARY.json via the Write tool instead of forge_artifact's canonical name), the error now surfaces the near-name file and points at the canonical write path (forge_artifact artifact:\"<kind>\"), instead of a silent 'file not found' dead-end. Surfaced by cartographer CART-S01-T01 validate, where the QA subagent wrote a non-canonical sidecar and the verdict never reached the store (orchestrator then correctly escalated via read-verdict). No behavior change when the canonical file is present.",
242
301
  "target": "tools:store-cli",
243
- "regenerate": ["tools"],
302
+ "regenerate": [
303
+ "tools"
304
+ ],
244
305
  "breaking": false,
245
- "manual": ["Run /forge:update to copy the updated tools into your project."]
306
+ "manual": [
307
+ "Run /forge:update to copy the updated tools into your project."
308
+ ]
246
309
  },
247
310
  "1.0.8": {
248
311
  "version": "1.0.9",
249
312
  "date": "2026-05-31",
250
313
  "notes": "v1.0.9 — store-cli suggestion drift-map: add create→write. Agents reach for REST-style `create <entity>`, but store-cli has no `create` verb (records are written with `write`), and create→write is beyond Levenshtein ≤2 so only the curated drift-map can surface it. `store-cli create sprint` now emits `Unknown command: create (Did you mean \"write\"?)`. Pure error-message UX improvement; no behavior change for valid commands. Observed on cartographer sprint-init dogfooding.",
251
314
  "target": "tools:store-cli",
252
- "regenerate": ["tools"],
315
+ "regenerate": [
316
+ "tools"
317
+ ],
253
318
  "breaking": false,
254
- "manual": ["Run /forge:update to copy the updated tools into your project."]
319
+ "manual": [
320
+ "Run /forge:update to copy the updated tools into your project."
321
+ ]
255
322
  },
256
323
  "1.0.7": {
257
324
  "version": "1.0.8",
258
325
  "date": "2026-05-31",
259
326
  "notes": "v1.0.8 — artifact-resolution Phase 4 (issue #111). ArtifactStore gains a backend registry: artifactStore.register(backend, impl) and per-call routing by the handle's `backend` (default 'fs'). Adding a storage backend is now implementing the six-method interface and registering it — no prompt or call-site changes (the acceptance criterion). Ships MemArtifactImpl, a complete synchronous in-memory reference backend, as the canonical template for real S3/CMS/DB providers. Per the ADR sync constraint, a networked impl is sync-bound for in-process callers and reachable async-internally only through the forge-cli subprocess surface — so no live remote backend is bundled. tools-only; no schema, workflow, or store-layout changes.",
260
327
  "target": "tools:artifact-store",
261
- "regenerate": ["tools"],
328
+ "regenerate": [
329
+ "tools"
330
+ ],
262
331
  "breaking": false,
263
- "manual": ["Run /forge:update to copy the updated tools into your project."]
332
+ "manual": [
333
+ "Run /forge:update to copy the updated tools into your project."
334
+ ]
264
335
  },
265
336
  "1.0.6": {
266
337
  "version": "1.0.7",
267
338
  "date": "2026-05-31",
268
339
  "notes": "v1.0.7 — artifact-resolution Phase 3 (issue #111). Introduces the ArtifactStore/FsArtifactImpl provider seam (tools/artifact-store.cjs), mirroring the store.cjs Store/FSImpl pattern: a backend-agnostic, synchronous facade (read/write/exists/url/list/delete) delegating to a swappable impl, default-wired to the filesystem. artifact.cjs is now a thin CLI over the facade and gains exists/url/delete subcommands; entity-dir resolution moved into artifact-store.cjs (re-exported for back-compat). Records gain an optional backend-agnostic locator {backend,ref} (schemas/_defs/locator.schema.json, $ref'd from task/bug/sprint); record.path stays REQUIRED as the read-time alias (the resolver derives {backend:'fs',ref:path} when locator is absent), so legacy records work unchanged and no store rewrite is forced. Concepts docs (task.md/bug.md) updated. Materialization of locator into stored records is deferred — the alias covers it. No FSM/lifecycle or store-layout changes.",
269
340
  "target": "tools:artifact,tools:artifact-store,schemas:task,schemas:bug,schemas:sprint",
270
- "regenerate": ["tools", "schemas"],
341
+ "regenerate": [
342
+ "tools",
343
+ "schemas"
344
+ ],
271
345
  "breaking": false,
272
- "manual": ["Run /forge:update to copy the updated tools and schemas into your project."]
346
+ "manual": [
347
+ "Run /forge:update to copy the updated tools and schemas into your project."
348
+ ]
273
349
  },
274
350
  "1.0.5": {
275
351
  "version": "1.0.6",
276
352
  "date": "2026-05-31",
277
353
  "notes": "v1.0.6 — artifact-resolution Phase 2 (issue #111). (1) The orchestrator's Verdict Detection no longer reads a markdown review artifact via the removed parse-verdict.cjs — meta-orchestrate.md was the last reference to that deleted tool, which would have crashed the verdict step. It now reads the verdict from the store record via read-verdict.cjs (--phase <role> --task|--bug <id>), branching on the stdout token; no artifact path is constructed. (2) Removed the stale verdict-source table that hard-coded {sprintDir}/{taskDir} markdown paths. (3) meta-commit reads ARCHITECT_APPROVAL by kind via forge_artifact instead of a built path; meta-collate anchors WRITEBACK-SUMMARY on the sprint record's path. (4) Documented canonical artifact addressing + the placeholder-token glossary in the store-cli-verbs fragment (rename-unsafe: tools parse the literal tokens). No schema or store-layout changes.",
278
354
  "target": "workflows:orchestrate_task,workflows:commit,workflows:collate",
279
- "regenerate": ["workflows:orchestrate_task", "workflows:commit", "workflows:collate"],
355
+ "regenerate": [
356
+ "workflows:orchestrate_task",
357
+ "workflows:commit",
358
+ "workflows:collate"
359
+ ],
280
360
  "breaking": false,
281
- "manual": ["Run /forge:update to copy the updated workflows into your project."]
361
+ "manual": [
362
+ "Run /forge:update to copy the updated workflows into your project."
363
+ ]
282
364
  },
283
365
  "1.0.4": {
284
366
  "version": "1.0.5",
285
367
  "date": "2026-05-31",
286
368
  "notes": "v1.0.5 — artifact-resolution Phase 1 (issue #111). set-summary / set-bug-summary now self-resolve the phase-summary sidecar from the store record's path: the JSON file argument is optional and the call collapses to `set-summary <id> <phase>`. This fixes the 2-arg arity failure (plan-phase set-summary previously exited 1 with Usage) and removes the hand-built `engineering/sprints/{sprint}/{task}/…-SUMMARY.json` paths from the implement/validate/approve/bug-triage workflows, which broke on non-default on-disk layouts. New canonical kind registry tools/lib/artifact-kinds.cjs (ARTIFACT_CATALOG + bug-mode overrides + phase→kind map) is now the single source consumed by artifact.cjs and store-cli.cjs. No schema or store-layout changes; the explicit jsonFile form remains accepted for back-compat.",
287
369
  "target": "tools:store-cli,tools:artifact,tools:artifact-kinds,workflows:implement_plan,workflows:validate_task,workflows:approve_task,workflows:triage",
288
- "regenerate": ["tools", "workflows:implement_plan", "workflows:validate_task", "workflows:approve_task", "workflows:triage"],
370
+ "regenerate": [
371
+ "tools",
372
+ "workflows:implement_plan",
373
+ "workflows:validate_task",
374
+ "workflows:approve_task",
375
+ "workflows:triage"
376
+ ],
289
377
  "breaking": false,
290
- "manual": ["Run /forge:update to copy the updated tools and workflows into your project."]
378
+ "manual": [
379
+ "Run /forge:update to copy the updated tools and workflows into your project."
380
+ ]
291
381
  },
292
382
  "1.0.3": {
293
383
  "version": "1.0.4",
294
384
  "date": "2026-05-30",
295
385
  "notes": "v1.0.4 — two store/path tool fixes. (1) preflight-gate.cjs now derives {sprint}/{task} from the store record's task.path instead of reconstructing sprints/{sprintId}/{taskId}/, fixing false 'artifact missing' halts on projects whose on-disk layout differs from the default (legacy sprint_NN_*/tasks/ trees and tasks/-nested dirs). (2) The store-query NLP path no longer degrades an explicit-ID query into a full-store scan: the parser keeps primary=tasks when a follow-word ('with sprint'/'with feature') accompanies an anchored task ID, and the engine re-routes a stripped anchored ID to its entity and applies a default result cap (new 'truncated' flag) so unbounded listings never dump the whole store. No schema, workflow, or store-layout changes.",
296
386
  "target": "tools:preflight-gate,tools:store-query",
297
- "regenerate": ["tools"],
387
+ "regenerate": [
388
+ "tools"
389
+ ],
298
390
  "breaking": false,
299
- "manual": ["Run /forge:update to copy the updated tools into your project."]
391
+ "manual": [
392
+ "Run /forge:update to copy the updated tools into your project."
393
+ ]
300
394
  },
301
395
  "1.0.2": {
302
396
  "version": "1.0.3",
303
397
  "date": "2026-05-28",
304
398
  "notes": "v1.0.3 — FORGE-BUG-040 (GH-110) fix. /forge:fix-bug now runs each phase as a phase-scoped subagent: triage uses the new triage.md workflow, plan-fix uses plan_task.md (bug-mode), implement uses implement_plan.md (bug-mode). Previously all three pointed at fix_bug.md (orchestrator-only body), causing the triage subagent to execute the entire lifecycle in one phase. Bug schema, transitions, and store layout are unchanged.",
305
399
  "target": "workflows:triage,workflows:fix_bug",
306
- "regenerate": ["workflows:triage", "workflows:fix_bug"],
400
+ "regenerate": [
401
+ "workflows:triage",
402
+ "workflows:fix_bug"
403
+ ],
307
404
  "breaking": false,
308
405
  "manual": []
309
406
  },
@@ -312,7 +409,9 @@
312
409
  "date": "2026-05-27",
313
410
  "notes": "v1.0.2 — deprecated command cleanup + init bug fixes. Redirect stubs removed (old command names are now unknown). paths.commands uses project prefix. artifact.cjs strips trailing filenames from store path. Deprecated command references replaced across all meta sources.",
314
411
  "target": "commands,init:phases,init:sdlc-init,tools:artifact",
315
- "regenerate": ["commands"],
412
+ "regenerate": [
413
+ "commands"
414
+ ],
316
415
  "breaking": false,
317
416
  "manual": [
318
417
  "Deprecated command redirect stubs removed. Old names (sprint-intake, sprint-plan, retrospective, regenerate, store-query, store-repair, quiz-agent, calibrate, update-tools, migrate, materialize) are now unknown commands. See docs/migration/v0-to-v1.md for the mapping.",
@@ -324,7 +423,9 @@
324
423
  "date": "2026-05-27",
325
424
  "notes": "v1.0.1 — init phase decomposition (FORGE-S26-T17). New shared verify-phase.cjs tool; sdlc-init.md rewritten as thin orchestrator; per-phase prompt files extracted to init/phases/.",
326
425
  "target": "tools:verify-phase,init:phases",
327
- "regenerate": ["tools"],
426
+ "regenerate": [
427
+ "tools"
428
+ ],
328
429
  "breaking": false,
329
430
  "manual": []
330
431
  },
@@ -333,7 +434,11 @@
333
434
  "date": "2026-05-26",
334
435
  "notes": "v1.0.0 — v1.0 DevX Overhaul (FORGE-S26). 7 command renames, 6 command removals, fast-mode dead code removal, health --fix, rebuild --enrich, init --migrate, pipeline step guards, revision loop visibility, post-init welcome, Tomoshibi enhancements, forge:status plugin command, v1.0 documentation restructure.",
335
436
  "target": "commands:new-sprint,commands:plan-sprint,commands:retro,commands:rebuild,commands:search,commands:repair,commands:check-agent,commands:calibrate,commands:enhance,commands:migrate,commands:materialize,commands:update-tools,commands:collate,commands:status,workflows:orchestrate_task,workflows:plan_task,workflows:implement_plan,workflows:fix_bug,config:mode",
336
- "regenerate": ["commands", "workflows", "tools"],
437
+ "regenerate": [
438
+ "commands",
439
+ "workflows",
440
+ "tools"
441
+ ],
337
442
  "breaking": true,
338
443
  "manual": [
339
444
  "Removed commands: /forge:update-tools, /forge:materialize, /forge:enhance, /forge:calibrate, /forge:migrate, /forge:collate (user surface). Use /forge:update for schema refresh, /forge:rebuild --enrich for enhancement, /forge:health --fix for calibration, /forge:init --migrate for migration.",
@@ -64,9 +64,10 @@ The Supervisor reviews the Engineer's implementation for correctness, quality, a
64
64
  - If present, extract:
65
65
  - `Iteration: N of M` — current attempt number and the configured limit
66
66
  - `Is final iteration: true/false`
67
- - If absent (user-invoked, not orchestrated): treat as iteration 1 of M, where M is
68
- read from `.forge/config.json` `maxReviewIterations` (default 3 if field absent).
69
- - Include `(iteration N of M)` in the opening line of the `CODE_REVIEW.md` artifact.
67
+ - If absent (user-invoked, not orchestrated): treat as `iteration 1`, no limit do
68
+ NOT read any iteration cap from config. The orchestrator owns loop budgets; a human
69
+ standalone re-run is the escape hatch for stuck items (forge-engineering#34).
70
+ - Include `(iteration N of M)` (orchestrated) or `(standalone review)` in the opening line of the `CODE_REVIEW.md` artifact.
70
71
  - If this is the final iteration (`N == M`) and the verdict is `Revision Required`,
71
72
  append a `### Next Steps` section to the artifact showing:
72
73
  ```
@@ -60,9 +60,10 @@ deps:
60
60
  - If present, extract:
61
61
  - `Iteration: N of M` — current attempt number and the configured limit
62
62
  - `Is final iteration: true/false`
63
- - If absent (user-invoked, not orchestrated): treat as iteration 1 of M, where M is
64
- read from `.forge/config.json` `maxReviewIterations` (default 3 if field absent).
65
- - Include `(iteration N of M)` in the opening line of the `PLAN_REVIEW.md` artifact.
63
+ - If absent (user-invoked, not orchestrated): treat as `iteration 1`, no limit do
64
+ NOT read any iteration cap from config. The orchestrator owns loop budgets; a human
65
+ standalone re-run is the escape hatch for stuck items (forge-engineering#34).
66
+ - Include `(iteration N of M)` (orchestrated) or `(standalone review)` in the opening line of the `PLAN_REVIEW.md` artifact.
66
67
  - If this is the final iteration (`N == M`) and the verdict is `Revision Required`,
67
68
  append a `### Next Steps` section to the artifact showing:
68
69
  ```
@@ -64,9 +64,10 @@ The Supervisor performs a final validation of the implementation against the acc
64
64
  - If present, extract:
65
65
  - `Iteration: N of M` — current attempt number and the configured limit
66
66
  - `Is final iteration: true/false`
67
- - If absent (user-invoked, not orchestrated): treat as iteration 1 of M, where M is
68
- read from `.forge/config.json` `maxReviewIterations` (default 3 if field absent).
69
- - Include `(iteration N of M)` in the opening line of the `VALIDATION_REPORT.md` artifact.
67
+ - If absent (user-invoked, not orchestrated): treat as `iteration 1`, no limit do
68
+ NOT read any iteration cap from config. The orchestrator owns loop budgets; a human
69
+ standalone re-run is the escape hatch for stuck items (forge-engineering#34).
70
+ - Include `(iteration N of M)` (orchestrated) or `(standalone review)` in the opening line of the `VALIDATION_REPORT.md` artifact.
70
71
  - If this is the final iteration (`N == M`) and the verdict is `Revision Required`,
71
72
  append a `### Next Steps` section to the artifact showing:
72
73
  ```
@@ -611,6 +611,38 @@ function buildCostReport(sprint, events, orphanSidecars, huskPrimaries) {
611
611
  }
612
612
  }
613
613
 
614
+ // --- Section 3b: Incomplete passes (aborted / failed attempts) ---
615
+ // forge-cli ≥1.0.16 emits phase events with verdict "aborted" (user cancel)
616
+ // or "failed" (halt-on-failure) so the provider-billed tokens of incomplete
617
+ // attempts reach the store. Those tokens ARE included in the Per-Task /
618
+ // Per-Role / Model Split totals above (totals are verdict-agnostic — this
619
+ // closes the under-count where aborted passes were invisible); this section
620
+ // makes the incomplete share visible rather than silently mixed in.
621
+ lines.push('## Incomplete Passes', '');
622
+ {
623
+ const INCOMPLETE_VERDICTS = new Set(['aborted', 'failed']);
624
+ const incompleteEvents = tokenEvents.filter(e => INCOMPLETE_VERDICTS.has(e.verdict));
625
+ if (incompleteEvents.length === 0) {
626
+ lines.push('_No incomplete passes in this sprint._', '');
627
+ } else {
628
+ const rows = [['Task', 'Phase', 'Outcome', 'Input Tokens', 'Output Tokens', 'Est. Cost USD']];
629
+ const sorted = [...incompleteEvents].sort((a, b) =>
630
+ String(a.taskId || '').localeCompare(String(b.taskId || '')) ||
631
+ String(a.phase || '').localeCompare(String(b.phase || '')));
632
+ for (const e of sorted) {
633
+ rows.push([
634
+ e.taskId || '(unknown)',
635
+ e.phase || '(unknown)',
636
+ e.verdict,
637
+ fmtTokens(e.inputTokens || 0),
638
+ fmtTokens(e.outputTokens || 0),
639
+ fmtCost(e.estimatedCostUSD || 0),
640
+ ]);
641
+ }
642
+ lines.push(padTable(rows), '');
643
+ }
644
+ }
645
+
614
646
  // --- Section 4: Model split ---
615
647
  lines.push('## Model Split', '');
616
648
  {
@@ -82,9 +82,9 @@ function applySubstitutions(template, subs) {
82
82
  });
83
83
  }
84
84
 
85
- function readField(dottedPath, state) {
85
+ function walkPath(dottedPath, root) {
86
86
  const parts = dottedPath.split('.');
87
- let cur = state;
87
+ let cur = root;
88
88
  for (const p of parts) {
89
89
  if (cur === null || cur === undefined) return undefined;
90
90
  cur = cur[p];
@@ -92,6 +92,53 @@ function readField(dottedPath, state) {
92
92
  return cur;
93
93
  }
94
94
 
95
+ function readField(dottedPath, state) {
96
+ // Direct walk first (supports explicit `task.status` / `bug.status` paths).
97
+ const direct = walkPath(dottedPath, state);
98
+ if (direct !== undefined) return direct;
99
+ // The materialized outputs blocks use BARE record paths
100
+ // (`summaries.plan.verdict`) while the CLI wraps the record as
101
+ // { task: record } — fall back to the entity record so those resolve.
102
+ // (CART-S03-T01 false-halt: every bare require evaluated undefined.)
103
+ if (state && state.task) {
104
+ const viaTask = walkPath(dottedPath, state.task);
105
+ if (viaTask !== undefined) return viaTask;
106
+ }
107
+ if (state && state.bug) {
108
+ const viaBug = walkPath(dottedPath, state.bug);
109
+ if (viaBug !== undefined) return viaBug;
110
+ }
111
+ return undefined;
112
+ }
113
+
114
+ /**
115
+ * Build template substitutions for an entity's artifact directory.
116
+ * Canonical layouts:
117
+ * tasks: <engineering>/sprints/<sprintId>/<taskId>/
118
+ * bugs: <engineering>/bugs/<bugId>/
119
+ * {sprint} is defined as the path segment under <engineering> that contains
120
+ * the entity dir — `sprints/<sprintId>` for tasks, `bugs` for bugs.
121
+ * (CART-S03-T01 false-halt: {sprint} was substituted with the bare sprintId,
122
+ * dropping the `sprints/` segment, so every artifact check missed.)
123
+ */
124
+ function buildSubstitutions({ taskRecord, engineeringRoot, entityId }) {
125
+ const rec = taskRecord || {};
126
+ const isBug = Boolean(rec.bugId) || /-BUG-/.test(String(entityId || ''));
127
+ let sprint;
128
+ if (isBug) {
129
+ sprint = 'bugs';
130
+ } else if (rec.sprintId) {
131
+ sprint = `sprints/${rec.sprintId}`;
132
+ } else {
133
+ sprint = '{sprint}'; // unresolvable — leave the placeholder visible in the report
134
+ }
135
+ return {
136
+ engineering: engineeringRoot,
137
+ sprint,
138
+ task: entityId,
139
+ };
140
+ }
141
+
95
142
  function evalPredicate(pred, state) {
96
143
  const actual = readField(pred.field, state);
97
144
  switch (pred.op) {
@@ -144,7 +191,7 @@ function buildStructuredFailure(phase, missing) {
144
191
  };
145
192
  }
146
193
 
147
- module.exports = { postflight };
194
+ module.exports = { postflight, buildSubstitutions };
148
195
 
149
196
  // CLI shim — only runs when invoked directly
150
197
  if (require.main === module) {
@@ -172,13 +219,12 @@ if (require.main === module) {
172
219
  // store.cjs not available or task not found — continue with substitutions from args
173
220
  }
174
221
 
175
- // Build substitutions
176
- const sprintId = taskRecord ? taskRecord.sprintId : undefined;
177
- const substitutions = {
178
- engineering: engineeringRoot,
179
- sprint: sprintId,
180
- task: args.task,
181
- };
222
+ // Build substitutions (canonical entity-dir resolution — sprints/<id> or bugs)
223
+ const substitutions = buildSubstitutions({
224
+ taskRecord,
225
+ engineeringRoot,
226
+ entityId: args.task,
227
+ });
182
228
 
183
229
  // Load workflow markdown (scan .forge/workflows/ for phase)
184
230
  const workflowMd = loadWorkflowMarkdown(args.phase);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@entelligentsia/forgecli",
3
- "version": "1.0.14",
4
- "description": "Forge SDLC ported onto @earendil-works/pi-coding-agent \u2014 production launcher with three bin aliases (forge/forgecli/4ge). Bundles a curated fork of pi-coding-agent vendored under earendil-works names.",
3
+ "version": "1.0.20",
4
+ "description": "Forge SDLC ported onto @earendil-works/pi-coding-agent production launcher with three bin aliases (forge/forgecli/4ge). Bundles a curated fork of pi-coding-agent vendored under earendil-works names.",
5
5
  "license": "MIT",
6
6
  "author": "Entelligentsia",
7
7
  "homepage": "https://github.com/Entelligentsia/forge-cli#readme",