@smartmemory/compose 0.2.58-beta → 0.3.0
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/README.md +2 -2
- package/bin/compose.js +55 -54
- package/dist/assets/{App-s4CulNkO.js → App-DJ5xk_Wx.js} +218 -218
- package/dist/assets/{abnfDiagram-VRR7QNED-CDwzQ5wr.js → abnfDiagram-VRR7QNED-BPWGdCFx.js} +1 -1
- package/dist/assets/{arc-kIU8DDth.js → arc-DX5jqmcO.js} +1 -1
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-CleorO_0.js → architectureDiagram-ZJ3FMSHR-B6jmNVw7.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-NvEFfoMk.js → blockDiagram-677ZJIJ3-DAx2i-sB.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-BcDQdTVB.js → c4Diagram-LMCZKHZV-zmsZCplj.js} +1 -1
- package/dist/assets/channel-vsDnTvkh.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BseRkpm4.js → chunk-2Q5K7J3B-3QSh6sI7.js} +1 -1
- package/dist/assets/{chunk-32BRIVSS-DCHhvgFV.js → chunk-32BRIVSS-BiH5Di_y.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DcTpMdJ4.js → chunk-5VM5RSS4-C0EfjFLd.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-BdN8fvWs.js → chunk-EX3LRPZG-BGyfmVm-.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DmZJP-8E.js → chunk-JWPE2WC7-Ba0mrNlg.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-V74uYM17.js → chunk-MOJQB5TN-D3QO9EzH.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-CoXUXbn_.js → chunk-RYQCIY6F-D1uvNA6d.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DUscIydR.js → chunk-V7JOEXUC-fihlnodT.js} +1 -1
- package/dist/assets/{chunk-VR4S4FIN-C4UDHq_a.js → chunk-VR4S4FIN-nmRUEo1H.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-DIH9i8mZ.js → chunk-XXDRQBXY-CM693yEg.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DO-wdSFZ.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-DO-wdSFZ.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-Dn6Gy6l1.js → cose-bilkent-JH36ORCC-CkacPn8W.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-C2LW5fzB.js → cynefin-VYW2F7L2-BiHaIptG.js} +1 -1
- package/dist/assets/{cynefinDiagram-TSTJHNR4-CGoAq7C9.js → cynefinDiagram-TSTJHNR4-CdKxMmiy.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-BaOKtuBE.js → dagre-VKFMJZFB-BQLY5y_W.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-BdQwqt4x.js → diagram-FQU43EPY-D7uMBHvq.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-Dkcf0o5z.js → diagram-G47NLZAW-B3Z1cuH7.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-C_fDi59r.js → diagram-NH7WQ7WH-BZyRD45e.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-jaTQcOxt.js → diagram-OA4YK3LP-DcThOTt7.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-dEtqVj3s.js → diagram-WEI45ONY-BcgRAkqY.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-s8ImqsSs.js → ebnfDiagram-CCIWWBDH-CGwfO_xH.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-5Zy29-SA.js → erDiagram-Q63AITRT-pV58-Ncc.js} +1 -1
- package/dist/assets/{flowDiagram-23GEKE2U-DV74V9he.js → flowDiagram-23GEKE2U-DJ_SqE8h.js} +1 -1
- package/dist/assets/{ganttDiagram-NO4QXBWP-BND-CiLO.js → ganttDiagram-NO4QXBWP-Dgy0Iyss.js} +1 -1
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-B_-Mf9Jo.js → gitGraphDiagram-IHSO6WYX-CbiZw9fb.js} +1 -1
- package/dist/assets/{index-CBWbmG7W.js → index-DZTJEk-y.js} +2 -2
- package/dist/assets/{infoDiagram-FWYZ7A6U-Duyz8rZM.js → infoDiagram-FWYZ7A6U-CtsyyEc-.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-D2Qft0e5.js → ishikawaDiagram-FXEZZL3T-BJilNFkK.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-q6rG4Rig.js → journeyDiagram-5HDEW3XC-C2UCMP4t.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-CsPX7gBL.js → kanban-definition-HUTT4EX6-DmLDJBRy.js} +1 -1
- package/dist/assets/{linear-CiccpAAd.js → linear-C1paCqE7.js} +1 -1
- package/dist/assets/{mindmap-definition-LN4V7U3C-DRW2njpA.js → mindmap-definition-LN4V7U3C-CX-RxKVn.js} +1 -1
- package/dist/assets/{pegDiagram-2B236MQR-DoDgm2S_.js → pegDiagram-2B236MQR-CTU32H2W.js} +1 -1
- package/dist/assets/{pieDiagram-ENE6RG2P-CNSYLVAF.js → pieDiagram-ENE6RG2P-D8L1aYoo.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-8-Nd6ony.js → quadrantDiagram-ABIIQ3AL-hQ3bXosy.js} +1 -1
- package/dist/assets/{railroadDiagram-RFXS5EU6-bfdg6Bo4.js → railroadDiagram-RFXS5EU6--_vuYcda.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-slDkxCQy.js → requirementDiagram-TGXJPOKE-C6h_m3Az.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-CR3CboiK.js → sankeyDiagram-HTMAVEWB-Dpc7CfIQ.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BIXw1JS1.js → sequenceDiagram-DBY2YBRQ-CK5ZzbvJ.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-BdKI7JyF.js → sizeCapture-X5ZJPWSS-BAxVvM9G.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-ktVgpBZS.js → stateDiagram-2N3HPSRC-7j33_2PY.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-GviN0FpJ.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-CaXpC3FM.js → swimlanes-5IMT3BWC-CBG2yOob.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-D0wMoxl1.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-CKWSlRUl.js → timeline-definition-FHXFAJF6-BOnZkQvc.js} +1 -1
- package/dist/assets/{vennDiagram-L72KCM5P-CwOhxDyv.js → vennDiagram-L72KCM5P-D52NhIfj.js} +1 -1
- package/dist/assets/{wardleyDiagram-EHGQE667-DGavMpEB.js → wardleyDiagram-EHGQE667-YoPY2ZU-.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-CWmnmLZz.js → xychartDiagram-FW5EYKEG-BanNfgtO.js} +1 -1
- package/dist/index.html +1 -1
- package/lib/build-all.js +0 -5
- package/lib/build-stream-schema.js +1 -1
- package/lib/build.js +1762 -2614
- package/lib/consumer-fanout.js +1317 -0
- package/lib/feature-validator.js +6 -4
- package/lib/flow-state.js +15 -14
- package/lib/gsd-budget.js +48 -10
- package/lib/gsd-prompt.js +3 -4
- package/lib/gsd-stuck.js +1 -1
- package/lib/gsd.js +303 -149
- package/lib/local-claude-connector.js +149 -0
- package/lib/new.js +162 -307
- package/lib/result-normalizer.js +233 -18
- package/lib/review-lenses.js +1 -1
- package/lib/step-prompt.js +41 -119
- package/lib/stratum-engine.js +297 -0
- package/lib/stratum-mcp-client.js +224 -213
- package/lib/vocabulary-compliance.js +268 -0
- package/lib/vocabulary-inject.js +1 -36
- package/package.json +2 -1
- package/pipelines/build-quick.stratum.yaml +5 -17
- package/pipelines/build.profiles.json +11 -0
- package/pipelines/build.stratum.yaml +288 -467
- package/pipelines/gsd.stratum.yaml +73 -125
- package/pipelines/new.stratum.yaml +68 -149
- package/server/build-routes.js +4 -1
- package/server/design-routes.js +37 -21
- package/server/index.js +13 -21
- package/server/lifecycle-guard.js +1 -1
- package/server/pipeline-routes.js +113 -31
- package/server/stratum-client.js +33 -47
- package/server/stratum-sync.js +3 -4
- package/server/vision-server.js +1 -1
- package/dist/assets/channel-DiJkE9og.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DS76VBEL.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-DS76VBEL.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CzPQnlan.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-rIDv6EuQ.js +0 -8
- package/lib/connector-factory-shim.js +0 -167
- package/server/agent-mcp.js +0 -10
package/lib/new.js
CHANGED
|
@@ -1,391 +1,246 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Orchestrates project creation through a Stratum workflow:
|
|
5
|
-
* research → brainstorm → gate → roadmap → gate → scaffold.
|
|
6
|
-
*
|
|
7
|
-
* Reuses the same dispatch loop pattern as build.js.
|
|
2
|
+
* Product kickoff runner for `compose new` using the Stratum TS ready loop.
|
|
8
3
|
*/
|
|
9
4
|
|
|
10
5
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
11
6
|
import { join, basename } from 'node:path';
|
|
7
|
+
import YAML from 'yaml';
|
|
12
8
|
|
|
13
9
|
import { StratumMcpClient } from './stratum-mcp-client.js';
|
|
14
10
|
import { runAndNormalize } from './result-normalizer.js';
|
|
15
|
-
import {
|
|
16
|
-
import { buildStepPrompt, buildRetryPrompt, buildGateContext } from './step-prompt.js';
|
|
11
|
+
import { buildStepPrompt, buildGateContext } from './step-prompt.js';
|
|
17
12
|
import { promptGate } from './gate-prompt.js';
|
|
18
13
|
import { VisionWriter } from './vision-writer.js';
|
|
19
14
|
import { readFlowRound } from './flow-state.js';
|
|
20
|
-
|
|
21
15
|
import { validateStep } from './step-validator.js';
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
const KICKOFF_VALIDATION = new Map([
|
|
18
|
+
['research', {
|
|
19
|
+
artifact: 'docs/discovery/research.md',
|
|
20
|
+
criteria: [
|
|
21
|
+
'Contains at least 2 existing tools or prior art entries',
|
|
22
|
+
'Mentions architectural patterns or common approaches',
|
|
23
|
+
'Lists risks or pitfalls',
|
|
24
|
+
],
|
|
25
|
+
}],
|
|
26
|
+
['brainstorm', {
|
|
27
|
+
artifact: 'docs/discovery/brainstorm.md',
|
|
28
|
+
criteria: [
|
|
29
|
+
'Contains at least 3 features with short codes',
|
|
30
|
+
'Contains user stories in As a, I want, so that format',
|
|
31
|
+
'Contains at least 2 architecture options with trade-offs',
|
|
32
|
+
],
|
|
33
|
+
}],
|
|
34
|
+
['roadmap', {
|
|
35
|
+
artifact: 'ROADMAP.md',
|
|
36
|
+
criteria: [
|
|
37
|
+
'Contains a markdown table with feature codes and status columns',
|
|
38
|
+
'Features are organized into phases',
|
|
39
|
+
'All features have PLANNED status',
|
|
40
|
+
],
|
|
41
|
+
}],
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
function outputContract(spec, stepId) {
|
|
45
|
+
const step = spec.flows?.new?.steps?.find((candidate) => candidate.id === stepId);
|
|
46
|
+
const fields = typeof step?.out === 'string' ? spec.contracts?.[step.out] : null;
|
|
47
|
+
return fields && typeof fields === 'object' ? fields : {};
|
|
48
|
+
}
|
|
25
49
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
function waitingGate(audit) {
|
|
51
|
+
return Object.entries(audit?.steps ?? {})
|
|
52
|
+
.find(([, state]) => state?.status === 'waiting_gate');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function gateDispatch(spec, stepId) {
|
|
56
|
+
const step = spec.flows?.new?.steps?.find((candidate) => candidate.id === stepId);
|
|
57
|
+
if (!step?.gate) throw new Error(`Kickoff gate ${stepId} is missing from the local TS spec`);
|
|
58
|
+
return { step_id: stepId, ...step.gate };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Run the product kickoff pipeline. */
|
|
36
62
|
export async function runNew(intent, opts = {}) {
|
|
37
63
|
const cwd = opts.cwd ?? process.cwd();
|
|
38
64
|
const projectName = opts.projectName ?? basename(cwd);
|
|
39
|
-
const skipResearch = opts.skipResearch ?? false;
|
|
40
65
|
|
|
41
|
-
// Check for existing design doc — use as enriched intent if present
|
|
42
66
|
const designDocPath = join(cwd, 'docs', 'design.md');
|
|
43
67
|
if (existsSync(designDocPath)) {
|
|
44
|
-
const designDoc = readFileSync(designDocPath, 'utf-8');
|
|
45
68
|
console.log('Found design doc at docs/design.md — using as enriched intent');
|
|
46
|
-
intent = `${intent}\n\n## Design Document\n${
|
|
69
|
+
intent = `${intent}\n\n## Design Document\n${readFileSync(designDocPath, 'utf-8')}`;
|
|
47
70
|
}
|
|
48
71
|
|
|
49
|
-
// Resolve project paths
|
|
50
72
|
const composeDir = join(cwd, '.compose');
|
|
51
73
|
const dataDir = join(composeDir, 'data');
|
|
52
|
-
|
|
53
|
-
// Ensure compose is initialized
|
|
54
|
-
const configPath = join(composeDir, 'compose.json');
|
|
55
|
-
if (!existsSync(configPath)) {
|
|
74
|
+
if (!existsSync(join(composeDir, 'compose.json'))) {
|
|
56
75
|
throw new Error(`No .compose/compose.json found at ${cwd}. Run 'compose init' first.`);
|
|
57
76
|
}
|
|
58
|
-
|
|
59
|
-
// Ensure discovery dir exists for brainstorm output
|
|
60
77
|
mkdirSync(join(cwd, 'docs', 'discovery'), { recursive: true });
|
|
61
78
|
|
|
62
|
-
// Load kickoff spec
|
|
63
79
|
const specPath = join(cwd, 'pipelines', 'new.stratum.yaml');
|
|
64
80
|
if (!existsSync(specPath)) {
|
|
65
81
|
throw new Error(`Kickoff spec not found: ${specPath}. Run 'compose init' to get default pipelines.`);
|
|
66
82
|
}
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
if (skipResearch) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (researchStep && !researchStep.skip_if) {
|
|
74
|
-
researchStep.skip_if = 'true';
|
|
75
|
-
researchStep.skip_reason = 'Skipped by user (questionnaire)';
|
|
76
|
-
writeFileSync(specPath, stringify(spec, { lineWidth: 120 }));
|
|
77
|
-
console.log('Skipping research step (per questionnaire).\n');
|
|
78
|
-
}
|
|
84
|
+
const spec = YAML.parse(readFileSync(specPath, 'utf-8'));
|
|
85
|
+
if (opts.skipResearch) {
|
|
86
|
+
const research = spec.flows?.new?.steps?.find((step) => step.id === 'research');
|
|
87
|
+
if (research) research.when = 'false';
|
|
88
|
+
console.log('Skipping research step (per questionnaire).\n');
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
const
|
|
91
|
+
const validateConfigs = KICKOFF_VALIDATION;
|
|
92
|
+
const cleanSpecYaml = YAML.stringify(spec, { lineWidth: 120 });
|
|
82
93
|
|
|
83
|
-
// Parse spec to extract validate configs per step
|
|
84
|
-
const { parse: parseYaml, stringify: stringifyYaml } = await import('yaml');
|
|
85
|
-
const specObj = parseYaml(specYaml);
|
|
86
|
-
const validateConfigs = new Map();
|
|
87
|
-
for (const step of specObj.flows?.new?.steps ?? []) {
|
|
88
|
-
if (step.validate) {
|
|
89
|
-
validateConfigs.set(step.id, step.validate);
|
|
90
|
-
delete step.validate; // strip before sending to stratum
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// Re-serialize with validate fields stripped
|
|
94
|
-
const cleanSpecYaml = stringifyYaml(specObj, { lineWidth: 120 });
|
|
95
|
-
|
|
96
|
-
// Vision writer
|
|
97
94
|
const visionWriter = new VisionWriter(dataDir);
|
|
98
95
|
const itemId = await visionWriter.ensureFeatureItem(projectName, projectName);
|
|
99
|
-
|
|
100
|
-
// Stratum MCP client (test override permitted via opts.stratum)
|
|
101
96
|
const stratum = opts.stratum ?? new StratumMcpClient();
|
|
102
97
|
if (!opts.stratum) await stratum.connect({ cwd });
|
|
103
98
|
|
|
104
|
-
if (opts.connectorFactory && !opts.stratum) {
|
|
105
|
-
installFactoryShim(stratum, opts.connectorFactory, cwd);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
99
|
try {
|
|
109
100
|
console.log(`Starting product kickoff for "${projectName}"...`);
|
|
110
101
|
console.log(`Intent: ${intent}\n`);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
let response = await stratum.plan(
|
|
103
|
+
cleanSpecYaml,
|
|
104
|
+
'new',
|
|
105
|
+
{ projectName, intent },
|
|
106
|
+
{ workspaceRoot: cwd },
|
|
107
|
+
);
|
|
108
|
+
const flowId = response.runId;
|
|
114
109
|
await visionWriter.updateItemStatus(itemId, 'in_progress');
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const prompt = buildStepPrompt(
|
|
132
|
-
const { result } = await runAndNormalize(null, prompt,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
stepId,
|
|
110
|
+
const context = { cwd, featureCode: projectName, projectName, intent, stepHistory: [] };
|
|
111
|
+
|
|
112
|
+
while (!['completed', 'failed', 'budget_exhausted'].includes(response.status)) {
|
|
113
|
+
if (response.status === 'ready') {
|
|
114
|
+
const step = response.ready?.[0];
|
|
115
|
+
if (!step) throw new Error('Stratum returned ready without a kickoff step');
|
|
116
|
+
const dispatch = {
|
|
117
|
+
...step,
|
|
118
|
+
step_id: step.id,
|
|
119
|
+
flow_id: response.runId,
|
|
120
|
+
intent: step.do,
|
|
121
|
+
output_fields: outputContract(spec, step.id),
|
|
122
|
+
has_out_contract: true,
|
|
123
|
+
};
|
|
124
|
+
console.log(`${step.id}...`);
|
|
125
|
+
await visionWriter.updateItemPhase(itemId, step.id);
|
|
126
|
+
const prompt = buildStepPrompt(dispatch, context);
|
|
127
|
+
const { result } = await runAndNormalize(null, prompt, dispatch, { stratum, cwd });
|
|
128
|
+
|
|
129
|
+
const validation = validateConfigs.get(step.id);
|
|
130
|
+
if (validation) {
|
|
131
|
+
console.log(` ✓ Validating ${step.id}...`);
|
|
132
|
+
const checked = await validateStep({
|
|
133
|
+
artifact: validation.artifact,
|
|
134
|
+
criteria: validation.criteria,
|
|
135
|
+
stepId: step.id,
|
|
142
136
|
stratum,
|
|
143
137
|
cwd,
|
|
144
138
|
});
|
|
145
|
-
if (!valid) {
|
|
146
|
-
console.log(
|
|
147
|
-
for (const issue of issues) console.log(` - ${issue}`);
|
|
148
|
-
console.log(` ↻ Fix (claude) for ${stepId}`);
|
|
139
|
+
if (!checked.valid) {
|
|
140
|
+
console.log(' ✗ Validation failed:');
|
|
141
|
+
for (const issue of checked.issues) console.log(` - ${issue}`);
|
|
149
142
|
const fixPrompt =
|
|
150
|
-
`Read "${
|
|
151
|
-
issues.map(
|
|
152
|
-
|
|
143
|
+
`Read "${validation.artifact}" and fix these issues:\n` +
|
|
144
|
+
checked.issues.map((issue) => `- ${issue}`).join('\n') +
|
|
145
|
+
`\n\nUpdate the file in place. Do not skip any issue.\n\n` +
|
|
153
146
|
`## Context\nWorking directory: ${cwd}\nProject: ${projectName}`;
|
|
154
|
-
|
|
155
|
-
await runAndNormalize(null, fixPrompt, fixDispatch, { stratum, cwd });
|
|
147
|
+
await runAndNormalize(null, fixPrompt, { ...dispatch, agent: 'claude' }, { stratum, cwd });
|
|
156
148
|
}
|
|
157
149
|
}
|
|
158
150
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
} else {
|
|
171
|
-
console.log(` ✓ ${stepId} complete`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
response = await stratum.stepDone(flowId, stepId, result ?? { summary: 'Step complete' });
|
|
151
|
+
if (result?.summary) console.log(` ✓ ${result.summary}`);
|
|
152
|
+
else console.log(` ✓ ${step.id} complete`);
|
|
153
|
+
context.stepHistory.push({ stepId: step.id, summary: result?.summary ?? 'Step complete' });
|
|
154
|
+
response = await stratum.stepDone(
|
|
155
|
+
response.runId,
|
|
156
|
+
step.id,
|
|
157
|
+
{ output: result ?? { summary: 'Step complete' } },
|
|
158
|
+
step.dispatchToken,
|
|
159
|
+
);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
175
162
|
|
|
176
|
-
|
|
163
|
+
if (response.status === 'running') {
|
|
164
|
+
const audit = await stratum.audit(response.runId);
|
|
165
|
+
const waiting = waitingGate(audit);
|
|
166
|
+
if (!waiting) throw new Error('Kickoff flow is running without ready work or a waiting gate');
|
|
167
|
+
const [stepId, state] = waiting;
|
|
168
|
+
const dispatch = gateDispatch(spec, stepId);
|
|
177
169
|
console.log(`\nGate: ${stepId}`);
|
|
178
170
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
console.log(`\n--- ${priorValConfig.artifact} ---`);
|
|
189
|
-
// Show full content (it's a discovery doc, should be readable)
|
|
190
|
-
const lines = content.split('\n');
|
|
191
|
-
if (lines.length <= 80) {
|
|
192
|
-
console.log(content);
|
|
193
|
-
} else {
|
|
194
|
-
console.log(lines.slice(0, 60).join('\n'));
|
|
195
|
-
console.log(`\n... (${lines.length - 60} more lines — see ${priorValConfig.artifact})`);
|
|
196
|
-
}
|
|
197
|
-
console.log(`--- end ---\n`);
|
|
198
|
-
}
|
|
171
|
+
const priorStepId = dispatch.on_revise;
|
|
172
|
+
const priorValidation = validateConfigs.get(priorStepId);
|
|
173
|
+
const artifact = priorValidation?.artifact ? join(cwd, priorValidation.artifact) : null;
|
|
174
|
+
if (artifact && existsSync(artifact)) {
|
|
175
|
+
const content = readFileSync(artifact, 'utf-8');
|
|
176
|
+
console.log(`\n--- ${priorValidation.artifact} ---`);
|
|
177
|
+
const lines = content.split('\n');
|
|
178
|
+
console.log(lines.length <= 80 ? content : `${lines.slice(0, 60).join('\n')}\n\n... (${lines.length - 60} more lines)`);
|
|
179
|
+
console.log('--- end ---\n');
|
|
199
180
|
}
|
|
200
181
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
: null;
|
|
209
|
-
|
|
210
|
-
// Agent Q&A callback for interactive gate — with full workflow context
|
|
211
|
-
const gatePreamble = buildGateContext(response, context, null);
|
|
182
|
+
const gateId = await visionWriter.createGate(
|
|
183
|
+
response.runId,
|
|
184
|
+
stepId,
|
|
185
|
+
itemId,
|
|
186
|
+
{ round: readFlowRound(response.runId) },
|
|
187
|
+
);
|
|
188
|
+
const preamble = buildGateContext(dispatch, context, null);
|
|
212
189
|
const askAgent = async (question, artifactPath) => {
|
|
213
190
|
const fileRef = artifactPath
|
|
214
191
|
? `Read the file "${artifactPath}" and answer`
|
|
215
|
-
:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
`${fileRef} this question concisely:\n\n
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const text = await stratum.runAgentText('claude', qaPrompt, { cwd });
|
|
222
|
-
return text || '(no answer)';
|
|
192
|
+
: 'Look at the project files in the working directory and answer';
|
|
193
|
+
return stratum.runAgentText(
|
|
194
|
+
'claude',
|
|
195
|
+
`${preamble}\n\n---\n\n${fileRef} this question concisely:\n\n${question}`,
|
|
196
|
+
{ cwd },
|
|
197
|
+
);
|
|
223
198
|
};
|
|
224
|
-
|
|
225
|
-
const { outcome, rationale } = await promptGate(response, {
|
|
199
|
+
const decision = await promptGate(dispatch, {
|
|
226
200
|
...(opts.gateOpts ?? {}),
|
|
227
|
-
artifact:
|
|
201
|
+
artifact: artifact ?? cwd,
|
|
228
202
|
askAgent,
|
|
229
203
|
});
|
|
230
|
-
await visionWriter.resolveGate(gateId, outcome);
|
|
231
|
-
|
|
232
|
-
// Inject gate decision into context so the re-run step sees reviewer feedback
|
|
233
|
-
if (!context.stepHistory) context.stepHistory = [];
|
|
204
|
+
await visionWriter.resolveGate(gateId, decision.outcome);
|
|
234
205
|
context.stepHistory.push({
|
|
235
206
|
stepId,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
outcome,
|
|
207
|
+
summary: `Gate ${decision.outcome}${decision.rationale ? `: ${decision.rationale}` : ''}`,
|
|
208
|
+
outcome: decision.outcome,
|
|
239
209
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
console.log(`[sub-flow] ${childFlowName}...`);
|
|
248
|
-
|
|
249
|
-
const childResult = await executeChildFlow(
|
|
250
|
-
response, stratum, context,
|
|
251
|
-
visionWriter, itemId, opts.gateOpts ?? {}
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
response = await stratum.stepDone(parentFlowId, parentStepId, childResult);
|
|
255
|
-
|
|
256
|
-
} else if (response.status === 'ensure_failed' || response.status === 'schema_failed') {
|
|
257
|
-
console.log(` ↻ Retrying ${stepId} (postconditions failed)`);
|
|
258
|
-
const violations = response.violations ?? [];
|
|
259
|
-
const agentType = response.agent ?? 'claude';
|
|
260
|
-
|
|
261
|
-
// Fix pass before retry
|
|
262
|
-
const fixAgent = agentType === 'codex' ? 'claude' : agentType;
|
|
263
|
-
console.log(` ↻ Fix (${fixAgent}) for ${stepId}`);
|
|
264
|
-
const fixPrompt =
|
|
265
|
-
`Fix step "${stepId}" — postconditions failed:\n` +
|
|
266
|
-
violations.map(v => `- ${v}`).join('\n') + '\n\n' +
|
|
267
|
-
`Fix every issue. Do not skip any.\n\n` +
|
|
268
|
-
`## Context\nWorking directory: ${cwd}\nProject: ${projectName}`;
|
|
269
|
-
const fixDispatch = { ...response, agent: fixAgent };
|
|
270
|
-
await runAndNormalize(null, fixPrompt, fixDispatch, { stratum, cwd });
|
|
271
|
-
|
|
272
|
-
console.log(` ↻ Retrying ${stepId} (${agentType})`);
|
|
273
|
-
const prompt = buildRetryPrompt(response, violations, context);
|
|
274
|
-
const { result } = await runAndNormalize(null, prompt, response, { stratum, cwd });
|
|
275
|
-
|
|
276
|
-
response = await stratum.stepDone(
|
|
277
|
-
response.flow_id, response.step_id,
|
|
278
|
-
result ?? { summary: 'Retry complete' }
|
|
210
|
+
response = await stratum.gateResolve(
|
|
211
|
+
response.runId,
|
|
212
|
+
stepId,
|
|
213
|
+
decision.outcome,
|
|
214
|
+
decision.rationale,
|
|
215
|
+
'human',
|
|
216
|
+
state.gateToken,
|
|
279
217
|
);
|
|
280
|
-
|
|
281
|
-
} else {
|
|
282
|
-
console.warn(`Unknown dispatch status: ${response.status}`);
|
|
283
|
-
break;
|
|
218
|
+
continue;
|
|
284
219
|
}
|
|
220
|
+
|
|
221
|
+
throw new Error(`Unknown TS kickoff status: ${response.status}`);
|
|
285
222
|
}
|
|
286
223
|
|
|
287
|
-
|
|
288
|
-
if (response.status === 'complete') {
|
|
224
|
+
if (response.status === 'completed') {
|
|
289
225
|
console.log('\nProduct kickoff complete.');
|
|
290
226
|
await visionWriter.updateItemStatus(itemId, 'complete');
|
|
291
|
-
} else if (response.status === 'killed') {
|
|
292
|
-
console.log('\nProduct kickoff killed.');
|
|
293
|
-
await visionWriter.updateItemStatus(itemId, 'killed');
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Write audit trace
|
|
297
|
-
if (response.trace) {
|
|
298
|
-
try {
|
|
299
|
-
const auditPath = join(cwd, 'docs', 'discovery', 'kickoff-audit.json');
|
|
300
|
-
writeFileSync(auditPath, JSON.stringify(response, null, 2));
|
|
301
|
-
console.log(`Audit trace written to docs/discovery/kickoff-audit.json`);
|
|
302
|
-
} catch (err) {
|
|
303
|
-
console.warn(`Warning: could not write audit trace: ${err.message}`);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Summary
|
|
308
|
-
if (response.status === 'complete') {
|
|
309
227
|
console.log('\nNext steps:');
|
|
310
228
|
console.log(' compose pipeline show # review the build pipeline');
|
|
311
229
|
console.log(' compose build <FEATURE-CODE> # build the first feature');
|
|
230
|
+
} else {
|
|
231
|
+
await visionWriter.updateItemStatus(itemId, 'failed');
|
|
312
232
|
}
|
|
313
233
|
|
|
234
|
+
try {
|
|
235
|
+
const audit = await stratum.audit(flowId);
|
|
236
|
+
const auditPath = join(cwd, 'docs', 'discovery', 'kickoff-audit.json');
|
|
237
|
+
writeFileSync(auditPath, JSON.stringify(audit, null, 2));
|
|
238
|
+
console.log('Audit trace written to docs/discovery/kickoff-audit.json');
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.warn(`Warning: could not write audit trace: ${err.message}`);
|
|
241
|
+
}
|
|
242
|
+
return response;
|
|
314
243
|
} finally {
|
|
315
244
|
await stratum.close();
|
|
316
245
|
}
|
|
317
246
|
}
|
|
318
|
-
|
|
319
|
-
// ---------------------------------------------------------------------------
|
|
320
|
-
// Child flow execution (simplified from build.js — no active-build tracking)
|
|
321
|
-
// ---------------------------------------------------------------------------
|
|
322
|
-
|
|
323
|
-
async function executeChildFlow(
|
|
324
|
-
flowDispatch, stratum, context,
|
|
325
|
-
visionWriter, itemId, gateOpts
|
|
326
|
-
) {
|
|
327
|
-
let resp = flowDispatch.child_step;
|
|
328
|
-
const childFlowId = flowDispatch.child_flow_id;
|
|
329
|
-
const childFlowName = flowDispatch.child_flow_name ?? 'sub-flow';
|
|
330
|
-
|
|
331
|
-
while (resp.status !== 'complete' && resp.status !== 'killed') {
|
|
332
|
-
if (resp.status === 'execute_step') {
|
|
333
|
-
console.log(` [${childFlowName}] ${resp.step_id}...`);
|
|
334
|
-
await visionWriter.updateItemPhase(itemId, `${childFlowName}:${resp.step_id}`);
|
|
335
|
-
|
|
336
|
-
const prompt = buildStepPrompt(resp, context);
|
|
337
|
-
const { result } = await runAndNormalize(null, prompt, resp, { stratum, cwd: context.cwd });
|
|
338
|
-
|
|
339
|
-
resp = await stratum.stepDone(
|
|
340
|
-
childFlowId, resp.step_id,
|
|
341
|
-
result ?? { summary: 'Step complete' }
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
} else if (resp.status === 'await_gate') {
|
|
345
|
-
console.log(` [${childFlowName}] Gate: ${resp.step_id}`);
|
|
346
|
-
// COMP-PLAN-GATE-LOOP: round-aware gate id (child flow has its own round).
|
|
347
|
-
const gateId = await visionWriter.createGate(childFlowId, resp.step_id, itemId, { round: readFlowRound(childFlowId) });
|
|
348
|
-
const { outcome, rationale } = await promptGate(resp, gateOpts);
|
|
349
|
-
await visionWriter.resolveGate(gateId, outcome);
|
|
350
|
-
resp = await stratum.gateResolve(childFlowId, resp.step_id, outcome, rationale, 'human');
|
|
351
|
-
|
|
352
|
-
} else if (resp.status === 'ensure_failed' || resp.status === 'schema_failed') {
|
|
353
|
-
const violations = resp.violations ?? [];
|
|
354
|
-
const stepAgent = resp.agent ?? 'claude';
|
|
355
|
-
const fixAgent = stepAgent === 'codex' ? 'claude' : stepAgent;
|
|
356
|
-
|
|
357
|
-
console.log(` [${childFlowName}] ↻ Fix (${fixAgent}) for ${resp.step_id}`);
|
|
358
|
-
const fixPrompt =
|
|
359
|
-
`Fix step "${resp.step_id}" — postconditions failed:\n` +
|
|
360
|
-
violations.map(v => `- ${v}`).join('\n') + '\n\n' +
|
|
361
|
-
`Fix every issue.\n\n` +
|
|
362
|
-
`## Context\nWorking directory: ${context.cwd}\nProject: ${context.projectName}`;
|
|
363
|
-
const fixDispatch = { ...resp, agent: fixAgent };
|
|
364
|
-
await runAndNormalize(null, fixPrompt, fixDispatch, { stratum, cwd: context.cwd });
|
|
365
|
-
|
|
366
|
-
console.log(` [${childFlowName}] ↻ Retrying ${resp.step_id} (${stepAgent})`);
|
|
367
|
-
const prompt = buildRetryPrompt(resp, violations, context);
|
|
368
|
-
const { result } = await runAndNormalize(null, prompt, resp, { stratum, cwd: context.cwd });
|
|
369
|
-
|
|
370
|
-
resp = await stratum.stepDone(
|
|
371
|
-
resp.flow_id ?? childFlowId, resp.step_id,
|
|
372
|
-
result ?? { summary: 'Retry complete' }
|
|
373
|
-
);
|
|
374
|
-
|
|
375
|
-
} else if (resp.status === 'execute_flow') {
|
|
376
|
-
const nestedParentFlowId = resp.parent_flow_id;
|
|
377
|
-
const nestedParentStepId = resp.parent_step_id;
|
|
378
|
-
const nestedResult = await executeChildFlow(
|
|
379
|
-
resp, stratum, context,
|
|
380
|
-
visionWriter, itemId, gateOpts
|
|
381
|
-
);
|
|
382
|
-
resp = await stratum.stepDone(nestedParentFlowId, nestedParentStepId, nestedResult);
|
|
383
|
-
|
|
384
|
-
} else {
|
|
385
|
-
console.warn(` [${childFlowName}] Unknown status: ${resp.status}`);
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return resp;
|
|
391
|
-
}
|