@tekyzinc/gsd-t 4.13.12 → 4.15.10
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 +22 -0
- package/README.md +1 -1
- package/bin/gsd-t-graph-freshness.cjs +8 -4
- package/bin/gsd-t-graph-index.cjs +7 -2
- package/bin/gsd-t-graph-k1-sqlite-stream.cjs +1 -1
- package/bin/gsd-t-graph-metrics-rollup.cjs +527 -0
- package/bin/gsd-t-graph-query-cli.cjs +131 -31
- package/bin/gsd-t-graph-scip-upgrade.cjs +14 -5
- package/bin/gsd-t-graph-store-bakeoff.cjs +1 -1
- package/bin/gsd-t-graph-store-resolver.cjs +484 -0
- package/bin/gsd-t-verify-gate.cjs +9 -2
- package/bin/gsd-t.js +46 -1
- package/commands/gsd-t-scan.md +3 -3
- package/package.json +1 -1
- package/scripts/gsd-t-graph-intercept.js +89 -8
- package/scripts/gsd-t-read-intercept.js +87 -18
- package/templates/workflows/gsd-t-debug.workflow.js +19 -0
- package/templates/workflows/gsd-t-integrate.workflow.js +19 -0
- package/templates/workflows/gsd-t-phase.workflow.js +19 -0
- package/templates/workflows/gsd-t-quick.workflow.js +19 -0
- package/templates/workflows/gsd-t-scan.workflow.js +111 -14
- package/templates/workflows/gsd-t-verify.workflow.js +19 -0
|
@@ -88,6 +88,7 @@ const PREFLIGHT_SCHEMA = {
|
|
|
88
88
|
properties: {
|
|
89
89
|
ok: { type: "boolean" },
|
|
90
90
|
branch: { type: "string" },
|
|
91
|
+
repoName: { type: "string", description: "the project directory's basename (e.g. 'hilo-figma-atos') — used to suffix shared scan/doc files" },
|
|
91
92
|
priorRegisterExists: { type: "boolean" },
|
|
92
93
|
priorMaxTd: { type: "integer", description: "highest TD-NNN in the prior register, 0 if none" },
|
|
93
94
|
notes: { type: "string" },
|
|
@@ -287,6 +288,46 @@ async function runCli(verb, target, label) {
|
|
|
287
288
|
return r || { ok: false, reason: "graph-unavailable", via: "error" };
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
// Build the graph index (`gsd-t graph index`) when it is absent. The wired path
|
|
292
|
+
// is documented "build index if absent, then query" but the BUILD step was never
|
|
293
|
+
// wired — so on any project without a pre-built index (the common case), scan
|
|
294
|
+
// silently grep-fell-back and the graph was NEVER used (observed on hilo-figma-atos
|
|
295
|
+
// 2026-06-30: a 30M-token scan grep-fell-back because the index was never built).
|
|
296
|
+
// `graph index` is a longer command than the verb queries → bigger timeout.
|
|
297
|
+
// [RULE] scan-builds-index-when-absent
|
|
298
|
+
async function runCliBuild() {
|
|
299
|
+
const prompt = [
|
|
300
|
+
`Build the GSD-T code-graph index for the project at \`${projectDir}\`, then report. Steps:`,
|
|
301
|
+
`1. If \`${projectDir}/bin/gsd-t.js\` exists, run: \`node ${projectDir}/bin/gsd-t.js graph index\` (via="local"). Otherwise run: \`gsd-t graph index\` (via="global"). Use cwd \`${projectDir}\`. This may take up to a few minutes on a large repo — wait for it to finish. Do NOT redirect stderr.`,
|
|
302
|
+
`2. Set ok=true if the command exits 0 (the index built). Set ok=false with a short reason + first ~200 chars of stderr otherwise.`,
|
|
303
|
+
`Do NOT do any other work. ONLY run this one build command and report the structured result.`,
|
|
304
|
+
].join("\n");
|
|
305
|
+
const r = await agent(prompt, {
|
|
306
|
+
label: "graph:index-build",
|
|
307
|
+
phase: "Graph-Wiring",
|
|
308
|
+
model: "haiku",
|
|
309
|
+
schema: GRAPH_BUILD_SCHEMA,
|
|
310
|
+
}).catch((e) => ({ ok: false, reason: `agent-error: ${e && e.message}` }));
|
|
311
|
+
return r || { ok: false, reason: "build-failed" };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// M99 D2: persist a kind:'wiring' ledger line for this workflow.
|
|
315
|
+
// M81 sandbox: all I/O through agent() Bash; no require/fs in the sandbox.
|
|
316
|
+
// Uses the `gsd-t graph wiring-log` CLI shim (avoids embedding require() in strings).
|
|
317
|
+
// [RULE] wiring-mode-three-states / [RULE] consumer-label-from-context-not-setenv
|
|
318
|
+
async function persistWiringMode(mode) {
|
|
319
|
+
const consumer = "scan";
|
|
320
|
+
await agent(
|
|
321
|
+
[
|
|
322
|
+
`Persist one graph-wiring-mode ledger line for the scan workflow.`,
|
|
323
|
+
`Run: \`gsd-t graph wiring-log --consumer ${consumer} --mode ${mode} --project '${projectDir}'\``,
|
|
324
|
+
`If the command is not found, exit 0 (ledger write is optional).`,
|
|
325
|
+
`Return ONLY: {"ok": true} or {"ok": false, "reason": "<short reason>"}.`,
|
|
326
|
+
].join("\n"),
|
|
327
|
+
{ label: "scan:wiring-ledger", phase: "Graph-Wiring", model: "haiku", schema: { type: "object", required: ["ok"], properties: { ok: { type: "boolean" }, reason: { type: "string" } } } }
|
|
328
|
+
).catch(() => null); // fail-open
|
|
329
|
+
}
|
|
330
|
+
|
|
290
331
|
// Preflight: an agent checks branch + whether a prior register exists, via Bash.
|
|
291
332
|
// (No fs in the body — that was the bug.)
|
|
292
333
|
phase("Preflight");
|
|
@@ -295,8 +336,9 @@ const pre = await agent(
|
|
|
295
336
|
`You are the preflight check for a GSD-T deep scan of the project at \`${projectDir}\`.`,
|
|
296
337
|
`Using Bash/Read tools, determine:`,
|
|
297
338
|
`1. The current git branch (\`git -C ${projectDir} rev-parse --abbrev-ref HEAD\`; if not a git repo, report branch "(no-git)").`,
|
|
298
|
-
`2.
|
|
299
|
-
`3.
|
|
339
|
+
`2. The repo name = the basename of the resolved project dir (\`basename "$(cd ${projectDir} && pwd)"\`) — e.g. "hilo-figma-atos". Report as repoName (used to label the team-shared share/ copies).`,
|
|
340
|
+
`3. Whether \`${projectDir}/.gsd-t/techdebt.md\` exists (priorRegisterExists).`,
|
|
341
|
+
`4. If it exists, the HIGHEST TD-NNN number in it (grep \`### TD-\`, parse the max integer; priorMaxTd). If absent, priorMaxTd=0.`,
|
|
300
342
|
`Set ok=true unless something makes scanning impossible (e.g. projectDir does not exist). Return JSON per the schema.`,
|
|
301
343
|
].join("\n"),
|
|
302
344
|
{ label: "preflight", phase: "Preflight", schema: PREFLIGHT_SCHEMA, model: "haiku" }
|
|
@@ -306,7 +348,12 @@ if (!pre || !pre.ok) {
|
|
|
306
348
|
return { status: "failed", reason: "preflight-failed", preflight: pre };
|
|
307
349
|
}
|
|
308
350
|
const tdStart = (pre.priorRegisterExists ? (pre.priorMaxTd || 0) : 0) + 1;
|
|
309
|
-
|
|
351
|
+
// #47: repo-name suffix for the TEAM-SHARED copies only. INTERNAL files keep their
|
|
352
|
+
// fixed names (.gsd-t/techdebt.md, .gsd-t/scan/<dim>.md, docs/*.md) so all internal
|
|
353
|
+
// tooling (promote-debt, gap-analysis, complete-milestone, …) keeps working — zero
|
|
354
|
+
// blast radius. The repo suffix appears ONLY on the share/ exports at the end.
|
|
355
|
+
const repoName = (pre.repoName && /^[A-Za-z0-9._-]+$/.test(pre.repoName)) ? pre.repoName : "project";
|
|
356
|
+
log(`preflight ok — branch=${pre.branch}, repo=${repoName}, priorRegister=${pre.priorRegisterExists}, TD numbering starts at TD-${tdStart}`);
|
|
310
357
|
|
|
311
358
|
// Volume probe — an agent measures the codebase (its own Bash) and carves slices.
|
|
312
359
|
phase("Probe");
|
|
@@ -355,15 +402,31 @@ if (graphMode === "disabled") {
|
|
|
355
402
|
// ZERO graph calls in this path — [RULE] no-graph-baseline-proven-graph-free.
|
|
356
403
|
graphWiringMode = "disabled";
|
|
357
404
|
log("graph-wiring: DISABLED (no-graph baseline mode — graph-query call-count == 0; AC-4 baseline)");
|
|
405
|
+
await persistWiringMode("disabled"); // M99 D2: persist wiring mode [RULE] wiring-mode-three-states
|
|
358
406
|
} else {
|
|
359
407
|
// graphMode === "wired": build index if absent, then query structural slice.
|
|
360
408
|
// Step 1: check if index exists and is queryable.
|
|
361
|
-
|
|
409
|
+
let statusResult = await runCli("status", null, "status");
|
|
410
|
+
// Step 1b: if the index is absent/unqueryable, BUILD it once, then re-probe.
|
|
411
|
+
// This is the previously-missing build step — without it scan grep-fell-back on
|
|
412
|
+
// every project lacking a pre-built index (hilo-figma-atos 2026-06-30).
|
|
413
|
+
// [RULE] scan-builds-index-when-absent
|
|
414
|
+
if (!statusResult || !statusResult.ok) {
|
|
415
|
+
log(`graph-wiring: index not queryable (${(statusResult && statusResult.reason) || "graph-unavailable"}) — building it now (gsd-t graph index)...`);
|
|
416
|
+
const buildResult = await runCliBuild();
|
|
417
|
+
if (buildResult && buildResult.ok) {
|
|
418
|
+
log(`graph-wiring: index build OK — re-probing status.`);
|
|
419
|
+
statusResult = await runCli("status", null, "status");
|
|
420
|
+
} else {
|
|
421
|
+
log(`graph-wiring: index build FAILED [${(buildResult && buildResult.reason) || "build-failed"}] — falling back to grep-mode.`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
362
424
|
if (!statusResult || !statusResult.ok) {
|
|
363
|
-
// Graph unavailable — announce fallback, continue
|
|
364
|
-
// [RULE] parser-fail-disables-loud-never-silent
|
|
425
|
+
// Graph still unavailable after a build attempt — announce fallback, continue
|
|
426
|
+
// with intact grep-mode scan. [RULE] parser-fail-disables-loud-never-silent
|
|
365
427
|
graphWiringMode = "fallback-announced";
|
|
366
|
-
log(`⚠ GRAPH-FALLBACK (ANNOUNCED): graph
|
|
428
|
+
log(`⚠ GRAPH-FALLBACK (ANNOUNCED): graph unavailable after build attempt [reason=${(statusResult && statusResult.reason) || "graph-unavailable"}, via=${(statusResult && statusResult.via) || "?"}] — scan continues in full grep-mode (today's architecture, intact). Structural findings from LLM reconstruction only.`);
|
|
429
|
+
await persistWiringMode("fallback-announced"); // M99 D2 [RULE] wiring-mode-three-states
|
|
367
430
|
} else {
|
|
368
431
|
// Step 2: query the structural slice (dead-code + dangling + clusters).
|
|
369
432
|
// These are the findings the deep-finders currently reconstruct by reading files (error-prone);
|
|
@@ -382,6 +445,7 @@ if (graphMode === "disabled") {
|
|
|
382
445
|
// All three verbs returned graph-unavailable — announce fallback.
|
|
383
446
|
graphWiringMode = "fallback-announced";
|
|
384
447
|
log(`⚠ GRAPH-FALLBACK (ANNOUNCED): all structural-slice queries returned graph-unavailable — scan continues in full grep-mode. Dead-code / dangling / cluster findings from LLM reconstruction only.`);
|
|
448
|
+
await persistWiringMode("fallback-announced"); // M99 D2 [RULE] wiring-mode-three-states
|
|
385
449
|
} else {
|
|
386
450
|
// Structural slice assembled — will be injected into scanSlice finder agents.
|
|
387
451
|
structuralSlice = {
|
|
@@ -396,8 +460,15 @@ if (graphMode === "disabled") {
|
|
|
396
460
|
},
|
|
397
461
|
tier: (deadCodeResult && deadCodeResult.tier) || (danglingResult && danglingResult.tier) || "unknown",
|
|
398
462
|
};
|
|
399
|
-
|
|
463
|
+
// M99 Red Team fix: emit "WIRED" (uppercase) to MATCH the --auto router
|
|
464
|
+
// producers (bin/gsd-t.js:3915) AND the rollup's comparison
|
|
465
|
+
// (gsd-t-graph-metrics-rollup.cjs:333 `=== "WIRED"`). Lowercase "wired" here
|
|
466
|
+
// fell through all three rollup branches → a successfully-wired scan reported
|
|
467
|
+
// WIRED:0, defeating success criterion 13 (the metric M99 exists to surface).
|
|
468
|
+
// [RULE] wiring-mode-casing-matches-rollup
|
|
469
|
+
graphWiringMode = "WIRED";
|
|
400
470
|
log(`graph-wiring: WIRED — structural slice ready (dead-code: ${structuralSlice.deadCode.length} candidates, dangling: ${structuralSlice.dangling.length} edges, clusters: ${structuralSlice.clusters.length} groups, tier: ${structuralSlice.tier}). Slice will be INJECTED ADDITIVELY into scanSlice deep-finders. [RULE] scan-injects-structural-slice`);
|
|
471
|
+
await persistWiringMode("WIRED"); // M99 D2 [RULE] wiring-mode-three-states / wiring-mode-casing-matches-rollup
|
|
401
472
|
}
|
|
402
473
|
}
|
|
403
474
|
}
|
|
@@ -722,6 +793,11 @@ function fmtChunks(today) {
|
|
|
722
793
|
head.push(`**Date:** ${today}`);
|
|
723
794
|
head.push(`**Slices run:** ${slices.length} | **Coverage:** ${coverageComplete ? `FULL - all ${slices.length} slices succeeded` : `PARTIAL - ${succeededCount}/${slices.length} succeeded`}`);
|
|
724
795
|
head.push(`**Verified findings:** ${counts.total}`, "");
|
|
796
|
+
// M99 D2 T4: stamp graphWiringMode into the header (north-star: invisible fallback leaves a trace).
|
|
797
|
+
// A `fallback-announced` mode co-occurring with a same-window outcome:'hit' is the machine-visible
|
|
798
|
+
// NiceNote scan-#12 contradiction. [RULE] wiring-mode-three-states
|
|
799
|
+
const _wiringEmoji = { "wired": "✅", "fallback-announced": "⚠️", "disabled": "🚫", "pending": "⏳" };
|
|
800
|
+
head.push(`**Graph wiring:** ${_wiringEmoji[graphWiringMode] || "?"} \`${graphWiringMode}\` (WIRED = graph answered structural queries; fallback-announced = graph unavailable, fell back to grep; disabled = no-graph baseline)`, "");
|
|
725
801
|
head.push(`> Effort estimates use GSD-T-native units (domain / wave / spawn / token-spend). Never human-hours.`);
|
|
726
802
|
head.push(`> TD numbering continues from the prior register (if any, archived). This scan begins at **TD-${tdStart}**.`, "");
|
|
727
803
|
if (!coverageComplete) head.push(`> ⚠️ **PARTIAL COVERAGE - ${failedSlices.length} of ${slices.length} codebase areas were NOT scanned this pass** (failed to return findings): ${ascii(failedSlices.join(", "))}. Findings UNDER-COUNT the real debt. Re-run (resume) for full coverage.`, "");
|
|
@@ -767,10 +843,15 @@ const today = (typeof todayAgent === "string" && /\d{4}-\d{2}-\d{2}/.test(todayA
|
|
|
767
843
|
|
|
768
844
|
let archivePath = "";
|
|
769
845
|
if (pre.priorRegisterExists) {
|
|
846
|
+
// #47: archive the prior register + ALL prior dimension files into
|
|
847
|
+
// .gsd-t/scan/archive/ with a DATETIME stamp so the user can diff new-vs-prior.
|
|
848
|
+
// Handles BOTH the new suffixed names and any legacy unsuffixed leftovers.
|
|
770
849
|
const arch = await agent(
|
|
771
850
|
[
|
|
772
|
-
`Archive the existing
|
|
773
|
-
`
|
|
851
|
+
`Archive the existing scan outputs in \`${projectDir}\` into a dated archive folder so the user can diff new-vs-prior, then report the archive dir. Steps (via Bash):`,
|
|
852
|
+
`1. STAMP="$(date +%Y%m%d-%H%M)". ARCH="${projectDir}/.gsd-t/scan/archive". mkdir -p "$ARCH".`,
|
|
853
|
+
`2. Move each existing scan output into "$ARCH" with the STAMP appended before .md. For EACH that exists, move it: \`${projectDir}/.gsd-t/techdebt.md\`, \`${projectDir}/.gsd-t/techdebt_in_plain_english.md\`, and \`${projectDir}/.gsd-t/scan/<dim>.md\` for each dim in {architecture,security,quality,business-rules,contract-drift}. Target name: same basename + "-$STAMP.md" (e.g. techdebt-20260630-1542.md). Use \`git mv\` if a git repo else \`mv\`. Skip any that don't exist (no error).`,
|
|
854
|
+
`3. Reply with ONLY the archive dir path "$ARCH".`,
|
|
774
855
|
].join("\n"),
|
|
775
856
|
{ label: "synthesis:archive", phase: "Synthesis", model: "haiku" }
|
|
776
857
|
).catch(() => null);
|
|
@@ -783,7 +864,7 @@ const { chunks, lastTd } = fmtChunks(today);
|
|
|
783
864
|
// each subsequent chunk is APPENDED. Done SEQUENTIALLY so the file builds in order and
|
|
784
865
|
// each agent's prompt+output stays small enough to pass intact. The register path is
|
|
785
866
|
// passed to each chunk; only the chunk content varies.
|
|
786
|
-
const regPath = `${projectDir}/.gsd-t/techdebt.md`;
|
|
867
|
+
const regPath = `${projectDir}/.gsd-t/techdebt.md`; // internal fixed name (shared copy suffixed in share/)
|
|
787
868
|
let chunkOk = 0;
|
|
788
869
|
for (let ci = 0; ci < chunks.length; ci++) {
|
|
789
870
|
const isFirst = ci === 0;
|
|
@@ -901,7 +982,7 @@ log(`document phase: ${docsOk.length}/${docTargets.length} written/merged${docsF
|
|
|
901
982
|
// out bounded generator agents (each writes its batch's entries via the shared gate),
|
|
902
983
|
// then ASSEMBLE deterministically with severity section headers, and chunk-write.
|
|
903
984
|
phase("Plain-English");
|
|
904
|
-
const peTarget = `${projectDir}/.gsd-t/techdebt_in_plain_english.md`;
|
|
985
|
+
const peTarget = `${projectDir}/.gsd-t/techdebt_in_plain_english.md`; // internal fixed name (shared copy suffixed in share/)
|
|
905
986
|
const sevLabel = { CRITICAL: "fix before launch", HIGH: "fix soon", MEDIUM: "schedule", LOW: "clean up eventually" };
|
|
906
987
|
// Attach the deterministic TD number (matches the register: severity-sorted, tdStart+).
|
|
907
988
|
const peItems = finalFindings.map((f, i) => ({
|
|
@@ -991,11 +1072,27 @@ const peComplete = peActual === peExpectedEntries;
|
|
|
991
1072
|
if (!peComplete) log(`⚠ plain-english INCOMPLETE: wrote ${peActual}/${peExpectedEntries} entries (writer said ${typeof peWriteRes === "string" ? peWriteRes.trim().slice(0, 20) : JSON.stringify(peWriteRes).slice(0, 40)})`);
|
|
992
1073
|
log(`plain-english: ${peExpectedEntries}/${peItems.length} entries, grouped by severity, ${peChunks.length} chunks, on-disk count ${peActual ?? "?"} (${peComplete ? "COMPLETE" : "INCOMPLETE"})${peFailed ? `; ${peFailed} gen batch(es) failed` : ""}`);
|
|
993
1074
|
|
|
994
|
-
//
|
|
1075
|
+
// #47: EXPORT-COPY the living docs into a share/ folder with the repo-name suffix —
|
|
1076
|
+
// the LAST scan step. Originals stay at their fixed docs/ names (GSD-T reads them by
|
|
1077
|
+
// hardcoded path under the No-Re-Research rule); the share/ copies are the team-shareable,
|
|
1078
|
+
// project-labeled set. Regenerated each scan (overwrite). The scan reports themselves are
|
|
1079
|
+
// already suffixed in place (techdebt-<repo>.md, scan/<dim>-<repo>.md).
|
|
1080
|
+
const shareAgent = await agent(
|
|
1081
|
+
[
|
|
1082
|
+
`Create a shareable, repo-labeled copy of this project's living docs in \`${projectDir}\` via Bash. Steps:`,
|
|
1083
|
+
`1. \`mkdir -p ${projectDir}/share\`.`,
|
|
1084
|
+
`2. COPY (do NOT move — keep the original at its fixed name) each of these that EXISTS into \`${projectDir}/share/\` with the repo name "${repoName}" suffixed before .md. Living docs: \`docs/architecture.md\`→\`share/architecture-${repoName}.md\`, \`docs/requirements.md\`→\`share/requirements-${repoName}.md\`, \`docs/workflows.md\`→\`share/workflows-${repoName}.md\`, \`docs/infrastructure.md\`→\`share/infrastructure-${repoName}.md\`, \`README.md\`→\`share/README-${repoName}.md\`. Scan reports: \`.gsd-t/techdebt.md\`→\`share/techdebt-${repoName}.md\`, \`.gsd-t/techdebt_in_plain_english.md\`→\`share/techdebt_in_plain_english-${repoName}.md\`, and each \`.gsd-t/scan/<dim>.md\`→\`share/<dim>-${repoName}.md\` for dim in {architecture,security,quality,business-rules,contract-drift} (NOTE these scan dimension files share basenames with the living-doc architecture — keep them distinct: prefer \`share/scan-<dim>-${repoName}.md\` for the .gsd-t/scan/ ones to avoid colliding with docs/architecture). Overwrite existing share/ files (always fresh). Skip any source that doesn't exist (no error).`,
|
|
1085
|
+
`3. Reply with ONLY the count of files copied.`,
|
|
1086
|
+
].join("\n"),
|
|
1087
|
+
{ label: "share-export", phase: "Document", model: "haiku" }
|
|
1088
|
+
).catch(() => null);
|
|
1089
|
+
log(`share/ export: ${typeof shareAgent === "string" ? shareAgent.trim().slice(0, 40) : "done"} → ${projectDir}/share/`);
|
|
1090
|
+
|
|
1091
|
+
// Commit the docs + dimension files + plain-english + share/ via a small agent (Bash git).
|
|
995
1092
|
const commitAgent = await agent(
|
|
996
1093
|
[
|
|
997
1094
|
`Commit the GSD-T scan's generated documents in \`${projectDir}\` via Bash git, IF it is a git repo (else report skipped).`,
|
|
998
|
-
`Stage: \`.gsd-t/scan\`, \`.gsd-t/techdebt_in_plain_english.md\`, \`docs\`, \`README.md\` (do NOT stage \`.gsd-t/scan/.doc-backup\` if present). Commit message: "scan: deep document cross-population (${docsOk.length} docs) + dimension files". Do NOT push. Return JSON per the schema (status "rendered" if committed, "skipped" if not a git repo / nothing to commit, "failed" on error; outputPath optional).`,
|
|
1095
|
+
`Stage: \`.gsd-t/scan\`, \`.gsd-t/techdebt.md\`, \`.gsd-t/techdebt_in_plain_english.md\`, \`share\`, \`docs\`, \`README.md\` (do NOT stage \`.gsd-t/scan/.doc-backup\` if present; \`.gsd-t/scan/archive\` MAY be staged — the dated history is worth keeping). Commit message: "scan: deep document cross-population (${docsOk.length} docs) + dimension files + share/ export". Do NOT push. Return JSON per the schema (status "rendered" if committed, "skipped" if not a git repo / nothing to commit, "failed" on error; outputPath optional).`,
|
|
999
1096
|
].join("\n"),
|
|
1000
1097
|
{ label: "commit-docs", phase: "Document", schema: RENDER_SCHEMA, model: "haiku" }
|
|
1001
1098
|
).catch((e) => ({ status: "failed", notes: String(e && e.message) }));
|
|
@@ -230,6 +230,23 @@ const VERDICT_SCHEMA = {
|
|
|
230
230
|
},
|
|
231
231
|
};
|
|
232
232
|
|
|
233
|
+
// M99 D2: persist a kind:'wiring' ledger line. M81 sandbox: all I/O through agent() Bash.
|
|
234
|
+
// Uses the `gsd-t graph wiring-log --auto` CLI shim (avoids embedding require() in strings).
|
|
235
|
+
// [RULE] wiring-mode-three-states / [RULE] consumer-label-from-context-not-setenv
|
|
236
|
+
async function persistWiringMode(phaseName) {
|
|
237
|
+
const consumer = "verify";
|
|
238
|
+
await agent(
|
|
239
|
+
[
|
|
240
|
+
`Persist one graph-wiring-mode ledger line for the verify workflow.`,
|
|
241
|
+
`Run: \`gsd-t graph wiring-log --consumer ${consumer} --auto --project '${projectDir}'\``,
|
|
242
|
+
`(--auto detects WIRED if the graph store exists, else fallback-announced)`,
|
|
243
|
+
`If the command is not found, exit 0 (ledger write is optional).`,
|
|
244
|
+
`Return ONLY: {"ok": true} or {"ok": false, "reason": "<short reason>"}.`,
|
|
245
|
+
].join("\n"),
|
|
246
|
+
{ label: "verify:wiring-ledger", phase: phaseName, model: "haiku", schema: { type: "object", required: ["ok"], properties: { ok: { type: "boolean" }, reason: { type: "string" } } } }
|
|
247
|
+
).catch(() => null);
|
|
248
|
+
}
|
|
249
|
+
|
|
233
250
|
// ───── Script body ─────
|
|
234
251
|
|
|
235
252
|
if (!milestone) {
|
|
@@ -244,6 +261,8 @@ if (!pre.ok) {
|
|
|
244
261
|
return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
|
|
245
262
|
}
|
|
246
263
|
const brief = await generateBrief(projectDir, { kind: "verify", milestone, id: `verify-${(milestone || "m").toLowerCase()}` });
|
|
264
|
+
// M99 D2: persist graphWiringMode for the verify consumer. [RULE] wiring-mode-three-states
|
|
265
|
+
await persistWiringMode("Preflight");
|
|
247
266
|
|
|
248
267
|
phase("Verify-Gate");
|
|
249
268
|
const vg = await runVerifyGate(projectDir);
|