@opengsd/gsd-core 1.6.1 → 1.7.0-rc.2
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 +20 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +711 -0
- package/agents/gsd-advisor-researcher.md +2 -0
- package/agents/gsd-ai-researcher.md +1 -1
- package/agents/gsd-assumptions-analyzer.md +2 -0
- package/agents/gsd-code-fixer.md +2 -0
- package/agents/gsd-code-reviewer.md +2 -0
- package/agents/gsd-codebase-mapper.md +2 -0
- package/agents/gsd-debugger.md +2 -0
- package/agents/gsd-doc-writer.md +2 -0
- package/agents/gsd-eval-auditor.md +2 -0
- package/agents/gsd-executor.md +9 -6
- package/agents/gsd-integration-checker.md +2 -0
- package/agents/gsd-nyquist-auditor.md +2 -0
- package/agents/gsd-phase-researcher.md +2 -0
- package/agents/gsd-plan-checker.md +2 -0
- package/agents/gsd-planner.md +2 -0
- package/agents/gsd-project-researcher.md +2 -0
- package/agents/gsd-research-synthesizer.md +2 -0
- package/agents/gsd-roadmapper.md +2 -0
- package/agents/gsd-security-auditor.md +2 -0
- package/agents/gsd-ui-auditor.md +2 -0
- package/agents/gsd-ui-checker.md +2 -0
- package/agents/gsd-ui-researcher.md +2 -0
- package/agents/gsd-verifier.md +5 -2
- package/bin/gsd-mcp-server.js +31 -0
- package/bin/install.js +411 -1146
- package/commands/gsd/review.md +6 -0
- package/gemini-extension.json +1 -1
- package/gsd-core/bin/gsd-tools.cjs +134 -8
- package/gsd-core/bin/lib/adapter-declarative.cjs +35 -0
- package/gsd-core/bin/lib/adapter-imperative.cjs +52 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +231 -0
- package/gsd-core/bin/lib/capability-lifecycle.cjs +7 -7
- package/gsd-core/bin/lib/capability-loader.cjs +45 -9
- package/gsd-core/bin/lib/capability-lock.cjs +2 -2
- package/gsd-core/bin/lib/capability-registry.cjs +891 -82
- package/gsd-core/bin/lib/capability-source.cjs +26 -11
- package/gsd-core/bin/lib/capability-validator.cjs +222 -2
- package/gsd-core/bin/lib/cli-skew-check.cjs +44 -0
- package/gsd-core/bin/lib/command-aliases.cjs +8 -0
- package/gsd-core/bin/lib/commands.cjs +2 -1
- package/gsd-core/bin/lib/config.cjs +27 -0
- package/gsd-core/bin/lib/embedding-adapter.cjs +27 -0
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +70 -0
- package/gsd-core/bin/lib/frontmatter.cjs +53 -6
- package/gsd-core/bin/lib/handshake-serialized.cjs +70 -0
- package/gsd-core/bin/lib/hook-bus.cjs +81 -0
- package/gsd-core/bin/lib/host-integration-sdk.cjs +53 -0
- package/gsd-core/bin/lib/host-integration.cjs +469 -0
- package/gsd-core/bin/lib/init.cjs +35 -7
- package/gsd-core/bin/lib/install-engine.cjs +755 -0
- package/gsd-core/bin/lib/install-profiles.cjs +35 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +1 -1
- package/gsd-core/bin/lib/mcp-server.cjs +194 -0
- package/gsd-core/bin/lib/milestone.cjs +68 -40
- package/gsd-core/bin/lib/model-adapter.cjs +50 -0
- package/gsd-core/bin/lib/phase-id.cjs +18 -0
- package/gsd-core/bin/lib/phase.cjs +57 -90
- package/gsd-core/bin/lib/phases-command-router.cjs +4 -3
- package/gsd-core/bin/lib/planning-workspace.cjs +1 -1
- package/gsd-core/bin/lib/probe-core.cjs +132 -2
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +129 -13
- package/gsd-core/bin/lib/roadmap-command-router.cjs +3 -2
- package/gsd-core/bin/lib/roadmap-parser.cjs +21 -11
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +3 -2
- package/gsd-core/bin/lib/roadmap.cjs +33 -22
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +65 -9
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +54 -4
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +5 -2
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +1 -1
- package/gsd-core/bin/lib/runtime-name-policy.cjs +160 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +16 -0
- package/gsd-core/bin/lib/stale-bake-guard.cjs +254 -0
- package/gsd-core/bin/lib/state-command-router.cjs +4 -0
- package/gsd-core/bin/lib/state-io.cjs +55 -0
- package/gsd-core/bin/lib/state-transition.cjs +1603 -0
- package/gsd-core/bin/lib/state.cjs +327 -683
- package/gsd-core/bin/lib/surface.cjs +4 -1
- package/gsd-core/bin/lib/validate.cjs +2 -1
- package/gsd-core/bin/lib/verify.cjs +6 -4
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +12 -2
- package/gsd-core/bin/lib/workstream-inventory.cjs +28 -0
- package/gsd-core/bin/lib/workstream.cjs +4 -4
- package/gsd-core/bin/shared/config-schema.manifest.json +9 -0
- package/gsd-core/references/agent-skills-bootstrap.md +60 -0
- package/gsd-core/references/honest-verifier.md +105 -0
- package/gsd-core/references/model-profiles.md +27 -0
- package/gsd-core/references/reviewer-instances.md +99 -0
- package/gsd-core/workflows/autonomous.md +30 -32
- package/gsd-core/workflows/complete-milestone.md +6 -10
- package/gsd-core/workflows/execute-phase.md +1 -1
- package/gsd-core/workflows/forensics.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +1 -1
- package/gsd-core/workflows/manager.md +15 -15
- package/gsd-core/workflows/milestone-summary.md +3 -3
- package/gsd-core/workflows/new-milestone.md +6 -0
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +42 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +102 -0
- package/gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md +23 -0
- package/gsd-core/workflows/plan-phase.md +4 -159
- package/gsd-core/workflows/review.md +33 -2
- package/gsd-core/workflows/thread.md +4 -4
- package/gsd-core/workflows/verify-phase.md +11 -4
- package/gsd-core/workflows/verify-work.md +1 -2
- package/hooks/dist/gsd-graphify-update.sh +7 -1
- package/hooks/gsd-graphify-update.sh +7 -1
- package/package.json +6 -4
- package/scripts/ci-test-scope.cjs +38 -9
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -1
- package/scripts/lint-regression-test-names.allowlist.json +3 -0
- package/scripts/lint-test-file-count.allowlist.json +19 -5
- package/scripts/mutation-matrix.cjs +45 -3
- package/scripts/prompt-injection-scan.sh +8 -0
- package/scripts/run-tests.cjs +51 -1
- package/scripts/sync-manifest-versions.cjs +66 -14
- package/skills/gsd-review/SKILL.md +6 -0
- package/scripts/lint-windows-test-portability.cjs +0 -178
|
@@ -91,46 +91,7 @@ Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_
|
|
|
91
91
|
|
|
92
92
|
## 1.5. Closed-Phase Gate (#3569)
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
Parse `phase_status` from the init JSON, then:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
FORCE_REPLAN=false
|
|
100
|
-
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--force([[:space:]]|$) ]]; then
|
|
101
|
-
FORCE_REPLAN=true
|
|
102
|
-
fi
|
|
103
|
-
|
|
104
|
-
if [ "${phase_status}" = "Complete" ]; then
|
|
105
|
-
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--reviews([[:space:]]|$) ]]; then
|
|
106
|
-
# --reviews on a closed phase is never legitimate — concerns belong in a
|
|
107
|
-
# new phase or issue against the closed phase's commits.
|
|
108
|
-
cat <<EOF >&2
|
|
109
|
-
Phase ${phase_number} (${phase_name}) is already CLOSED (VERIFICATION status: passed).
|
|
110
|
-
/gsd:plan-phase --reviews cannot replan a closed phase. If the review surfaced
|
|
111
|
-
real concerns, open a follow-up phase or file an issue against the closed
|
|
112
|
-
phase's commits. There is no --force override for --reviews on a closed phase.
|
|
113
|
-
EOF
|
|
114
|
-
exit 1
|
|
115
|
-
fi
|
|
116
|
-
if [ "$FORCE_REPLAN" != "true" ]; then
|
|
117
|
-
cat <<EOF >&2
|
|
118
|
-
Phase ${phase_number} (${phase_name}) is already CLOSED (VERIFICATION status: passed).
|
|
119
|
-
Replanning a closed phase will overwrite plan docs that no longer match the
|
|
120
|
-
shipped code. If you intentionally want to replan over closed work, re-run
|
|
121
|
-
with: /gsd:plan-phase ${phase_number} --force
|
|
122
|
-
|
|
123
|
-
Otherwise, to view what shipped, see: ${verification_path}
|
|
124
|
-
EOF
|
|
125
|
-
exit 1
|
|
126
|
-
fi
|
|
127
|
-
# FORCE_REPLAN=true: continue, but emit a banner so the operator sees the
|
|
128
|
-
# decision in the transcript and in any committed plan docs.
|
|
129
|
-
echo "WARNING: Replanning CLOSED phase ${phase_number} under --force. Verify the closeout was wrong before committing new plan docs." >&2
|
|
130
|
-
fi
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
The gate fires only on `Complete`. `Executed` and `Needs Review` are not gated — those states mean planning was finished but verification did not pass, and replanning is a legitimate next step.
|
|
94
|
+
Read and execute `gsd-core/workflows/plan-phase/steps/closed-phase-gate.md` — it parses `phase_status` from the init JSON, sets `FORCE_REPLAN` from `$ARGUMENTS`, and hard-stops replanning a `Complete` phase: `--reviews` on a closed phase is never overridable (exit 1), and replanning otherwise requires `--force` (else exit 1, pointing at `${verification_path}`); under `--force` it continues but emits a WARNING banner. Only `Complete` is gated — `Executed` / `Needs Review` are legitimate replans.
|
|
134
95
|
|
|
135
96
|
## 2. Parse and Normalize Arguments
|
|
136
97
|
|
|
@@ -249,103 +210,7 @@ MVP_MODE=$(gsd_run query phase.mvp-mode "${PHASE}" $MVP_FLAG_ARG --pick active)
|
|
|
249
210
|
|
|
250
211
|
**If `--prd <filepath>` provided:**
|
|
251
212
|
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
PRD_CONTENT=$(cat "$PRD_FILE" 2>/dev/null)
|
|
255
|
-
if [ -z "$PRD_CONTENT" ]; then
|
|
256
|
-
echo "Error: PRD file not found: $PRD_FILE"
|
|
257
|
-
exit 1
|
|
258
|
-
fi
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
2. Display banner:
|
|
262
|
-
```
|
|
263
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
264
|
-
GSD ► PRD EXPRESS PATH
|
|
265
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
266
|
-
|
|
267
|
-
Using PRD: {PRD_FILE}
|
|
268
|
-
Generating CONTEXT.md from requirements...
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
3. Parse the PRD content and generate CONTEXT.md. The orchestrator should:
|
|
272
|
-
- Extract all requirements, user stories, acceptance criteria, and constraints from the PRD
|
|
273
|
-
- Map each to a locked decision (everything in the PRD is treated as a locked decision)
|
|
274
|
-
- Identify any areas the PRD doesn't cover and mark as "Claude's Discretion"
|
|
275
|
-
- **Extract canonical refs** from ROADMAP.md for this phase, plus any specs/ADRs referenced in the PRD — expand to full file paths (MANDATORY)
|
|
276
|
-
- Create CONTEXT.md in the phase directory
|
|
277
|
-
|
|
278
|
-
4. Write CONTEXT.md:
|
|
279
|
-
```markdown
|
|
280
|
-
# Phase [X]: [Name] - Context
|
|
281
|
-
|
|
282
|
-
**Gathered:** [date]
|
|
283
|
-
**Status:** Ready for planning
|
|
284
|
-
**Source:** PRD Express Path ({PRD_FILE})
|
|
285
|
-
|
|
286
|
-
<domain>
|
|
287
|
-
## Phase Boundary
|
|
288
|
-
|
|
289
|
-
[Extracted from PRD — what this phase delivers]
|
|
290
|
-
|
|
291
|
-
</domain>
|
|
292
|
-
|
|
293
|
-
<decisions>
|
|
294
|
-
## Implementation Decisions
|
|
295
|
-
|
|
296
|
-
{For each requirement/story/criterion in the PRD:}
|
|
297
|
-
### [Category derived from content]
|
|
298
|
-
- [Requirement as locked decision]
|
|
299
|
-
|
|
300
|
-
### Claude's Discretion
|
|
301
|
-
[Areas not covered by PRD — implementation details, technical choices]
|
|
302
|
-
|
|
303
|
-
</decisions>
|
|
304
|
-
|
|
305
|
-
<canonical_refs>
|
|
306
|
-
## Canonical References
|
|
307
|
-
|
|
308
|
-
**Downstream agents MUST read these before planning or implementing.**
|
|
309
|
-
|
|
310
|
-
[MANDATORY. Extract from ROADMAP.md and any docs referenced in the PRD.
|
|
311
|
-
Use full relative paths. Group by topic area.]
|
|
312
|
-
|
|
313
|
-
### [Topic area]
|
|
314
|
-
- `path/to/spec-or-adr.md` — [What it decides/defines]
|
|
315
|
-
|
|
316
|
-
[If no external specs: "No external specs — requirements fully captured in decisions above"]
|
|
317
|
-
|
|
318
|
-
</canonical_refs>
|
|
319
|
-
|
|
320
|
-
<specifics>
|
|
321
|
-
## Specific Ideas
|
|
322
|
-
|
|
323
|
-
[Any specific references, examples, or concrete requirements from PRD]
|
|
324
|
-
|
|
325
|
-
</specifics>
|
|
326
|
-
|
|
327
|
-
<deferred>
|
|
328
|
-
## Deferred Ideas
|
|
329
|
-
|
|
330
|
-
[Items in PRD explicitly marked as future/v2/out-of-scope]
|
|
331
|
-
[If none: "None — PRD covers phase scope"]
|
|
332
|
-
|
|
333
|
-
</deferred>
|
|
334
|
-
|
|
335
|
-
---
|
|
336
|
-
|
|
337
|
-
*Phase: XX-name*
|
|
338
|
-
*Context gathered: [date] via PRD Express Path*
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
5. Commit:
|
|
342
|
-
```bash
|
|
343
|
-
gsd_run query commit "docs(${padded_phase}): generate context from PRD" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
6. Set `context_content` to the generated CONTEXT.md content and continue to step 5 (Handle Research).
|
|
347
|
-
|
|
348
|
-
**Effect:** This completely bypasses step 4 (Load CONTEXT.md) since we just created it. The rest of the workflow (research, planning, verification) proceeds normally with the PRD-derived context.
|
|
213
|
+
Read and execute `gsd-core/workflows/plan-phase/steps/prd-express-path.md` — it reads the PRD (`$PRD_FILE`), generates `CONTEXT.md` (every PRD requirement/story/criterion → locked decision, uncovered areas → "Claude's Discretion", canonical refs extracted from ROADMAP.md + PRD-referenced specs), commits it, sets `context_content`, and bypasses step 4 (Load CONTEXT.md). The rest of the workflow proceeds normally with the PRD-derived context.
|
|
349
214
|
|
|
350
215
|
## 3.6. Handle ADR Ingest Express Path
|
|
351
216
|
|
|
@@ -912,7 +777,7 @@ Output consumed by /gsd:execute-phase. Plans need:
|
|
|
912
777
|
- Tasks in XML format with read_first and acceptance_criteria fields (MANDATORY on every task)
|
|
913
778
|
- Verification criteria
|
|
914
779
|
- must_haves for goal-backward verification
|
|
915
|
-
- If the SPEC has an `## Edge Coverage` section, lift every `covered` edge's acceptance criterion into `must_haves.truths
|
|
780
|
+
- If the SPEC has an `## Edge Coverage` section, lift every `covered` edge's acceptance criterion into `must_haves.truths` as a plain string, and every `backstop` edge **as a structured flat-scalar marker** — an object item `{ statement: <the check>, verification: backstop }`, NOT a prose note (the verifier branches deterministically on the `verification: backstop` field; a parenthetical is unparseable — the #1110 fragility). Use a flat scalar `verification:` continuation key, never a nested object (ADR-550 #1278). At verify time a `backstop` truth the verifier cannot confirm with explicit evidence abstains → `human_needed` (reason `insufficient_spec`), never a silent pass (#1154; see `references/honest-verifier.md`). `unresolved` edges are explicit assumptions — surface them in the plan, do not silently drop them.
|
|
916
781
|
- If the SPEC has a `## Prohibitions` section, lift every resolved prohibition into the `must_haves.prohibitions:` sibling block (NOT `truths` — ADR-550 D3) carrying `statement` + `status` + `verification`; unresolved prohibitions are explicit assumptions — surface them in the plan, do not silently drop them. A prohibition is a must-NOT (negative) check that belongs in its own `must_haves.prohibitions` block. Never place a must-NOT under `must_haves.truths` — that block keeps positive-observable semantics only.
|
|
917
782
|
- **"Artifacts this phase produces" section (MANDATORY)** — list every symbol this phase creates: decorators, classes, functions, CLI flags, struct/dataclass fields, new file paths. The plan-review-convergence source-grounding pass reads this section to exclude newly-created symbols from drift verification; omitting it causes new symbols to be flagged for acknowledgement.
|
|
918
783
|
</downstream_consumer>
|
|
@@ -1730,27 +1595,7 @@ Verification: {Passed | Passed with override | Skipped}
|
|
|
1730
1595
|
</offer_next>
|
|
1731
1596
|
|
|
1732
1597
|
<windows_troubleshooting>
|
|
1733
|
-
|
|
1734
|
-
stdio deadlocks with MCP servers — see Claude Code issue anthropics/claude-code#28126):
|
|
1735
|
-
|
|
1736
|
-
1. **Force-kill:** Close the terminal (Ctrl+C may not work)
|
|
1737
|
-
2. **Clean up orphaned processes:**
|
|
1738
|
-
```powershell
|
|
1739
|
-
# Kill orphaned node processes from stale MCP servers
|
|
1740
|
-
Get-Process node -ErrorAction SilentlyContinue | Where-Object {$_.StartTime -lt (Get-Date).AddHours(-1)} | Stop-Process -Force
|
|
1741
|
-
```
|
|
1742
|
-
3. **Clean up stale task directories:**
|
|
1743
|
-
```powershell
|
|
1744
|
-
# Remove stale subagent task dirs (Claude Code never cleans these on crash)
|
|
1745
|
-
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\tasks\*" -ErrorAction SilentlyContinue
|
|
1746
|
-
```
|
|
1747
|
-
4. **Reduce MCP server count:** Temporarily disable non-essential MCP servers in settings.json
|
|
1748
|
-
5. **Retry:** Restart Claude Code and run `/gsd:plan-phase` again
|
|
1749
|
-
|
|
1750
|
-
If freezes persist, try `--skip-research` to reduce the agent chain from 3 to 2 agents:
|
|
1751
|
-
```
|
|
1752
|
-
/gsd:plan-phase N --skip-research
|
|
1753
|
-
```
|
|
1598
|
+
Read `gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md` if plan-phase freezes on Windows during agent spawning (stdio deadlocks with MCP servers, anthropics/claude-code#28126) — it covers force-kill, orphaned-node cleanup, stale task-dir cleanup, reducing the MCP server count, and the `--skip-research` fallback.
|
|
1754
1599
|
</windows_troubleshooting>
|
|
1755
1600
|
|
|
1756
1601
|
<success_criteria>
|
|
@@ -66,6 +66,11 @@ Reviewer-selection precedence:
|
|
|
66
66
|
- Known-but-undetected slugs emit an info note and are ignored
|
|
67
67
|
- If all configured reviewers are unavailable, fail with an actionable message
|
|
68
68
|
|
|
69
|
+
**Reviewer instances (#1517, optional):** if `review.reviewer_instances` is configured,
|
|
70
|
+
instance names in `review.default_reviewers` run as independent identities. Resolution rules
|
|
71
|
+
are in `gsd-core/references/reviewer-instances.md` — load it lazily only when instances are
|
|
72
|
+
configured. Unconfigured → default path unchanged.
|
|
73
|
+
|
|
69
74
|
If no CLIs are available:
|
|
70
75
|
```
|
|
71
76
|
No external AI CLIs found. Install at least one:
|
|
@@ -243,6 +248,10 @@ else
|
|
|
243
248
|
fi
|
|
244
249
|
```
|
|
245
250
|
|
|
251
|
+
**Reviewer instances (#1517, optional):** when instances are configured, each selected
|
|
252
|
+
instance invokes its base `cli` with its own `model`/`agent` (opaque argv, never
|
|
253
|
+
shell-interpolated). Exact invocation in `gsd-core/references/reviewer-instances.md`.
|
|
254
|
+
|
|
246
255
|
For each selected CLI, invoke in sequence (not parallel — avoid rate limits):
|
|
247
256
|
|
|
248
257
|
**Gemini:**
|
|
@@ -268,10 +277,15 @@ fi
|
|
|
268
277
|
# $CODEX_BYPASS_FLAG is capability-gated above (#1115). Capture stderr to a .err
|
|
269
278
|
# file (not /dev/null) so a non-zero exit — e.g. a flag the installed codex-cli
|
|
270
279
|
# does not support — is diagnosable instead of a silent empty review.
|
|
280
|
+
# Capture the review via codex's own `-o/--output-last-message <FILE>` (only the
|
|
281
|
+
# final agent message) and discard stdout (#1698): on some platforms (Windows)
|
|
282
|
+
# codex writes process-teardown output to stdout *after* the final message, and a
|
|
283
|
+
# stdout redirect would append that noise to a non-empty file — slipping past the
|
|
284
|
+
# `[ ! -s … ]` empty-output guard as a silently polluted review.
|
|
271
285
|
if [ -n "$CODEX_MODEL" ] && [ "$CODEX_MODEL" != "null" ]; then
|
|
272
|
-
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --model "$CODEX_MODEL" --skip-git-repo-check -
|
|
286
|
+
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --model "$CODEX_MODEL" --skip-git-repo-check -o /tmp/gsd-review-codex-{phase}.md - 2>/tmp/gsd-review-codex-{phase}.err >/dev/null
|
|
273
287
|
else
|
|
274
|
-
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --skip-git-repo-check -
|
|
288
|
+
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --skip-git-repo-check -o /tmp/gsd-review-codex-{phase}.md - 2>/tmp/gsd-review-codex-{phase}.err >/dev/null
|
|
275
289
|
fi
|
|
276
290
|
if [ ! -s /tmp/gsd-review-codex-{phase}.md ]; then
|
|
277
291
|
echo "Codex review failed or returned empty output. stderr:" > /tmp/gsd-review-codex-{phase}.md
|
|
@@ -634,6 +648,11 @@ Combine all review responses into `{phase_dir}/{padded_phase}-REVIEWS.md`:
|
|
|
634
648
|
|
|
635
649
|
After all reviewers complete, collect trim metadata files written during the run. For each reviewer that was trimmed (i.e. a `.metadata.json` file exists and `hardFailed` or `omitted` is non-empty, or `projectMdShrunk` is true, or `planTruncationPct > 0`), include a `trimmed_reviewers` block in the frontmatter. Omit the key entirely if no reviewer was trimmed.
|
|
636
650
|
|
|
651
|
+
**Reviewer instances (#1517, optional):** when instances ran, frontmatter records their
|
|
652
|
+
names, each gets its own `## <Adapter> Review (<instance>)` section, and ≥2 same-cli
|
|
653
|
+
instances print a one-line shared-adapter caveat. Format in
|
|
654
|
+
`gsd-core/references/reviewer-instances.md`.
|
|
655
|
+
|
|
637
656
|
```markdown
|
|
638
657
|
---
|
|
639
658
|
phase: {N}
|
|
@@ -684,6 +703,18 @@ trimmed_reviewers: # only present if at least one reviewer was trimmed
|
|
|
684
703
|
|
|
685
704
|
---
|
|
686
705
|
|
|
706
|
+
## OpenCode Review (opencode-deepseek)
|
|
707
|
+
|
|
708
|
+
{opencode-deepseek instance review content — only present when this instance was selected}
|
|
709
|
+
|
|
710
|
+
---
|
|
711
|
+
|
|
712
|
+
## OpenCode Review (opencode-mimo)
|
|
713
|
+
|
|
714
|
+
{opencode-mimo instance review content — only present when this instance was selected}
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
687
718
|
## Qwen Review
|
|
688
719
|
|
|
689
720
|
{qwen review content}
|
|
@@ -68,8 +68,8 @@ When SUBCMD=close and SLUG is set (already sanitized):
|
|
|
68
68
|
|
|
69
69
|
2. Update the thread file's frontmatter `status` field to `resolved` and `updated` to today's ISO date:
|
|
70
70
|
```bash
|
|
71
|
-
gsd_run query frontmatter.set .planning/threads/{SLUG}.md status resolved
|
|
72
|
-
gsd_run query frontmatter.set .planning/threads/{SLUG}.md updated YYYY-MM-DD
|
|
71
|
+
gsd_run query frontmatter.set .planning/threads/{SLUG}.md --field status --value resolved
|
|
72
|
+
gsd_run query frontmatter.set .planning/threads/{SLUG}.md --field updated --value YYYY-MM-DD
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
3. Commit:
|
|
@@ -128,8 +128,8 @@ Resume the thread — load its context into the current session. Read the file c
|
|
|
128
128
|
|
|
129
129
|
Update the thread's frontmatter `status` to `in_progress` if it was `open`:
|
|
130
130
|
```bash
|
|
131
|
-
gsd_run query frontmatter.set .planning/threads/{SLUG}.md status in_progress
|
|
132
|
-
gsd_run query frontmatter.set .planning/threads/{SLUG}.md updated YYYY-MM-DD
|
|
131
|
+
gsd_run query frontmatter.set .planning/threads/{SLUG}.md --field status --value in_progress
|
|
132
|
+
gsd_run query frontmatter.set .planning/threads/{SLUG}.md --field updated --value YYYY-MM-DD
|
|
133
133
|
```
|
|
134
134
|
|
|
135
135
|
Thread content is displayed as plain text only — never executed or passed to agent prompts without DATA_START/DATA_END markers.
|
|
@@ -117,6 +117,8 @@ For each truth: identify supporting artifacts → check artifact status → chec
|
|
|
117
117
|
|
|
118
118
|
**Behavior-dependent truths:** when a truth asserts a state transition or a cancellation/cleanup/ordering invariant, symbol presence + wiring is necessary but not sufficient — the code can be present and wired yet still leak state on the path the invariant covers. Mark such a truth ✓ VERIFIED only when a pre-existing test exercises the transition/invariant and passes (one named test, never the full suite); otherwise mark it ⚠️ PRESENT_BEHAVIOR_UNVERIFIED, emit a human-verification item, and exclude it from the verified score.
|
|
119
119
|
|
|
120
|
+
**Non-inferable (`backstop`) truths (#1154):** a `must_haves.truths` item in object form `{ statement, verification: backstop }` is non-inferable — the correct behavior is not derivable from the spec alone, so the verifier cannot self-detect the gap and would false-pass it confidently. Branch on the `verification: backstop` field (read via `truthVerification()`, never prose): if confirmable with **explicit evidence** (a passing wired held-out/property test, or a directly-observed behavior) → ✓ VERIFIED; otherwise **abstain** — mark ⚠️ `insufficient_spec`, emit an `unverified — held-out test recommended` human-verification item, exclude from the verified score (routes to `human_needed`). Exogenous only (never a self-judged "abstain if unsure"); an inferable truth is never abstained. See `references/honest-verifier.md`.
|
|
121
|
+
|
|
120
122
|
**Example:** Truth "User can see existing messages" depends on Chat.tsx (renders), /api/chat GET (provides), Message model (schema). If Chat.tsx is a stub or API returns hardcoded [] → FAILED. If all exist, are substantive, and connected → VERIFIED.
|
|
121
123
|
</step>
|
|
122
124
|
|
|
@@ -488,17 +490,22 @@ Classify status using this decision tree IN ORDER (most restrictive first):
|
|
|
488
490
|
- **judgment-tier, autonomous run** (non-authoritative LLM-judge verdict): emit the `unverified-prohibition — human review recommended` flag and classify → **human_needed** (autonomous completion reads "complete with N flagged prohibitions"; never a silent pass, never a hard halt).
|
|
489
491
|
- **judgment-tier, interactive run**: route to the end-of-phase human checkpoint → **human_needed**.
|
|
490
492
|
|
|
491
|
-
|
|
493
|
+
2b. IF any `must_haves.truths` item carries the `verification: backstop` marker (#1154 — the verify-time truth-axis mirror of ADR-550 D4) AND the verifier cannot confirm it with **explicit evidence** (a wired held-out/property-based test that PASSES, or a directly-observed behavior — i.e. `dispositionForUnverifiableTruth()` returns `status: 'unverified'`, `flagged: true`, `reason: 'insufficient_spec'`):
|
|
494
|
+
- **abstain → human_needed**, NEVER `passed` and never silently graded green. Emit a prominent `unverified — held-out test recommended` flag carrying the distinguishable `reason: insufficient_spec` (so it is not conflated with ordinary manual-UAT `human_needed`).
|
|
495
|
+
- *Autonomous run:* record it and continue — completion reads "complete with N unverified non-inferable checks"; never a hard halt of an AFK run. *Interactive run:* route to the end-of-phase human checkpoint.
|
|
496
|
+
- **Exogenous only:** abstention fires SOLELY on the `backstop` tag, never a self-judged "abstain if unsure" (N17). An **inferable** truth is NEVER abstained (over-abstention guard); a `backstop` truth WITH a passing wired held-out test reaches **passed**. Reliable on capable tiers (`sonnet`+); the budget `haiku` tier degrades — see `references/honest-verifier.md`.
|
|
497
|
+
|
|
498
|
+
3. IF the previous step produced ANY human verification items — this includes every ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truth and every abstained `insufficient_spec` backstop truth:
|
|
492
499
|
→ **human_needed** (even if all other truths VERIFIED)
|
|
493
500
|
|
|
494
|
-
4. IF all checks pass AND no human verification items AND no flagged prohibitions:
|
|
501
|
+
4. IF all checks pass AND no human verification items AND no flagged prohibitions AND no abstained (`insufficient_spec`) truths:
|
|
495
502
|
→ **passed**
|
|
496
503
|
|
|
497
|
-
**passed is ONLY valid when no human verification items
|
|
504
|
+
**passed is ONLY valid when no human verification items, no flagged prohibitions, AND no abstained `insufficient_spec` truths exist.** Neither a prohibition (must-NOT) nor an unconfirmable non-inferable truth can ever be silently absorbed into a `passed` verdict — that is the core failure mode ADR-550 D4 forbids (now closed on both the prohibition and truth axes).
|
|
498
505
|
|
|
499
506
|
A ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truth is never FAILED and never VERIFIED: it does not trigger gaps_found (the code is present and wired) and is not counted as verified (its runtime behavior was not exercised). It routes through the existing human_needed sink — no new overall status.
|
|
500
507
|
|
|
501
|
-
**Score:** `verified_truths / total_truths` — `verified_truths` counts ✓ VERIFIED truths plus PASSED (override) truths; ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truths are
|
|
508
|
+
**Score:** `verified_truths / total_truths` — `verified_truths` counts ✓ VERIFIED truths plus PASSED (override) truths; excluded are ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truths (the `behavior_unverified` count) and abstained ⚠️ `insufficient_spec` backstop truths (#1154) — both are not ✓ VERIFIED and both route to `human_needed`. A headline N/N therefore certifies behavioral evidence for every behavior-dependent truth and explicit evidence for every non-inferable one, not merely symbol presence.
|
|
502
509
|
</step>
|
|
503
510
|
|
|
504
511
|
<step name="filter_deferred_items">
|
|
@@ -417,6 +417,7 @@ If no more tests → Go to `complete_session`
|
|
|
417
417
|
Read the full UAT file.
|
|
418
418
|
|
|
419
419
|
Find first test with `result: [pending]`.
|
|
420
|
+
If no `[pending]` test found → go to `complete_session`.
|
|
420
421
|
|
|
421
422
|
Announce:
|
|
422
423
|
```
|
|
@@ -514,8 +515,6 @@ Resolve the security review failure before advancing to the next phase.
|
|
|
514
515
|
All tests passed, but phase advancement is blocked until security review produces SECURITY.md.
|
|
515
516
|
|
|
516
517
|
- `/gsd:secure-phase {phase}` — security review (required before advancing)
|
|
517
|
-
- `/gsd:plan-phase {next}` — Plan next phase
|
|
518
|
-
- `/gsd:execute-phase {next}` — Execute next phase
|
|
519
518
|
- `/gsd:ui-review {phase}` — visual quality audit (if frontend files were modified)
|
|
520
519
|
```
|
|
521
520
|
|
|
@@ -45,7 +45,13 @@ process.stdin.on("end", () => {
|
|
|
45
45
|
});
|
|
46
46
|
' 2>/dev/null || printf '\n')
|
|
47
47
|
TOOL_NAME=$(printf '%s\n' "$TOOL_INFO" | sed -n '1p')
|
|
48
|
-
|
|
48
|
+
# Capture the FULL command (line 2 through EOF). Agent runtimes routinely emit
|
|
49
|
+
# HEAD-advancing commits as multi-line scripts (`cd /path` then `git add` then
|
|
50
|
+
# `git commit …`); reading only line 2 (`sed -n '2p'`) missed a `git commit`
|
|
51
|
+
# that was not on the first command line and silently no-op'd the rebuild
|
|
52
|
+
# (#1772). Line 2..EOF preserves embedded newlines; the `case` glob below
|
|
53
|
+
# matches the substring anywhere in the multi-line string.
|
|
54
|
+
COMMAND=$(printf '%s\n' "$TOOL_INFO" | sed -n '2,$p')
|
|
49
55
|
|
|
50
56
|
[ "$TOOL_NAME" = "Bash" ] || exit 0
|
|
51
57
|
|
|
@@ -45,7 +45,13 @@ process.stdin.on("end", () => {
|
|
|
45
45
|
});
|
|
46
46
|
' 2>/dev/null || printf '\n')
|
|
47
47
|
TOOL_NAME=$(printf '%s\n' "$TOOL_INFO" | sed -n '1p')
|
|
48
|
-
|
|
48
|
+
# Capture the FULL command (line 2 through EOF). Agent runtimes routinely emit
|
|
49
|
+
# HEAD-advancing commits as multi-line scripts (`cd /path` then `git add` then
|
|
50
|
+
# `git commit …`); reading only line 2 (`sed -n '2p'`) missed a `git commit`
|
|
51
|
+
# that was not on the first command line and silently no-op'd the rebuild
|
|
52
|
+
# (#1772). Line 2..EOF preserves embedded newlines; the `case` glob below
|
|
53
|
+
# matches the substring anywhere in the multi-line string.
|
|
54
|
+
COMMAND=$(printf '%s\n' "$TOOL_INFO" | sed -n '2,$p')
|
|
49
55
|
|
|
50
56
|
[ "$TOOL_NAME" = "Bash" ] || exit 0
|
|
51
57
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengsd/gsd-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-rc.2",
|
|
4
4
|
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
|
|
5
|
+
"main": ".opencode/plugins/gsd-core.js",
|
|
5
6
|
"bin": {
|
|
6
7
|
"gsd-core": "bin/install.js",
|
|
7
8
|
"gsd-tools": "gsd-core/bin/gsd-tools.cjs",
|
|
8
|
-
"gsd_run": "gsd-core/bin/gsd_run"
|
|
9
|
+
"gsd_run": "gsd-core/bin/gsd_run",
|
|
10
|
+
"gsd-mcp-server": "bin/gsd-mcp-server.js"
|
|
9
11
|
},
|
|
10
12
|
"files": [
|
|
11
13
|
"bin",
|
|
@@ -15,6 +17,7 @@
|
|
|
15
17
|
"assets",
|
|
16
18
|
"agents",
|
|
17
19
|
".claude-plugin",
|
|
20
|
+
".opencode",
|
|
18
21
|
"gemini-extension.json",
|
|
19
22
|
"GEMINI.md",
|
|
20
23
|
"hooks",
|
|
@@ -94,9 +97,8 @@
|
|
|
94
97
|
"pretest:coverage": "npm run build:lib && npm run lint:skill-deps",
|
|
95
98
|
"lint": "eslint . --cache --cache-location node_modules/.cache/eslint/",
|
|
96
99
|
"lint:fix": "eslint . --fix",
|
|
97
|
-
"lint:ci": "npm run lint && npm run lint:skill-deps && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-
|
|
100
|
+
"lint:ci": "npm run lint && npm run lint:skill-deps && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs",
|
|
98
101
|
"lint:allow-test-rule-refs": "node scripts/lint-allow-test-rule-refs.cjs",
|
|
99
|
-
"lint:windows-test-portability": "node scripts/lint-windows-test-portability.cjs",
|
|
100
102
|
"lint:regression-names": "node scripts/lint-regression-test-names.cjs",
|
|
101
103
|
"lint:descriptions": "node scripts/lint-descriptions.cjs",
|
|
102
104
|
"lint:skill-deps": "node scripts/lint-skill-deps.cjs",
|
|
@@ -118,6 +118,7 @@ const RULES = [
|
|
|
118
118
|
tests: [
|
|
119
119
|
'tests/semver-compare.test.cjs',
|
|
120
120
|
'tests/bug-10-semver-policy-consolidation.test.cjs',
|
|
121
|
+
'tests/golden-install-parity.test.cjs', // any src/installer change can alter emitted install artifacts → re-verify golden install parity (drift guard)
|
|
121
122
|
],
|
|
122
123
|
},
|
|
123
124
|
{
|
|
@@ -134,6 +135,7 @@ const RULES = [
|
|
|
134
135
|
'tests/install-path-detection.test.cjs',
|
|
135
136
|
'tests/release-tarball-smoke.install.test.cjs',
|
|
136
137
|
'tests/runtime-artifact-layout.test.cjs',
|
|
138
|
+
'tests/golden-install-parity.test.cjs', // any src/installer change can alter emitted install artifacts → re-verify golden install parity (drift guard)
|
|
137
139
|
],
|
|
138
140
|
},
|
|
139
141
|
{
|
|
@@ -212,17 +214,44 @@ const RULES = [
|
|
|
212
214
|
],
|
|
213
215
|
},
|
|
214
216
|
{
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
name: 'configuration',
|
|
218
|
+
match: path => ['config', 'configuration', 'model-catalog', 'model-profile'].some(k => path.includes(k)),
|
|
219
|
+
tests: [
|
|
220
|
+
'tests/config.test.cjs',
|
|
221
|
+
'tests/config-get-default.test.cjs',
|
|
222
|
+
'tests/configuration-migrate-config.test.cjs',
|
|
223
|
+
'tests/model-catalog-runtime-defaults.test.cjs',
|
|
224
|
+
'tests/model-profiles.test.cjs',
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
// ADR-1703 portability lint surface. Editing a rule, the shared vocab/guard
|
|
229
|
+
// helpers, or the eslint config that wires them must re-run the rule suites
|
|
230
|
+
// + the disable-ban. The disable-ban also scans bin/install.js and
|
|
231
|
+
// scripts/build-hooks.js (the Phase 6 glob-expansion surface), so changes
|
|
232
|
+
// to those files re-run it too.
|
|
233
|
+
name: 'portability lint rules (ADR-1703)',
|
|
234
|
+
match: path => path.startsWith('eslint-rules/') ||
|
|
235
|
+
path === 'eslint.config.mjs' ||
|
|
236
|
+
path === 'bin/install.js' ||
|
|
237
|
+
path === 'scripts/build-hooks.js',
|
|
217
238
|
tests: [
|
|
218
|
-
'tests/
|
|
219
|
-
'tests/
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
'tests/
|
|
239
|
+
'tests/portability-rule-disable-ban.test.cjs',
|
|
240
|
+
'tests/portability-vocab-drift.test.cjs',
|
|
241
|
+
// All nine RuleTester suites (P1–P6) — editing any rule / the shared
|
|
242
|
+
// vocab+guard helpers / the eslint config re-runs the full rule family.
|
|
243
|
+
'tests/no-path-literal-in-assert.rule.test.cjs',
|
|
244
|
+
'tests/no-posix-mode-bit-assert.rule.test.cjs',
|
|
245
|
+
'tests/no-unguarded-nonportable-exec.rule.test.cjs',
|
|
246
|
+
'tests/no-crlf-fragile-split.rule.test.cjs',
|
|
247
|
+
'tests/no-hardcoded-tmp.rule.test.cjs',
|
|
248
|
+
'tests/no-bare-npm-exec.rule.test.cjs',
|
|
249
|
+
'tests/require-userprofile-with-home.rule.test.cjs',
|
|
250
|
+
'tests/normalize-path-in-content.rule.test.cjs',
|
|
251
|
+
'tests/require-fs-op-fallback.rule.test.cjs',
|
|
223
252
|
],
|
|
224
253
|
},
|
|
225
|
-
];
|
|
254
|
+
];
|
|
226
255
|
|
|
227
256
|
function usage() {
|
|
228
257
|
return [
|
|
@@ -329,7 +358,7 @@ function classify(files) {
|
|
|
329
358
|
// Determine if this file is product/pipeline code.
|
|
330
359
|
// docs/ and root-level .md files are intentionally excluded.
|
|
331
360
|
if (
|
|
332
|
-
['bin/', 'src/', 'gsd-core/', 'agents/', 'commands/', 'hooks/', 'tests/', 'scripts/'].some(p => file.startsWith(p)) ||
|
|
361
|
+
['bin/', 'src/', 'gsd-core/', 'agents/', 'commands/', 'hooks/', 'tests/', 'scripts/', 'eslint-rules/'].some(p => file.startsWith(p)) ||
|
|
333
362
|
file === 'package.json' || file === 'package-lock.json' ||
|
|
334
363
|
(file.startsWith('tsconfig') && file.endsWith('.json')) ||
|
|
335
364
|
file.startsWith('.github/rulesets/')
|
|
@@ -313,7 +313,6 @@
|
|
|
313
313
|
"tests/verify-test-quality.test.cjs :: source-text-is-the-product",
|
|
314
314
|
"tests/verify-work-auto-transition.test.cjs :: source-text-is-the-product",
|
|
315
315
|
"tests/windows-robustness.test.cjs :: source-text-is-the-product",
|
|
316
|
-
"tests/windows-test-parity-guard.test.cjs :: structural-regression-guard",
|
|
317
316
|
"tests/workflow-compat.test.cjs :: source-text-is-the-product",
|
|
318
317
|
"tests/workflow-guard-registration.test.cjs :: structural-regression-guard",
|
|
319
318
|
"tests/workflow-maintainer-skip.test.cjs :: source-text-is-the-product",
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
"bug-1367-claude-local-flat-command-layout.test.cjs",
|
|
6
6
|
"bug-14-progress-auto-flag-dropped.test.cjs",
|
|
7
7
|
"bug-167-query-meta-command.test.cjs",
|
|
8
|
+
"bug-1695-state-patch-clobbers-phase-name.test.cjs",
|
|
8
9
|
"bug-17-askuserquestion-option-cap.test.cjs",
|
|
9
10
|
"bug-170-workflow-fallback-install-hint.test.cjs",
|
|
10
11
|
"bug-1736-local-install-commands.test.cjs",
|
|
11
12
|
"bug-1754-js-hook-guard.test.cjs",
|
|
13
|
+
"bug-1760-state-prune-noop-template-field.test.cjs",
|
|
14
|
+
"bug-1761-state-sync-wrong-progress.test.cjs",
|
|
12
15
|
"bug-1817-sh-hook-guard.test.cjs",
|
|
13
16
|
"bug-1818-unknown-flags.test.cjs",
|
|
14
17
|
"bug-1826-phases-clear-confirm.test.cjs",
|
|
@@ -99,6 +99,9 @@
|
|
|
99
99
|
},
|
|
100
100
|
"state": {
|
|
101
101
|
"files": [
|
|
102
|
+
"bug-1695-state-patch-clobbers-phase-name.test.cjs",
|
|
103
|
+
"bug-1760-state-prune-noop-template-field.test.cjs",
|
|
104
|
+
"bug-1761-state-sync-wrong-progress.test.cjs",
|
|
102
105
|
"bug-21-state-md-template-frontmatter.test.cjs",
|
|
103
106
|
"bug-2630-state-frontmatter-milestone-switch.test.cjs",
|
|
104
107
|
"bug-3127-state-begin-phase-idempotent.test.cjs",
|
|
@@ -107,10 +110,12 @@
|
|
|
107
110
|
"bug-3454-state-dollar-backreference-growth.test.cjs",
|
|
108
111
|
"bug-397-state-preserve-executor-authored.test.cjs",
|
|
109
112
|
"bug-905-state-syncstatefrontmatter-preserve-scalars.test.cjs",
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
"bug-948-state-noop-write-guard.test.cjs",
|
|
114
|
+
"state-acquirestatelock-non-eexist.test.cjs",
|
|
115
|
+
"state-prune.test.cjs",
|
|
116
|
+
"state-rebuild-cli.test.cjs",
|
|
117
|
+
"state-rebuild.test.cjs",
|
|
118
|
+
"state.test.cjs"
|
|
114
119
|
],
|
|
115
120
|
"issue": "180"
|
|
116
121
|
},
|
|
@@ -142,9 +147,10 @@
|
|
|
142
147
|
"install-regressions.test.cjs",
|
|
143
148
|
"install-runtime-artifacts.test.cjs",
|
|
144
149
|
"install-update-marker.test.cjs",
|
|
150
|
+
"install-write-confinement.test.cjs",
|
|
145
151
|
"install.test.cjs"
|
|
146
152
|
],
|
|
147
|
-
"issue": "
|
|
153
|
+
"issue": "1679"
|
|
148
154
|
},
|
|
149
155
|
"validate": {
|
|
150
156
|
"files": [
|
|
@@ -184,6 +190,14 @@
|
|
|
184
190
|
"fix-1464-docs-manifest-validation.test.cjs"
|
|
185
191
|
],
|
|
186
192
|
"issue": "1496"
|
|
193
|
+
},
|
|
194
|
+
"host-integration": {
|
|
195
|
+
"files": [
|
|
196
|
+
"host-integration.test.cjs",
|
|
197
|
+
"host-integration-validator-parity.test.cjs",
|
|
198
|
+
"host-integration-descriptors.test.cjs"
|
|
199
|
+
],
|
|
200
|
+
"issue": "1684"
|
|
187
201
|
}
|
|
188
202
|
}
|
|
189
203
|
}
|
|
@@ -30,10 +30,52 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
const { execFileSync } = require('child_process');
|
|
33
|
-
const
|
|
33
|
+
const fs = require('fs');
|
|
34
34
|
|
|
35
35
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
36
36
|
|
|
37
|
+
// ── Resilient stdin reader ────────────────────────────────────────────────────
|
|
38
|
+
// On macOS, libuv sets the stdin pipe fd to non-blocking mode. A synchronous
|
|
39
|
+
// readFileSync(process.stdin.fd) can therefore throw EAGAIN ("resource
|
|
40
|
+
// temporarily unavailable") when the writer hasn't yet filled the pipe — this
|
|
41
|
+
// is intermittent under heavy CI shard load and causes a spurious status 2
|
|
42
|
+
// exit. We work around it by calling fs.readSync in a loop and retrying on
|
|
43
|
+
// EAGAIN with a 1 ms synchronous pause (Atomics.wait on a fresh SharedArrayBuffer
|
|
44
|
+
// — no hot spin, no real-clock dependency, works under --experimental-vm-modules).
|
|
45
|
+
/**
|
|
46
|
+
* Read all of stdin synchronously, retrying on EAGAIN.
|
|
47
|
+
*
|
|
48
|
+
* @returns {string} UTF-8 decoded full stdin content.
|
|
49
|
+
*/
|
|
50
|
+
function readStdinSync() {
|
|
51
|
+
const BUF_SIZE = 64 * 1024; // 64 KB chunks
|
|
52
|
+
const buf = Buffer.allocUnsafe(BUF_SIZE);
|
|
53
|
+
const chunks = [];
|
|
54
|
+
|
|
55
|
+
for (;;) {
|
|
56
|
+
let bytesRead;
|
|
57
|
+
try {
|
|
58
|
+
bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUF_SIZE, null);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (err.code === 'EAGAIN') {
|
|
61
|
+
// Non-blocking pipe not yet ready — yield for ~1 ms then retry.
|
|
62
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 1);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (err.code === 'EOF') {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
if (bytesRead === 0) {
|
|
71
|
+
break; // Clean EOF
|
|
72
|
+
}
|
|
73
|
+
chunks.push(Buffer.from(buf.slice(0, bytesRead)));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
77
|
+
}
|
|
78
|
+
|
|
37
79
|
// ── Per-module mutation score ratchet ─────────────────────────────────────────
|
|
38
80
|
// ADR-456 / issue #1187: every covered module declares a minScore floor.
|
|
39
81
|
//
|
|
@@ -195,7 +237,7 @@ function resolveChangedFiles(args) {
|
|
|
195
237
|
// When --base is absent AND stdin is not a TTY (isTTY is falsy / undefined),
|
|
196
238
|
// read a newline-delimited file list from stdin.
|
|
197
239
|
if (!args.base && process.stdin.isTTY !== true) {
|
|
198
|
-
const raw =
|
|
240
|
+
const raw = readStdinSync();
|
|
199
241
|
return raw.split('\n').map(l => l.trim()).filter(Boolean);
|
|
200
242
|
}
|
|
201
243
|
|
|
@@ -318,6 +360,6 @@ function resolveMutationBreak(raw) {
|
|
|
318
360
|
|
|
319
361
|
// Export internals for programmatic use (tests/mutation-matrix-ratchet.test.cjs).
|
|
320
362
|
// The require.main guard prevents main() from running when this file is require()d.
|
|
321
|
-
module.exports = { COVERED, TARGET_MUTATION_SCORE, resolveMutationBreak };
|
|
363
|
+
module.exports = { COVERED, TARGET_MUTATION_SCORE, resolveMutationBreak, readStdinSync };
|
|
322
364
|
|
|
323
365
|
if (require.main === module) runMain(main);
|