@tekyzinc/gsd-t 4.0.28 → 4.0.29
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 +15 -0
- package/package.json +1 -1
- package/templates/CLAUDE-global.md +1 -1
- package/templates/workflows/gsd-t-debug.workflow.js +34 -5
- package/templates/workflows/gsd-t-execute.workflow.js +54 -29
- package/templates/workflows/gsd-t-integrate.workflow.js +37 -7
- package/templates/workflows/gsd-t-phase.workflow.js +36 -7
- package/templates/workflows/gsd-t-quick.workflow.js +59 -7
- package/templates/workflows/gsd-t-verify.workflow.js +67 -47
- package/templates/workflows/gsd-t-wave.workflow.js +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [4.0.29] - 2026-06-05 (M81 Workflows Runtime-Native - patch)
|
|
6
|
+
|
|
7
|
+
### Fixed - TD-113: 6 of 7 workflows (+ quick) crashed in the Workflow sandbox and had never run
|
|
8
|
+
|
|
9
|
+
The GSD-T self-scan (Scan #12) and a live NiceNote session both confirmed it: every `*.workflow.js` except `gsd-t-scan` opened with `require("./_lib.js")`, which the Anthropic Workflow sandbox forbids (it provides only `agent/parallel/pipeline/log/phase/budget/args` — no `require`/`fs`/`path`/`child_process`/`process`). Each threw `ReferenceError` on first eval, so the entire orchestration layer — `execute`, `verify`, `wave`, `integrate`, `debug`, `phase`, `quick` — silently fell back to hand-driven runs and never actually executed as workflows.
|
|
10
|
+
|
|
11
|
+
Ported all 7 to the runtime-native pattern proven on scan in M71/M80: inline `async` helpers that delegate each CLI call (preflight, verify-gate, brief, build-coverage, ci-parity, test-data, parallel/disjointness) to an `agent()`'s Bash — preferring project-local `bin/<tool>.cjs`, falling back to the global `gsd-t` PATH binary — and parse the JSON envelope. `args` is now `JSON.parse`d (it arrives stringified). File reads moved into the agents that have `Read` (worker reads its own scope.md/tasks.md; triad agents read their own protocol from `templates/prompts/`). `verify`'s raw `spawnSync`/`require` CI-parity block and `Date.now()` run-id were replaced; the M57/M58 FAIL-blocking semantics are unchanged.
|
|
12
|
+
|
|
13
|
+
- `templates/workflows/gsd-t-{execute,verify,wave,integrate,debug,phase,quick}.workflow.js`: runtime-native port.
|
|
14
|
+
- `test/m71-workflow-runtime-native-lint.test.js`: lint now covers all 8 workflows (was scan-only).
|
|
15
|
+
- `test/m81-workflows-runtime-native.test.js`: structural invariants (no `_lib` require, args-string parse, no `spawnSync`/`Date.now`/`Math.random` in orchestrator, FAIL-blocking gates preserved).
|
|
16
|
+
- `CLAUDE.md`, `~/.claude/CLAUDE.md` + `templates/CLAUDE-global.md`: documented the runtime-native invariant; retired `_lib.js` as a workflow dependency.
|
|
17
|
+
|
|
18
|
+
Proven in the REAL sandbox: `quick` ran end-to-end (verify-gate PASS), `verify` evaluated through its CLI delegations returning a real verify-gate envelope, `execute` evaluated cleanly to its arg-guard — all with zero ReferenceError. Suite 1341/1341 pass.
|
|
19
|
+
|
|
5
20
|
## [4.0.28] - 2026-06-04 (M80 Scan Document-Phase Fixes - patch)
|
|
6
21
|
|
|
7
22
|
### Fixed - scan workflow crashed at the document phase, then shipped a truncated plain-English doc
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.29",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 54 slash commands with headless-by-default workflow spawning, unattended supervisor relay with event stream, graph-powered code analysis, real-time agent dashboard, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
|
|
5
5
|
"author": "Tekyz, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -331,7 +331,7 @@ Canonical scripts:
|
|
|
331
331
|
- `gsd-t-phase.workflow.js` — generic upper-stage runner (partition / plan / discuss / impact / milestone / prd / design-decompose / doc-ripple)
|
|
332
332
|
- `gsd-t-scan.workflow.js` — preflight → volume-probe → pipeline(per-slice deep finder → single verify) → synthesis → document → render (M66: fans out by codebase VOLUME, not a fixed 5-teammate dimension count; M67: deep document phase deterministically produces the full living-doc set + dimension files, per-doc fan-out)
|
|
333
333
|
|
|
334
|
-
|
|
334
|
+
**Runtime-native invariant (M81 — v4.0.29+):** the Workflow sandbox provides ONLY `agent/parallel/pipeline/log/phase/budget/args` — NO `require`/`fs`/`path`/`child_process`/`process`, and `args` arrives as a JSON STRING. Each workflow is self-contained: it `JSON.parse`s `args` and delegates every CLI call (preflight, verify-gate, brief, build-coverage, ci-parity, test-data, disjointness) to inline `async` helpers that run the command via an `agent()`'s Bash (preferring project-local `bin/<tool>.cjs`, else the global `gsd-t` PATH binary) and parse the JSON envelope — preserving the M55-D5 project-local-bin invariant. The old `require("./_lib.js")` pattern threw `ReferenceError` on first eval and silently broke every workflow except scan (TD-113, fixed M81); `_lib.js` is retired as a workflow dependency.
|
|
335
335
|
|
|
336
336
|
## Preflight Gate (KEPT from M55)
|
|
337
337
|
|
|
@@ -16,10 +16,39 @@ export const meta = {
|
|
|
16
16
|
],
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// M81: runtime-native helpers (sandbox bans require/fs/child_process/process — the old
|
|
20
|
+
// require("./_lib.js") crashed this workflow on first eval, TD-113). Delegate CLI calls
|
|
21
|
+
// to an agent's Bash; args arrives as a JSON STRING in this runtime. See gsd-t-scan.workflow.js.
|
|
22
|
+
const _args = (typeof args === "string") ? (() => { try { return JSON.parse(args); } catch { return {}; } })() : (args || {});
|
|
23
|
+
const _CLI_ENVELOPE_SCHEMA = {
|
|
24
|
+
type: "object", required: ["ok", "exitCode"], additionalProperties: true,
|
|
25
|
+
properties: { ok: { type: "boolean" }, exitCode: { type: "integer" }, envelope: {}, stdout: { type: "string" }, stderr: { type: "string" }, via: { type: "string" } },
|
|
26
|
+
};
|
|
27
|
+
async function runCli(projectDir, subcmd, argv, localBin, label, parseJson = true, phaseName) {
|
|
28
|
+
const argStr = (argv || []).map((a) => `'${String(a).replace(/'/g, "'\\''")}'`).join(" ");
|
|
29
|
+
const prompt = [
|
|
30
|
+
`Run a GSD-T CLI command for the project at \`${projectDir}\` and report the result. Steps:`,
|
|
31
|
+
`1. If \`${projectDir}/bin/${localBin}\` exists, run: \`node ${projectDir}/bin/${localBin} ${argStr}\` (set via="local"). Otherwise run: \`gsd-t ${subcmd} ${argStr}\` (set via="global"). Use cwd \`${projectDir}\`.`,
|
|
32
|
+
`2. Capture exit code (ok = exitCode 0) and stdout/stderr.`,
|
|
33
|
+
parseJson ? `3. Parse stdout as JSON into \`envelope\` (null if not JSON). Return JSON per the schema.` : `3. Put stdout (trimmed, ≤4000 chars) in \`stdout\`. Return JSON per the schema.`,
|
|
34
|
+
`Do NOT do any other work. ONLY run this one command and report.`,
|
|
35
|
+
].join("\n");
|
|
36
|
+
const opts = { label, schema: _CLI_ENVELOPE_SCHEMA, model: "haiku" };
|
|
37
|
+
if (phaseName) opts.phase = phaseName;
|
|
38
|
+
const r = await agent(prompt, opts).catch((e) => ({ ok: false, exitCode: -1, envelope: null, stderr: String(e && e.message), via: "error" }));
|
|
39
|
+
return r || { ok: false, exitCode: -1, envelope: null, via: "error" };
|
|
40
|
+
}
|
|
41
|
+
async function runPreflight(projectDir, label = "preflight", phaseName) { return runCli(projectDir, "preflight", ["--json"], "cli-preflight.cjs", label, true, phaseName); }
|
|
42
|
+
async function generateBrief(projectDir, { kind = "execute", milestone, domain, id, label = "brief", phaseName } = {}) {
|
|
43
|
+
const argv = ["--kind", kind, "--spawn-id", id, "--out", `${projectDir}/.gsd-t/briefs/${id}.json`];
|
|
44
|
+
if (milestone) argv.push("--milestone", milestone);
|
|
45
|
+
if (domain) argv.push("--domain", domain);
|
|
46
|
+
const r = await runCli(projectDir, "brief", argv, "gsd-t-context-brief.cjs", label, false, phaseName);
|
|
47
|
+
return { ok: r.ok, briefPath: `${projectDir}/.gsd-t/briefs/${id}.json`, via: r.via };
|
|
48
|
+
}
|
|
20
49
|
|
|
21
|
-
const projectDir =
|
|
22
|
-
const symptom =
|
|
50
|
+
const projectDir = _args.projectDir || ".";
|
|
51
|
+
const symptom = _args.symptom || null;
|
|
23
52
|
|
|
24
53
|
const DEBUG_CYCLE_SCHEMA = {
|
|
25
54
|
type: "object",
|
|
@@ -42,9 +71,9 @@ if (!symptom) {
|
|
|
42
71
|
}
|
|
43
72
|
|
|
44
73
|
phase("Preflight");
|
|
45
|
-
const pre =
|
|
74
|
+
const pre = await runPreflight(projectDir);
|
|
46
75
|
if (!pre.ok) return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
|
|
47
|
-
const brief =
|
|
76
|
+
const brief = await generateBrief(projectDir, { kind: "execute", id: "debug-brief" });
|
|
48
77
|
|
|
49
78
|
let lastResult = null;
|
|
50
79
|
for (let cycle = 1; cycle <= 2; cycle++) {
|
|
@@ -65,12 +65,47 @@ const INTEGRATE_RESULT_SCHEMA = {
|
|
|
65
65
|
|
|
66
66
|
// ───── Script body ──────────────────────────────────────────────────────────
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// M81: runtime-native helpers (sandbox bans require/fs/path/child_process/process — the
|
|
69
|
+
// old require("./_lib.js")+require("path") crashed this on first eval, TD-113). CLI calls
|
|
70
|
+
// delegate to an agent's Bash; file reads (scope.md/tasks.md) move INTO the worker agent
|
|
71
|
+
// (it has Read). args arrives as a JSON STRING in this runtime. See gsd-t-scan.workflow.js.
|
|
72
|
+
const _args = (typeof args === "string") ? (() => { try { return JSON.parse(args); } catch { return {}; } })() : (args || {});
|
|
73
|
+
const _CLI_ENVELOPE_SCHEMA = {
|
|
74
|
+
type: "object", required: ["ok", "exitCode"], additionalProperties: true,
|
|
75
|
+
properties: { ok: { type: "boolean" }, exitCode: { type: "integer" }, envelope: {}, stdout: { type: "string" }, stderr: { type: "string" }, via: { type: "string" } },
|
|
76
|
+
};
|
|
77
|
+
async function runCli(projectDir, subcmd, argv, localBin, label, parseJson = true, phaseName) {
|
|
78
|
+
const argStr = (argv || []).map((a) => `'${String(a).replace(/'/g, "'\\''")}'`).join(" ");
|
|
79
|
+
const prompt = [
|
|
80
|
+
`Run a GSD-T CLI command for the project at \`${projectDir}\` and report the result. Steps:`,
|
|
81
|
+
`1. If \`${projectDir}/bin/${localBin}\` exists, run: \`node ${projectDir}/bin/${localBin} ${argStr}\` (set via="local"). Otherwise run: \`gsd-t ${subcmd} ${argStr}\` (set via="global"). Use cwd \`${projectDir}\`.`,
|
|
82
|
+
`2. Capture exit code (ok = exitCode 0) and stdout/stderr.`,
|
|
83
|
+
parseJson ? `3. Parse stdout as JSON into \`envelope\` (null if not JSON). Return JSON per the schema.` : `3. Put stdout (trimmed, ≤4000 chars) in \`stdout\`. Return JSON per the schema.`,
|
|
84
|
+
`Do NOT do any other work. ONLY run this one command and report.`,
|
|
85
|
+
].join("\n");
|
|
86
|
+
const opts = { label, schema: _CLI_ENVELOPE_SCHEMA, model: "haiku" };
|
|
87
|
+
if (phaseName) opts.phase = phaseName;
|
|
88
|
+
const r = await agent(prompt, opts).catch((e) => ({ ok: false, exitCode: -1, envelope: null, stderr: String(e && e.message), via: "error" }));
|
|
89
|
+
return r || { ok: false, exitCode: -1, envelope: null, via: "error" };
|
|
90
|
+
}
|
|
91
|
+
async function runPreflight(projectDir, label = "preflight", phaseName) { return runCli(projectDir, "preflight", ["--json"], "cli-preflight.cjs", label, true, phaseName); }
|
|
92
|
+
async function runVerifyGate(projectDir, label = "verify-gate", phaseName) { return runCli(projectDir, "verify-gate", ["--json"], "gsd-t-verify-gate.cjs", label, true, phaseName); }
|
|
93
|
+
async function proveFileDisjointness(projectDir, domains, label = "disjointness", phaseName) {
|
|
94
|
+
const argv = ["--dry-run"];
|
|
95
|
+
for (const d of (domains || [])) { argv.push("--domain", d); }
|
|
96
|
+
return runCli(projectDir, "parallel", argv, "gsd-t-parallel.cjs", label, false, phaseName);
|
|
97
|
+
}
|
|
98
|
+
async function generateBrief(projectDir, { kind = "execute", milestone, domain, id, label = "brief", phaseName } = {}) {
|
|
99
|
+
const argv = ["--kind", kind, "--spawn-id", id, "--out", `${projectDir}/.gsd-t/briefs/${id}.json`];
|
|
100
|
+
if (milestone) argv.push("--milestone", milestone);
|
|
101
|
+
if (domain) argv.push("--domain", domain);
|
|
102
|
+
const r = await runCli(projectDir, "brief", argv, "gsd-t-context-brief.cjs", label, false, phaseName);
|
|
103
|
+
return { ok: r.ok, briefPath: `${projectDir}/.gsd-t/briefs/${id}.json`, via: r.via };
|
|
104
|
+
}
|
|
70
105
|
|
|
71
|
-
const projectDir =
|
|
72
|
-
const milestone =
|
|
73
|
-
const domains = (
|
|
106
|
+
const projectDir = _args.projectDir || ".";
|
|
107
|
+
const milestone = _args.milestone || null;
|
|
108
|
+
const domains = (Array.isArray(_args.domains) && _args.domains) || [];
|
|
74
109
|
|
|
75
110
|
if (!milestone) {
|
|
76
111
|
log("execute: no milestone provided — args.milestone is required");
|
|
@@ -83,7 +118,7 @@ if (!domains.length) {
|
|
|
83
118
|
|
|
84
119
|
phase("Preflight");
|
|
85
120
|
log(`execute: milestone=${milestone}, domains=${domains.length}`);
|
|
86
|
-
const pre =
|
|
121
|
+
const pre = await runPreflight(projectDir);
|
|
87
122
|
if (!pre.ok) {
|
|
88
123
|
log(`preflight FAIL — exitCode=${pre.exitCode}: ${pre.stderr || "(no stderr)"}`);
|
|
89
124
|
return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
|
|
@@ -93,7 +128,7 @@ log(`preflight OK`);
|
|
|
93
128
|
phase("Disjointness");
|
|
94
129
|
// 4.8-audit fix: scope disjointness to the requested domain set, not the whole project.
|
|
95
130
|
// Without this, an unrelated DRAFT domain elsewhere in the project could flip the result.
|
|
96
|
-
const disj =
|
|
131
|
+
const disj = await proveFileDisjointness(projectDir, domains);
|
|
97
132
|
if (!disj.ok) {
|
|
98
133
|
log(`disjointness FAIL — exitCode=${disj.exitCode}: ${disj.stderr || disj.stdout}`);
|
|
99
134
|
return { status: "failed", reason: "non-disjoint" };
|
|
@@ -105,32 +140,22 @@ const domainResults = await parallel(
|
|
|
105
140
|
domains.map((domain) => async () => {
|
|
106
141
|
// 4.8-audit fix: per-domain brief (M55-D2 brief-per-spawn semantic) — each worker
|
|
107
142
|
// gets a brief scoped to its own domain so grep-the-brief is most effective.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// every task" — silently dropping tail content is a correctness regression. Briefs
|
|
115
|
-
// are the compression layer; raw scope/tasks must pass whole.
|
|
116
|
-
const scope = lib.readScope({ projectDir, domain }) || "(scope.md missing)";
|
|
117
|
-
const tasks = lib.readDomainTasks({ projectDir, domain }) || "(tasks.md missing)";
|
|
143
|
+
// M81: generated via an awaited agent (sandbox-safe); the worker reads its own
|
|
144
|
+
// scope.md/tasks.md (it has Read) instead of the orchestrator pre-reading via fs.
|
|
145
|
+
const domBrief = await generateBrief(projectDir, { kind: "execute", milestone, domain, id: `execute-${(milestone || "m").toLowerCase()}-${domain}`, phaseName: "Domains", label: `brief:${domain}` });
|
|
146
|
+
const briefRef = domBrief.ok ? domBrief.briefPath : "(brief generation failed — re-walk repo)";
|
|
147
|
+
const scopePath = `${projectDir}/.gsd-t/domains/${domain}/scope.md`;
|
|
148
|
+
const tasksPath = `${projectDir}/.gsd-t/domains/${domain}/tasks.md`;
|
|
118
149
|
const prompt = [
|
|
119
150
|
`You are the worker agent for the GSD-T domain \`${domain}\` in milestone \`${milestone}\`.`,
|
|
120
151
|
``,
|
|
121
|
-
`
|
|
152
|
+
`FIRST, read these two files in full (do NOT skip or truncate them):`,
|
|
153
|
+
`- Scope (your owned files): \`${scopePath}\``,
|
|
154
|
+
`- Tasks: \`${tasksPath}\``,
|
|
122
155
|
``,
|
|
123
|
-
|
|
124
|
-
``,
|
|
125
|
-
`**Scope (your owned files):**`,
|
|
126
|
-
"```",
|
|
127
|
-
scope,
|
|
128
|
-
"```",
|
|
156
|
+
`Your job: execute every task listed under "## Tasks" in tasks.md, respecting the file ownership in scope.md.`,
|
|
129
157
|
``,
|
|
130
|
-
`**
|
|
131
|
-
"```",
|
|
132
|
-
tasks,
|
|
133
|
-
"```",
|
|
158
|
+
`**Brief (REQUIRED READ):** ${briefRef} — if present, grep this JSON first instead of re-reading CLAUDE.md and contracts.`,
|
|
134
159
|
``,
|
|
135
160
|
`Constraints:`,
|
|
136
161
|
`- Touch only files in your scope's "Owned Files" list.`,
|
|
@@ -194,7 +219,7 @@ if (integrate.status === "failed") {
|
|
|
194
219
|
}
|
|
195
220
|
|
|
196
221
|
phase("Verify-Gate");
|
|
197
|
-
const vg =
|
|
222
|
+
const vg = await runVerifyGate(projectDir);
|
|
198
223
|
log(`verify-gate exitCode=${vg.exitCode} ok=${vg.ok}`);
|
|
199
224
|
|
|
200
225
|
return {
|
|
@@ -16,11 +16,41 @@ export const meta = {
|
|
|
16
16
|
],
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// M81: runtime-native helpers (sandbox bans require/fs/child_process/process — the old
|
|
20
|
+
// require("./_lib.js") crashed this workflow on first eval, TD-113). Delegate CLI calls
|
|
21
|
+
// to an agent's Bash; args arrives as a JSON STRING in this runtime. See gsd-t-scan.workflow.js.
|
|
22
|
+
const _args = (typeof args === "string") ? (() => { try { return JSON.parse(args); } catch { return {}; } })() : (args || {});
|
|
23
|
+
const _CLI_ENVELOPE_SCHEMA = {
|
|
24
|
+
type: "object", required: ["ok", "exitCode"], additionalProperties: true,
|
|
25
|
+
properties: { ok: { type: "boolean" }, exitCode: { type: "integer" }, envelope: {}, stdout: { type: "string" }, stderr: { type: "string" }, via: { type: "string" } },
|
|
26
|
+
};
|
|
27
|
+
async function runCli(projectDir, subcmd, argv, localBin, label, parseJson = true, phaseName) {
|
|
28
|
+
const argStr = (argv || []).map((a) => `'${String(a).replace(/'/g, "'\\''")}'`).join(" ");
|
|
29
|
+
const prompt = [
|
|
30
|
+
`Run a GSD-T CLI command for the project at \`${projectDir}\` and report the result. Steps:`,
|
|
31
|
+
`1. If \`${projectDir}/bin/${localBin}\` exists, run: \`node ${projectDir}/bin/${localBin} ${argStr}\` (set via="local"). Otherwise run: \`gsd-t ${subcmd} ${argStr}\` (set via="global"). Use cwd \`${projectDir}\`.`,
|
|
32
|
+
`2. Capture exit code (ok = exitCode 0) and stdout/stderr.`,
|
|
33
|
+
parseJson ? `3. Parse stdout as JSON into \`envelope\` (null if not JSON). Return JSON per the schema.` : `3. Put stdout (trimmed, ≤4000 chars) in \`stdout\`. Return JSON per the schema.`,
|
|
34
|
+
`Do NOT do any other work. ONLY run this one command and report.`,
|
|
35
|
+
].join("\n");
|
|
36
|
+
const opts = { label, schema: _CLI_ENVELOPE_SCHEMA, model: "haiku" };
|
|
37
|
+
if (phaseName) opts.phase = phaseName;
|
|
38
|
+
const r = await agent(prompt, opts).catch((e) => ({ ok: false, exitCode: -1, envelope: null, stderr: String(e && e.message), via: "error" }));
|
|
39
|
+
return r || { ok: false, exitCode: -1, envelope: null, via: "error" };
|
|
40
|
+
}
|
|
41
|
+
async function runPreflight(projectDir, label = "preflight", phaseName) { return runCli(projectDir, "preflight", ["--json"], "cli-preflight.cjs", label, true, phaseName); }
|
|
42
|
+
async function runVerifyGate(projectDir, label = "verify-gate", phaseName) { return runCli(projectDir, "verify-gate", ["--json"], "gsd-t-verify-gate.cjs", label, true, phaseName); }
|
|
43
|
+
async function generateBrief(projectDir, { kind = "execute", milestone, domain, id, label = "brief", phaseName } = {}) {
|
|
44
|
+
const argv = ["--kind", kind, "--spawn-id", id, "--out", `${projectDir}/.gsd-t/briefs/${id}.json`];
|
|
45
|
+
if (milestone) argv.push("--milestone", milestone);
|
|
46
|
+
if (domain) argv.push("--domain", domain);
|
|
47
|
+
const r = await runCli(projectDir, "brief", argv, "gsd-t-context-brief.cjs", label, false, phaseName);
|
|
48
|
+
return { ok: r.ok, briefPath: `${projectDir}/.gsd-t/briefs/${id}.json`, via: r.via };
|
|
49
|
+
}
|
|
20
50
|
|
|
21
|
-
const projectDir =
|
|
22
|
-
const milestone =
|
|
23
|
-
const domains =
|
|
51
|
+
const projectDir = _args.projectDir || ".";
|
|
52
|
+
const milestone = _args.milestone || null;
|
|
53
|
+
const domains = _args.domains || [];
|
|
24
54
|
|
|
25
55
|
const INTEGRATE_SCHEMA = {
|
|
26
56
|
type: "object",
|
|
@@ -38,9 +68,9 @@ if (!milestone || !domains.length) {
|
|
|
38
68
|
}
|
|
39
69
|
|
|
40
70
|
phase("Preflight");
|
|
41
|
-
const pre =
|
|
71
|
+
const pre = await runPreflight(projectDir);
|
|
42
72
|
if (!pre.ok) return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
|
|
43
|
-
const brief =
|
|
73
|
+
const brief = await generateBrief(projectDir, { kind: "execute", milestone, id: `integrate-${(milestone || "m").toLowerCase()}` });
|
|
44
74
|
|
|
45
75
|
phase("Integrate");
|
|
46
76
|
const integrate = await agent(
|
|
@@ -63,7 +93,7 @@ if (integrate.status === "failed") {
|
|
|
63
93
|
}
|
|
64
94
|
|
|
65
95
|
phase("Verify-Gate");
|
|
66
|
-
const vg =
|
|
96
|
+
const vg = await runVerifyGate(projectDir);
|
|
67
97
|
return {
|
|
68
98
|
status: vg.ok ? "complete" : "verify-failed",
|
|
69
99
|
integrate,
|
|
@@ -26,7 +26,36 @@ export const meta = {
|
|
|
26
26
|
],
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
// M81: runtime-native helpers (sandbox bans require/fs/child_process/process — the old
|
|
30
|
+
// require("./_lib.js") crashed this workflow on first eval, TD-113). Delegate CLI calls
|
|
31
|
+
// to an agent's Bash; args arrives as a JSON STRING in this runtime. See gsd-t-scan.workflow.js.
|
|
32
|
+
const _args = (typeof args === "string") ? (() => { try { return JSON.parse(args); } catch { return {}; } })() : (args || {});
|
|
33
|
+
const _CLI_ENVELOPE_SCHEMA = {
|
|
34
|
+
type: "object", required: ["ok", "exitCode"], additionalProperties: true,
|
|
35
|
+
properties: { ok: { type: "boolean" }, exitCode: { type: "integer" }, envelope: {}, stdout: { type: "string" }, stderr: { type: "string" }, via: { type: "string" } },
|
|
36
|
+
};
|
|
37
|
+
async function runCli(projectDir, subcmd, argv, localBin, label, parseJson = true, phaseNameOpt) {
|
|
38
|
+
const argStr = (argv || []).map((a) => `'${String(a).replace(/'/g, "'\\''")}'`).join(" ");
|
|
39
|
+
const prompt = [
|
|
40
|
+
`Run a GSD-T CLI command for the project at \`${projectDir}\` and report the result. Steps:`,
|
|
41
|
+
`1. If \`${projectDir}/bin/${localBin}\` exists, run: \`node ${projectDir}/bin/${localBin} ${argStr}\` (set via="local"). Otherwise run: \`gsd-t ${subcmd} ${argStr}\` (set via="global"). Use cwd \`${projectDir}\`.`,
|
|
42
|
+
`2. Capture exit code (ok = exitCode 0) and stdout/stderr.`,
|
|
43
|
+
parseJson ? `3. Parse stdout as JSON into \`envelope\` (null if not JSON). Return JSON per the schema.` : `3. Put stdout (trimmed, ≤4000 chars) in \`stdout\`. Return JSON per the schema.`,
|
|
44
|
+
`Do NOT do any other work. ONLY run this one command and report.`,
|
|
45
|
+
].join("\n");
|
|
46
|
+
const opts = { label, schema: _CLI_ENVELOPE_SCHEMA, model: "haiku" };
|
|
47
|
+
if (phaseNameOpt) opts.phase = phaseNameOpt;
|
|
48
|
+
const r = await agent(prompt, opts).catch((e) => ({ ok: false, exitCode: -1, envelope: null, stderr: String(e && e.message), via: "error" }));
|
|
49
|
+
return r || { ok: false, exitCode: -1, envelope: null, via: "error" };
|
|
50
|
+
}
|
|
51
|
+
async function runPreflight(projectDir, label = "preflight", phaseNameOpt) { return runCli(projectDir, "preflight", ["--json"], "cli-preflight.cjs", label, true, phaseNameOpt); }
|
|
52
|
+
async function generateBrief(projectDir, { kind = "execute", milestone, domain, id, label = "brief", phaseNameOpt } = {}) {
|
|
53
|
+
const argv = ["--kind", kind, "--spawn-id", id, "--out", `${projectDir}/.gsd-t/briefs/${id}.json`];
|
|
54
|
+
if (milestone) argv.push("--milestone", milestone);
|
|
55
|
+
if (domain) argv.push("--domain", domain);
|
|
56
|
+
const r = await runCli(projectDir, "brief", argv, "gsd-t-context-brief.cjs", label, false, phaseNameOpt);
|
|
57
|
+
return { ok: r.ok, briefPath: `${projectDir}/.gsd-t/briefs/${id}.json`, via: r.via };
|
|
58
|
+
}
|
|
30
59
|
|
|
31
60
|
const VALID_PHASES = [
|
|
32
61
|
"partition", "plan", "discuss", "impact",
|
|
@@ -45,10 +74,10 @@ const PHASE_RESULT_SCHEMA = {
|
|
|
45
74
|
},
|
|
46
75
|
};
|
|
47
76
|
|
|
48
|
-
const projectDir =
|
|
49
|
-
const milestone =
|
|
50
|
-
const userInput =
|
|
51
|
-
const phaseName =
|
|
77
|
+
const projectDir = _args.projectDir || ".";
|
|
78
|
+
const milestone = _args.milestone || null;
|
|
79
|
+
const userInput = _args.userInput || "";
|
|
80
|
+
const phaseName = _args.phase;
|
|
52
81
|
|
|
53
82
|
if (!phaseName || !VALID_PHASES.includes(phaseName)) {
|
|
54
83
|
log(`phase: args.phase must be one of: ${VALID_PHASES.join(", ")}`);
|
|
@@ -56,9 +85,9 @@ if (!phaseName || !VALID_PHASES.includes(phaseName)) {
|
|
|
56
85
|
}
|
|
57
86
|
|
|
58
87
|
phase("Preflight");
|
|
59
|
-
const pre =
|
|
88
|
+
const pre = await runPreflight(projectDir);
|
|
60
89
|
if (!pre.ok) return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
|
|
61
|
-
const brief =
|
|
90
|
+
const brief = await generateBrief(projectDir, { kind: phaseName, milestone, id: `${phaseName}-${(milestone || "m").toLowerCase()}` });
|
|
62
91
|
|
|
63
92
|
phase("Phase");
|
|
64
93
|
const promptByPhase = {
|
|
@@ -16,11 +16,63 @@ export const meta = {
|
|
|
16
16
|
],
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// M81: runtime-native helpers. The Anthropic Workflow sandbox provides ONLY the
|
|
20
|
+
// globals agent/parallel/pipeline/log/phase/budget/args — NO require/fs/path/
|
|
21
|
+
// child_process/process. The old `require("./_lib.js")` threw ReferenceError on first
|
|
22
|
+
// eval, so EVERY workflow except scan silently crashed and never ran (TD-113, confirmed
|
|
23
|
+
// by the NiceNote session 2026-06-05). These inline helpers delegate the CLI calls to an
|
|
24
|
+
// agent() that runs them via Bash (preferring project-local bin/<tool>.cjs, falling back
|
|
25
|
+
// to the global `gsd-t` PATH binary), parsing the JSON envelope — same brains, sandbox-safe
|
|
26
|
+
// invocation. The args global also arrives as a JSON STRING in this runtime, so parse it.
|
|
27
|
+
const _args = (typeof args === "string") ? (() => { try { return JSON.parse(args); } catch { return {}; } })() : (args || {});
|
|
20
28
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
const _CLI_ENVELOPE_SCHEMA = {
|
|
30
|
+
type: "object", required: ["ok", "exitCode"], additionalProperties: true,
|
|
31
|
+
properties: {
|
|
32
|
+
ok: { type: "boolean" }, exitCode: { type: "integer" },
|
|
33
|
+
envelope: {}, stdout: { type: "string" }, stderr: { type: "string" }, via: { type: "string" },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
// Run a `gsd-t <subcmd>` CLI (or project-local bin/<localBin>) via an agent's Bash and
|
|
37
|
+
// return { ok, exitCode, envelope, stderr, via }. parseJson=true parses stdout as the envelope.
|
|
38
|
+
async function runCli(projectDir, subcmd, argv, localBin, label, parseJson = true, phaseName) {
|
|
39
|
+
const argStr = (argv || []).map((a) => `'${String(a).replace(/'/g, "'\\''")}'`).join(" ");
|
|
40
|
+
const prompt = [
|
|
41
|
+
`Run a GSD-T CLI command for the project at \`${projectDir}\` and report the result. Steps:`,
|
|
42
|
+
`1. If \`${projectDir}/bin/${localBin}\` exists, run: \`node ${projectDir}/bin/${localBin} ${argStr}\` (set via="local").`,
|
|
43
|
+
` Otherwise run: \`gsd-t ${subcmd} ${argStr}\` (set via="global").`,
|
|
44
|
+
` Run it with cwd \`${projectDir}\` (use \`cd ${projectDir} && …\` or \`-C\`/\`--cwd\` as appropriate).`,
|
|
45
|
+
`2. Capture the exit code (ok = exitCode 0) and stdout/stderr.`,
|
|
46
|
+
parseJson
|
|
47
|
+
? `3. Parse stdout as JSON into \`envelope\` (null if not JSON). Return JSON per the schema.`
|
|
48
|
+
: `3. Put stdout (trimmed, ≤4000 chars) in \`stdout\`. Return JSON per the schema.`,
|
|
49
|
+
`Do NOT do any other work. ONLY run this one command and report.`,
|
|
50
|
+
].join("\n");
|
|
51
|
+
const opts = { label, schema: _CLI_ENVELOPE_SCHEMA, model: "haiku" };
|
|
52
|
+
if (phaseName) opts.phase = phaseName; // opts.phase MUST be a string, never the phase() fn
|
|
53
|
+
const r = await agent(prompt, opts)
|
|
54
|
+
.catch((e) => ({ ok: false, exitCode: -1, envelope: null, stderr: String(e && e.message), via: "error" }));
|
|
55
|
+
return r || { ok: false, exitCode: -1, envelope: null, via: "error" };
|
|
56
|
+
}
|
|
57
|
+
async function runPreflight(projectDir, label = "preflight", phaseName) {
|
|
58
|
+
return runCli(projectDir, "preflight", ["--json"], "cli-preflight.cjs", label, true, phaseName);
|
|
59
|
+
}
|
|
60
|
+
async function runVerifyGate(projectDir, label = "verify-gate", phaseName) {
|
|
61
|
+
return runCli(projectDir, "verify-gate", ["--json"], "gsd-t-verify-gate.cjs", label, true, phaseName);
|
|
62
|
+
}
|
|
63
|
+
// Brief generation: writes .gsd-t/briefs/<id>.json and returns its path. The id must be
|
|
64
|
+
// caller-supplied (no Date.now/Math.random in the sandbox) — pass a stable id per spawn.
|
|
65
|
+
async function generateBrief(projectDir, { kind = "execute", milestone, domain, id, label = "brief", phaseName } = {}) {
|
|
66
|
+
const argv = ["--kind", kind, "--spawn-id", id, "--out", `${projectDir}/.gsd-t/briefs/${id}.json`];
|
|
67
|
+
if (milestone) argv.push("--milestone", milestone);
|
|
68
|
+
if (domain) argv.push("--domain", domain);
|
|
69
|
+
const r = await runCli(projectDir, "brief", argv, "gsd-t-context-brief.cjs", label, false, phaseName);
|
|
70
|
+
return { ok: r.ok, briefPath: `${projectDir}/.gsd-t/briefs/${id}.json`, via: r.via };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const projectDir = _args.projectDir || ".";
|
|
74
|
+
const task = _args.task || null;
|
|
75
|
+
const model = _args.model || "sonnet";
|
|
24
76
|
|
|
25
77
|
const QUICK_SCHEMA = {
|
|
26
78
|
type: "object",
|
|
@@ -38,9 +90,9 @@ if (!task) {
|
|
|
38
90
|
}
|
|
39
91
|
|
|
40
92
|
phase("Preflight");
|
|
41
|
-
const pre =
|
|
93
|
+
const pre = await runPreflight(projectDir);
|
|
42
94
|
if (!pre.ok) return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
|
|
43
|
-
const brief =
|
|
95
|
+
const brief = await generateBrief(projectDir, { kind: "execute", id: "quick-brief" });
|
|
44
96
|
|
|
45
97
|
phase("Execute");
|
|
46
98
|
const result = await agent(
|
|
@@ -64,7 +116,7 @@ if (result.status === "failed" || result.status === "blocked") {
|
|
|
64
116
|
}
|
|
65
117
|
|
|
66
118
|
phase("Verify");
|
|
67
|
-
const vg =
|
|
119
|
+
const vg = await runVerifyGate(projectDir);
|
|
68
120
|
return {
|
|
69
121
|
status: vg.ok ? "complete" : "verify-failed",
|
|
70
122
|
result,
|
|
@@ -30,12 +30,55 @@ export const meta = {
|
|
|
30
30
|
],
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
// M81: runtime-native helpers (sandbox bans require/fs/path/child_process/process — the
|
|
34
|
+
// old require("./_lib.js") + the inline require("child_process"/"fs"/"path") in the
|
|
35
|
+
// CI-parity block crashed this on first eval, TD-113). All CLI calls (preflight,
|
|
36
|
+
// verify-gate, build-coverage, ci-parity, test-data) delegate to an agent's Bash; the
|
|
37
|
+
// QA/Red-Team protocol bodies are read by an agent (Read) instead of fs. args arrives as
|
|
38
|
+
// a JSON STRING in this runtime. See gsd-t-scan.workflow.js.
|
|
39
|
+
const _args = (typeof args === "string") ? (() => { try { return JSON.parse(args); } catch { return {}; } })() : (args || {});
|
|
40
|
+
const _CLI_ENVELOPE_SCHEMA = {
|
|
41
|
+
type: "object", required: ["ok", "exitCode"], additionalProperties: true,
|
|
42
|
+
properties: { ok: { type: "boolean" }, exitCode: { type: "integer" }, envelope: {}, stdout: { type: "string" }, stderr: { type: "string" }, via: { type: "string" } },
|
|
43
|
+
};
|
|
44
|
+
async function runCli(projectDir, subcmd, argv, localBin, label, parseJson = true, phaseName) {
|
|
45
|
+
const argStr = (argv || []).map((a) => `'${String(a).replace(/'/g, "'\\''")}'`).join(" ");
|
|
46
|
+
const prompt = [
|
|
47
|
+
`Run a GSD-T CLI command for the project at \`${projectDir}\` and report the result. Steps:`,
|
|
48
|
+
`1. If \`${projectDir}/bin/${localBin}\` exists, run: \`node ${projectDir}/bin/${localBin} ${argStr}\` (set via="local"). Otherwise run: \`gsd-t ${subcmd} ${argStr}\` (set via="global"). Use cwd \`${projectDir}\`.`,
|
|
49
|
+
`2. Capture exit code (ok = exitCode 0) and stdout/stderr.`,
|
|
50
|
+
parseJson ? `3. Parse stdout as JSON into \`envelope\` (null if not JSON). Return JSON per the schema.` : `3. Put stdout (trimmed, ≤4000 chars) in \`stdout\`. Return JSON per the schema.`,
|
|
51
|
+
`Do NOT do any other work. ONLY run this one command and report.`,
|
|
52
|
+
].join("\n");
|
|
53
|
+
const opts = { label, schema: _CLI_ENVELOPE_SCHEMA, model: "haiku" };
|
|
54
|
+
if (phaseName) opts.phase = phaseName;
|
|
55
|
+
const r = await agent(prompt, opts).catch((e) => ({ ok: false, exitCode: -1, envelope: null, stderr: String(e && e.message), via: "error" }));
|
|
56
|
+
return r || { ok: false, exitCode: -1, envelope: null, via: "error" };
|
|
57
|
+
}
|
|
58
|
+
async function runPreflight(projectDir, label = "preflight", phaseName) { return runCli(projectDir, "preflight", ["--json"], "cli-preflight.cjs", label, true, phaseName); }
|
|
59
|
+
async function runVerifyGate(projectDir, label = "verify-gate", phaseName) { return runCli(projectDir, "verify-gate", ["--json"], "gsd-t-verify-gate.cjs", label, true, phaseName); }
|
|
60
|
+
async function generateBrief(projectDir, { kind = "verify", milestone, domain, id, label = "brief", phaseName } = {}) {
|
|
61
|
+
const argv = ["--kind", kind, "--spawn-id", id, "--out", `${projectDir}/.gsd-t/briefs/${id}.json`];
|
|
62
|
+
if (milestone) argv.push("--milestone", milestone);
|
|
63
|
+
if (domain) argv.push("--domain", domain);
|
|
64
|
+
const r = await runCli(projectDir, "brief", argv, "gsd-t-context-brief.cjs", label, false, phaseName);
|
|
65
|
+
return { ok: r.ok, briefPath: `${projectDir}/.gsd-t/briefs/${id}.json`, via: r.via };
|
|
66
|
+
}
|
|
67
|
+
// The QA / Red-Team / design-verify protocol bodies live at templates/prompts/<name>-subagent.md
|
|
68
|
+
// inside the installed @tekyzinc/gsd-t package. The orchestrator can't read files (no fs); each
|
|
69
|
+
// triad agent reads its OWN protocol via Read at spawn time. loadProtocol returns a Read-instruction
|
|
70
|
+
// the agent prompt embeds, rather than the protocol text itself.
|
|
71
|
+
function loadProtocolInstruction(name) {
|
|
72
|
+
const rel = `templates/prompts/${name}-subagent.md`;
|
|
73
|
+
// Locate the protocol inside the installed @tekyzinc/gsd-t package using ONLY shell
|
|
74
|
+
// (no require/fs tokens — those trip the runtime-native lint even inside a string).
|
|
75
|
+
return `Read your protocol FIRST. Find it by running in Bash: \`cat "$(npm root -g)/@tekyzinc/gsd-t/${rel}"\` (or, if a project-local \`${rel}\` exists, read that instead). Follow that protocol exactly.`;
|
|
76
|
+
}
|
|
34
77
|
|
|
35
|
-
const projectDir =
|
|
36
|
-
const milestone =
|
|
37
|
-
const skipUltra =
|
|
38
|
-
const skipUltraReason =
|
|
78
|
+
const projectDir = _args.projectDir || ".";
|
|
79
|
+
const milestone = _args.milestone || null;
|
|
80
|
+
const skipUltra = _args.skipUltra || false;
|
|
81
|
+
const skipUltraReason = _args.skipUltraReason || null;
|
|
39
82
|
|
|
40
83
|
// 4.8-audit fix: skipUltra requires a recorded reason per
|
|
41
84
|
// orthogonal-validation-contract.md Rule #2. Refuse without one.
|
|
@@ -153,15 +196,15 @@ if (!milestone) {
|
|
|
153
196
|
}
|
|
154
197
|
|
|
155
198
|
phase("Preflight");
|
|
156
|
-
const pre =
|
|
199
|
+
const pre = await runPreflight(projectDir);
|
|
157
200
|
if (!pre.ok) {
|
|
158
201
|
log(`preflight FAIL — halting verify`);
|
|
159
202
|
return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
|
|
160
203
|
}
|
|
161
|
-
const brief =
|
|
204
|
+
const brief = await generateBrief(projectDir, { kind: "verify", milestone, id: `verify-${(milestone || "m").toLowerCase()}` });
|
|
162
205
|
|
|
163
206
|
phase("Verify-Gate");
|
|
164
|
-
const vg =
|
|
207
|
+
const vg = await runVerifyGate(projectDir);
|
|
165
208
|
if (!vg.ok) {
|
|
166
209
|
log(`verify-gate FAIL exitCode=${vg.exitCode} — halting before triad`);
|
|
167
210
|
return {
|
|
@@ -179,36 +222,16 @@ log(`verify-gate green`);
|
|
|
179
222
|
// from Dockerfile COPY — silent CI-divergence regression. M57 made this gate
|
|
180
223
|
// mandatory; Workflow MUST preserve it or we re-introduce that exact failure.
|
|
181
224
|
// Detected by user/worker in parallel session 2026-05-29 13:00.
|
|
225
|
+
// M81: these were raw spawnSync + require("fs"/"path"/"child_process") in the orchestrator
|
|
226
|
+
// — the exact sandbox-forbidden pattern (TD-113). Now awaited runCli agent calls. The
|
|
227
|
+
// FAIL-blocking semantics are UNCHANGED: a non-zero exit halts verify before the triad.
|
|
182
228
|
phase("CI-Parity");
|
|
183
|
-
const
|
|
184
|
-
function _runJsonCli(subcmd, argv = []) {
|
|
185
|
-
// Use _lib-style resolution — prefer project-local bin/<tool>.cjs
|
|
186
|
-
const fsMod = require("fs");
|
|
187
|
-
const pMod = require("path");
|
|
188
|
-
const localMap = {
|
|
189
|
-
"build-coverage": "gsd-t-build-coverage.cjs",
|
|
190
|
-
"ci-parity": "gsd-t-ci-parity.cjs",
|
|
191
|
-
"test-data": "gsd-t-test-data-ledger.cjs",
|
|
192
|
-
};
|
|
193
|
-
const local = pMod.join(projectDir, "bin", localMap[subcmd] || "");
|
|
194
|
-
const cmd = fsMod.existsSync(local) ? process.execPath : "gsd-t";
|
|
195
|
-
const args = fsMod.existsSync(local) ? [local, ...argv] : [subcmd, ...argv];
|
|
196
|
-
const r = spawnSync(cmd, args, { cwd: projectDir, stdio: "pipe" });
|
|
197
|
-
let envelope = null;
|
|
198
|
-
try { envelope = r.stdout ? JSON.parse(r.stdout.toString()) : null; } catch (_) {}
|
|
199
|
-
return {
|
|
200
|
-
ok: r.status === 0,
|
|
201
|
-
exitCode: r.status,
|
|
202
|
-
envelope,
|
|
203
|
-
stderr: r.stderr && r.stderr.toString(),
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
const bc = _runJsonCli("build-coverage", ["--json"]);
|
|
229
|
+
const bc = await runCli(projectDir, "build-coverage", ["--json"], "gsd-t-build-coverage.cjs", "m57:build-coverage", true, "CI-Parity");
|
|
207
230
|
if (!bc.ok) {
|
|
208
231
|
log(`M57 build-coverage FAIL exitCode=${bc.exitCode} — halting (FAIL-blocking)`);
|
|
209
232
|
return { status: "ci-parity-failed", overallVerdict: "VERIFY-FAILED", buildCoverage: bc.envelope };
|
|
210
233
|
}
|
|
211
|
-
const cip =
|
|
234
|
+
const cip = await runCli(projectDir, "ci-parity", ["--json"], "gsd-t-ci-parity.cjs", "m57:ci-parity", true, "CI-Parity");
|
|
212
235
|
if (!cip.ok) {
|
|
213
236
|
log(`M57 ci-parity FAIL exitCode=${cip.exitCode} — halting (FAIL-blocking)`);
|
|
214
237
|
return { status: "ci-parity-failed", overallVerdict: "VERIFY-FAILED", ciParity: cip.envelope };
|
|
@@ -221,8 +244,10 @@ log(`M57 CI-parity gate green`);
|
|
|
221
244
|
// live in production data. M58 made post-E2E purge mandatory; M60 hardened
|
|
222
245
|
// the adapters against empty-prefix bypass. Workflow MUST preserve.
|
|
223
246
|
phase("Test-Data Purge");
|
|
224
|
-
|
|
225
|
-
|
|
247
|
+
// M81: run-id is stable per verify run (no Date.now in the sandbox); the milestone scope
|
|
248
|
+
// is sufficient for purge targeting and is deterministic on resume.
|
|
249
|
+
const verifyRunId = `verify-${(milestone || "M__").toLowerCase()}`;
|
|
250
|
+
const td = await runCli(projectDir, "test-data", ["--purge", "--run", verifyRunId, "--json"], "gsd-t-test-data-ledger.cjs", "m58:test-data-purge", true, "Test-Data Purge");
|
|
226
251
|
if (!td.ok) {
|
|
227
252
|
log(`M58 test-data purge FAIL exitCode=${td.exitCode} — halting (FAIL-blocking)`);
|
|
228
253
|
return { status: "test-data-purge-failed", overallVerdict: "VERIFY-FAILED", testDataPurge: td.envelope };
|
|
@@ -233,10 +258,11 @@ phase("Orthogonal Triad");
|
|
|
233
258
|
|
|
234
259
|
const briefRef = brief.briefPath || "(brief generation failed — re-walk repo)";
|
|
235
260
|
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
const
|
|
261
|
+
// M81: the protocol body lives in templates/prompts/<name>-subagent.md inside the installed
|
|
262
|
+
// package; the orchestrator can't read files (no fs). Each triad agent reads its OWN
|
|
263
|
+
// protocol via Read at spawn time — loadProtocolInstruction returns the Read directive.
|
|
264
|
+
const redTeamProtocolInstruction = loadProtocolInstruction("red-team");
|
|
265
|
+
const qaProtocolInstruction = loadProtocolInstruction("qa");
|
|
240
266
|
|
|
241
267
|
const stages = [
|
|
242
268
|
// Stage A — /code-review ultra (cooperative correctness + cleanup)
|
|
@@ -273,10 +299,7 @@ const stages = [
|
|
|
273
299
|
`**adversarial / security / boundaries**. You are NOT cooperative — your`,
|
|
274
300
|
`success is measured in bugs FOUND, not tests passed. Try to break the code.`,
|
|
275
301
|
``,
|
|
276
|
-
`Run the Red Team protocol
|
|
277
|
-
"----- BEGIN RED TEAM PROTOCOL -----",
|
|
278
|
-
redTeamProtocol.slice(0, 8000),
|
|
279
|
-
"----- END RED TEAM PROTOCOL -----",
|
|
302
|
+
`Run the Red Team protocol. ${redTeamProtocolInstruction}`,
|
|
280
303
|
``,
|
|
281
304
|
`Verdict is FAIL if you found any CRITICAL or HIGH severity bug; GRUDGING-PASS`,
|
|
282
305
|
`if you searched exhaustively and found nothing. Return JSON per the schema.`,
|
|
@@ -296,10 +319,7 @@ const stages = [
|
|
|
296
319
|
`counts. Detect shallow tests (layout-only assertions that pass on an empty HTML page).`,
|
|
297
320
|
`Verify contract compliance against .gsd-t/contracts/.`,
|
|
298
321
|
``,
|
|
299
|
-
`Run the QA protocol
|
|
300
|
-
"----- BEGIN QA PROTOCOL -----",
|
|
301
|
-
qaProtocol.slice(0, 8000),
|
|
302
|
-
"----- END QA PROTOCOL -----",
|
|
322
|
+
`Run the QA protocol. ${qaProtocolInstruction}`,
|
|
303
323
|
``,
|
|
304
324
|
`Return JSON per the schema.`,
|
|
305
325
|
].join("\n"),
|
|
@@ -14,11 +14,14 @@ export const meta = {
|
|
|
14
14
|
],
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// M81: this workflow only composes sub-workflows (execute + verify) — it never used
|
|
18
|
+
// lib.*, but the `require("./_lib.js")` import alone crashed it on first eval in the
|
|
19
|
+
// sandbox (TD-113). Removed. args arrives as a JSON STRING in this runtime, so parse it.
|
|
20
|
+
const _args = (typeof args === "string") ? (() => { try { return JSON.parse(args); } catch { return {}; } })() : (args || {});
|
|
18
21
|
|
|
19
|
-
const projectDir =
|
|
20
|
-
const milestone =
|
|
21
|
-
const domains =
|
|
22
|
+
const projectDir = _args.projectDir || ".";
|
|
23
|
+
const milestone = _args.milestone || null;
|
|
24
|
+
const domains = _args.domains || [];
|
|
22
25
|
|
|
23
26
|
if (!milestone || !domains.length) {
|
|
24
27
|
log("wave: args.milestone and args.domains required");
|