@entelligentsia/forgecli 1.0.21 → 1.0.36
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 +346 -0
- package/README.md +2 -0
- package/dist/CHANGELOG-forge-plugin.md +281 -0
- package/dist/bin/argv.d.ts +2 -2
- package/dist/bin/argv.js +25 -0
- package/dist/bin/argv.js.map +1 -1
- package/dist/bin/forge.js +12 -0
- package/dist/bin/forge.js.map +1 -1
- package/dist/bin/init.d.ts +23 -0
- package/dist/bin/init.js +123 -0
- package/dist/bin/init.js.map +1 -0
- package/dist/bin/uninstall.d.ts +20 -0
- package/dist/bin/uninstall.js +141 -0
- package/dist/bin/uninstall.js.map +1 -0
- package/dist/extensions/forgecli/claude-bootstrap/bootstrap.d.ts +40 -0
- package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js +593 -0
- package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js.map +1 -0
- package/dist/extensions/forgecli/claude-bootstrap/settings-merge.d.ts +46 -0
- package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js +245 -0
- package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js.map +1 -0
- package/dist/extensions/forgecli/claude-bootstrap/uninstall.d.ts +23 -0
- package/dist/extensions/forgecli/claude-bootstrap/uninstall.js +215 -0
- package/dist/extensions/forgecli/claude-bootstrap/uninstall.js.map +1 -0
- package/dist/extensions/forgecli/dashboard/component.js +10 -7
- package/dist/extensions/forgecli/dashboard/component.js.map +1 -1
- package/dist/extensions/forgecli/forge-tools.d.ts +1 -0
- package/dist/extensions/forgecli/forge-tools.js +73 -0
- package/dist/extensions/forgecli/forge-tools.js.map +1 -1
- package/dist/extensions/forgecli/lib/forge-root.d.ts +5 -0
- package/dist/extensions/forgecli/lib/forge-root.js +14 -1
- package/dist/extensions/forgecli/lib/forge-root.js.map +1 -1
- package/dist/extensions/forgecli/orchestrators/bug/bug-body.d.ts +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-body.js +65 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-body.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-id.d.ts +23 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-id.js +140 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-id.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.d.ts +54 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js +349 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phases.d.ts +8 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js +60 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-state.d.ts +14 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-state.js +100 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-state.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.d.ts +72 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js +204 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.d.ts +38 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js +166 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.d.ts +3 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js +55 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.d.ts +7 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js +293 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.d.ts +2 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js +501 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.d.ts +41 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js +5 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.d.ts +43 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js +85 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.d.ts +8 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js +37 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.d.ts +28 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js +45 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.d.ts +26 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js +75 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/summary-recovery.d.ts +24 -0
- package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js +37 -0
- package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/fix-bug.d.ts +9 -92
- package/dist/extensions/forgecli/orchestrators/fix-bug.js +23 -1695
- package/dist/extensions/forgecli/orchestrators/fix-bug.js.map +1 -1
- package/dist/extensions/forgecli/orchestrators/run-sprint.d.ts +3 -12
- package/dist/extensions/forgecli/orchestrators/run-sprint.js +97 -270
- package/dist/extensions/forgecli/orchestrators/run-sprint.js.map +1 -1
- package/dist/extensions/forgecli/orchestrators/run-task.d.ts +10 -214
- package/dist/extensions/forgecli/orchestrators/run-task.js +31 -1481
- package/dist/extensions/forgecli/orchestrators/run-task.js.map +1 -1
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.d.ts +33 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js +135 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.d.ts +18 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js +55 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-command.d.ts +9 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-command.js +174 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-command.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.d.ts +2 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js +494 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-types.d.ts +62 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-types.js +5 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-types.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-body.d.ts +4 -0
- package/dist/extensions/forgecli/orchestrators/task/task-body.js +48 -0
- package/dist/extensions/forgecli/orchestrators/task/task-body.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-events.d.ts +63 -0
- package/dist/extensions/forgecli/orchestrators/task/task-events.js +185 -0
- package/dist/extensions/forgecli/orchestrators/task/task-events.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-gates.d.ts +34 -0
- package/dist/extensions/forgecli/orchestrators/task/task-gates.js +78 -0
- package/dist/extensions/forgecli/orchestrators/task/task-gates.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.d.ts +42 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js +370 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phases.d.ts +14 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phases.js +26 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phases.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-record.d.ts +9 -0
- package/dist/extensions/forgecli/orchestrators/task/task-record.js +58 -0
- package/dist/extensions/forgecli/orchestrators/task/task-record.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-state.d.ts +14 -0
- package/dist/extensions/forgecli/orchestrators/task/task-state.js +35 -0
- package/dist/extensions/forgecli/orchestrators/task/task-state.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.d.ts +36 -0
- package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js +152 -0
- package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js.map +1 -0
- package/dist/extensions/forgecli/update/forge-update-command.js +10 -7
- package/dist/extensions/forgecli/update/forge-update-command.js.map +1 -1
- package/dist/forge-payload/.base-pack/commands/approve.md +2 -2
- package/dist/forge-payload/.base-pack/commands/check-agent.md +2 -2
- package/dist/forge-payload/.base-pack/commands/collate.md +2 -2
- package/dist/forge-payload/.base-pack/commands/commit.md +2 -2
- package/dist/forge-payload/.base-pack/commands/enhance.md +2 -2
- package/dist/forge-payload/.base-pack/commands/fix-bug.md +2 -2
- package/dist/forge-payload/.base-pack/commands/implement.md +2 -2
- package/dist/forge-payload/.base-pack/commands/init.md +278 -0
- package/dist/forge-payload/.base-pack/commands/new-sprint.md +2 -2
- package/dist/forge-payload/.base-pack/commands/plan-sprint.md +2 -2
- package/dist/forge-payload/.base-pack/commands/plan.md +2 -2
- package/dist/forge-payload/.base-pack/commands/retro.md +2 -2
- package/dist/forge-payload/.base-pack/commands/review-code.md +2 -2
- package/dist/forge-payload/.base-pack/commands/review-plan.md +2 -2
- package/dist/forge-payload/.base-pack/commands/run-sprint.md +2 -2
- package/dist/forge-payload/.base-pack/commands/run-task.md +2 -2
- package/dist/forge-payload/.base-pack/commands/validate.md +2 -2
- package/dist/forge-payload/.base-pack/workflows/_fragments/event-emission-schema.md +4 -0
- package/dist/forge-payload/.base-pack/workflows/_fragments/event-vocabulary.md +88 -0
- package/dist/forge-payload/.base-pack/workflows/collator_agent.md +5 -6
- package/dist/forge-payload/.base-pack/workflows/commit_task.md +41 -38
- package/dist/forge-payload/.base-pack/workflows/implement_plan.md +3 -3
- package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +1 -1
- package/dist/forge-payload/.base-pack/workflows-js/wfl-fix-bug.js +42 -6
- package/dist/forge-payload/.base-pack/workflows-js/wfl-init.js +449 -0
- package/dist/forge-payload/.base-pack/workflows-js/wfl-run-task.js +32 -1
- package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
- package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/.schemas/event.schema.json +8 -3
- package/dist/forge-payload/.schemas/migrations.json +141 -0
- package/dist/forge-payload/commands/add-pipeline.md +1 -1
- package/dist/forge-payload/commands/add-task.md +3 -3
- package/dist/forge-payload/commands/ask.md +1 -1
- package/dist/forge-payload/commands/check-agent.md +1 -1
- package/dist/forge-payload/commands/config.md +1 -1
- package/dist/forge-payload/commands/health.md +1 -1
- package/dist/forge-payload/commands/init.md +62 -7
- package/dist/forge-payload/commands/rebuild.md +3 -3
- package/dist/forge-payload/commands/remove.md +1 -1
- package/dist/forge-payload/commands/repair.md +1 -1
- package/dist/forge-payload/commands/report-bug.md +1 -1
- package/dist/forge-payload/commands/status.md +1 -1
- package/dist/forge-payload/commands/update.md +3 -3
- package/dist/forge-payload/hooks/lib/common.cjs +228 -0
- package/dist/forge-payload/hooks/lib/plugin-detection.cjs +106 -0
- package/dist/forge-payload/hooks/lib/update-msg.cjs +23 -0
- package/dist/forge-payload/hooks/lib/update-url.cjs +46 -0
- package/dist/forge-payload/hooks/lib/write-registry.js +53 -0
- package/dist/forge-payload/init/discovery/discover-database.md +32 -0
- package/dist/forge-payload/init/discovery/discover-processes.md +31 -0
- package/dist/forge-payload/init/discovery/discover-routing.md +31 -0
- package/dist/forge-payload/init/discovery/discover-stack.md +33 -0
- package/dist/forge-payload/init/discovery/discover-testing.md +34 -0
- package/dist/forge-payload/init/generation/generate-commands.md +171 -0
- package/dist/forge-payload/init/generation/generate-kb-doc.md +60 -0
- package/dist/forge-payload/init/generation/generate-knowledge-base.md +56 -0
- package/dist/forge-payload/init/generation/generate-persona.md +73 -0
- package/dist/forge-payload/init/generation/generate-personas.md +54 -0
- package/dist/forge-payload/init/generation/generate-skill.md +66 -0
- package/dist/forge-payload/init/generation/generate-skills.md +36 -0
- package/dist/forge-payload/init/generation/generate-template.md +60 -0
- package/dist/forge-payload/init/generation/generate-templates.md +39 -0
- package/dist/forge-payload/init/generation/generate-tools.md +133 -0
- package/dist/forge-payload/init/generation/generate-workflows.md +78 -0
- package/dist/forge-payload/init/phases/phase-1-collect.md +10 -2
- package/dist/forge-payload/init/phases/phase-3-materialize.md +1 -1
- package/dist/forge-payload/init/phases/phase-4-register.md +8 -0
- package/dist/forge-payload/init/workflow-gen-plan.json +17 -0
- package/dist/forge-payload/integrity.json +16 -16
- package/dist/forge-payload/meta/store-schema/event.schema.md +7 -0
- package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +4 -0
- package/dist/forge-payload/meta/workflows/_fragments/event-vocabulary.md +88 -0
- package/dist/forge-payload/meta/workflows/meta-collate.md +5 -6
- package/dist/forge-payload/meta/workflows/meta-commit.md +46 -43
- package/dist/forge-payload/meta/workflows/meta-fix-bug.md +7 -2
- package/dist/forge-payload/meta/workflows/meta-implement.md +3 -3
- package/dist/forge-payload/meta/workflows/meta-migrate.md +1 -1
- package/dist/forge-payload/meta/workflows/meta-orchestrate.md +4 -1
- package/dist/forge-payload/schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/schemas/event.schema.json +8 -3
- package/dist/forge-payload/schemas/structure-manifest.json +5 -12
- package/dist/forge-payload/tools/commit-task.cjs +218 -0
- package/dist/forge-payload/tools/forge-preflight.cjs +268 -0
- package/dist/forge-payload/tools/lib/paths.cjs +12 -11
- package/dist/forge-payload/tools/lib/pricing.cjs +31 -11
- package/dist/forge-payload/tools/query-logger.cjs +34 -0
- package/dist/forge-payload/tools/store-cli.cjs +6 -1
- package/dist/forge-payload/tools/substitute-placeholders.cjs +5 -6
- package/package.json +2 -2
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'wfl:init',
|
|
3
|
+
description: 'Code-orchestrated /forge:init LLM half — parallel discovery fan-out → config-writer (Phase 1 Collect), parallel KB-doc fan-out → index → context (Phase 2 Discover), deterministic materialize (Phase 3), content-register (Phase 4). JS holds the phase index, verify gates, retry caps, and fan-out; subagents execute the phase rulebooks. Args: { forgeRoot, kbFolder, startPhase, createClaudeMd, isoTimestamp, rawArguments }.',
|
|
4
|
+
whenToUse: "Run the LLM-orchestrated half of /forge:init after `4ge init claude .` has bootstrapped the project structure. Dispatch by name: workflow('wfl:init', { forgeRoot, kbFolder, startPhase, createClaudeMd, isoTimestamp, rawArguments }).",
|
|
5
|
+
phases: [
|
|
6
|
+
{ title: 'Collect', detail: 'parallel() 5 discovery agents scan codebase domains; config-writer agent merges findings and writes config + init-progress.json; verify-phase gate with one retry cap' },
|
|
7
|
+
{ title: 'Discover', detail: 'gate+scaffold agent verifies phase 1; parallel() 7 KB-doc agents generate architecture docs with JS-held retry-once; sequential index + context agents close the phase' },
|
|
8
|
+
{ title: 'Materialize', detail: 'single haiku agent runs deterministic substitute-placeholders + generation-manifest + build-overlay; Phase 3 verify failure is a hard halt (no retry — rebuild/restart)' },
|
|
9
|
+
{ title: 'Register', detail: 'single haiku agent runs content-register steps; CLAUDE.md creation gated on args.createClaudeMd === true; returns pendingActions for Tomoshibi' },
|
|
10
|
+
{ title: 'Report', detail: 'return structured result { ok, lastPhase, stack, skillMatches, counts, confidence, pendingActions, failure? } for the command wrapper to render' },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// wfl:init — code-orchestrated LLM half of /forge:init
|
|
15
|
+
//
|
|
16
|
+
// Why a script: init is a deterministic 4-phase FSM with mechanical verify
|
|
17
|
+
// gates (verify-phase.cjs --phase N), bounded retries (max 1), fan-out steps
|
|
18
|
+
// (5 discovery scans, 7 KB docs), and escalate-don't-continue semantics.
|
|
19
|
+
// JS holds the phase index, the verify-gate routing, the retry counters, and
|
|
20
|
+
// the fan-out; subagents only execute one phase rulebook each.
|
|
21
|
+
//
|
|
22
|
+
// WORKFLOW-API CONTRACT (forge#112 — field failure; enforced by
|
|
23
|
+
// wfl-drivers-parse.test.cjs):
|
|
24
|
+
// - Exactly ONE export: the meta literal. The body runs at top level in the
|
|
25
|
+
// harness's async context (top-level await/return valid; args is a global).
|
|
26
|
+
// - phase(title) takes ONLY a title — a callback second arg is silently
|
|
27
|
+
// discarded. Phase bodies run inline after the phase() call.
|
|
28
|
+
// - parallel() takes thunks: parallel([() => agent(...), ...]).
|
|
29
|
+
// - agent(prompt, opts) — model goes in opts.model; structured results
|
|
30
|
+
// REQUIRE opts.schema (otherwise agent() returns plain text and .ok
|
|
31
|
+
// reads are undefined).
|
|
32
|
+
//
|
|
33
|
+
// CLI-FIRST BOOTSTRAP ADR (doc/decisions/cli-first-bootstrap.md):
|
|
34
|
+
// `4ge init claude .` runs first (deterministic, zero tokens): scaffolds
|
|
35
|
+
// .forge/ as a complete vendored Forge root (tools, schemas, hooks, init/,
|
|
36
|
+
// .base-pack/, meta/) and installs THIS FILE into .claude/workflows/.
|
|
37
|
+
// All rulebook reads use vendored .forge/init/... paths; discovery prompts
|
|
38
|
+
// fall back to direct project analysis if a prompt file is absent.
|
|
39
|
+
//
|
|
40
|
+
// SIDE-EFFECT OWNERSHIP: this script has NO filesystem/shell access. Each
|
|
41
|
+
// per-phase subagent owns rulebook execution (artifact writes, checkpoint
|
|
42
|
+
// writes, verify runs). The JS driver holds ONLY control flow.
|
|
43
|
+
//
|
|
44
|
+
// Timestamps: the Workflow sandbox blocks timestamp minting (Date.now,
|
|
45
|
+
// Math.random, and the zero-arg Date constructor are all unavailable).
|
|
46
|
+
// The command wrapper supplies args.isoTimestamp.
|
|
47
|
+
//
|
|
48
|
+
// MODEL TIERING: generation/discovery → sonnet; deterministic gates and
|
|
49
|
+
// registration → haiku. No opus (init has no review/approve gates).
|
|
50
|
+
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
|
+
// Schemas
|
|
53
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
const DISCOVERY_SCHEMA = {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
domain: { type: 'string', description: 'discovery domain name (stack|routing|processes|database|testing)' },
|
|
59
|
+
findings: { type: 'object', description: 'domain-specific structured findings' },
|
|
60
|
+
confidence: { type: 'number', description: '0–1 confidence in completeness of scan' },
|
|
61
|
+
warnings: { type: 'array', items: { type: 'string' }, description: 'ambiguities or partial coverage notes' },
|
|
62
|
+
},
|
|
63
|
+
required: ['domain', 'findings', 'confidence'],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const KB_DOC_SCHEMA = {
|
|
67
|
+
type: 'object',
|
|
68
|
+
properties: {
|
|
69
|
+
id: { type: 'string', description: 'KB doc id matching the phase-2 table row' },
|
|
70
|
+
ok: { type: 'boolean', description: 'true if doc written successfully' },
|
|
71
|
+
confidence: { type: 'number', description: '0–1 confidence in doc completeness' },
|
|
72
|
+
error: { type: 'string', description: 'error message if ok=false' },
|
|
73
|
+
},
|
|
74
|
+
required: ['id', 'ok', 'confidence'],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const PHASE_RESULT_SCHEMA = {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
verifyExit: { type: 'number', description: 'exit code from verify-phase.cjs (0=pass, non-zero=fail)' },
|
|
81
|
+
verifyError: { type: 'string', description: 'stderr/stdout from failed verify run' },
|
|
82
|
+
stack: { type: 'string', description: 'technology stack summary (Phase 1 output)' },
|
|
83
|
+
skillMatches: {
|
|
84
|
+
type: 'array',
|
|
85
|
+
items: { type: 'string' },
|
|
86
|
+
description: 'skill IDs matching project tech stack (from skill-recommendations.md)',
|
|
87
|
+
},
|
|
88
|
+
confidence: { type: 'number', description: '0–1 confidence' },
|
|
89
|
+
ok: { type: 'boolean', description: 'true if phase completed and verify passed' },
|
|
90
|
+
},
|
|
91
|
+
required: ['verifyExit', 'ok'],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const OK_SCHEMA = {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
ok: { type: 'boolean', description: 'true if all steps completed' },
|
|
98
|
+
error: { type: 'string', description: 'error message if ok=false' },
|
|
99
|
+
},
|
|
100
|
+
required: ['ok'],
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const REGISTER_SCHEMA = {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
ok: { type: 'boolean', description: 'true if all register steps completed' },
|
|
107
|
+
error: { type: 'string', description: 'error message if ok=false' },
|
|
108
|
+
pendingActions: { type: 'array', items: { type: 'string' }, description: 'orchestrator-owned follow-ups, e.g. ["refresh-kb-links"]' },
|
|
109
|
+
},
|
|
110
|
+
required: ['ok'],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
114
|
+
// Model tiering
|
|
115
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
const ROLE_TIER = {
|
|
118
|
+
'discovery': 'sonnet',
|
|
119
|
+
'config': 'sonnet',
|
|
120
|
+
'kb-doc': 'sonnet',
|
|
121
|
+
'index': 'sonnet',
|
|
122
|
+
'context': 'sonnet',
|
|
123
|
+
'gate': 'haiku',
|
|
124
|
+
'materialize': 'haiku',
|
|
125
|
+
'register': 'haiku',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
129
|
+
// Helpers
|
|
130
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
function halt(lastPhase, reason, extra) {
|
|
133
|
+
return { ok: false, lastPhase, failure: reason, ...extra };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const VENDORED = 'Use only .forge/tools/ paths for all tool invocations (vendored-tools world).';
|
|
137
|
+
|
|
138
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
139
|
+
// Main workflow body (top-level async context; top-level return is valid)
|
|
140
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
const {
|
|
143
|
+
forgeRoot,
|
|
144
|
+
kbFolder = 'engineering',
|
|
145
|
+
startPhase = 1,
|
|
146
|
+
createClaudeMd = null,
|
|
147
|
+
isoTimestamp,
|
|
148
|
+
rawArguments = '',
|
|
149
|
+
} = args || {};
|
|
150
|
+
|
|
151
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
152
|
+
// Phase 1 — Collect (startPhase <= 1)
|
|
153
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
154
|
+
let phase1Result;
|
|
155
|
+
if (startPhase <= 1) {
|
|
156
|
+
phase('Collect');
|
|
157
|
+
|
|
158
|
+
// Fan-out: 5 parallel discovery agents
|
|
159
|
+
const DOMAINS = ['stack', 'routing', 'processes', 'database', 'testing'];
|
|
160
|
+
const discoveryResults = await parallel(
|
|
161
|
+
DOMAINS.map((domain) => () =>
|
|
162
|
+
agent(`
|
|
163
|
+
You are a codebase discovery agent for the "${domain}" domain.
|
|
164
|
+
1. Read \`.forge/init/discovery/discover-${domain}.md\` if it exists and follow
|
|
165
|
+
its instructions against the current project.
|
|
166
|
+
2. If that file does NOT exist, read \`.forge/init/phases/phase-1-collect.md\`
|
|
167
|
+
Step 2 for context, then perform a best-effort "${domain}" discovery of the
|
|
168
|
+
project directly (read manifests, source files, configs) and add a warning
|
|
169
|
+
noting the missing discovery prompt file.
|
|
170
|
+
Set domain="${domain}" in your structured output. ${VENDORED}
|
|
171
|
+
`, { model: ROLE_TIER['discovery'], label: `discover:${domain}`, phase: 'Collect', schema: DISCOVERY_SCHEMA })
|
|
172
|
+
)
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Config-writer agent: merge findings, write config, verify Phase 1
|
|
176
|
+
const configResult = await agent(`
|
|
177
|
+
You are the Forge init config-writer agent. You have received the following
|
|
178
|
+
discovery findings from 5 parallel discovery agents:
|
|
179
|
+
|
|
180
|
+
${JSON.stringify(discoveryResults.filter(Boolean), null, 2)}
|
|
181
|
+
|
|
182
|
+
Execute the Phase 1 Collect rulebook steps:
|
|
183
|
+
1. Read \`.forge/init/phases/phase-1-collect.md\` for the full step list.
|
|
184
|
+
2. Call \`node .forge/tools/manage-config.cjs\` to write the config. If kbFolder
|
|
185
|
+
is non-default, set paths.engineering="${kbFolder}". Set mode=full.
|
|
186
|
+
3. Compute skill-recommendation matches from .forge/meta/skill-recommendations.md
|
|
187
|
+
and the output of \`node .forge/tools/list-skills.js\`. Do NOT install —
|
|
188
|
+
report matches in skillMatches only.
|
|
189
|
+
4. Write .forge/init-progress.json: { "lastPhase": 1, "timestamp": "${isoTimestamp}" }.
|
|
190
|
+
5. Run: node .forge/tools/verify-phase.cjs --phase 1
|
|
191
|
+
6. In your structured output set verifyExit=<exit code>,
|
|
192
|
+
verifyError=<stderr if non-zero>, stack=<one-line stack summary>,
|
|
193
|
+
skillMatches=[<matched skill ids>], confidence=<0-1>, ok=(verifyExit===0).
|
|
194
|
+
${VENDORED}
|
|
195
|
+
kbFolder="${kbFolder}", isoTimestamp="${isoTimestamp}".
|
|
196
|
+
`, { model: ROLE_TIER['config'], label: 'config-writer', phase: 'Collect', schema: PHASE_RESULT_SCHEMA });
|
|
197
|
+
|
|
198
|
+
// Verify routing: one retry on failure
|
|
199
|
+
if (configResult && configResult.verifyExit !== 0) {
|
|
200
|
+
const retryResult = await agent(`
|
|
201
|
+
Phase 1 of Forge init verify failed. Error:
|
|
202
|
+
${configResult.verifyError || '(no error text)'}
|
|
203
|
+
|
|
204
|
+
Read the error carefully. Fix the config by re-running
|
|
205
|
+
\`node .forge/tools/manage-config.cjs\` with the correct values, then
|
|
206
|
+
re-run \`node .forge/tools/verify-phase.cjs --phase 1\`.
|
|
207
|
+
In your structured output set verifyExit=<exit code>, ok=(verifyExit===0).
|
|
208
|
+
${VENDORED}
|
|
209
|
+
`, { model: ROLE_TIER['config'], label: 'config-writer:retry', phase: 'Collect', schema: PHASE_RESULT_SCHEMA });
|
|
210
|
+
if (!retryResult || retryResult.verifyExit !== 0) {
|
|
211
|
+
return halt(1, 'Phase 1 verify failed after retry', {
|
|
212
|
+
verifyError: retryResult ? retryResult.verifyError : 'retry agent returned null',
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
phase1Result = { ...configResult, ...retryResult };
|
|
216
|
+
} else if (configResult) {
|
|
217
|
+
phase1Result = configResult;
|
|
218
|
+
} else {
|
|
219
|
+
return halt(1, 'config-writer agent returned null');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!phase1Result.ok) {
|
|
223
|
+
return halt(1, 'Phase 1 failed', {
|
|
224
|
+
verifyError: phase1Result.verifyError,
|
|
225
|
+
stack: phase1Result.stack,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
phase1Result = { ok: true, lastPhase: 1, skipped: true };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
233
|
+
// Phase 2 — Discover (startPhase <= 2)
|
|
234
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
235
|
+
let phase2Result;
|
|
236
|
+
const KB_DOC_IDS = [
|
|
237
|
+
'architecture/stack',
|
|
238
|
+
'architecture/processes',
|
|
239
|
+
'architecture/routing',
|
|
240
|
+
'architecture/database',
|
|
241
|
+
'architecture/testing',
|
|
242
|
+
'business-domain/domain-model',
|
|
243
|
+
'business-domain/domain-concepts',
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
if (startPhase <= 2) {
|
|
247
|
+
phase('Discover');
|
|
248
|
+
|
|
249
|
+
// Gate+scaffold agent: verify Phase 1 passed, mkdir scaffold
|
|
250
|
+
const gateResult = await agent(`
|
|
251
|
+
You are the Forge init Phase 2 gate agent. Execute these steps in order:
|
|
252
|
+
1. Run: node .forge/tools/verify-phase.cjs --phase 1
|
|
253
|
+
If exit non-zero, set ok=false and error=<stderr> in your output and stop.
|
|
254
|
+
2. Read \`.forge/init/phases/phase-2-discover.md\` Step 2 (scaffold mkdir
|
|
255
|
+
commands) and execute them. Create the KB directory structure under ${kbFolder}/.
|
|
256
|
+
3. Set ok=true.
|
|
257
|
+
${VENDORED}
|
|
258
|
+
`, { model: ROLE_TIER['gate'], label: 'phase2-gate', phase: 'Discover', schema: OK_SCHEMA });
|
|
259
|
+
|
|
260
|
+
if (!gateResult || !gateResult.ok) {
|
|
261
|
+
return halt(2, 'Phase 2 gate failed — Phase 1 verify did not pass', {
|
|
262
|
+
verifyError: gateResult ? gateResult.error : 'gate agent returned null',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Fan-out: 7 parallel KB-doc agents
|
|
267
|
+
const kbDocPrompt = (docId) => `
|
|
268
|
+
You are a Forge KB-doc generation agent. Your doc id is: "${docId}".
|
|
269
|
+
1. Read \`.forge/init/phases/phase-2-discover.md\` for the full doc spec for "${docId}".
|
|
270
|
+
2. If \`.forge/init/generation/generate-kb-doc.md\` exists, read it for the
|
|
271
|
+
generation rulebook; otherwise follow the doc spec from step 1 directly.
|
|
272
|
+
3. Generate the doc using all available project context (including
|
|
273
|
+
.forge/config.json written in Phase 1).
|
|
274
|
+
4. Write the doc to the correct path under ${kbFolder}/.
|
|
275
|
+
5. Set id="${docId}", ok=<true if written>, confidence=<0-1>, error=<if not ok>.
|
|
276
|
+
${VENDORED}
|
|
277
|
+
`;
|
|
278
|
+
|
|
279
|
+
const kbDocResults = await parallel(
|
|
280
|
+
KB_DOC_IDS.map((docId) => () =>
|
|
281
|
+
agent(kbDocPrompt(docId), { model: ROLE_TIER['kb-doc'], label: `kb-doc:${docId}`, phase: 'Discover', schema: KB_DOC_SCHEMA })
|
|
282
|
+
)
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
// JS-held retry-once for any failed KB-doc
|
|
286
|
+
const failedDocs = KB_DOC_IDS.filter((docId, i) => !kbDocResults[i] || !kbDocResults[i].ok);
|
|
287
|
+
if (failedDocs.length > 0) {
|
|
288
|
+
const retryResults = await parallel(
|
|
289
|
+
failedDocs.map((docId) => () =>
|
|
290
|
+
agent(`
|
|
291
|
+
KB-doc "${docId}" failed on first attempt. Retry it:
|
|
292
|
+
${kbDocPrompt(docId)}
|
|
293
|
+
`, { model: ROLE_TIER['kb-doc'], label: `kb-doc-retry:${docId}`, phase: 'Discover', schema: KB_DOC_SCHEMA })
|
|
294
|
+
)
|
|
295
|
+
);
|
|
296
|
+
const stillFailed = failedDocs.filter((docId, i) => !retryResults[i] || !retryResults[i].ok);
|
|
297
|
+
if (stillFailed.length > 0) {
|
|
298
|
+
return halt(2, `KB-doc generation failed after retry for: ${stillFailed.join(', ')}`, {
|
|
299
|
+
failedDocs: stillFailed.join(', '),
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Sequential index agent (after all leaf docs — real data dependency)
|
|
305
|
+
const indexResult = await agent(`
|
|
306
|
+
You are the Forge init index agent. All 7 KB architecture docs have been generated.
|
|
307
|
+
Read \`.forge/init/phases/phase-2-discover.md\` Step 4 (index files).
|
|
308
|
+
Generate the 3 INDEX files:
|
|
309
|
+
- ${kbFolder}/architecture/INDEX.md
|
|
310
|
+
- ${kbFolder}/business-domain/INDEX.md
|
|
311
|
+
- ${kbFolder}/INDEX.md
|
|
312
|
+
Set ok=true when done, or ok=false with error=<message>.
|
|
313
|
+
${VENDORED}
|
|
314
|
+
`, { model: ROLE_TIER['index'], label: 'index', phase: 'Discover', schema: OK_SCHEMA });
|
|
315
|
+
|
|
316
|
+
if (!indexResult || !indexResult.ok) {
|
|
317
|
+
return halt(2, 'Index agent failed', { error: indexResult ? indexResult.error : 'null result' });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Context agent: project-context.json + calibration + init-progress.json + verify
|
|
321
|
+
const contextResult = await agent(`
|
|
322
|
+
You are the Forge init context agent. KB docs and index files are complete.
|
|
323
|
+
Execute \`.forge/init/phases/phase-2-discover.md\` Steps 5–6:
|
|
324
|
+
5. Write project-context.json (combined structured context from all discovery
|
|
325
|
+
findings). Write calibration baseline.
|
|
326
|
+
6. Write .forge/init-progress.json: { "lastPhase": 2, "timestamp": "${isoTimestamp}" }.
|
|
327
|
+
Run: node .forge/tools/verify-phase.cjs --phase 2 --kb-path "${kbFolder}"
|
|
328
|
+
Set verifyExit=<exit code>, verifyError=<stderr if non-zero>, ok=(verifyExit===0).
|
|
329
|
+
${VENDORED}
|
|
330
|
+
`, { model: ROLE_TIER['context'], label: 'context', phase: 'Discover', schema: PHASE_RESULT_SCHEMA });
|
|
331
|
+
|
|
332
|
+
// Verify routing: one retry on failure
|
|
333
|
+
if (contextResult && contextResult.verifyExit !== 0) {
|
|
334
|
+
const verifyRetry = await agent(`
|
|
335
|
+
Phase 2 of Forge init verify failed. Error:
|
|
336
|
+
${contextResult.verifyError || '(no error text)'}
|
|
337
|
+
Read the error, fix the missing or malformed outputs, re-run
|
|
338
|
+
\`node .forge/tools/verify-phase.cjs --phase 2 --kb-path "${kbFolder}"\`.
|
|
339
|
+
Set verifyExit=<exit code>, ok=(verifyExit===0).
|
|
340
|
+
${VENDORED}
|
|
341
|
+
`, { model: ROLE_TIER['context'], label: 'context:retry', phase: 'Discover', schema: PHASE_RESULT_SCHEMA });
|
|
342
|
+
if (!verifyRetry || verifyRetry.verifyExit !== 0) {
|
|
343
|
+
return halt(2, 'Phase 2 verify failed after retry', {
|
|
344
|
+
verifyError: verifyRetry ? verifyRetry.verifyError : 'retry returned null',
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
phase2Result = verifyRetry;
|
|
348
|
+
} else if (contextResult) {
|
|
349
|
+
phase2Result = contextResult;
|
|
350
|
+
} else {
|
|
351
|
+
return halt(2, 'context agent returned null');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!phase2Result.ok) {
|
|
355
|
+
return halt(2, 'Phase 2 failed', { verifyError: phase2Result.verifyError });
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
phase2Result = { ok: true, lastPhase: 2, skipped: true };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
362
|
+
// Phase 3 — Materialize (startPhase <= 3)
|
|
363
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
364
|
+
let phase3Result;
|
|
365
|
+
if (startPhase <= 3) {
|
|
366
|
+
phase('Materialize');
|
|
367
|
+
|
|
368
|
+
// Single haiku agent — deterministic shell steps
|
|
369
|
+
const materializeResult = await agent(`
|
|
370
|
+
You are the Forge init materialize agent. Read
|
|
371
|
+
\`.forge/init/phases/phase-3-materialize.md\` for the rulebook, then execute
|
|
372
|
+
these deterministic steps:
|
|
373
|
+
1. Run: node .forge/tools/build-init-context.cjs
|
|
374
|
+
2. Run: node .forge/tools/substitute-placeholders.cjs
|
|
375
|
+
3. Record generation-manifest entries for all materialized assets.
|
|
376
|
+
4. Run a build-overlay smoke check: node .forge/tools/build-overlay.cjs --check
|
|
377
|
+
5. Write .forge/init-progress.json: { "lastPhase": 3, "timestamp": "${isoTimestamp}" }
|
|
378
|
+
6. Run: node .forge/tools/verify-phase.cjs --phase 3
|
|
379
|
+
Set verifyExit=<exit code>, verifyError=<stderr if non-zero>, ok=(verifyExit===0).
|
|
380
|
+
${VENDORED}
|
|
381
|
+
IMPORTANT: If verify-phase exits non-zero, report it faithfully — do NOT retry.
|
|
382
|
+
Phase 3 verify failure is a hard halt. The rulebook says: rebuild or restart init.
|
|
383
|
+
`, { model: ROLE_TIER['materialize'], label: 'materialize', phase: 'Materialize', schema: PHASE_RESULT_SCHEMA });
|
|
384
|
+
|
|
385
|
+
// Phase 3: hard halt on verify failure (no retry)
|
|
386
|
+
if (!materializeResult || materializeResult.verifyExit !== 0) {
|
|
387
|
+
const verifyError = materializeResult ? materializeResult.verifyError : 'materialize agent returned null';
|
|
388
|
+
return halt(3, [
|
|
389
|
+
'Phase 3 (Materialize) verify failed. This is a hard halt — no retry.',
|
|
390
|
+
'Per the phase-3-materialize.md rulebook: you must rebuild or restart /forge:init.',
|
|
391
|
+
'Run: /forge:init (or `4ge init claude .` to re-scaffold, then /forge:init).',
|
|
392
|
+
`Verify error: ${verifyError}`,
|
|
393
|
+
].join(' '), { verifyError, rebuild: true, restart: true });
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
phase3Result = materializeResult;
|
|
397
|
+
} else {
|
|
398
|
+
phase3Result = { ok: true, lastPhase: 3, skipped: true };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
402
|
+
// Phase 4 — Register (startPhase <= 4)
|
|
403
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
404
|
+
let phase4Result;
|
|
405
|
+
if (startPhase <= 4) {
|
|
406
|
+
phase('Register');
|
|
407
|
+
|
|
408
|
+
const registerResult = await agent(`
|
|
409
|
+
You are the Forge init register agent. Execute the content-register steps:
|
|
410
|
+
1. Read \`.forge/init/phases/phase-4-register.md\` for the full step list.
|
|
411
|
+
2. Execute steps 1–10 and step 12 verbatim.
|
|
412
|
+
3. Step 13 (CLAUDE.md file creation): ${createClaudeMd === true
|
|
413
|
+
? 'createClaudeMd=true — execute this step (create the CLAUDE.md file).'
|
|
414
|
+
: 'createClaudeMd is not true — SKIP step 13 (the prompt was hoisted to the wrapper).'}
|
|
415
|
+
4. Step 11 (Tomoshibi forge:refresh-kb-links): DO NOT execute. This is
|
|
416
|
+
orchestrator-owned. Set pendingActions=["refresh-kb-links"] and the
|
|
417
|
+
wrapper (init.md) will run it post-workflow.
|
|
418
|
+
5. Delete .forge/init-progress.json (phase 4 complete — no resume needed).
|
|
419
|
+
6. Set ok=true, pendingActions=["refresh-kb-links"].
|
|
420
|
+
${VENDORED}
|
|
421
|
+
createClaudeMd=${JSON.stringify(createClaudeMd)}, isoTimestamp="${isoTimestamp}".
|
|
422
|
+
`, { model: ROLE_TIER['register'], label: 'register', phase: 'Register', schema: REGISTER_SCHEMA });
|
|
423
|
+
|
|
424
|
+
if (!registerResult || !registerResult.ok) {
|
|
425
|
+
return halt(4, registerResult ? (registerResult.error || 'Phase 4 failed') : 'register agent returned null');
|
|
426
|
+
}
|
|
427
|
+
phase4Result = registerResult;
|
|
428
|
+
} else {
|
|
429
|
+
phase4Result = { ok: true, lastPhase: 4, skipped: true };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
433
|
+
// Report
|
|
434
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
435
|
+
phase('Report');
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
ok: true,
|
|
439
|
+
lastPhase: 4,
|
|
440
|
+
stack: phase1Result && phase1Result.stack,
|
|
441
|
+
skillMatches: phase1Result && phase1Result.skillMatches,
|
|
442
|
+
counts: {
|
|
443
|
+
kbDocs: KB_DOC_IDS.length,
|
|
444
|
+
workflows: 4, // wfl-run-task, wfl-run-sprint, wfl-fix-bug, wfl-init (installed by the CLI half)
|
|
445
|
+
commands: 3, // run-task, run-sprint, fix-bug (init.md is the wrapper, not a base-pack command)
|
|
446
|
+
},
|
|
447
|
+
confidence: phase1Result && phase1Result.confidence,
|
|
448
|
+
pendingActions: (phase4Result && phase4Result.pendingActions) || ['refresh-kb-links'],
|
|
449
|
+
};
|
|
@@ -133,6 +133,22 @@ const SKIP_STATUS = ['blocked', 'escalated', 'committed', 'abandoned']
|
|
|
133
133
|
// NOTE: `approve` is NOT here — orchestrate_task advances it on completion like a
|
|
134
134
|
// non-review phase (the approve workflow self-escalates if it rejects).
|
|
135
135
|
const REVIEW_ROLES = ['review-plan', 'review-code', 'validate']
|
|
136
|
+
|
|
137
|
+
// Task-phase `type` tokens — verbatim port of the canonical table in
|
|
138
|
+
// .forge/workflows/_fragments/event-vocabulary.md § Task pipeline
|
|
139
|
+
// (forge-engineering#39). The COMPLETE event carries the pass token (or the
|
|
140
|
+
// fail token when a review phase's verdict is "revision"); the START event is
|
|
141
|
+
// always untyped. Roles outside this map emit untyped events (valid).
|
|
142
|
+
const TASK_TYPE_TOKENS = {
|
|
143
|
+
'plan': { pass: 'task-planned', fail: 'task-planned' },
|
|
144
|
+
'review-plan': { pass: 'plan-approved', fail: 'review-failed' },
|
|
145
|
+
'implement': { pass: 'task-implemented', fail: 'task-implemented' },
|
|
146
|
+
'review-code': { pass: 'review-passed', fail: 'review-failed' },
|
|
147
|
+
'validate': { pass: 'task-validated', fail: 'review-failed' },
|
|
148
|
+
'approve': { pass: 'task-approved', fail: 'review-failed' },
|
|
149
|
+
'commit': { pass: 'task-committed', fail: 'task-committed' },
|
|
150
|
+
}
|
|
151
|
+
|
|
136
152
|
// Per-phase model tier — verbatim port of orchestrate_task.md § Role-to-Tier Mapping.
|
|
137
153
|
// The resolve agent uses this as a reference; JS loop calls resolveModel() not tierFor().
|
|
138
154
|
const ROLE_TIER = {
|
|
@@ -338,17 +354,30 @@ function runPhase(taskId, sprintId, phase, iteration, { firstPhase = false, simp
|
|
|
338
354
|
const eventIdLine = eventId
|
|
339
355
|
? ' Use eventId="' + eventId + '" for the COMPLETE event (the driver will call merge-sidecar with this id).'
|
|
340
356
|
: ' Use a fresh crypto.randomUUID() for both start and complete event ids.'
|
|
357
|
+
// forge-engineering#39: explicit type-token guidance per
|
|
358
|
+
// .forge/workflows/_fragments/event-vocabulary.md. Without it, subagents
|
|
359
|
+
// guessed and leaked the action value into `type` ("start"/"complete" —
|
|
360
|
+
// schema-rejected store residue).
|
|
361
|
+
const typeTokens = TASK_TYPE_TOKENS[phase.role]
|
|
362
|
+
const typeTokenLine = typeTokens
|
|
363
|
+
? (REVIEW_ROLES.includes(phase.role)
|
|
364
|
+
? 'set type="' + typeTokens.pass + '" when your verdict is Approved, type="' + typeTokens.fail + '" when it is Revision Required.'
|
|
365
|
+
: 'set type="' + typeTokens.pass + '".')
|
|
366
|
+
: 'omit the "type" field entirely (untyped events are valid; this role has no table entry).'
|
|
341
367
|
lines.push(
|
|
342
368
|
'',
|
|
343
369
|
'3. EMIT YOUR PHASE EVENTS. You are the only actor that knows your runtime attribution.',
|
|
344
370
|
' 3a. BEFORE running the phase workflow: note the start timestamp (startTimestamp = new Date().toISOString()).',
|
|
345
371
|
' Emit a start event via `node .forge/tools/store-cli.cjs emit ' + sprintId + " '{event-json}'\`",
|
|
346
372
|
' with action="start", role="' + phase.role + '", iteration=' + iteration + ', startTimestamp and endTimestamp both equal to startTimestamp (0-duration placeholder).',
|
|
373
|
+
' The start event MUST NOT include a "type" field.',
|
|
347
374
|
' 3b. AFTER the phase workflow completes: note the end timestamp (endTimestamp = new Date().toISOString()).',
|
|
348
375
|
' Compute durationMinutes = (new Date(endTimestamp) - new Date(startTimestamp)) / 60000.',
|
|
349
376
|
' Emit a complete event via `node .forge/tools/store-cli.cjs emit ' + sprintId + " '{event-json}'\`",
|
|
350
377
|
' conforming to `.forge/schemas/event.schema.json` (role, action="complete", phase, iteration=' + iteration + ',',
|
|
351
378
|
' startTimestamp, endTimestamp, durationMinutes, plus your own model/provider/token usage — do NOT invent placeholder model strings).',
|
|
379
|
+
' COMPLETE-event type (per .forge/workflows/_fragments/event-vocabulary.md): ' + typeTokenLine,
|
|
380
|
+
' NEVER copy the action value ("start"/"complete") into "type" — those tokens are schema-rejected and the event would be dropped.',
|
|
352
381
|
' ' + eventIdLine,
|
|
353
382
|
' If `/cost` data is available, also write the token sidecar via the `--sidecar` form with the COMPLETE eventId. Best-effort; skip silently if unavailable.',
|
|
354
383
|
'',
|
|
@@ -404,7 +433,9 @@ function emitSkip(taskId, sprintId, taskStatus) {
|
|
|
404
433
|
`Emit a task_skipped event for Forge task ${taskId} (sprint ${sprintId}).`,
|
|
405
434
|
`node .forge/tools/store-cli.cjs emit ${sprintId}`,
|
|
406
435
|
`'{"type":"task-dispatch","action":"skip","taskId":"${taskId}","sprintId":"${sprintId}",`,
|
|
407
|
-
|
|
436
|
+
// forge-engineering#39: iteration must be >= 1 (schema minimum) — the
|
|
437
|
+
// former zero value made every skip event silently schema-rejected.
|
|
438
|
+
`"role":"orchestrator","phase":"pre-task","iteration":1,`,
|
|
408
439
|
`"notes":"pre-task SKIP_STATUS guard: task status is ${taskStatus}",`,
|
|
409
440
|
`"startTimestamp":"<ISO-now>","endTimestamp":"<ISO-now>","durationMinutes":0,`,
|
|
410
441
|
`"model":"<your-model-id>","provider":"anthropic"}'`,
|
|
@@ -59,11 +59,11 @@
|
|
|
59
59
|
"sprint-complete",
|
|
60
60
|
"sprint-halted",
|
|
61
61
|
"task-dispatch",
|
|
62
|
-
"bug-fixed",
|
|
63
62
|
|
|
64
63
|
"bug-triaged", "fix-planned", "fix-review-passed", "fix-review-failed",
|
|
65
64
|
"fix-implemented", "fix-code-review-passed", "fix-code-review-failed",
|
|
66
|
-
"fix-approved", "bug-committed",
|
|
65
|
+
"fix-approved", "fix-revision-requested", "bug-committed",
|
|
66
|
+
"bug-commit-failed", "bug-skipped",
|
|
67
67
|
|
|
68
68
|
"skill_usage"
|
|
69
69
|
]
|
|
@@ -129,7 +129,8 @@
|
|
|
129
129
|
"enum": [
|
|
130
130
|
"bug-triaged", "fix-planned", "fix-review-passed", "fix-review-failed",
|
|
131
131
|
"fix-implemented", "fix-code-review-passed", "fix-code-review-failed",
|
|
132
|
-
"fix-approved", "
|
|
132
|
+
"fix-approved", "fix-revision-requested", "bug-committed",
|
|
133
|
+
"bug-commit-failed"
|
|
133
134
|
]
|
|
134
135
|
}
|
|
135
136
|
},
|
|
@@ -137,6 +138,10 @@
|
|
|
137
138
|
},
|
|
138
139
|
"then": { "required": ["bugId", "phase", "iteration"] }
|
|
139
140
|
},
|
|
141
|
+
{
|
|
142
|
+
"if": { "properties": { "type": { "const": "bug-skipped" } }, "required": ["type"] },
|
|
143
|
+
"then": { "required": ["bugId"] }
|
|
144
|
+
},
|
|
140
145
|
{
|
|
141
146
|
"if": { "properties": { "type": { "const": "sprint-start" } }, "required": ["type"] },
|
|
142
147
|
"then": {
|