@entelligentsia/forgecli 0.9.4 → 0.10.1
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.
- package/CHANGELOG.md +70 -0
- package/README.md +38 -0
- package/dist/CHANGELOG-forge-plugin.md +68 -0
- package/dist/bin/config.js +13 -2
- package/dist/bin/config.js.map +1 -1
- package/dist/extensions/forgecli/config-command.js +1 -0
- package/dist/extensions/forgecli/config-command.js.map +1 -1
- package/dist/extensions/forgecli/config-layer.js +3 -4
- package/dist/extensions/forgecli/config-layer.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/component.js +6 -0
- package/dist/extensions/forgecli/config-tui/component.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/handler.d.ts +4 -0
- package/dist/extensions/forgecli/config-tui/handler.js +17 -9
- package/dist/extensions/forgecli/config-tui/handler.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/screens/override-editor.js +114 -47
- package/dist/extensions/forgecli/config-tui/screens/override-editor.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/screens/persona-editor.js +73 -50
- package/dist/extensions/forgecli/config-tui/screens/persona-editor.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/screens/shared.d.ts +32 -0
- package/dist/extensions/forgecli/config-tui/screens/shared.js +74 -1
- package/dist/extensions/forgecli/config-tui/screens/shared.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/screens/tier-picker.js +69 -28
- package/dist/extensions/forgecli/config-tui/screens/tier-picker.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/state/model.d.ts +8 -0
- package/dist/extensions/forgecli/config-tui/state/reducer.js +18 -1
- package/dist/extensions/forgecli/config-tui/state/reducer.js.map +1 -1
- package/dist/extensions/forgecli/config-writer.js +7 -6
- package/dist/extensions/forgecli/config-writer.js.map +1 -1
- package/dist/extensions/forgecli/fix-bug.js +85 -37
- package/dist/extensions/forgecli/fix-bug.js.map +1 -1
- package/dist/extensions/forgecli/forge-subagent.d.ts +16 -1
- package/dist/extensions/forgecli/forge-subagent.js +11 -1
- package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
- package/dist/extensions/forgecli/forge-update-command.js +2 -3
- package/dist/extensions/forgecli/forge-update-command.js.map +1 -1
- package/dist/extensions/forgecli/foundry-collision.js +5 -3
- package/dist/extensions/forgecli/foundry-collision.js.map +1 -1
- package/dist/extensions/forgecli/index.js +16 -9
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/paths/migrator.d.ts +24 -0
- package/dist/extensions/forgecli/paths/migrator.js +150 -0
- package/dist/extensions/forgecli/paths/migrator.js.map +1 -0
- package/dist/extensions/forgecli/paths/paths.d.ts +36 -0
- package/dist/extensions/forgecli/paths/paths.js +144 -0
- package/dist/extensions/forgecli/paths/paths.js.map +1 -0
- package/dist/extensions/forgecli/run-sprint.js +2 -3
- package/dist/extensions/forgecli/run-sprint.js.map +1 -1
- package/dist/extensions/forgecli/run-task.js +2 -3
- package/dist/extensions/forgecli/run-task.js.map +1 -1
- package/dist/extensions/forgecli/session-registry.d.ts +13 -0
- package/dist/extensions/forgecli/session-registry.js +19 -0
- package/dist/extensions/forgecli/session-registry.js.map +1 -1
- package/dist/extensions/forgecli/subagent/agents.d.ts +9 -0
- package/dist/extensions/forgecli/subagent/agents.js +12 -2
- package/dist/extensions/forgecli/subagent/agents.js.map +1 -1
- package/dist/extensions/forgecli/subagent/index.d.ts +9 -0
- package/dist/extensions/forgecli/subagent/index.js +12 -4
- package/dist/extensions/forgecli/subagent/index.js.map +1 -1
- package/dist/extensions/forgecli/test-orchestrate.js +1 -0
- package/dist/extensions/forgecli/test-orchestrate.js.map +1 -1
- package/dist/extensions/forgecli/thread-switcher.js +76 -16
- package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
- package/dist/extensions/forgecli/transition-guard.js +7 -2
- package/dist/extensions/forgecli/transition-guard.js.map +1 -1
- package/dist/extensions/forgecli/update-check.js +2 -3
- package/dist/extensions/forgecli/update-check.js.map +1 -1
- package/dist/extensions/forgecli/viewport-events.js +10 -0
- package/dist/extensions/forgecli/viewport-events.js.map +1 -1
- package/dist/extensions/forgecli/viewport-renderer.d.ts +18 -0
- package/dist/extensions/forgecli/viewport-renderer.js +27 -0
- package/dist/extensions/forgecli/viewport-renderer.js.map +1 -1
- package/dist/extensions/forgecli/whats-new-widget.d.ts +13 -8
- package/dist/extensions/forgecli/whats-new-widget.js +113 -45
- package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
- package/dist/extensions/forgecli/whats-new.js +7 -6
- package/dist/extensions/forgecli/whats-new.js.map +1 -1
- package/dist/forge-payload/.base-pack/workflows/architect_approve.md +29 -3
- package/dist/forge-payload/.base-pack/workflows/commit_task.md +15 -8
- package/dist/forge-payload/.base-pack/workflows/fix_bug.md +327 -185
- package/dist/forge-payload/.base-pack/workflows/implement_plan.md +18 -10
- package/dist/forge-payload/.base-pack/workflows/plan_task.md +15 -9
- package/dist/forge-payload/.base-pack/workflows/review_code.md +14 -6
- package/dist/forge-payload/.base-pack/workflows/review_plan.md +18 -10
- package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
- package/dist/forge-payload/.schemas/bug.schema.json +3 -2
- package/dist/forge-payload/tools/collate.cjs +34 -9
- package/dist/forge-payload/tools/parse-gates.cjs +8 -2
- package/dist/forge-payload/tools/store-cli.cjs +29 -10
- package/dist/forge-payload/tools/store.cjs +61 -0
- package/dist/forge-payload/tools/validate-store.cjs +6 -2
- package/package.json +2 -2
|
@@ -27,7 +27,8 @@ deps:
|
|
|
27
27
|
|
|
28
28
|
0. Pre-flight Gate Check:
|
|
29
29
|
- Resolve FORGE_ROOT (`node -e "console.log(require('./.forge/config.json').paths.forgeRoot)"`).
|
|
30
|
-
-
|
|
30
|
+
- **Entity-mode resolution:** read the kickoff arguments. `--task {id}` → `entity_kind = "task"`, `record_id = {id}`. `--bug {id}` → `entity_kind = "bug"`, `record_id = {id}`. All store-cli calls below substitute `{entity_kind}` and `{record_id}` for the literal "task"/{taskId} placeholders.
|
|
31
|
+
- Run: `node "$FORGE_ROOT/tools/preflight-gate.cjs" --phase implement --{entity_kind} {record_id}`
|
|
31
32
|
- Exit 1 (gate failed) → print stderr and HALT. Do not proceed; do not attempt to produce the artifact.
|
|
32
33
|
- Exit 2 (misconfiguration) → print stderr and HALT.
|
|
33
34
|
- Exit 0 → continue.
|
|
@@ -59,15 +60,17 @@ deps:
|
|
|
59
60
|
- Tag updates: `<!-- Discovered during {TASK_ID} — {date} -->`
|
|
60
61
|
|
|
61
62
|
6. Finalize:
|
|
62
|
-
- Transitions:
|
|
63
|
-
- `planned`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
- Transitions:
|
|
64
|
+
- **Task mode** — legal predecessors are `planned`, `plan-approved`, or `implementing`; target is `implemented`.
|
|
65
|
+
- `planned` → `implemented` (workflow-prose path — direct)
|
|
66
|
+
- `plan-approved` → `implementing` → `implemented` (supervisor-review path)
|
|
67
|
+
- Out-of-band escapes (any state): `plan-revision-required`, `code-revision-required`, `blocked`, `escalated`, `abandoned`
|
|
68
|
+
Update status: `node "$FORGE_ROOT/tools/store-cli.cjs" update-status task {taskId} status implemented`
|
|
69
|
+
- **Bug mode** — NO status write. The bug remains `in-progress` until the commit phase transitions it to `fixed`. Writing `bug.status` here violates `meta-fix-bug.md § Iron Laws #2`.
|
|
67
70
|
- **Do NOT emit a phase event yourself.** The orchestrator owns event emission — it composes the canonical event from runtime telemetry (model, provider, tokens, wall times) plus the SUMMARY you write in the next step. Subagents that call `store-cli emit` for phase events hallucinate runtime facts (see Plan 11 / Slice 2). Write the SUMMARY and return.
|
|
68
71
|
|
|
69
72
|
7. Emit Summary Sidecar:
|
|
70
|
-
- Write `IMPLEMENTATION-SUMMARY.json` to the
|
|
73
|
+
- Write `IMPLEMENTATION-SUMMARY.json` to the record's directory with the following shape:
|
|
71
74
|
```json
|
|
72
75
|
{
|
|
73
76
|
"objective": "<one sentence — what this implementation delivered>",
|
|
@@ -77,12 +80,17 @@ deps:
|
|
|
77
80
|
"artifact_ref":"PROGRESS.md"
|
|
78
81
|
}
|
|
79
82
|
```
|
|
80
|
-
- Call:
|
|
83
|
+
- Call (task mode):
|
|
81
84
|
```
|
|
82
|
-
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {
|
|
85
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {taskId} implementation \
|
|
83
86
|
engineering/sprints/{sprint}/{task}/IMPLEMENTATION-SUMMARY.json
|
|
84
87
|
```
|
|
85
|
-
|
|
88
|
+
Or (bug mode):
|
|
89
|
+
```
|
|
90
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-bug-summary {bugId} implementation \
|
|
91
|
+
engineering/bugs/{bugDir}/IMPLEMENTATION-SUMMARY.json
|
|
92
|
+
```
|
|
93
|
+
- If the set-summary call exits non-zero, fix the sidecar JSON and retry. Do not proceed without a valid summary.
|
|
86
94
|
```
|
|
87
95
|
|
|
88
96
|
## Iron Laws
|
|
@@ -27,7 +27,8 @@ deps:
|
|
|
27
27
|
|
|
28
28
|
0. Pre-flight Gate Check:
|
|
29
29
|
- Resolve FORGE_ROOT (`node -e "console.log(require('./.forge/config.json').paths.forgeRoot)"`).
|
|
30
|
-
-
|
|
30
|
+
- **Entity-mode resolution:** read the kickoff arguments. `--task {id}` → `entity_kind = "task"`, `record_id = {id}`. `--bug {id}` → `entity_kind = "bug"`, `record_id = {id}`. All store-cli calls below substitute `{entity_kind}` and `{record_id}` for the literal "task"/{taskId} placeholders.
|
|
31
|
+
- Run: `node "$FORGE_ROOT/tools/preflight-gate.cjs" --phase plan --{entity_kind} {record_id}`
|
|
31
32
|
- Exit 1 (gate failed) → print stderr and HALT. Do not proceed; do not attempt to produce the artifact.
|
|
32
33
|
- Exit 2 (misconfiguration) → print stderr and HALT.
|
|
33
34
|
- Exit 0 → continue.
|
|
@@ -64,14 +65,14 @@ deps:
|
|
|
64
65
|
- If new patterns were discovered, update architecture or business domain docs
|
|
65
66
|
|
|
66
67
|
5. Finalize:
|
|
67
|
-
- Transitions:
|
|
68
|
-
- `draft
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
- Transitions:
|
|
69
|
+
- **Task mode** — legal target from this step: `draft → planned`. Out-of-band escapes (any state): `plan-revision-required`, `code-revision-required`, `blocked`, `escalated`, `abandoned`.
|
|
70
|
+
Update status: `node "$FORGE_ROOT/tools/store-cli.cjs" update-status task {taskId} status planned`
|
|
71
|
+
- **Bug mode** — NO status write. The bug remains `in-progress` until the commit phase transitions it to `fixed`. Writing `bug.status` here violates `meta-fix-bug.md § Iron Laws #2`.
|
|
71
72
|
- **Do NOT emit a phase event yourself.** The orchestrator owns event emission — it composes the canonical event from runtime telemetry (model, provider, tokens, wall times) plus the SUMMARY you write in the next step. Subagents that call `store-cli emit` for phase events hallucinate runtime facts (see Plan 11 / Slice 2). Write the SUMMARY and return.
|
|
72
73
|
|
|
73
74
|
6. Emit Summary Sidecar:
|
|
74
|
-
- Write `PLAN-SUMMARY.json`
|
|
75
|
+
- Write `PLAN-SUMMARY.json` (task mode) or `BUG-FIX-PLAN-SUMMARY.json` (bug mode) to the record's directory. Shape:
|
|
75
76
|
```json
|
|
76
77
|
{
|
|
77
78
|
"objective": "<one sentence — what this plan sets out to build>",
|
|
@@ -81,12 +82,17 @@ deps:
|
|
|
81
82
|
"artifact_ref":"PLAN.md"
|
|
82
83
|
}
|
|
83
84
|
```
|
|
84
|
-
- Call:
|
|
85
|
+
- Call (task mode):
|
|
85
86
|
```
|
|
86
|
-
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {
|
|
87
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {taskId} plan \
|
|
87
88
|
engineering/sprints/{sprint}/{task}/PLAN-SUMMARY.json
|
|
88
89
|
```
|
|
89
|
-
|
|
90
|
+
Or (bug mode):
|
|
91
|
+
```
|
|
92
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-bug-summary {bugId} plan \
|
|
93
|
+
engineering/bugs/{bugDir}/BUG-FIX-PLAN-SUMMARY.json
|
|
94
|
+
```
|
|
95
|
+
- If the set-summary call exits non-zero, fix the sidecar JSON and retry. Do not proceed without a valid summary.
|
|
90
96
|
```
|
|
91
97
|
|
|
92
98
|
## Iron Laws
|
|
@@ -45,7 +45,8 @@ Never set `FORGE_SKIP_WRITE_VALIDATION=1` — operator-only emergency switch.
|
|
|
45
45
|
|
|
46
46
|
0. Pre-flight Gate Check:
|
|
47
47
|
- Resolve FORGE_ROOT (`node -e "console.log(require('./.forge/config.json').paths.forgeRoot)"`).
|
|
48
|
-
-
|
|
48
|
+
- **Entity-mode resolution:** read the kickoff arguments. `--task {id}` → `entity_kind = "task"`, `record_id = {id}`. `--bug {id}` → `entity_kind = "bug"`, `record_id = {id}`. All store-cli calls below substitute `{entity_kind}` and `{record_id}` for the literal "task"/{taskId} placeholders.
|
|
49
|
+
- Run: `node "$FORGE_ROOT/tools/preflight-gate.cjs" --phase review-code --{entity_kind} {record_id}`
|
|
49
50
|
- Exit 1 (gate failed) → print stderr and HALT. Do not proceed; do not attempt to produce the artifact.
|
|
50
51
|
- Exit 2 (misconfiguration) → print stderr and HALT.
|
|
51
52
|
- Exit 0 → continue.
|
|
@@ -71,11 +72,13 @@ Never set `FORGE_SKIP_WRITE_VALIDATION=1` — operator-only emergency switch.
|
|
|
71
72
|
- Update stack-checklist.md if new patterns or pitfalls were discovered
|
|
72
73
|
|
|
73
74
|
5. Finalize:
|
|
74
|
-
-
|
|
75
|
+
- Transitions:
|
|
76
|
+
- **Task mode** — Update status: `node "$FORGE_ROOT/tools/store-cli.cjs" update-status task {taskId} status review-approved` (if Approved) or `... status code-revision-required` (if Revision Required).
|
|
77
|
+
- **Bug mode** — NO status write. The bug remains `in-progress`. The verdict signal travels through `summaries.code_review.verdict` (read by `read-verdict.cjs § BUG_PHASE_VERDICT_SOURCE`), not `bug.status`. Writing `bug.status` here violates `meta-fix-bug.md § Iron Laws #2`.
|
|
75
78
|
- **Do NOT emit a phase event yourself.** The orchestrator (or kickoff handler) owns event emission — it composes the canonical event from runtime telemetry (model, provider, tokens, wall times) plus the SUMMARY you write in the next step. Subagents that call `store-cli emit` for phase events hallucinate runtime facts (see Plan 11 / Slice 2). Write the SUMMARY and return.
|
|
76
79
|
|
|
77
80
|
6. Emit Summary Sidecar:
|
|
78
|
-
- Write `REVIEW-IMPL-SUMMARY.json` to the
|
|
81
|
+
- Write `REVIEW-IMPL-SUMMARY.json` to the record's directory with the following shape:
|
|
79
82
|
```json
|
|
80
83
|
{
|
|
81
84
|
"objective": "<one sentence — what this review assessed>",
|
|
@@ -85,10 +88,15 @@ Never set `FORGE_SKIP_WRITE_VALIDATION=1` — operator-only emergency switch.
|
|
|
85
88
|
"artifact_ref":"CODE_REVIEW.md"
|
|
86
89
|
}
|
|
87
90
|
```
|
|
88
|
-
- Call:
|
|
91
|
+
- Call (task mode):
|
|
89
92
|
```
|
|
90
|
-
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {
|
|
93
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {taskId} code_review \
|
|
91
94
|
engineering/sprints/{sprint}/{task}/REVIEW-IMPL-SUMMARY.json
|
|
92
95
|
```
|
|
93
|
-
|
|
96
|
+
Or (bug mode):
|
|
97
|
+
```
|
|
98
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-bug-summary {bugId} code_review \
|
|
99
|
+
engineering/bugs/{bugDir}/REVIEW-IMPL-SUMMARY.json
|
|
100
|
+
```
|
|
101
|
+
- If the set-summary call exits non-zero, fix the sidecar JSON and retry. Do not proceed without a valid summary.
|
|
94
102
|
```
|
|
@@ -46,7 +46,8 @@ Never set `FORGE_SKIP_WRITE_VALIDATION=1` — operator-only emergency switch.
|
|
|
46
46
|
|
|
47
47
|
0. Pre-flight Gate Check:
|
|
48
48
|
- Resolve FORGE_ROOT (`node -e "console.log(require('./.forge/config.json').paths.forgeRoot)"`).
|
|
49
|
-
-
|
|
49
|
+
- **Entity-mode resolution:** read the kickoff arguments. `--task {id}` → `entity_kind = "task"`, `record_id = {id}`. `--bug {id}` → `entity_kind = "bug"`, `record_id = {id}`. All store-cli calls below substitute `{entity_kind}` and `{record_id}` for the literal "task"/{taskId} placeholders.
|
|
50
|
+
- Run: `node "$FORGE_ROOT/tools/preflight-gate.cjs" --phase review-plan --{entity_kind} {record_id}`
|
|
50
51
|
- Exit 1 (gate failed) → print stderr and HALT. Do not proceed; do not attempt to produce the artifact.
|
|
51
52
|
- Exit 2 (misconfiguration) → print stderr and HALT.
|
|
52
53
|
- Exit 0 → continue.
|
|
@@ -66,15 +67,17 @@ Never set `FORGE_SKIP_WRITE_VALIDATION=1` — operator-only emergency switch.
|
|
|
66
67
|
- If Approved: provide any advisory notes
|
|
67
68
|
|
|
68
69
|
4. Finalize:
|
|
69
|
-
- Transitions:
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
- Transitions:
|
|
71
|
+
- **Task mode** — predecessor must be `planned`.
|
|
72
|
+
- Approved → `plan-approved`
|
|
73
|
+
- Revision Required → `plan-revision-required`
|
|
74
|
+
- Out-of-band escapes (any state): `code-revision-required`, `blocked`, `escalated`, `abandoned`
|
|
75
|
+
Update status: `node "$FORGE_ROOT/tools/store-cli.cjs" update-status task {taskId} status plan-approved` (if Approved) or `... status plan-revision-required` (if Revision Required)
|
|
76
|
+
- **Bug mode** — NO status write. The bug remains `in-progress`. The verdict signal travels through `summaries.review_plan.verdict` (read by `read-verdict.cjs § BUG_PHASE_VERDICT_SOURCE`), not `bug.status`. Writing `bug.status` here violates `meta-fix-bug.md § Iron Laws #2`.
|
|
74
77
|
- **Do NOT emit a phase event yourself.** The orchestrator owns event emission — it composes the canonical event from runtime telemetry (model, provider, tokens, wall times) plus the SUMMARY you write in the next step. Subagents that call `store-cli emit` for phase events hallucinate runtime facts (see Plan 11 / Slice 2). Write the SUMMARY and return.
|
|
75
78
|
|
|
76
79
|
5. Emit Summary Sidecar:
|
|
77
|
-
- Write `REVIEW-PLAN-SUMMARY.json` to the
|
|
80
|
+
- Write `REVIEW-PLAN-SUMMARY.json` to the record's directory with the following shape:
|
|
78
81
|
```json
|
|
79
82
|
{
|
|
80
83
|
"objective": "<one sentence — what this review assessed>",
|
|
@@ -84,12 +87,17 @@ Never set `FORGE_SKIP_WRITE_VALIDATION=1` — operator-only emergency switch.
|
|
|
84
87
|
"artifact_ref":"PLAN_REVIEW.md"
|
|
85
88
|
}
|
|
86
89
|
```
|
|
87
|
-
- Call:
|
|
90
|
+
- Call (task mode):
|
|
88
91
|
```
|
|
89
|
-
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {
|
|
92
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-summary {taskId} review_plan \
|
|
90
93
|
engineering/sprints/{sprint}/{task}/REVIEW-PLAN-SUMMARY.json
|
|
91
94
|
```
|
|
92
|
-
|
|
95
|
+
Or (bug mode):
|
|
96
|
+
```
|
|
97
|
+
node "$FORGE_ROOT/tools/store-cli.cjs" set-bug-summary {bugId} review_plan \
|
|
98
|
+
engineering/bugs/{bugDir}/REVIEW-PLAN-SUMMARY.json
|
|
99
|
+
```
|
|
100
|
+
- If the set-summary call exits non-zero, fix the sidecar JSON and retry. Do not proceed without a valid summary.
|
|
93
101
|
```
|
|
94
102
|
|
|
95
103
|
## Generation Notes
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"title": { "type": "string" },
|
|
10
10
|
"description": { "type": "string" },
|
|
11
11
|
"severity": { "type": "string", "enum": ["critical", "major", "minor"] },
|
|
12
|
-
"status": { "type": "string", "enum": ["reported", "triaged", "in-progress", "fixed"
|
|
12
|
+
"status": { "type": "string", "enum": ["reported", "triaged", "in-progress", "fixed"] },
|
|
13
13
|
"path": { "type": "string" },
|
|
14
14
|
"rootCauseCategory": {
|
|
15
15
|
"type": "string",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"findings": { "type": "array", "items": { "type": "string", "maxLength": 200 }, "maxItems": 12 },
|
|
48
48
|
"verdict": { "type": "string", "enum": ["approved", "revision", "n/a"] },
|
|
49
49
|
"written_at": { "type": "string", "format": "date-time" },
|
|
50
|
-
"artifact_ref":{ "type": "string", "description": "Relative path to the full artifact" }
|
|
50
|
+
"artifact_ref":{ "type": "string", "description": "Relative path to the full artifact" },
|
|
51
|
+
"route": { "type": "string", "enum": ["A", "B"], "description": "Fix-bug pipeline route decision. Only set on the triage phase summary by meta-fix-bug.md. A = short-circuit (skip plan-fix + review-plan); B = full loop (default). Optional and meaningless on non-triage phases — see meta-fix-bug.md § Triage Judgement." }
|
|
51
52
|
},
|
|
52
53
|
"additionalProperties": false
|
|
53
54
|
}
|
|
@@ -952,10 +952,16 @@ for (const bug of allBugs) {
|
|
|
952
952
|
// When purging events for a bug, aggregate cost data from event files
|
|
953
953
|
// before they are deleted. The aggregated cost summary is embedded in
|
|
954
954
|
// the bug's INDEX.md so the information survives the purge.
|
|
955
|
+
//
|
|
956
|
+
// Bug-phase events live in the shared `events/bugs/` virtual sprint dir
|
|
957
|
+
// (see meta-fix-bug.md § Event Emission). Read from that dir and filter
|
|
958
|
+
// by `event.bugId === bug.bugId` — the per-bug `events/{bugId}/` path
|
|
959
|
+
// never existed (silent data loss before this fix).
|
|
955
960
|
let costTotals;
|
|
956
961
|
if (PURGE_EVENTS && SPRINT_ARG && SPRINT_ARG === bug.bugId) {
|
|
957
|
-
const { events } = loadSprintEvents(
|
|
958
|
-
const
|
|
962
|
+
const { events } = loadSprintEvents('bugs');
|
|
963
|
+
const bugEvents = events.filter(e => e.bugId === bug.bugId);
|
|
964
|
+
const tokenEvents = bugEvents.filter(e => e.inputTokens !== undefined);
|
|
959
965
|
if (tokenEvents.length > 0) {
|
|
960
966
|
const totals = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, estimatedCostUSD: 0, sources: new Set() };
|
|
961
967
|
for (const e of tokenEvents) {
|
|
@@ -1022,16 +1028,35 @@ const tag = DRY_RUN ? '[dry-run] ' : '';
|
|
|
1022
1028
|
console.log(`${tag}Collated: ${targetSprints.length} sprint(s), ${allBugs.length} bug(s) → MASTER_INDEX.md updated, ${sprintIndexesWritten} sprint INDEX(es), ${taskIndexesWritten} task INDEX(es), ${bugIndexesWritten} bug INDEX(es), ${costReportsWritten} COST_REPORT(s) written`);
|
|
1023
1029
|
|
|
1024
1030
|
// --- Purge event directory if requested ---
|
|
1031
|
+
//
|
|
1032
|
+
// Bug arg → purge only the bug-matching events from the shared
|
|
1033
|
+
// `events/bugs/` dir (filter by event.bugId). Sprint arg → purge the
|
|
1034
|
+
// whole sprint event directory as before.
|
|
1025
1035
|
if (PURGE_EVENTS) {
|
|
1026
|
-
const relDir = path.relative(cwd, path.join(storeRoot, 'events', SPRINT_ARG));
|
|
1027
1036
|
try {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1037
|
+
let result;
|
|
1038
|
+
let relDir;
|
|
1039
|
+
if (IS_BUG_ARG) {
|
|
1040
|
+
relDir = path.relative(cwd, path.join(storeRoot, 'events', 'bugs'));
|
|
1041
|
+
result = _getStore().purgeBugEvents(SPRINT_ARG, { dryRun: DRY_RUN });
|
|
1042
|
+
const label = `'${SPRINT_ARG}' in ${relDir}/`;
|
|
1043
|
+
if (result.fileCount === 0) {
|
|
1044
|
+
console.log(`${tag}Purge: no events for ${label} — nothing to delete`);
|
|
1045
|
+
} else if (DRY_RUN) {
|
|
1046
|
+
console.log(`[dry-run] would purge: ${label} (${result.fileCount} file(s))`);
|
|
1047
|
+
} else {
|
|
1048
|
+
console.log(`Purged: ${label} (${result.fileCount} event file(s) deleted)`);
|
|
1049
|
+
}
|
|
1033
1050
|
} else {
|
|
1034
|
-
|
|
1051
|
+
relDir = path.relative(cwd, path.join(storeRoot, 'events', SPRINT_ARG));
|
|
1052
|
+
result = _getStore().purgeEvents(SPRINT_ARG, { dryRun: DRY_RUN });
|
|
1053
|
+
if (result.fileCount === 0) {
|
|
1054
|
+
console.log(`${tag}Purge: no events directory found for '${SPRINT_ARG}' — nothing to delete`);
|
|
1055
|
+
} else if (DRY_RUN) {
|
|
1056
|
+
console.log(`[dry-run] would purge: ${relDir}/ (${result.fileCount} file(s))`);
|
|
1057
|
+
} else {
|
|
1058
|
+
console.log(`Purged: ${relDir}/ (${result.fileCount} event file(s) deleted)`);
|
|
1059
|
+
}
|
|
1035
1060
|
}
|
|
1036
1061
|
} catch (err) {
|
|
1037
1062
|
console.error(`Error: ${err.message}`);
|
|
@@ -18,7 +18,13 @@
|
|
|
18
18
|
const FENCE_OPEN = /^```gates\s+phase=([A-Za-z0-9_-]+)\s*$/;
|
|
19
19
|
const FENCE_CLOSE = /^```\s*$/;
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// Verdicts a workflow can carry in an `after <phase> = <verdict>` directive.
|
|
22
|
+
// Mirrors `read-verdict.cjs § ALLOWED_VERDICTS` — both must accept the same
|
|
23
|
+
// set. `n/a` is the legitimate verdict for setup phases (plan, implement,
|
|
24
|
+
// triage) that do not produce an approval/revision signal. Rejecting it
|
|
25
|
+
// here caused EMBERGLOW-BUG-001 (v0.44.1) to halt at preflight exit 2
|
|
26
|
+
// when the new fix-bug meta tried to use `after triage = n/a`.
|
|
27
|
+
const VALID_VERDICTS = new Set(['approved', 'revision', 'n/a']);
|
|
22
28
|
|
|
23
29
|
function parseGates(markdown) {
|
|
24
30
|
if (typeof markdown !== 'string' || markdown.length === 0) return {};
|
|
@@ -142,7 +148,7 @@ function parseAfter(rest, lineNo) {
|
|
|
142
148
|
const verdict = m[2].toLowerCase();
|
|
143
149
|
if (!VALID_VERDICTS.has(verdict)) {
|
|
144
150
|
throw new Error(
|
|
145
|
-
`parse-gates: line ${lineNo}: "after" verdict must be "approved" or "
|
|
151
|
+
`parse-gates: line ${lineNo}: "after" verdict must be "approved", "revision", or "n/a", got "${m[2]}"`,
|
|
146
152
|
);
|
|
147
153
|
}
|
|
148
154
|
return { phase: m[1], verdict };
|
|
@@ -140,6 +140,12 @@ const { suggest, suggestEntityType, formatSuggestion } = require('./lib/suggest.
|
|
|
140
140
|
const VALID_SUMMARY_PHASES = new Set(['plan', 'review_plan', 'implementation', 'code_review', 'validation', 'triage', 'approve']);
|
|
141
141
|
|
|
142
142
|
// Schema for a single phase summary (used by set-summary / set-bug-summary)
|
|
143
|
+
// Mirror of bug.schema.json § $defs.phaseSummary. Both definitions MUST stay
|
|
144
|
+
// in sync — this constant validates set-summary / set-bug-summary writes;
|
|
145
|
+
// the JSON schema validates entity reads. `route` is an optional triage-only
|
|
146
|
+
// field carrying the fix-bug pipeline route decision (A | B) and exists here
|
|
147
|
+
// for bug.summaries.triage in particular; non-triage phases simply do not
|
|
148
|
+
// set it.
|
|
143
149
|
const PHASE_SUMMARY_SCHEMA = {
|
|
144
150
|
type: 'object',
|
|
145
151
|
required: ['objective', 'written_at'],
|
|
@@ -149,7 +155,8 @@ const PHASE_SUMMARY_SCHEMA = {
|
|
|
149
155
|
findings: { type: 'array', items: { type: 'string', maxLength: 200 }, maxItems: 12 },
|
|
150
156
|
verdict: { type: 'string', enum: ['approved', 'revision', 'n/a'] },
|
|
151
157
|
written_at: { type: 'string' },
|
|
152
|
-
artifact_ref:{ type: 'string' }
|
|
158
|
+
artifact_ref:{ type: 'string' },
|
|
159
|
+
route: { type: 'string', enum: ['A', 'B'] }
|
|
153
160
|
},
|
|
154
161
|
additionalProperties: false
|
|
155
162
|
};
|
|
@@ -181,12 +188,21 @@ const SPRINT_TRANSITIONS = {
|
|
|
181
188
|
};
|
|
182
189
|
|
|
183
190
|
const BUG_TRANSITIONS = {
|
|
184
|
-
reported:
|
|
185
|
-
triaged:
|
|
186
|
-
'in-progress': ['fixed'
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
//
|
|
191
|
+
reported: ['triaged'],
|
|
192
|
+
triaged: ['in-progress'],
|
|
193
|
+
'in-progress': ['fixed'],
|
|
194
|
+
// Terminal: fixed
|
|
195
|
+
//
|
|
196
|
+
// The `approved` and `verified` enum members were removed (forge#GH-NNN).
|
|
197
|
+
// The architect-approve verdict signal for bugs travels through
|
|
198
|
+
// `bug.summaries.approve.verdict` (see read-verdict.cjs §
|
|
199
|
+
// BUG_PHASE_VERDICT_SOURCE), not `bug.status`. Keeping them in the enum
|
|
200
|
+
// created an LLM-translation trap whereby a task-shaped approve workflow
|
|
201
|
+
// run on a bug improvised `update-status bug ... approved`, which failed
|
|
202
|
+
// either at the schema layer (illegal transition out of terminal
|
|
203
|
+
// `verified`) or, post-FORGE-BUG-002 preflight defence, at the gate
|
|
204
|
+
// layer (forbid bug.status == approved). Dropping the enum removes the
|
|
205
|
+
// trap at its source.
|
|
190
206
|
};
|
|
191
207
|
|
|
192
208
|
const FEATURE_TRANSITIONS = {
|
|
@@ -205,7 +221,7 @@ const TRANSITION_MAP = {
|
|
|
205
221
|
const TERMINAL_STATES = new Set([
|
|
206
222
|
'committed', 'abandoned', // task
|
|
207
223
|
'retrospective-done', // sprint
|
|
208
|
-
'
|
|
224
|
+
'fixed', // bug
|
|
209
225
|
'shipped', 'retired' // feature
|
|
210
226
|
]);
|
|
211
227
|
|
|
@@ -785,10 +801,13 @@ function cmdEmit() {
|
|
|
785
801
|
}
|
|
786
802
|
|
|
787
803
|
// FK check: reject sprintIds that are not in the store and not reserved.
|
|
788
|
-
//
|
|
804
|
+
// Reserved sprintIds:
|
|
805
|
+
// - SYS-* — system-generated events that predate sprint records
|
|
806
|
+
// - bugs — virtual sprint dir for fix-bug phase events (see
|
|
807
|
+
// meta/tool-specs/validate-store.spec.md §"event.sprintId")
|
|
789
808
|
// --allow-synthetic bypasses the check for test-harness or synthetic events.
|
|
790
809
|
if (!allowSynthetic) {
|
|
791
|
-
const RESERVED_GLOB = /^SYS
|
|
810
|
+
const RESERVED_GLOB = /^(SYS-|bugs$)/;
|
|
792
811
|
if (!RESERVED_GLOB.test(sprintId)) {
|
|
793
812
|
const validSprintIds = resolveValidSprintIds();
|
|
794
813
|
if (!validSprintIds.includes(sprintId)) {
|
|
@@ -56,6 +56,7 @@ class Store {
|
|
|
56
56
|
* @returns {{ purged: boolean, fileCount: number, files: string[] }}
|
|
57
57
|
*/
|
|
58
58
|
purgeEvents(sprintId, opts) { return this.impl.purgeEvents(sprintId, opts); }
|
|
59
|
+
purgeBugEvents(bugId, opts) { return this.impl.purgeBugEvents(bugId, opts); }
|
|
59
60
|
/**
|
|
60
61
|
* List event filenames for a sprint directory.
|
|
61
62
|
* @param {string} sprintId
|
|
@@ -287,6 +288,66 @@ class FSImpl {
|
|
|
287
288
|
return { purged: true, fileCount: files.length, files };
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Purge only the events belonging to a specific bug from the shared
|
|
293
|
+
* `.forge/store/events/bugs/` virtual sprint dir.
|
|
294
|
+
*
|
|
295
|
+
* Bugs share a single events directory (`events/bugs/`) — see
|
|
296
|
+
* `meta-fix-bug.md § Event Emission` and `validate-store.spec.md`. Purging
|
|
297
|
+
* by bug therefore must filter primary events by `event.bugId === bugId`
|
|
298
|
+
* and sweep sidecars whose filename pattern `_{eventId}_usage.json`
|
|
299
|
+
* matches a purged primary. The `events/bugs/` directory itself is
|
|
300
|
+
* never removed — other bugs' events remain.
|
|
301
|
+
*
|
|
302
|
+
* Returns `{ purged, fileCount, files }` matching `purgeEvents`.
|
|
303
|
+
*/
|
|
304
|
+
purgeBugEvents(bugId, { dryRun = false } = {}) {
|
|
305
|
+
const eventsBugsDir = path.join(this.storeRoot, 'events', 'bugs');
|
|
306
|
+
if (!fs.existsSync(eventsBugsDir)) {
|
|
307
|
+
return { purged: false, fileCount: 0, files: [] };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const all = fs.readdirSync(eventsBugsDir).filter(f => f.endsWith('.json'));
|
|
311
|
+
const primaries = all.filter(f => !f.startsWith('_'));
|
|
312
|
+
const sidecars = all.filter(f => f.startsWith('_') && f.endsWith('_usage.json'));
|
|
313
|
+
|
|
314
|
+
// Identify primaries whose payload.bugId matches the requested bug.
|
|
315
|
+
const matchedPrimaries = [];
|
|
316
|
+
const matchedEventIds = new Set();
|
|
317
|
+
for (const filename of primaries) {
|
|
318
|
+
const filePath = path.join(eventsBugsDir, filename);
|
|
319
|
+
let payload;
|
|
320
|
+
try {
|
|
321
|
+
payload = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
322
|
+
} catch (_) {
|
|
323
|
+
continue; // malformed file — skip
|
|
324
|
+
}
|
|
325
|
+
if (payload && payload.bugId === bugId) {
|
|
326
|
+
matchedPrimaries.push(filename);
|
|
327
|
+
if (payload.eventId) matchedEventIds.add(payload.eventId);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Match sidecars by filename pattern: `_{eventId}_usage.json`.
|
|
332
|
+
const matchedSidecars = sidecars.filter(filename => {
|
|
333
|
+
const m = filename.match(/^_(.+)_usage\.json$/);
|
|
334
|
+
return m && matchedEventIds.has(m[1]);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const allMatches = [...matchedPrimaries, ...matchedSidecars];
|
|
338
|
+
if (dryRun) {
|
|
339
|
+
return { purged: false, fileCount: allMatches.length, files: allMatches };
|
|
340
|
+
}
|
|
341
|
+
for (const filename of allMatches) {
|
|
342
|
+
fs.unlinkSync(path.join(eventsBugsDir, filename));
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
purged: allMatches.length > 0,
|
|
346
|
+
fileCount: allMatches.length,
|
|
347
|
+
files: allMatches,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
290
351
|
/**
|
|
291
352
|
* List all event filenames for a sprint directory.
|
|
292
353
|
* Returns { filename, id } objects for ALL .json files including
|
|
@@ -494,8 +494,12 @@ for (const sprint of allSprints) {
|
|
|
494
494
|
|
|
495
495
|
// --- Pass 2b: Orphan event directories ---
|
|
496
496
|
// Scan .forge/store/events/ for subdirectories whose name does not match any
|
|
497
|
-
// known sprintId and is not a reserved
|
|
498
|
-
|
|
497
|
+
// known sprintId and is not a reserved virtual dir.
|
|
498
|
+
// Reserved:
|
|
499
|
+
// - SYS-* — system-generated events that predate sprint records
|
|
500
|
+
// - bugs — virtual sprint dir for fix-bug phase events (see
|
|
501
|
+
// meta/tool-specs/validate-store.spec.md §"event.sprintId")
|
|
502
|
+
const RESERVED_EVENT_PREFIX = /^(SYS-|bugs$)/;
|
|
499
503
|
const eventsBaseDir = path.join(storeRootFromConfig, 'events');
|
|
500
504
|
if (fs.existsSync(eventsBaseDir)) {
|
|
501
505
|
let eventDirEntries;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entelligentsia/forgecli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
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",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
51
|
"forge": {
|
|
52
|
-
"bundledVersion": "0.
|
|
52
|
+
"bundledVersion": "0.44.3",
|
|
53
53
|
"forgeRoot": "../forge/forge"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|