@opengsd/gsd-core 1.16.0 → 1.18.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -2
- package/.opencode/plugins/gsd-core.js +251 -1
- package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
- package/GEMINI.md +1 -1
- package/agents/gsd-advisor-researcher.compact.md +1 -1
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-assumptions-analyzer.compact.md +1 -1
- package/agents/gsd-assumptions-analyzer.md +1 -1
- package/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +1 -1
- package/agents/gsd-code-reviewer.md +1 -1
- package/agents/gsd-codebase-mapper.compact.md +1 -1
- package/agents/gsd-codebase-mapper.md +1 -1
- package/agents/gsd-debug-session-manager.compact.md +6 -0
- package/agents/gsd-debug-session-manager.md +6 -0
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-verifier.compact.md +4 -4
- package/agents/gsd-doc-verifier.md +4 -3
- package/agents/gsd-doc-writer.compact.md +23 -5
- package/agents/gsd-doc-writer.md +19 -6
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +7 -3
- package/agents/gsd-integration-checker.compact.md +1 -1
- package/agents/gsd-integration-checker.md +1 -1
- package/agents/gsd-nyquist-auditor.compact.md +1 -1
- package/agents/gsd-nyquist-auditor.md +1 -1
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +2 -1
- package/agents/gsd-planner.md +5 -4
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +5 -5
- package/agents/gsd-research-synthesizer.md +5 -5
- package/agents/gsd-roadmapper.compact.md +1 -1
- package/agents/gsd-roadmapper.md +1 -1
- package/agents/gsd-security-auditor.compact.md +1 -1
- package/agents/gsd-security-auditor.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +1 -1
- package/agents/gsd-ui-auditor.md +1 -1
- package/agents/gsd-ui-checker.compact.md +1 -1
- package/agents/gsd-ui-checker.md +1 -1
- package/agents/gsd-ui-researcher.compact.md +13 -1
- package/agents/gsd-ui-researcher.md +28 -1
- package/agents/gsd-verifier.md +1 -1
- package/bin/gsd-mcp-server.js +3 -1
- package/bin/install.js +398 -95
- package/commands/gsd/code-review.md +8 -0
- package/commands/gsd/pause-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +34 -12
- package/gsd-core/bin/lib/adr-parser.cjs +37 -5
- package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
- package/gsd-core/bin/lib/capability-lock.cjs +29 -7
- package/gsd-core/bin/lib/capability-registry.cjs +138 -80
- package/gsd-core/bin/lib/capability-source.cjs +3 -1
- package/gsd-core/bin/lib/capability-state.cjs +6 -3
- package/gsd-core/bin/lib/capability-validator.cjs +8 -5
- package/gsd-core/bin/lib/capability-writer.cjs +2 -1
- package/gsd-core/bin/lib/commands.cjs +33 -9
- package/gsd-core/bin/lib/config-loader.cjs +3 -3
- package/gsd-core/bin/lib/decision-coverage-support.cjs +19 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +5 -4
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +15 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +13 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
- package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +60 -5
- package/gsd-core/bin/lib/gate-ui-safety.cjs +2 -1
- package/gsd-core/bin/lib/graphify.cjs +3 -2
- package/gsd-core/bin/lib/init.cjs +43 -16
- package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
- package/gsd-core/bin/lib/install-engine.cjs +11 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
- package/gsd-core/bin/lib/install-profiles.cjs +5 -3
- package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
- package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
- package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
- package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
- package/gsd-core/bin/lib/mcp-server.cjs +5 -1
- package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
- package/gsd-core/bin/lib/opencode-version.cjs +486 -0
- package/gsd-core/bin/lib/phase.cjs +141 -11
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +2 -1
- package/gsd-core/bin/lib/report-parser.cjs +57 -1
- package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
- package/gsd-core/bin/lib/roadmap.cjs +56 -5
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +142 -6
- package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +58 -3
- package/gsd-core/bin/lib/runtime-slash.cjs +13 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +2 -0
- package/gsd-core/bin/lib/smart-entry.cjs +100 -53
- package/gsd-core/bin/lib/state-document.cjs +37 -3
- package/gsd-core/bin/lib/state-transition.cjs +98 -19
- package/gsd-core/bin/lib/state.cjs +544 -126
- package/gsd-core/bin/lib/task-command-router.cjs +2 -3
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
- package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
- package/gsd-core/bin/lib/update-context.cjs +18 -1
- package/gsd-core/bin/lib/verification.cjs +107 -17
- package/gsd-core/bin/lib/verify.cjs +36 -1
- package/gsd-core/references/agent-contracts.md +1 -1
- package/gsd-core/references/agent-skills-bootstrap.md +26 -7
- package/gsd-core/references/git-integration.md +3 -3
- package/gsd-core/references/planner-reversibility.md +18 -4
- package/gsd-core/references/planner-verify-command-grounding.md +18 -0
- package/gsd-core/references/tdd.md +4 -3
- package/gsd-core/references/verifier-phase-gates.md +8 -1
- package/gsd-core/workflows/add-backlog.md +10 -2
- package/gsd-core/workflows/add-phase.md +5 -1
- package/gsd-core/workflows/add-tests.md +5 -1
- package/gsd-core/workflows/add-todo.md +5 -1
- package/gsd-core/workflows/ai-integration-phase.md +5 -1
- package/gsd-core/workflows/audit-milestone.md +21 -9
- package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
- package/gsd-core/workflows/autonomous.md +52 -64
- package/gsd-core/workflows/check-todos.md +5 -1
- package/gsd-core/workflows/code-review-fix.md +5 -1
- package/gsd-core/workflows/code-review.md +21 -2
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
- package/gsd-core/workflows/complete-milestone.md +25 -5
- package/gsd-core/workflows/debug.md +5 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +11 -3
- package/gsd-core/workflows/discuss-phase.md +5 -1
- package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
- package/gsd-core/workflows/docs-update.md +27 -5
- package/gsd-core/workflows/edit-phase.md +5 -1
- package/gsd-core/workflows/eval-review.md +11 -3
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +12 -6
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +19 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +5 -1
- package/gsd-core/workflows/execute-phase.md +40 -7
- package/gsd-core/workflows/execute-plan.md +8 -4
- package/gsd-core/workflows/extract-learnings.md +6 -2
- package/gsd-core/workflows/forensics.md +5 -1
- package/gsd-core/workflows/import.md +5 -1
- package/gsd-core/workflows/insert-phase.md +5 -1
- package/gsd-core/workflows/manager.md +10 -2
- package/gsd-core/workflows/map-codebase.md +5 -1
- package/gsd-core/workflows/milestone-summary.md +10 -2
- package/gsd-core/workflows/new-milestone.md +13 -5
- package/gsd-core/workflows/new-project.md +9 -5
- package/gsd-core/workflows/next.md +8 -4
- package/gsd-core/workflows/pause-work.md +3 -3
- package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +5 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
- package/gsd-core/workflows/plan-phase.md +41 -73
- package/gsd-core/workflows/plan-review-convergence.md +17 -5
- package/gsd-core/workflows/plant-seed.md +8 -4
- package/gsd-core/workflows/progress.md +6 -2
- package/gsd-core/workflows/quick-batch.md +5 -1
- package/gsd-core/workflows/quick.md +22 -7
- package/gsd-core/workflows/remove-phase.md +5 -1
- package/gsd-core/workflows/resume-project.md +19 -11
- package/gsd-core/workflows/review.md +6 -2
- package/gsd-core/workflows/scan.md +5 -1
- package/gsd-core/workflows/secure-phase.md +6 -2
- package/gsd-core/workflows/settings.md +3 -3
- package/gsd-core/workflows/ship.md +13 -10
- package/gsd-core/workflows/thread.md +10 -2
- package/gsd-core/workflows/transition.md +5 -1
- package/gsd-core/workflows/ui-phase.md +5 -1
- package/gsd-core/workflows/ui-review.md +6 -2
- package/gsd-core/workflows/ultraplan-phase.md +5 -1
- package/gsd-core/workflows/update.md +5 -1
- package/gsd-core/workflows/validate-phase.md +6 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
- package/gsd-core/workflows/verify-work.md +11 -3
- package/hooks/dist/gsd-agent-isolation-guard.js +3 -1
- package/hooks/dist/gsd-check-update.js +26 -20
- package/hooks/dist/gsd-context-monitor.js +14 -5
- package/hooks/dist/gsd-cursor-subagent-start.js +2 -1
- package/hooks/dist/gsd-secret-read-guard.js +189 -15
- package/hooks/dist/gsd-statusline.js +321 -22
- package/hooks/dist/gsd-validate-commit.sh +27 -5
- package/hooks/gsd-agent-isolation-guard.js +3 -1
- package/hooks/gsd-check-update.js +26 -20
- package/hooks/gsd-context-monitor.js +14 -5
- package/hooks/gsd-cursor-subagent-start.js +2 -1
- package/hooks/gsd-secret-read-guard.js +189 -15
- package/hooks/gsd-statusline.js +321 -22
- package/hooks/gsd-validate-commit.sh +27 -5
- package/package.json +1 -1
- package/scripts/audit-workflow-script-paths.cjs +1 -1
- package/scripts/diagnose-validate-commit-in-tier.cjs +270 -0
- package/scripts/diagnose-validate-commit-phases.cjs +348 -0
- package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
- package/scripts/docs-guard-registry.cjs +2 -0
- package/scripts/gen-capability-registry.cjs +7 -1
- package/scripts/gen-loop-host-contract.cjs +4 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +13 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +26 -0
- package/scripts/lint-phase-arg-assignment.cjs +2 -2
- package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
- package/scripts/mutation-matrix.cjs +3 -1
- package/scripts/release-tarball-smoke.cjs +15 -7
- package/scripts/xtrace-phase-timing.cjs +205 -0
- package/skills/gsd-code-review/SKILL.md +8 -0
- package/skills/gsd-pause-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* #5473 — a job-long watcher for the Windows conformance tier's bounded-child stalls.
|
|
5
|
+
*
|
|
6
|
+
* In one Windows run (job 39083981500) three different tests each hit their
|
|
7
|
+
* bound EXACTLY: the #4784 `sh -c 'printf | sed | head -1'` at 10 s, an
|
|
8
|
+
* `echo | grep | awk` bash pipeline at 5 s, and `npm ls` through cmd.exe at 30 s.
|
|
9
|
+
* Measured inside the tier, a bare `bash` starts in ~35 ms, Defender real-time
|
|
10
|
+
* protection is off, and 2,407 pipeline calls under 6-way concurrency never
|
|
11
|
+
* stalled past 2 s. So the stall is rare and does not reproduce on demand. This
|
|
12
|
+
* watcher runs beside the whole test step on the same VM and writes down, with a
|
|
13
|
+
* UTC wall-clock timestamp, every moment the VM itself stops making progress:
|
|
14
|
+
*
|
|
15
|
+
* - `loop-gap`: this process's own timer fired more than LOOP_GAP_MS late
|
|
16
|
+
* (the whole VM, or at least this process, was not scheduled);
|
|
17
|
+
* - `probe-slow`: a probe child (the #4784 sh pipeline, or `cmd /c exit`)
|
|
18
|
+
* took more than PROBE_SLOW_MS, or hit its bound;
|
|
19
|
+
* - `procs`: a snapshot of the live node/sh/bash/cmd/npm/git processes (name,
|
|
20
|
+
* parent, age, CPU time so far, and the *.test.cjs names in their
|
|
21
|
+
* arguments), taken after either anomaly and every two minutes, so a stall
|
|
22
|
+
* can be lined up with what else was running in the chunk. Earlier red
|
|
23
|
+
* `next`s (see defaultMaxUnmeasuredPerChunk in scripts/run-tests.cjs) lost
|
|
24
|
+
* whole chunks at 600000 ms with zero failing tests; the periodic snapshots
|
|
25
|
+
* show which test file a killed chunk was inside and whether it was idle.
|
|
26
|
+
*
|
|
27
|
+
* Timestamps are ISO UTC, the same clock as the lines in the job log, so a
|
|
28
|
+
* failing test's end time minus its duration can be lined up against these
|
|
29
|
+
* events: a stall that is the VM freezing shows up here at the same instant; a
|
|
30
|
+
* stall that is only that one child hanging does not.
|
|
31
|
+
*
|
|
32
|
+
* node scripts/diagnose-windows-spawn-watcher.cjs start [dir] spawn detached, return at once
|
|
33
|
+
* node scripts/diagnose-windows-spawn-watcher.cjs report [dir] stop it, print and summarize the events
|
|
34
|
+
*
|
|
35
|
+
* `dir` defaults to RUNNER_TEMP/spawn-watcher. No environment variable or value
|
|
36
|
+
* is read into the report. TEMPORARY: delete
|
|
37
|
+
* with scripts/diagnose-validate-commit-*.cjs once #5473 is resolved.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
const fs = require('node:fs');
|
|
41
|
+
const path = require('node:path');
|
|
42
|
+
const { spawn } = require('node:child_process');
|
|
43
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
44
|
+
|
|
45
|
+
const TICK_MS = 250;
|
|
46
|
+
const LOOP_GAP_MS = 750;
|
|
47
|
+
// The watcher must not become the load it is looking for: a stall that shows up once in
|
|
48
|
+
// thousands of calls is easy to shift with extra spawn churn, so probes are sparse (one
|
|
49
|
+
// sh and one cmd every 5 s, about 480 pairs over a 40 minute job) and the CIM query,
|
|
50
|
+
// the heavy part, runs every 5 minutes plus at most once a minute after an anomaly.
|
|
51
|
+
const PROBE_EVERY_MS = 5000;
|
|
52
|
+
const PROBE_BOUND_MS = 15_000;
|
|
53
|
+
const PROBE_SLOW_MS = 1500;
|
|
54
|
+
const MAX_LIFETIME_MS = 40 * 60_000;
|
|
55
|
+
const PIPELINE = "printf '%s\\n' x | sed -n 's/x/y/p' | head -1";
|
|
56
|
+
|
|
57
|
+
const REPORT_WAIT_MS = 20_000;
|
|
58
|
+
const EVENTS_FILE = 'events.log';
|
|
59
|
+
const STOP_FILE = 'stop';
|
|
60
|
+
const SUMMARY_FILE = 'summary.json';
|
|
61
|
+
|
|
62
|
+
/** One event line: `<ISO UTC> <kind> <ms> <detail>`. */
|
|
63
|
+
function formatEvent(at, kind, ms, detail) {
|
|
64
|
+
return `${new Date(at).toISOString()} ${kind} ${ms} ${detail}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const SHELL_NAMES = new Set(['sh', 'bash', 'cmd']);
|
|
68
|
+
const LINGER_AGE_S = 30;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* One compact line for a process snapshot. `rows` are `{pid, ppid, name, born, cpuMs, cmd}`
|
|
72
|
+
* (born = epoch ms, cmd = the command line). Only what identifies the work is kept: the
|
|
73
|
+
* process name, its age, its CPU time so far, and the `*.test.cjs` file names in its
|
|
74
|
+
* arguments (the node:test chunk children carry them). Nothing else from a command line
|
|
75
|
+
* is reported, because the job environment carries tokens and a report is public.
|
|
76
|
+
*/
|
|
77
|
+
function formatProcs(rows, now) {
|
|
78
|
+
return rows
|
|
79
|
+
.map((r) => {
|
|
80
|
+
const tests = [...new Set(String(r.cmd || '').match(/[\w.-]+\.test\.cjs/g) || [])].slice(0, 3);
|
|
81
|
+
const age = Math.max(0, Math.round((now - r.born) / 1000));
|
|
82
|
+
const cpu = Math.round(r.cpuMs / 1000);
|
|
83
|
+
// A shell that outlives every bound the tests set is the signature of a descendant
|
|
84
|
+
// still holding a spawnSync stdio pipe: node's timer then reports ETIMEDOUT at the
|
|
85
|
+
// exact bound although the direct child already exited (node src/spawn_sync.cc).
|
|
86
|
+
const lingering = SHELL_NAMES.has(r.name) && age > LINGER_AGE_S ? ' LINGERING' : '';
|
|
87
|
+
return `${r.name}#${r.pid}<${r.ppid} age ${age}s cpu ${cpu}s${lingering}${tests.length ? ` [${tests.join(' ')}]` : ''}`;
|
|
88
|
+
})
|
|
89
|
+
.join(' | ');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Inverse of formatEvent; null for a line that is not an event. */
|
|
93
|
+
function parseEvent(line) {
|
|
94
|
+
const m = /^(\d{4}-\d{2}-\d{2}T[\d:.]+Z) (loop-gap|probe-slow|probe-error|procs) (\d+) (.*)$/.exec(line);
|
|
95
|
+
if (!m) return null;
|
|
96
|
+
return { at: m[1], kind: m[2], ms: Number(m[3]), detail: m[4] };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Counts and extremes per kind, over parsed events. */
|
|
100
|
+
function summarizeEvents(events) {
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const e of events) {
|
|
103
|
+
const s = out[e.kind] || (out[e.kind] = { count: 0, maxMs: 0 });
|
|
104
|
+
s.count += 1;
|
|
105
|
+
if (e.ms > s.maxMs) s.maxMs = e.ms;
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Percentile (0..1) of numbers, nearest-rank on a sorted copy; null when empty. */
|
|
111
|
+
function percentile(values, p) {
|
|
112
|
+
if (values.length === 0) return null;
|
|
113
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
114
|
+
return sorted[Math.min(sorted.length - 1, Math.floor(p * sorted.length))];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Run one probe child. `onDone(label, ms, timedOut, started, error)`: `error` is the spawn
|
|
119
|
+
* error code (or the thrown message) when the probe never ran, else null. A probe that
|
|
120
|
+
* never ran must not look like a fast success, so callers keep it out of the latencies.
|
|
121
|
+
*/
|
|
122
|
+
function probe(label, bin, args, onDone) {
|
|
123
|
+
const started = Date.now();
|
|
124
|
+
let timedOut = false;
|
|
125
|
+
let settled = false;
|
|
126
|
+
let timer = null;
|
|
127
|
+
const finish = (error) => {
|
|
128
|
+
if (settled) return;
|
|
129
|
+
settled = true;
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
onDone(label, Date.now() - started, timedOut, started, error || null);
|
|
132
|
+
};
|
|
133
|
+
let child;
|
|
134
|
+
try {
|
|
135
|
+
child = spawn(bin, args, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
136
|
+
} catch (e) {
|
|
137
|
+
finish(`threw ${e && e.code ? e.code : 'error'}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
timer = setTimeout(() => { timedOut = true; try { child.kill('SIGKILL'); } catch { /* gone */ } finish(); }, PROBE_BOUND_MS);
|
|
141
|
+
child.stdout.resume();
|
|
142
|
+
child.stderr.resume();
|
|
143
|
+
child.on('error', (e) => finish(`spawn ${e && e.code ? e.code : 'error'}`));
|
|
144
|
+
child.on('close', () => finish());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const PS_PROCS = "Get-CimInstance Win32_Process | Where-Object { $_.Name -match '^(node|sh|bash|cmd|npm|git)\\.exe$' } | ForEach-Object { [pscustomobject]@{ pid = $_.ProcessId; ppid = $_.ParentProcessId; name = $_.Name -replace '\\.exe$', ''; born = [int64](([datetime]$_.CreationDate).ToUniversalTime() - [datetime]'1970-01-01').TotalMilliseconds; cpuMs = [int64](($_.KernelModeTime + $_.UserModeTime) / 10000); cmd = $_.CommandLine } } | ConvertTo-Json -Compress";
|
|
148
|
+
const SNAPSHOT_EVERY_MS = 300_000;
|
|
149
|
+
const SNAPSHOT_COOLDOWN_MS = 60_000;
|
|
150
|
+
const SNAPSHOT_BOUND_MS = 30_000;
|
|
151
|
+
|
|
152
|
+
/** Snapshot the live node/sh/bash/cmd/npm/git processes; calls back with the compact line. */
|
|
153
|
+
function snapshotProcs(onDone) {
|
|
154
|
+
const started = Date.now();
|
|
155
|
+
let out = '';
|
|
156
|
+
let err = '';
|
|
157
|
+
let child;
|
|
158
|
+
try {
|
|
159
|
+
child = spawn('powershell', ['-NoProfile', '-NonInteractive', '-Command', PS_PROCS], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
160
|
+
} catch (e) {
|
|
161
|
+
onDone(Date.now() - started, `snapshot unavailable (threw ${e && e.code ? e.code : 'error'})`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const timer = setTimeout(() => { try { child.kill('SIGKILL'); } catch { /* gone */ } }, SNAPSHOT_BOUND_MS);
|
|
165
|
+
child.stdout.on('data', (d) => { out += d; });
|
|
166
|
+
// PowerShell's own error text carries no command lines; a short head tells a failure from a timeout.
|
|
167
|
+
child.stderr.on('data', (d) => { if (err.length < 200) err += d; });
|
|
168
|
+
child.on('error', (e) => { clearTimeout(timer); onDone(Date.now() - started, `snapshot unavailable (spawn ${e && e.code ? e.code : 'error'})`); });
|
|
169
|
+
child.on('close', (code, signal) => {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
let rows = [];
|
|
172
|
+
try { const parsed = JSON.parse(out); rows = Array.isArray(parsed) ? parsed : [parsed]; } catch { /* no rows */ }
|
|
173
|
+
// The watcher's own process and its probe/snapshot children are not what we are lining up against.
|
|
174
|
+
rows = rows.filter((r) => r.pid !== process.pid && r.ppid !== process.pid);
|
|
175
|
+
const why = signal ? `killed ${signal}` : `exit ${code}`;
|
|
176
|
+
onDone(
|
|
177
|
+
Date.now() - started,
|
|
178
|
+
rows.length ? `${rows.length} procs: ${formatProcs(rows, Date.now())}` : `snapshot unavailable (${why}${err ? `: ${err.replace(/\s+/g, ' ').trim().slice(0, 200)}` : ''})`,
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function run(dir) {
|
|
184
|
+
const eventsPath = path.join(dir, EVENTS_FILE);
|
|
185
|
+
const record = (at, kind, ms, detail) => {
|
|
186
|
+
try { fs.appendFileSync(eventsPath, `${formatEvent(at, kind, ms, detail)}\n`); } catch { /* dir gone: nothing to do */ }
|
|
187
|
+
};
|
|
188
|
+
let lastSnapshotAt = 0;
|
|
189
|
+
let snapshotting = false;
|
|
190
|
+
const takeSnapshot = (why) => {
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
if (snapshotting || now - lastSnapshotAt < SNAPSHOT_COOLDOWN_MS) return;
|
|
193
|
+
snapshotting = true;
|
|
194
|
+
lastSnapshotAt = now;
|
|
195
|
+
snapshotProcs((ms, detail) => { snapshotting = false; record(Date.now(), 'procs', ms, `${why}: ${detail}`); });
|
|
196
|
+
};
|
|
197
|
+
const snapshotTimer = setInterval(() => takeSnapshot('periodic'), SNAPSHOT_EVERY_MS);
|
|
198
|
+
const lifetimeEnd = Date.now() + MAX_LIFETIME_MS;
|
|
199
|
+
const probeMs = { sh: [], cmd: [] };
|
|
200
|
+
let probeCount = 0;
|
|
201
|
+
let last = Date.now();
|
|
202
|
+
|
|
203
|
+
const tick = setInterval(() => {
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
const late = now - last - TICK_MS;
|
|
206
|
+
if (late > LOOP_GAP_MS) { record(now, 'loop-gap', late, `timer fired ${late}ms late`); takeSnapshot('after loop-gap'); }
|
|
207
|
+
last = now;
|
|
208
|
+
if (now > lifetimeEnd || fs.existsSync(path.join(dir, STOP_FILE))) finishRun();
|
|
209
|
+
}, TICK_MS);
|
|
210
|
+
|
|
211
|
+
const probeErrors = { sh: 0, cmd: 0 };
|
|
212
|
+
const onProbe = (label, ms, timedOut, started, error) => {
|
|
213
|
+
if (error) {
|
|
214
|
+
probeErrors[label] += 1;
|
|
215
|
+
record(Date.now(), 'probe-error', ms, `${label} never ran: ${error}`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
probeCount += 1;
|
|
219
|
+
probeMs[label].push(ms);
|
|
220
|
+
if (ms > PROBE_SLOW_MS || timedOut) {
|
|
221
|
+
record(Date.now(), 'probe-slow', ms, `${label} started ${new Date(started).toISOString()}${timedOut ? ' hit the bound' : ''}`);
|
|
222
|
+
takeSnapshot('after probe-slow');
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
let probing = true;
|
|
227
|
+
let loopTimer = null;
|
|
228
|
+
// Run the two probes one after the other, then wait out the rest of the interval.
|
|
229
|
+
const loop = () => {
|
|
230
|
+
if (!probing) return;
|
|
231
|
+
const cycleStart = Date.now();
|
|
232
|
+
probe('sh', 'sh', ['-c', PIPELINE], (l, ms, to, st, er) => {
|
|
233
|
+
onProbe(l, ms, to, st, er);
|
|
234
|
+
probe('cmd', 'cmd', ['/c', 'exit', '0'], (l2, ms2, to2, st2, er2) => {
|
|
235
|
+
onProbe(l2, ms2, to2, st2, er2);
|
|
236
|
+
loopTimer = setTimeout(loop, Math.max(0, PROBE_EVERY_MS - (Date.now() - cycleStart)));
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
loop();
|
|
241
|
+
|
|
242
|
+
function finishRun() {
|
|
243
|
+
probing = false;
|
|
244
|
+
clearInterval(tick);
|
|
245
|
+
clearInterval(snapshotTimer);
|
|
246
|
+
clearTimeout(loopTimer);
|
|
247
|
+
const summary = {
|
|
248
|
+
probes: probeCount,
|
|
249
|
+
sh: { n: probeMs.sh.length, errors: probeErrors.sh, median: percentile(probeMs.sh, 0.5), p99: percentile(probeMs.sh, 0.99), max: percentile(probeMs.sh, 1) },
|
|
250
|
+
cmd: { n: probeMs.cmd.length, errors: probeErrors.cmd, median: percentile(probeMs.cmd, 0.5), p99: percentile(probeMs.cmd, 0.99), max: percentile(probeMs.cmd, 1) },
|
|
251
|
+
};
|
|
252
|
+
// Write then rename, so report never sees the file before its content has landed.
|
|
253
|
+
try {
|
|
254
|
+
const target = path.join(dir, SUMMARY_FILE);
|
|
255
|
+
fs.writeFileSync(`${target}.tmp`, JSON.stringify(summary));
|
|
256
|
+
fs.renameSync(`${target}.tmp`, target);
|
|
257
|
+
} catch { /* best effort */ }
|
|
258
|
+
// No process.exit (ADR-3889): every timer is cleared above, so the process ends when the
|
|
259
|
+
// probe or snapshot child still in flight (each bounded) has exited; report has already
|
|
260
|
+
// been released by the summary file, so nothing waits on this.
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function start(dir) {
|
|
265
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
266
|
+
const child = spawn(process.execPath, [__filename, 'run', dir], { detached: true, stdio: 'ignore', windowsHide: true });
|
|
267
|
+
child.unref();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function report(dir) {
|
|
271
|
+
try { fs.writeFileSync(path.join(dir, STOP_FILE), ''); } catch { /* start never ran */ }
|
|
272
|
+
// The stop file is only seen on the watcher's 250ms tick, so a VM stall longer than this
|
|
273
|
+
// wait (the very case under study) leaves no summary: say so below instead of "crashed".
|
|
274
|
+
const deadline = Date.now() + REPORT_WAIT_MS;
|
|
275
|
+
while (!fs.existsSync(path.join(dir, SUMMARY_FILE)) && Date.now() < deadline) {
|
|
276
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
|
|
277
|
+
}
|
|
278
|
+
let events = [];
|
|
279
|
+
try {
|
|
280
|
+
events = fs.readFileSync(path.join(dir, EVENTS_FILE), 'utf8').split('\n').map(parseEvent).filter(Boolean);
|
|
281
|
+
} catch { /* no events file: nothing recorded */ }
|
|
282
|
+
let summary = null;
|
|
283
|
+
try { summary = JSON.parse(fs.readFileSync(path.join(dir, SUMMARY_FILE), 'utf8')); } catch { /* watcher did not finish */ }
|
|
284
|
+
const lines = [
|
|
285
|
+
'### #5473 Windows spawn-stall watcher (whole test step, same VM)',
|
|
286
|
+
'',
|
|
287
|
+
summary
|
|
288
|
+
? `probes: ${summary.probes} ; sh pipeline median ${summary.sh.median} / p99 ${summary.sh.p99} / max ${summary.sh.max} ms (n=${summary.sh.n}, never ran ${summary.sh.errors}) ; cmd /c exit median ${summary.cmd.median} / p99 ${summary.cmd.p99} / max ${summary.cmd.max} ms (n=${summary.cmd.n}, never ran ${summary.cmd.errors})`
|
|
289
|
+
: `watcher summary missing after ${REPORT_WAIT_MS / 1000}s: it either crashed or was stalled past the wait (the events below show which: a stall leaves loop-gap/probe-slow lines up to the last tick)`,
|
|
290
|
+
`events: ${JSON.stringify(summarizeEvents(events))}`,
|
|
291
|
+
'',
|
|
292
|
+
...events.map((e) => `- ${e.at} ${e.kind} ${e.ms}ms ${e.detail}`),
|
|
293
|
+
];
|
|
294
|
+
const text = lines.join('\n');
|
|
295
|
+
process.stdout.write(`${text}\n`);
|
|
296
|
+
if (process.env.GITHUB_STEP_SUMMARY) {
|
|
297
|
+
try { fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `${text}\n`); } catch { /* best effort */ }
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// CONTRIBUTING forbids `${{ }}` inside a workflow `run:` block, so the workflow
|
|
302
|
+
// passes no directory: GitHub sets RUNNER_TEMP itself, and start and report agree on it here.
|
|
303
|
+
function defaultDir() {
|
|
304
|
+
return path.join(process.env.RUNNER_TEMP || require('node:os').tmpdir(), 'spawn-watcher');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function main() {
|
|
308
|
+
const [, , mode, dirArg] = process.argv;
|
|
309
|
+
if (!['start', 'run', 'report'].includes(mode)) {
|
|
310
|
+
throw new ExitError(2, 'usage: diagnose-windows-spawn-watcher.cjs start|report [dir]');
|
|
311
|
+
}
|
|
312
|
+
({ start, run, report })[mode](path.resolve(dirArg || defaultDir()));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (require.main === module) runMain(main);
|
|
316
|
+
|
|
317
|
+
module.exports = { formatEvent, formatProcs, parseEvent, summarizeEvents, percentile, LOOP_GAP_MS, PROBE_SLOW_MS };
|
|
@@ -338,12 +338,14 @@ const DOCS_GUARD_TESTS = {
|
|
|
338
338
|
// (no-pending-3212-markers.test.cjs:37-46), which includes all of docs/ —
|
|
339
339
|
// cannot be resolved to a fixed docs/ path set.
|
|
340
340
|
'tests/no-pending-3212-markers.test.cjs': ['*'],
|
|
341
|
+
'tests/pause-work-improvements.test.cjs': ['docs/zh-CN/references/git-integration.md'],
|
|
341
342
|
'tests/phase6-capability-docs.test.cjs': ['docs/how-to/develop-a-capability.md', 'docs/README.md'],
|
|
342
343
|
'tests/phase6-review-capabilities.test.cjs': [
|
|
343
344
|
'docs/reference/review-verification-capabilities.md',
|
|
344
345
|
'docs/how-to/develop-a-capability.md',
|
|
345
346
|
'docs/README.md',
|
|
346
347
|
],
|
|
348
|
+
'tests/plan-authoring-verification-gaps.test.cjs': ['docs/reference/plan-md.md'],
|
|
347
349
|
'tests/plan-checker-coupling.test.cjs': ['docs/AGENTS.md'],
|
|
348
350
|
'tests/plan-phase-drift-guard.test.cjs': [
|
|
349
351
|
'docs/CONFIGURATION.md',
|
|
@@ -38,6 +38,7 @@ const { getWiredKinds } = require('./gen-loop-host-contract.cjs');
|
|
|
38
38
|
|
|
39
39
|
// Capability validator — shared runtime-callable module extracted per ADR-1244 D2.
|
|
40
40
|
const capValidator = require('../gsd-core/bin/lib/capability-validator.cjs');
|
|
41
|
+
const { transformContentToHyphen, readCmdNames } = require('./fix-slash-commands.cjs');
|
|
41
42
|
// Destructure only what the generator's own function bodies reference directly.
|
|
42
43
|
// Everything else is re-exported from capValidator in module.exports below.
|
|
43
44
|
const {
|
|
@@ -795,7 +796,12 @@ function serializeRegistry(registry, capMap) {
|
|
|
795
796
|
lines.push('};');
|
|
796
797
|
lines.push('');
|
|
797
798
|
|
|
798
|
-
|
|
799
|
+
// #4859: capability descriptions and fragment `inline` text are compiled into this
|
|
800
|
+
// file and printed verbatim — the install-time conversion of markdown artifacts never
|
|
801
|
+
// reaches them — so the colon form `/gsd:<cmd>` (the repo-source spelling, which no
|
|
802
|
+
// install route registers) is rewritten to the hyphen form here. Same-length rewrite,
|
|
803
|
+
// restricted to known command names, so `/gsd:section` and other tokens are untouched.
|
|
804
|
+
return transformContentToHyphen(lines.join('\n'), readCmdNames());
|
|
799
805
|
}
|
|
800
806
|
|
|
801
807
|
// ─── --check diff helper ──────────────────────────────────────────────────────
|
|
@@ -85,6 +85,9 @@ const ROLE_TO_AGENT = {
|
|
|
85
85
|
checker: 'gsd-plan-checker',
|
|
86
86
|
executor: 'gsd-executor',
|
|
87
87
|
verifier: 'gsd-verifier',
|
|
88
|
+
// #4790 — the built-in source-code reviewer the code-review skill dispatches one hop below
|
|
89
|
+
// `execute:post`. Admitting the role is what lets a capability contribution name it.
|
|
90
|
+
'code-reviewer': 'gsd-code-reviewer',
|
|
88
91
|
};
|
|
89
92
|
|
|
90
93
|
// #4740 — Role → family mapping. Three families: orchestration, planning,
|
|
@@ -94,7 +97,7 @@ const ROLE_TO_AGENT = {
|
|
|
94
97
|
const ROLE_FAMILY = {
|
|
95
98
|
orchestrator: 'orchestration',
|
|
96
99
|
researcher: 'planning', planner: 'planning', checker: 'planning',
|
|
97
|
-
executor: 'execution', verifier: 'execution',
|
|
100
|
+
executor: 'execution', verifier: 'execution', 'code-reviewer': 'execution',
|
|
98
101
|
};
|
|
99
102
|
const EXPECTED_FAMILY_BY_STEP = {
|
|
100
103
|
discuss: 'orchestration',
|
|
@@ -46,6 +46,7 @@ module.exports = {
|
|
|
46
46
|
"tests/codex-config-hooks.test.cjs",
|
|
47
47
|
"tests/codex-config-install.test.cjs",
|
|
48
48
|
"tests/codex-config.test.cjs",
|
|
49
|
+
"tests/command-arguments-block.test.cjs",
|
|
49
50
|
"tests/commands.platform.test.cjs",
|
|
50
51
|
"tests/commit-docs-bypass.test.cjs",
|
|
51
52
|
"tests/commit-files-deletion.test.cjs",
|
|
@@ -71,6 +72,7 @@ module.exports = {
|
|
|
71
72
|
"tests/execute-phase-decimal-arithmetic.test.cjs",
|
|
72
73
|
"tests/executed-plan.test.cjs",
|
|
73
74
|
"tests/executor-mvp-tdd-section.test.cjs",
|
|
75
|
+
"tests/executor-sequential-linked-worktree.test.cjs",
|
|
74
76
|
"tests/external-job.test.cjs",
|
|
75
77
|
"tests/failing-direction.test.cjs",
|
|
76
78
|
"tests/fallow-runner.test.cjs",
|
|
@@ -80,6 +82,7 @@ module.exports = {
|
|
|
80
82
|
"tests/gap-checker.property.test.cjs",
|
|
81
83
|
"tests/gate-api-coverage-verify-pre-escape.test.cjs",
|
|
82
84
|
"tests/gate-codebase-drift.unit.test.cjs",
|
|
85
|
+
"tests/gate-predicate.unit.test.cjs",
|
|
83
86
|
"tests/gate-tdd-red-evidence-containment.test.cjs",
|
|
84
87
|
"tests/gate-ui-plan.unit.test.cjs",
|
|
85
88
|
"tests/gate-verify-command-paths.unit.test.cjs",
|
|
@@ -104,6 +107,7 @@ module.exports = {
|
|
|
104
107
|
"tests/hooks-crash-policy.test.cjs",
|
|
105
108
|
"tests/hooks-opt-in.test.cjs",
|
|
106
109
|
"tests/host-integration.test.cjs",
|
|
110
|
+
"tests/in-tier-commit-hook-timing.test.cjs",
|
|
107
111
|
"tests/init-manager.test.cjs",
|
|
108
112
|
"tests/install-minimal-hooks.test.cjs",
|
|
109
113
|
"tests/install-path-detection.test.cjs",
|
|
@@ -143,7 +147,11 @@ module.exports = {
|
|
|
143
147
|
"tests/nsegment-phase-grammar.test.cjs",
|
|
144
148
|
"tests/observability/event.test.cjs",
|
|
145
149
|
"tests/onboard-command.test.cjs",
|
|
150
|
+
"tests/opencode-generations.smoke.test.cjs",
|
|
151
|
+
"tests/opencode-install-notices.test.cjs",
|
|
152
|
+
"tests/opencode-parity-hooks.test.cjs",
|
|
146
153
|
"tests/opencode-plugin-adapter.test.cjs",
|
|
154
|
+
"tests/opencode-uninstall-roundtrip.test.cjs",
|
|
147
155
|
"tests/pause-work-context-detection.test.cjs",
|
|
148
156
|
"tests/pause-work-improvements.test.cjs",
|
|
149
157
|
"tests/perf-317-context-monitor-fs.test.cjs",
|
|
@@ -155,6 +163,7 @@ module.exports = {
|
|
|
155
163
|
"tests/phase-status.test.cjs",
|
|
156
164
|
"tests/phase.platform.test.cjs",
|
|
157
165
|
"tests/phase.test.cjs",
|
|
166
|
+
"tests/plan-authoring-verification-gaps.test.cjs",
|
|
158
167
|
"tests/plan-count-single-owner.test.cjs",
|
|
159
168
|
"tests/plan-document.test.cjs",
|
|
160
169
|
"tests/plan-phase-stall-detection.test.cjs",
|
|
@@ -177,6 +186,7 @@ module.exports = {
|
|
|
177
186
|
"tests/quick-batch-command-router.stdin.test.cjs",
|
|
178
187
|
"tests/quick-batch.test.cjs",
|
|
179
188
|
"tests/quick-branching.test.cjs",
|
|
189
|
+
"tests/quick-commit-boundary.test.cjs",
|
|
180
190
|
"tests/quick-research.test.cjs",
|
|
181
191
|
"tests/reapply-verify-hunks.test.cjs",
|
|
182
192
|
"tests/refactor-1390-t3-characterization.test.cjs",
|
|
@@ -184,6 +194,7 @@ module.exports = {
|
|
|
184
194
|
"tests/response-language-coverage.test.cjs",
|
|
185
195
|
"tests/retired-artifact-cleanup.test.cjs",
|
|
186
196
|
"tests/review-build-prompt-optional-sections.test.cjs",
|
|
197
|
+
"tests/review-lane-invocation.test.cjs",
|
|
187
198
|
"tests/review-lane-runner.test.cjs",
|
|
188
199
|
"tests/review-lane-windows-spawn-resolution.test.cjs",
|
|
189
200
|
"tests/review-parallel-lanes.test.cjs",
|
|
@@ -207,11 +218,13 @@ module.exports = {
|
|
|
207
218
|
"tests/shared-hooks-dir-resolution.test.cjs",
|
|
208
219
|
"tests/shell-command-projection-dispatch.test.cjs",
|
|
209
220
|
"tests/shipped-reference-cites.test.cjs",
|
|
221
|
+
"tests/skill-manifest.test.cjs",
|
|
210
222
|
"tests/spawn-liveness-banner.test.cjs",
|
|
211
223
|
"tests/state-document.test.cjs",
|
|
212
224
|
"tests/state-transition.test.cjs",
|
|
213
225
|
"tests/state.platform.test.cjs",
|
|
214
226
|
"tests/state.test.cjs",
|
|
227
|
+
"tests/tdd-mode.platform.test.cjs",
|
|
215
228
|
"tests/todos-workstream-scope.test.cjs",
|
|
216
229
|
"tests/tracer-bullet.test.cjs",
|
|
217
230
|
"tests/uat-complete-session.test.cjs",
|
|
@@ -24,6 +24,7 @@ module.exports = {
|
|
|
24
24
|
"tests/capability-ledger.test.cjs",
|
|
25
25
|
"tests/capability-lifecycle.test.cjs",
|
|
26
26
|
"tests/capability-loader.test.cjs",
|
|
27
|
+
"tests/capability-lock-lazy-self-probe-5327.test.cjs",
|
|
27
28
|
"tests/capability-lock-mkdir-failure-3987.test.cjs",
|
|
28
29
|
"tests/capability-probe-fallback.test.cjs",
|
|
29
30
|
"tests/capability-source.test.cjs",
|
|
@@ -39,6 +40,7 @@ module.exports = {
|
|
|
39
40
|
"tests/check-tdd-review-checkpoint-e2e.test.cjs",
|
|
40
41
|
"tests/check-ui-safety-gate.test.cjs",
|
|
41
42
|
"tests/check-update-config-dir.test.cjs",
|
|
43
|
+
"tests/check-update-fail-open.test.cjs",
|
|
42
44
|
"tests/chunked-planning-parallel.test.cjs",
|
|
43
45
|
"tests/ci-docs-guard-registry.test.cjs",
|
|
44
46
|
"tests/ci-rebase-check.test.cjs",
|
|
@@ -51,11 +53,14 @@ module.exports = {
|
|
|
51
53
|
"tests/code-review-pipeline-regression.test.cjs",
|
|
52
54
|
"tests/code-review-tier3-files-override-scoping.test.cjs",
|
|
53
55
|
"tests/code-review.test.cjs",
|
|
56
|
+
"tests/code-reviewer-contribution.test.cjs",
|
|
54
57
|
"tests/codex-config-agents.test.cjs",
|
|
55
58
|
"tests/codex-config-hooks.test.cjs",
|
|
56
59
|
"tests/codex-config-install.test.cjs",
|
|
57
60
|
"tests/codex-config.test.cjs",
|
|
58
61
|
"tests/codex-inherit-smoke.test.cjs",
|
|
62
|
+
"tests/codex-windows-shim-node-resolution.test.cjs",
|
|
63
|
+
"tests/command-arguments-block.test.cjs",
|
|
59
64
|
"tests/command-routing-hub.test.cjs",
|
|
60
65
|
"tests/commands.platform.test.cjs",
|
|
61
66
|
"tests/commit-files-deletion.test.cjs",
|
|
@@ -70,6 +75,7 @@ module.exports = {
|
|
|
70
75
|
"tests/config.platform.test.cjs",
|
|
71
76
|
"tests/configured-entrypoint-validation.test.cjs",
|
|
72
77
|
"tests/context-drift.test.cjs",
|
|
78
|
+
"tests/context-monitor-host-contract.test.cjs",
|
|
73
79
|
"tests/copilot-install.test.cjs",
|
|
74
80
|
"tests/copilot-upgrades.test.cjs",
|
|
75
81
|
"tests/core-utils.test.cjs",
|
|
@@ -77,6 +83,8 @@ module.exports = {
|
|
|
77
83
|
"tests/cursor-hooks.test.cjs",
|
|
78
84
|
"tests/cursor-subagent-isolation.test.cjs",
|
|
79
85
|
"tests/debug-session-management.test.cjs",
|
|
86
|
+
"tests/defaults-json-fallback.test.cjs",
|
|
87
|
+
"tests/diagnose-windows-spawn-watcher.test.cjs",
|
|
80
88
|
"tests/dispatch-identity.test.cjs",
|
|
81
89
|
"tests/dispatcher.test.cjs",
|
|
82
90
|
"tests/drift-detection.test.cjs",
|
|
@@ -89,12 +97,14 @@ module.exports = {
|
|
|
89
97
|
"tests/emitted-provenance.test.cjs",
|
|
90
98
|
"tests/ensure-runtime-build.test.cjs",
|
|
91
99
|
"tests/eslint-rules.test.cjs",
|
|
100
|
+
"tests/execute-phase-cross-block-state.test.cjs",
|
|
92
101
|
"tests/execute-phase-decimal-arithmetic.test.cjs",
|
|
93
102
|
"tests/execute-phase-worktree-guard.test.cjs",
|
|
94
103
|
"tests/execute-plan-update-codebase-map-diff-base.test.cjs",
|
|
95
104
|
"tests/execute-wave-post-gate-pipeline-e2e.test.cjs",
|
|
96
105
|
"tests/executed-plan.test.cjs",
|
|
97
106
|
"tests/executor-mvp-tdd-section.test.cjs",
|
|
107
|
+
"tests/executor-sequential-linked-worktree.test.cjs",
|
|
98
108
|
"tests/external-descriptor-confinement.test.cjs",
|
|
99
109
|
"tests/failing-direction.test.cjs",
|
|
100
110
|
"tests/fallow-runner.test.cjs",
|
|
@@ -107,6 +117,7 @@ module.exports = {
|
|
|
107
117
|
"tests/gate-consumer-exit-awareness.test.cjs",
|
|
108
118
|
"tests/gate-decision-coverage-verify-stderr.test.cjs",
|
|
109
119
|
"tests/gate-exit.test.cjs",
|
|
120
|
+
"tests/gate-predicate.unit.test.cjs",
|
|
110
121
|
"tests/gate-tdd-red-evidence-containment.test.cjs",
|
|
111
122
|
"tests/gate-tdd-review-checkpoint-hardening.test.cjs",
|
|
112
123
|
"tests/gate-tdd-review-checkpoint.unit.test.cjs",
|
|
@@ -141,6 +152,8 @@ module.exports = {
|
|
|
141
152
|
"tests/hooks-crash-policy.test.cjs",
|
|
142
153
|
"tests/hooks-opt-in.test.cjs",
|
|
143
154
|
"tests/host-integration.test.cjs",
|
|
155
|
+
"tests/in-tier-msys-fork-stress.test.cjs",
|
|
156
|
+
"tests/in-tier-sh-pipeline-timing.test.cjs",
|
|
144
157
|
"tests/init-manager.test.cjs",
|
|
145
158
|
"tests/install-minimal-hooks.test.cjs",
|
|
146
159
|
"tests/install-nested-layout.test.cjs",
|
|
@@ -176,6 +189,7 @@ module.exports = {
|
|
|
176
189
|
"tests/mcp-catalog.test.cjs",
|
|
177
190
|
"tests/milestone-archive.test.cjs",
|
|
178
191
|
"tests/milestone-window-single-owner.test.cjs",
|
|
192
|
+
"tests/milestone.test.cjs",
|
|
179
193
|
"tests/model-resolver.test.cjs",
|
|
180
194
|
"tests/mutation-workflow-base-ref.test.cjs",
|
|
181
195
|
"tests/new-milestone-clear-phases.test.cjs",
|
|
@@ -195,7 +209,13 @@ module.exports = {
|
|
|
195
209
|
"tests/nsegment-phase-grammar.test.cjs",
|
|
196
210
|
"tests/onboard-command.test.cjs",
|
|
197
211
|
"tests/opencode-command-dir-plural.test.cjs",
|
|
212
|
+
"tests/opencode-config-jsonc.test.cjs",
|
|
213
|
+
"tests/opencode-generations.smoke.test.cjs",
|
|
214
|
+
"tests/opencode-install-notices.test.cjs",
|
|
215
|
+
"tests/opencode-parity-hooks.test.cjs",
|
|
198
216
|
"tests/opencode-plugin-adapter.test.cjs",
|
|
217
|
+
"tests/opencode-statusline-plugin.test.cjs",
|
|
218
|
+
"tests/opencode-uninstall-roundtrip.test.cjs",
|
|
199
219
|
"tests/overlay-repo-helpers.test.cjs",
|
|
200
220
|
"tests/packaging-shipped-scripts-require-only-shipped.test.cjs",
|
|
201
221
|
"tests/path-replacement.test.cjs",
|
|
@@ -235,6 +255,7 @@ module.exports = {
|
|
|
235
255
|
"tests/quick-batch-command-router.stdin.test.cjs",
|
|
236
256
|
"tests/quick-batch.test.cjs",
|
|
237
257
|
"tests/quick-branching.test.cjs",
|
|
258
|
+
"tests/quick-commit-boundary.test.cjs",
|
|
238
259
|
"tests/quick-research.test.cjs",
|
|
239
260
|
"tests/quick-review-scope-tip-bound.test.cjs",
|
|
240
261
|
"tests/read-guard.test.cjs",
|
|
@@ -248,6 +269,7 @@ module.exports = {
|
|
|
248
269
|
"tests/retired-artifact-cleanup.test.cjs",
|
|
249
270
|
"tests/review-build-prompt-optional-sections.test.cjs",
|
|
250
271
|
"tests/review-default-reviewers-config.test.cjs",
|
|
272
|
+
"tests/review-lane-invocation.test.cjs",
|
|
251
273
|
"tests/review-lane-runner.test.cjs",
|
|
252
274
|
"tests/review-lane-windows-spawn-resolution.test.cjs",
|
|
253
275
|
"tests/review-model-config.test.cjs",
|
|
@@ -283,6 +305,9 @@ module.exports = {
|
|
|
283
305
|
"tests/slug-derivation-drift-guard.test.cjs",
|
|
284
306
|
"tests/state-todos-render.test.cjs",
|
|
285
307
|
"tests/state.platform.test.cjs",
|
|
308
|
+
"tests/subject-first-line.platform.test.cjs",
|
|
309
|
+
"tests/tdd-mode.platform.test.cjs",
|
|
310
|
+
"tests/tdd-review-checkpoint-cross-plan.test.cjs",
|
|
286
311
|
"tests/teams-status.test.cjs",
|
|
287
312
|
"tests/test-gate-watch-mode.test.cjs",
|
|
288
313
|
"tests/todos-workstream-scope.test.cjs",
|
|
@@ -312,5 +337,6 @@ module.exports = {
|
|
|
312
337
|
"tests/worktree-cleanup.test.cjs",
|
|
313
338
|
"tests/worktree-safety.test.cjs",
|
|
314
339
|
"tests/worktree.test.cjs",
|
|
340
|
+
"tests/xtrace-phase-timing.platform.test.cjs",
|
|
315
341
|
],
|
|
316
342
|
};
|
|
@@ -93,10 +93,10 @@ const SHELL_INFO_STRINGS = new Set(['bash', 'sh', 'shell']);
|
|
|
93
93
|
const CANONICAL_FORMS = Object.freeze({
|
|
94
94
|
/** `/gsd:secure-phase 4` — the phase is the whole argument string, minus a `--ws <name>` pair. */
|
|
95
95
|
positional:
|
|
96
|
-
'PHASE_ARG=$(echo "$
|
|
96
|
+
'PHASE_ARG=$(echo "$GSD_ARGS" | sed -E \'s/--ws[[:space:]]+[A-Za-z0-9._-]+//g\' | xargs)',
|
|
97
97
|
/** `/gsd:review --phase 4 --codex` — the phase is the value of a named flag. */
|
|
98
98
|
phaseFlag:
|
|
99
|
-
'PHASE_ARG=$(echo "$
|
|
99
|
+
'PHASE_ARG=$(echo "$GSD_ARGS" | sed -nE \'s/.*--phase[[:space:]]+([A-Za-z0-9._-]+).*/\\1/p\')',
|
|
100
100
|
/** Skill dispatch passes the phase as `$1` (`Skill(skill="gsd-code-review", args="4")`). */
|
|
101
101
|
firstPositional: 'PHASE_ARG="${1}"',
|
|
102
102
|
});
|
|
@@ -384,6 +384,21 @@
|
|
|
384
384
|
"code": "2086",
|
|
385
385
|
"message": "Double quote to prevent globbing and word splitting."
|
|
386
386
|
},
|
|
387
|
+
{
|
|
388
|
+
"file": "gsd-core/workflows/execute-phase/steps/code-review-disposition.md",
|
|
389
|
+
"code": "2317",
|
|
390
|
+
"message": "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"file": "gsd-core/workflows/execute-phase/steps/code-review-disposition.md",
|
|
394
|
+
"code": "2317",
|
|
395
|
+
"message": "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"file": "gsd-core/workflows/execute-phase/steps/code-review-disposition.md",
|
|
399
|
+
"code": "2317",
|
|
400
|
+
"message": "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
|
|
401
|
+
},
|
|
387
402
|
{
|
|
388
403
|
"file": "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md",
|
|
389
404
|
"code": "2016",
|
|
@@ -690,7 +705,7 @@
|
|
|
690
705
|
"message": "Double quote to prevent globbing and word splitting."
|
|
691
706
|
},
|
|
692
707
|
{
|
|
693
|
-
"file": "gsd-core/workflows/plan-phase.md",
|
|
708
|
+
"file": "gsd-core/workflows/plan-phase/steps/auto-advance-check.md",
|
|
694
709
|
"code": "2235",
|
|
695
710
|
"message": "Use { ..; } instead of (..) to avoid subshell overhead."
|
|
696
711
|
},
|
|
@@ -1108,20 +1123,5 @@
|
|
|
1108
1123
|
"file": "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
|
1109
1124
|
"code": "2086",
|
|
1110
1125
|
"message": "Double quote to prevent globbing and word splitting."
|
|
1111
|
-
},
|
|
1112
|
-
{
|
|
1113
|
-
"file": "gsd-core/workflows/execute-phase/steps/code-review-disposition.md",
|
|
1114
|
-
"code": "2317",
|
|
1115
|
-
"message": "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
|
|
1116
|
-
},
|
|
1117
|
-
{
|
|
1118
|
-
"file": "gsd-core/workflows/execute-phase/steps/code-review-disposition.md",
|
|
1119
|
-
"code": "2317",
|
|
1120
|
-
"message": "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
|
|
1121
|
-
},
|
|
1122
|
-
{
|
|
1123
|
-
"file": "gsd-core/workflows/execute-phase/steps/code-review-disposition.md",
|
|
1124
|
-
"code": "2317",
|
|
1125
|
-
"message": "Command appears to be unreachable. Check usage (or ignore if invoked indirectly)."
|
|
1126
1126
|
}
|
|
1127
1127
|
]
|
|
@@ -480,7 +480,9 @@ const COVERED = {
|
|
|
480
480
|
// dependency of something else under test.
|
|
481
481
|
'adr-parser': {
|
|
482
482
|
cjs: 'gsd-core/bin/lib/adr-parser.cjs',
|
|
483
|
-
|
|
483
|
+
// CI (2026-10-13, #5227 PR): measured 73.24% after the list-entry splitter tests. Floor =
|
|
484
|
+
// floor(73.24) - 1 = 72.
|
|
485
|
+
minScore: 72,
|
|
484
486
|
},
|
|
485
487
|
'config-schema': {
|
|
486
488
|
cjs: 'gsd-core/bin/lib/config-schema.cjs',
|