@opengsd/gsd-core 1.5.0-rc.4 → 1.5.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/plugin.json +1 -1
- package/LICENSE +1 -1
- package/agents/gsd-executor.md +22 -0
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-planner.md +4 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-verifier.md +45 -13
- package/bin/install.js +56 -100
- package/commands/gsd/autonomous.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/plan-phase.md +1 -1
- package/gemini-extension.json +1 -1
- package/gsd-core/bin/gsd-tools.cjs +85 -53
- package/gsd-core/bin/lib/agent-command-router.cjs +2 -2
- package/gsd-core/bin/lib/agent-install-check.cjs +143 -0
- package/gsd-core/bin/lib/audit-command-router.cjs +4 -4
- package/gsd-core/bin/lib/capability-activation.cjs +37 -10
- package/gsd-core/bin/lib/capability-registry.cjs +2 -0
- package/gsd-core/bin/lib/capability-state.cjs +80 -30
- package/gsd-core/bin/lib/capability-writer.cjs +5 -4
- package/gsd-core/bin/lib/check-command-router.cjs +50 -51
- package/gsd-core/bin/lib/commands.cjs +20 -2
- package/gsd-core/bin/lib/config-loader.cjs +3 -4
- package/gsd-core/bin/lib/config-schema.cjs +1 -1
- package/gsd-core/bin/lib/config-types.cjs +2 -1
- package/gsd-core/bin/lib/config.cjs +5 -2
- package/gsd-core/bin/lib/decisions.cjs +19 -1
- package/gsd-core/bin/lib/docs.cjs +14 -2
- package/gsd-core/bin/lib/frontmatter.cjs +2 -2
- package/gsd-core/bin/lib/gap-checker.cjs +5 -2
- package/gsd-core/bin/lib/git-base-branch.cjs +27 -1
- package/gsd-core/bin/lib/graphify-command-router.cjs +6 -8
- package/gsd-core/bin/lib/graphify.cjs +7 -31
- package/gsd-core/bin/lib/gsd2-import.cjs +2 -2
- package/gsd-core/bin/lib/init.cjs +30 -4
- package/gsd-core/bin/lib/intel-command-router.cjs +6 -3
- package/gsd-core/bin/lib/intel.cjs +28 -34
- package/gsd-core/bin/lib/io.cjs +2 -4
- package/gsd-core/bin/lib/learnings.cjs +2 -2
- package/gsd-core/bin/lib/loop-resolver.cjs +45 -167
- package/gsd-core/bin/lib/milestone.cjs +13 -5
- package/gsd-core/bin/lib/model-resolver.cjs +3 -4
- package/gsd-core/bin/lib/phase-id.cjs +3 -5
- package/gsd-core/bin/lib/phase-locator.cjs +3 -6
- package/gsd-core/bin/lib/phase.cjs +59 -13
- package/gsd-core/bin/lib/probe-core.cjs +40 -11
- package/gsd-core/bin/lib/profile-output.cjs +5 -2
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +660 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-parser.cjs +28 -25
- package/gsd-core/bin/lib/roadmap.cjs +9 -4
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +4 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +24 -3
- package/gsd-core/bin/lib/state.cjs +75 -17
- package/gsd-core/bin/lib/task-command-router.cjs +2 -2
- package/gsd-core/bin/lib/teams-status.cjs +74 -0
- package/gsd-core/bin/lib/template.cjs +11 -2
- package/gsd-core/bin/lib/uat.cjs +64 -2
- package/gsd-core/bin/lib/verification.cjs +8 -5
- package/gsd-core/bin/lib/verify.cjs +311 -4
- package/gsd-core/bin/lib/workstream-inventory.cjs +2 -2
- package/gsd-core/bin/lib/workstream.cjs +8 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +44 -3
- package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
- package/gsd-core/references/planner-antipatterns.md +46 -0
- package/gsd-core/references/planning-config.md +5 -1
- package/gsd-core/references/prohibition-probe.md +80 -2
- package/gsd-core/references/worktree-branch-check.md +11 -5
- package/gsd-core/templates/verification-report.md +16 -3
- package/gsd-core/workflows/docs-update.md +23 -31
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +9 -0
- package/gsd-core/workflows/execute-phase.md +7 -2
- package/gsd-core/workflows/map-codebase.md +8 -10
- package/gsd-core/workflows/plan-phase.md +6 -0
- package/gsd-core/workflows/quick.md +26 -2
- package/gsd-core/workflows/settings-advanced.md +5 -5
- package/gsd-core/workflows/settings-integrations.md +5 -5
- package/gsd-core/workflows/spec-phase.md +30 -2
- package/gsd-core/workflows/verify-phase.md +22 -7
- package/hooks/dist/gsd-worktree-path-guard.js +34 -18
- package/hooks/gsd-worktree-path-guard.js +34 -18
- package/package.json +3 -3
- package/scripts/ci-prepare-test-scope.cjs +56 -14
- package/scripts/diff-touches-shipped-paths.cjs +5 -11
- package/scripts/gen-capability-registry.cjs +27 -1
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/research-profiles.cjs +2 -2
- package/scripts/run-tests.cjs +1 -0
- package/gsd-core/bin/lib/core.cjs +0 -345
package/LICENSE
CHANGED
package/agents/gsd-executor.md
CHANGED
|
@@ -103,6 +103,24 @@ PLAN_START_EPOCH=$(date +%s)
|
|
|
103
103
|
```
|
|
104
104
|
</step>
|
|
105
105
|
|
|
106
|
+
<worktree_metadata_capture>
|
|
107
|
+
If running inside a git worktree, capture authoritative worktree identity before
|
|
108
|
+
any task commit changes HEAD. The execute-phase orchestrator consumes this from
|
|
109
|
+
your final `<worktree_metadata>` return block to build the wave cleanup manifest
|
|
110
|
+
without relying on runtime harness metadata (#1297).
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
GSD_WORKTREE_PATH=""
|
|
114
|
+
GSD_WORKTREE_BRANCH=""
|
|
115
|
+
GSD_WORKTREE_EXPECTED_BASE=""
|
|
116
|
+
if [ -f .git ]; then
|
|
117
|
+
GSD_WORKTREE_PATH=$(git rev-parse --show-toplevel)
|
|
118
|
+
GSD_WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
119
|
+
GSD_WORKTREE_EXPECTED_BASE=$(git rev-parse HEAD)
|
|
120
|
+
fi
|
|
121
|
+
```
|
|
122
|
+
</worktree_metadata_capture>
|
|
123
|
+
|
|
106
124
|
<step name="determine_execution_pattern">
|
|
107
125
|
```bash
|
|
108
126
|
grep -n "type=\"checkpoint" [plan-path]
|
|
@@ -761,6 +779,10 @@ into the user's project history.
|
|
|
761
779
|
**Tasks:** {completed}/{total}
|
|
762
780
|
**SUMMARY:** {path to SUMMARY.md}
|
|
763
781
|
|
|
782
|
+
<worktree_metadata>
|
|
783
|
+
{"agent_id":"{phase}-{plan}","worktree_path":"${GSD_WORKTREE_PATH:-}","branch":"${GSD_WORKTREE_BRANCH:-}","expected_base":"${GSD_WORKTREE_EXPECTED_BASE:-}"}
|
|
784
|
+
</worktree_metadata>
|
|
785
|
+
|
|
764
786
|
**Commits:**
|
|
765
787
|
- {hash}: {message}
|
|
766
788
|
- {hash}: {message}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gsd-phase-researcher
|
|
3
3
|
description: Researches how to implement a phase before planning. Produces RESEARCH.md consumed by gsd-planner. Spawned by /gsd:plan-phase orchestrator.
|
|
4
|
-
tools: Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*
|
|
4
|
+
tools: Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*, mcp__perplexity__*
|
|
5
5
|
color: cyan
|
|
6
6
|
# hooks:
|
|
7
7
|
# PostToolUse:
|
package/agents/gsd-planner.md
CHANGED
|
@@ -198,6 +198,10 @@ Every task has four required fields:
|
|
|
198
198
|
Full rules + worked examples: @gsd-core/references/planner-antipatterns.md ("Comment-Text Discipline").
|
|
199
199
|
</comment_text_discipline>
|
|
200
200
|
|
|
201
|
+
<region_scoped_negative_gate>
|
|
202
|
+
**Region-scoped negative gates (WARN, #968):** Region-scope a file-wide negative grep when a sibling task needs that construct elsewhere in the same file; `validate_plan` WARNS. See: @gsd-core/references/planner-antipatterns.md ("Region-Scoped Negative Gates").
|
|
203
|
+
</region_scoped_negative_gate>
|
|
204
|
+
|
|
201
205
|
**<done>:** Acceptance criteria - measurable state of completion.
|
|
202
206
|
- Good: "Valid credentials return 200 + JWT cookie, invalid credentials return 401"
|
|
203
207
|
- Bad: "Authentication is complete"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gsd-project-researcher
|
|
3
3
|
description: Researches domain ecosystem before roadmap creation. Produces files in .planning/research/ consumed during roadmap creation. Spawned by /gsd:new-project or /gsd:new-milestone orchestrators.
|
|
4
|
-
tools: Read, Write, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*
|
|
4
|
+
tools: Read, Write, Bash, Grep, Glob, Skill, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*, mcp__perplexity__*
|
|
5
5
|
color: cyan
|
|
6
6
|
# hooks:
|
|
7
7
|
# PostToolUse:
|
package/agents/gsd-verifier.md
CHANGED
|
@@ -180,21 +180,28 @@ For each truth, determine if codebase enables it.
|
|
|
180
180
|
|
|
181
181
|
**Verification status:**
|
|
182
182
|
|
|
183
|
-
- ✓ VERIFIED: All supporting artifacts pass all checks
|
|
183
|
+
- ✓ VERIFIED: All supporting artifacts pass all checks — and, for a behavior-dependent truth, a behavioral test exercises the asserted behavior (see below)
|
|
184
|
+
- ⚠️ PRESENT_BEHAVIOR_UNVERIFIED: Supporting artifacts are present and wired, but the truth asserts runtime behavior that no test exercises — present, not behaviorally proven. Routes to human verification (Step 8) and does NOT count toward the verified score (Step 9).
|
|
184
185
|
- ✗ FAILED: One or more artifacts missing, stub, or unwired
|
|
185
186
|
- ? UNCERTAIN: Can't verify programmatically (needs human)
|
|
186
187
|
|
|
188
|
+
**Behavior-dependent truths.** A truth is *behavior-dependent* when its correctness hinges on runtime behavior grep/presence checks cannot see — a **state transition** or a **cancellation / cleanup / ordering invariant** (e.g. "cancels the in-flight task and bumps the generation counter", "resets the busy flag on abort", "rolls back on failure"). For these, symbol presence + wiring is *necessary but not sufficient*: the code can be present and wired yet still leak state on the very path the invariant covers.
|
|
189
|
+
|
|
187
190
|
For each truth:
|
|
188
191
|
|
|
189
192
|
1. Identify supporting artifacts
|
|
190
193
|
2. Check artifact status (Step 4)
|
|
191
194
|
3. Check wiring status (Step 5)
|
|
192
|
-
4. **Before marking FAIL:** Check for override (Step 3b)
|
|
193
|
-
5.
|
|
195
|
+
4. **Before marking FAIL or PRESENT_BEHAVIOR_UNVERIFIED:** Check for override (Step 3b)
|
|
196
|
+
5. **Classify behavior-dependence.** If the truth asserts a state transition or a cancellation/cleanup/ordering invariant, its status cannot be VERIFIED on presence alone:
|
|
197
|
+
- A pre-existing test exercises the transition/invariant and passes (confirm via Step 7b's single-named-test path) → ✓ VERIFIED.
|
|
198
|
+
- No such test exists, or it can't run without a server/state mutation → ⚠️ PRESENT_BEHAVIOR_UNVERIFIED. Emit a human-verification item (Step 8) and do not count it toward the verified score (Step 9).
|
|
199
|
+
- An accepted override (Step 3b) carries the truth as PASSED (override), exactly as it does for a FAILED truth.
|
|
200
|
+
6. Determine truth status
|
|
194
201
|
|
|
195
202
|
## Step 3b: Check Verification Overrides
|
|
196
203
|
|
|
197
|
-
Before marking any must-have as FAILED, check the VERIFICATION.md frontmatter for an `overrides:` entry that matches this must-have.
|
|
204
|
+
Before marking any must-have as FAILED or ⚠️ PRESENT_BEHAVIOR_UNVERIFIED, check the VERIFICATION.md frontmatter for an `overrides:` entry that matches this must-have.
|
|
198
205
|
|
|
199
206
|
**Override check procedure:**
|
|
200
207
|
|
|
@@ -204,12 +211,12 @@ Before marking any must-have as FAILED, check the VERIFICATION.md frontmatter fo
|
|
|
204
211
|
4. Key technical terms (file paths, component names, API endpoints) have higher weight
|
|
205
212
|
|
|
206
213
|
**If override found:**
|
|
207
|
-
- Mark as `PASSED (override)` instead of FAIL
|
|
214
|
+
- Mark as `PASSED (override)` instead of FAIL/PRESENT_BEHAVIOR_UNVERIFIED
|
|
208
215
|
- Evidence: `Override: {reason} — accepted by {accepted_by} on {accepted_at}`
|
|
209
|
-
- Count toward passing score, not failing score
|
|
216
|
+
- Count toward passing score (`verified_truths`), not failing score
|
|
210
217
|
|
|
211
218
|
**If no override found:**
|
|
212
|
-
- Mark as FAILED as normal
|
|
219
|
+
- Mark as FAILED (or ⚠️ PRESENT_BEHAVIOR_UNVERIFIED, per Step 3 step 5) as normal
|
|
213
220
|
- Consider suggesting an override if the failure looks intentional (alternative implementation exists)
|
|
214
221
|
|
|
215
222
|
**Suggesting overrides:** When a must-have FAILs but evidence shows an alternative implementation that achieves the same intent, include an override suggestion in the report:
|
|
@@ -461,6 +468,8 @@ Anti-pattern scanning (Step 7) checks for code smells. Behavioral spot-checks go
|
|
|
461
468
|
|
|
462
469
|
**When to run:** For phases that produce runnable code (APIs, CLI tools, build scripts, data pipelines). Skip for documentation-only or config-only phases.
|
|
463
470
|
|
|
471
|
+
**Behavioral evidence for behavior-dependent truths (Step 3).** When a truth asserts a state transition or a cancellation/cleanup/ordering invariant, the single named test below is what upgrades it from ⚠️ PRESENT_BEHAVIOR_UNVERIFIED to ✓ VERIFIED. Run only the one named test that exercises the transition/invariant — never the full suite (per #25/#753). If no such test exists, leave the truth ⚠️ PRESENT_BEHAVIOR_UNVERIFIED and route it to human verification (Step 8); do not mark it VERIFIED on presence.
|
|
472
|
+
|
|
464
473
|
**How:**
|
|
465
474
|
|
|
466
475
|
1. **Identify checkable behaviors** from must-haves truths. Select 2-4 that can be tested with a single command:
|
|
@@ -548,6 +557,8 @@ done
|
|
|
548
557
|
|
|
549
558
|
**Needs human if uncertain:** Complex wiring grep can't trace, dynamic state behavior, edge cases.
|
|
550
559
|
|
|
560
|
+
**Behavior-unverified truths (Step 3):** Every truth left ⚠️ PRESENT_BEHAVIOR_UNVERIFIED is recorded in the `behavior_unverified_items` frontmatter list (emitted whenever the count > 0, regardless of overall status, so it survives a gaps_found phase) and surfaces for human verification; when the overall status is human_needed it also appears in the human_verification section. Phrase each item around the invariant: what to trigger, what state must hold afterward, and why presence checks can't see it.
|
|
561
|
+
|
|
551
562
|
**Harvest deferred items from PLAN.md (#3309 / `workflow.human_verify_mode = end-of-phase`):** Scan every PLAN file in the phase for `<verify><human-check>` blocks on `auto` tasks. These are verification items the planner deliberately deferred from `checkpoint:human-verify` to end-of-phase to avoid the executor cold-start cost. Each block has the same shape used by the planner:
|
|
552
563
|
|
|
553
564
|
```xml
|
|
@@ -579,18 +590,30 @@ Classify status using this decision tree IN ORDER (most restrictive first):
|
|
|
579
590
|
1. IF any truth FAILED, artifact MISSING/STUB, key link NOT_WIRED, or blocker anti-pattern found:
|
|
580
591
|
→ **status: gaps_found**
|
|
581
592
|
|
|
582
|
-
2. IF Step 8 produced ANY human verification items (section is non-empty):
|
|
593
|
+
2. IF Step 8 produced ANY human verification items (section is non-empty) — this includes every ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truth from Step 3:
|
|
583
594
|
→ **status: human_needed**
|
|
584
|
-
(Even if all truths are VERIFIED
|
|
595
|
+
(Even if all other truths are VERIFIED — human items take priority)
|
|
585
596
|
|
|
586
597
|
3. IF all truths VERIFIED, all artifacts pass, all links WIRED, no blockers, AND no human verification items:
|
|
587
598
|
→ **status: passed**
|
|
588
599
|
|
|
589
|
-
**passed is ONLY valid when the human verification section is empty.** If
|
|
600
|
+
**passed is ONLY valid when the human verification section is empty.** If Step 8 produced any items — including any truth left ⚠️ PRESENT_BEHAVIOR_UNVERIFIED — the status is not `passed`: it is `human_needed`, or `gaps_found` when rule 1 also fires (the ordered tree keeps gaps_found's precedence).
|
|
601
|
+
|
|
602
|
+
**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 (behavior unexercised). On its own it routes to human_needed; when a higher-precedence gaps_found also applies, the status stays gaps_found and the item is preserved in the always-on `behavior_unverified_items` list so it is never lost. Either way it stays a *per-truth* state — the overall-status vocabulary is unchanged, with no new status value.
|
|
590
603
|
|
|
591
604
|
> **Shared status seam**: the status vocabulary (`passed`, `gaps_found`, `human_needed`) and the per-status routing (next action and next command for each value) are owned by `src/verification.cts` via `gsd_run query verification.status`. This agent is the single emitter of the frontmatter status field; consumers (ship.md, execute-phase.md) read routing from that query instead of re-deriving it.
|
|
592
605
|
|
|
593
|
-
**Score
|
|
606
|
+
**Score (presence- vs behavior-verified split):**
|
|
607
|
+
|
|
608
|
+
- `verified_truths` counts ✓ VERIFIED truths plus PASSED (override) truths (Step 3b). For a behavior-dependent truth, VERIFIED means a behavioral test passed, not just that symbols are present.
|
|
609
|
+
- ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truths are the *only* ones excluded from `verified_truths`; they are reported separately as `behavior_unverified`.
|
|
610
|
+
|
|
611
|
+
```text
|
|
612
|
+
score: verified_truths / total_truths # e.g. 6/7
|
|
613
|
+
behavior_unverified: P # truths present + wired but behavior not exercised
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
A headline N/N therefore certifies that every behavior-dependent truth had behavioral evidence — a clean score can no longer be reached on symbol presence alone.
|
|
594
617
|
|
|
595
618
|
## Step 9b: Filter Deferred Items
|
|
596
619
|
|
|
@@ -699,6 +722,7 @@ phase: XX-name
|
|
|
699
722
|
verified: YYYY-MM-DDTHH:MM:SSZ
|
|
700
723
|
status: passed | gaps_found | human_needed
|
|
701
724
|
score: N/M must-haves verified
|
|
725
|
+
behavior_unverified: 0 # Count of ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truths (present + wired, behavior not exercised); each is detailed in behavior_unverified_items below (and in human_verification when status is human_needed)
|
|
702
726
|
overrides_applied: 0 # Count of PASSED (override) items included in score
|
|
703
727
|
overrides: # Only if overrides exist — carried forward or newly added
|
|
704
728
|
- must_have: "Must-have text that was overridden"
|
|
@@ -725,6 +749,11 @@ deferred: # Only if deferred items exist (Step 9b)
|
|
|
725
749
|
- truth: "Observable truth addressed in a later phase"
|
|
726
750
|
addressed_in: "Phase N"
|
|
727
751
|
evidence: "Matching goal or success criteria text"
|
|
752
|
+
behavior_unverified_items: # Only if behavior_unverified > 0 — emitted regardless of overall status, so these survive a gaps_found phase
|
|
753
|
+
- truth: "Observable truth whose state transition or cancellation/cleanup/ordering invariant no test exercises"
|
|
754
|
+
test: "What to trigger"
|
|
755
|
+
expected: "What state must hold afterward"
|
|
756
|
+
why_human: "Why presence checks can't see it"
|
|
728
757
|
human_verification: # Only if status: human_needed
|
|
729
758
|
- test: "What to do"
|
|
730
759
|
expected: "What should happen"
|
|
@@ -746,8 +775,9 @@ human_verification: # Only if status: human_needed
|
|
|
746
775
|
| --- | ------- | ---------- | -------------- |
|
|
747
776
|
| 1 | {truth} | ✓ VERIFIED | {evidence} |
|
|
748
777
|
| 2 | {truth} | ✗ FAILED | {what's wrong} |
|
|
778
|
+
| 3 | {truth} | ⚠️ PRESENT_BEHAVIOR_UNVERIFIED | {present + wired; no test exercises the transition/invariant — see Human Verification} |
|
|
749
779
|
|
|
750
|
-
**Score:** {N}/{M} truths verified
|
|
780
|
+
**Score:** {N}/{M} truths verified ({P} present, behavior-unverified)
|
|
751
781
|
|
|
752
782
|
### Deferred Items
|
|
753
783
|
|
|
@@ -834,7 +864,7 @@ Structured gaps in VERIFICATION.md frontmatter for `/gsd:plan-phase --gaps`.
|
|
|
834
864
|
|
|
835
865
|
{If human_needed:}
|
|
836
866
|
### Human Verification Required
|
|
837
|
-
{N} items need human testing:
|
|
867
|
+
{N} items need human testing (including {P} present-but-behavior-unverified truths — code wired, transition/invariant not exercised by a test):
|
|
838
868
|
1. **{Test name}** — {what to do}
|
|
839
869
|
- Expected: {what should happen}
|
|
840
870
|
|
|
@@ -857,6 +887,8 @@ Automated checks passed. Awaiting human verification.
|
|
|
857
887
|
|
|
858
888
|
**Keep verification fast.** Use grep/file checks, not running the app.
|
|
859
889
|
|
|
890
|
+
**Presence is not behavior.** Grep/file checks prove a symbol is present and wired — they do not prove a state transition or a cancellation/cleanup/ordering invariant holds at runtime. For a behavior-dependent truth, require a passing behavioral test (Step 7b's single named test) or mark it ⚠️ PRESENT_BEHAVIOR_UNVERIFIED and route to human verification. Never let symbol presence alone produce a VERIFIED on a behavior-dependent truth.
|
|
891
|
+
|
|
860
892
|
**DO NOT commit.** Leave committing to the orchestrator.
|
|
861
893
|
|
|
862
894
|
</critical_rules>
|
package/bin/install.js
CHANGED
|
@@ -265,9 +265,11 @@ const _gsdLibDir = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib');
|
|
|
265
265
|
const { MODEL_PROFILES: GSD_MODEL_PROFILES } = require(path.join(_gsdLibDir, 'model-profiles.cjs'));
|
|
266
266
|
const {
|
|
267
267
|
RUNTIME_PROFILE_MAP: GSD_RUNTIME_PROFILE_MAP,
|
|
268
|
+
} = require(path.join(_gsdLibDir, 'model-catalog.cjs'));
|
|
269
|
+
const {
|
|
268
270
|
resolveTierEntry: gsdResolveTierEntry,
|
|
269
271
|
EFFORT_SET: GSD_EFFORT_SET,
|
|
270
|
-
} = require(path.join(_gsdLibDir, '
|
|
272
|
+
} = require(path.join(_gsdLibDir, 'model-resolver.cjs'));
|
|
271
273
|
|
|
272
274
|
// #443 — model-catalog and config-defaults.manifest.json exports needed only
|
|
273
275
|
// by effort-resolution code paths (resolveInstallTimeEffort /
|
|
@@ -1794,6 +1796,10 @@ function skillFrontmatterName(skillDirName) {
|
|
|
1794
1796
|
return skillDirName;
|
|
1795
1797
|
}
|
|
1796
1798
|
|
|
1799
|
+
function normalizeClaudeSkillEffort(effort) {
|
|
1800
|
+
return effort === 'xhigh' ? 'max' : effort;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1797
1803
|
/**
|
|
1798
1804
|
* Qwen Code skills accept an optional numeric `priority` frontmatter field.
|
|
1799
1805
|
* Per the Qwen skills spec (qwen-code/docs/users/features/skills.md, verified
|
|
@@ -1890,7 +1896,7 @@ function convertClaudeCommandToClaudeSkill(content, skillName, runtime = null, c
|
|
|
1890
1896
|
// token-budget tier). Fields are Claude-specific; unknown frontmatter
|
|
1891
1897
|
// fields are silently ignored by other runtimes (backward-compatible).
|
|
1892
1898
|
if (context) fm += `context: ${context}\n`;
|
|
1893
|
-
if (effort) fm += `effort: ${effort}\n`;
|
|
1899
|
+
if (effort) fm += `effort: ${normalizeClaudeSkillEffort(effort)}\n`;
|
|
1894
1900
|
if (toolsBlock) fm += toolsBlock;
|
|
1895
1901
|
fm += '---';
|
|
1896
1902
|
|
|
@@ -3192,110 +3198,55 @@ function generateCodexAgentToml(agentName, agentContent, modelOverrides = null,
|
|
|
3192
3198
|
}
|
|
3193
3199
|
|
|
3194
3200
|
/**
|
|
3195
|
-
*
|
|
3196
|
-
*
|
|
3197
|
-
* This file is written alongside SKILL.md as <skill-dir>/agents/openai.yaml.
|
|
3198
|
-
* Codex loads it as a SkillMetadataFile (codex-rs/core-skills/src/loader.rs),
|
|
3199
|
-
* making the skill discoverable in the /skills TUI popup with a display name
|
|
3200
|
-
* and short description. If the file is absent, Codex silently skips it (fails open).
|
|
3201
|
-
*
|
|
3202
|
-
* Schema (interface section):
|
|
3203
|
-
* display_name: short human-readable skill name (strip gsd- prefix)
|
|
3204
|
-
* short_description: 1-2 sentence description for TUI chip, ≤180 chars
|
|
3205
|
-
*
|
|
3206
|
-
* @param {string} skillName - Full skill name e.g. "gsd-plan-phase"
|
|
3207
|
-
* @param {string} shortDescription - Description text (already truncated by caller)
|
|
3208
|
-
* @returns {string} YAML content for agents/openai.yaml
|
|
3209
|
-
*/
|
|
3210
|
-
function generateCodexSkillMetadataYaml(skillName, shortDescription) {
|
|
3211
|
-
// Display name: strip "gsd-" prefix and convert hyphens to spaces for readability.
|
|
3212
|
-
const displayName = skillName.replace(/^gsd-/, '').replace(/-/g, ' ');
|
|
3213
|
-
// yamlQuote (= JSON.stringify) handles all YAML-unsafe chars: backslashes,
|
|
3214
|
-
// quotes, newlines, control characters, and Unicode escapes.
|
|
3215
|
-
return [
|
|
3216
|
-
'interface:',
|
|
3217
|
-
` display_name: ${yamlQuote(displayName)}`,
|
|
3218
|
-
` short_description: ${yamlQuote(shortDescription)}`,
|
|
3219
|
-
'',
|
|
3220
|
-
].join('\n');
|
|
3221
|
-
}
|
|
3222
|
-
|
|
3223
|
-
/**
|
|
3224
|
-
* Write agents/openai.yaml TUI chip metadata for each gsd-* skill directory.
|
|
3225
|
-
*
|
|
3226
|
-
* Called after layout-driven skill install for Codex. Iterates every gsd-*
|
|
3227
|
-
* skill directory in skillsDir, reads the SKILL.md frontmatter to extract the
|
|
3228
|
-
* short-description already emitted by convertClaudeCommandToCodexSkill, then
|
|
3229
|
-
* writes <skill-dir>/agents/openai.yaml using generateCodexSkillMetadataYaml.
|
|
3201
|
+
* Remove stale agents/openai.yaml sidecar files from GSD-managed Codex skill dirs.
|
|
3230
3202
|
*
|
|
3231
|
-
*
|
|
3232
|
-
*
|
|
3203
|
+
* Prior to #1326, GSD's Codex install path wrote an agents/openai.yaml file
|
|
3204
|
+
* alongside each gsd-* SKILL.md. Recent Codex builds index BOTH SKILL.md and
|
|
3205
|
+
* the sidecar, causing each GSD skill to appear twice in autocomplete. This
|
|
3206
|
+
* function removes those stale sidecars and — if the agents/ subdirectory is
|
|
3207
|
+
* now empty — prunes it too.
|
|
3233
3208
|
*
|
|
3234
|
-
*
|
|
3235
|
-
*
|
|
3236
|
-
*
|
|
3237
|
-
*
|
|
3238
|
-
*
|
|
3239
|
-
*
|
|
3240
|
-
*
|
|
3241
|
-
*
|
|
3242
|
-
*
|
|
3209
|
+
* Behaviour:
|
|
3210
|
+
* - Returns immediately if skillsDir does not exist (fails open).
|
|
3211
|
+
* - Only touches directories whose names start with "gsd-".
|
|
3212
|
+
* - Skips user-owned dirs (gsd-dev-preferences) — their agents/ content is
|
|
3213
|
+
* never modified, mirroring the same USER_OWNED_SKILL_DIRS guard used by
|
|
3214
|
+
* installOpencodeFamilySkills.
|
|
3215
|
+
* - For each managed gsd-* dir, if agents/openai.yaml exists, deletes it.
|
|
3216
|
+
* - If agents/ is now empty, removes the directory; if it still contains
|
|
3217
|
+
* other files (e.g. user-added content), leaves it in place.
|
|
3218
|
+
* - Non-gsd-* dirs and their agents/ content are never touched.
|
|
3219
|
+
* - Individual failures are caught and swallowed so a single bad dir cannot
|
|
3220
|
+
* block the install (fail-open, matching the original design).
|
|
3243
3221
|
*
|
|
3244
3222
|
* @param {string} skillsDir - Path to the skills/ directory (e.g. ~/.codex/skills)
|
|
3245
3223
|
*/
|
|
3246
|
-
function
|
|
3224
|
+
function cleanupCodexSkillMetadataSidecars(skillsDir) {
|
|
3247
3225
|
if (!fs.existsSync(skillsDir)) return;
|
|
3248
3226
|
// Mirror the user-owned list from installOpencodeFamilySkills (#2973).
|
|
3249
3227
|
// We MUST skip these dirs — their contents are user-generated and must
|
|
3250
|
-
// never be
|
|
3228
|
+
// never be modified by GSD's install path.
|
|
3251
3229
|
const _userOwnedSkillDirs = new Set(['gsd-dev-preferences']);
|
|
3252
3230
|
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
3253
3231
|
if (!entry.isDirectory() || !entry.name.startsWith('gsd-')) continue;
|
|
3254
3232
|
if (_userOwnedSkillDirs.has(entry.name)) continue; // preserve user content
|
|
3255
|
-
const
|
|
3256
|
-
const
|
|
3233
|
+
const agentsSubdir = path.join(skillsDir, entry.name, 'agents');
|
|
3234
|
+
const sidecarPath = path.join(agentsSubdir, 'openai.yaml');
|
|
3257
3235
|
try {
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
if (
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
const metaBlock = metaMatch[1];
|
|
3270
|
-
const sdMatch = metaBlock.match(/^[ \t]+short-description\s*:\s*(.+)$/m);
|
|
3271
|
-
if (sdMatch) {
|
|
3272
|
-
// Unescape YAML double-quoted scalar escapes before embedding.
|
|
3273
|
-
// convertClaudeCommandToCodexSkill always emits a double-quoted
|
|
3274
|
-
// value (via yamlQuote) so only double-quote unescaping is needed.
|
|
3275
|
-
let raw = sdMatch[1].trim();
|
|
3276
|
-
if (raw.startsWith('"') && raw.endsWith('"')) {
|
|
3277
|
-
// Strip outer double-quotes and decode \" → " and \\ → \
|
|
3278
|
-
raw = raw.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
3279
|
-
} else {
|
|
3280
|
-
// Single-quoted or unquoted: strip surrounding quotes/whitespace
|
|
3281
|
-
raw = raw.replace(/^["']|["']$/g, '');
|
|
3282
|
-
}
|
|
3283
|
-
shortDesc = raw;
|
|
3284
|
-
}
|
|
3285
|
-
}
|
|
3286
|
-
if (!shortDesc) {
|
|
3287
|
-
shortDesc = extractFrontmatterField(frontmatter, 'description') || '';
|
|
3288
|
-
}
|
|
3236
|
+
// Symlink guard: if agents/ is a symlink pointing outside the skills tree,
|
|
3237
|
+
// deleting through it could escape the tree. Skip this dir entirely.
|
|
3238
|
+
let agentsStat;
|
|
3239
|
+
try { agentsStat = fs.lstatSync(agentsSubdir); } catch (_e) { continue; }
|
|
3240
|
+
if (agentsStat.isSymbolicLink()) continue;
|
|
3241
|
+
if (fs.existsSync(sidecarPath)) {
|
|
3242
|
+
fs.rmSync(sidecarPath);
|
|
3243
|
+
}
|
|
3244
|
+
// Prune the agents/ dir only if it is now empty (leave it if other files remain).
|
|
3245
|
+
if (fs.existsSync(agentsSubdir) && fs.readdirSync(agentsSubdir).length === 0) {
|
|
3246
|
+
fs.rmdirSync(agentsSubdir);
|
|
3289
3247
|
}
|
|
3290
|
-
if (!shortDesc) {
|
|
3291
|
-
shortDesc = `Run GSD workflow ${entry.name}.`;
|
|
3292
|
-
}
|
|
3293
|
-
const yamlContent = generateCodexSkillMetadataYaml(entry.name, shortDesc);
|
|
3294
|
-
const agentsSubdir = path.join(skillDir, 'agents');
|
|
3295
|
-
fs.mkdirSync(agentsSubdir, { recursive: true });
|
|
3296
|
-
fs.writeFileSync(path.join(agentsSubdir, 'openai.yaml'), yamlContent);
|
|
3297
3248
|
} catch (_err) {
|
|
3298
|
-
// Fail open —
|
|
3249
|
+
// Fail open — a single bad dir must not block the install.
|
|
3299
3250
|
}
|
|
3300
3251
|
}
|
|
3301
3252
|
}
|
|
@@ -6841,6 +6792,12 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false) {
|
|
|
6841
6792
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
6842
6793
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
6843
6794
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
6795
|
+
// Bare forms (no trailing slash) — use (?![\w-]) instead of \b so that
|
|
6796
|
+
// .claude-plugin / .claudeignore are NOT corrupted (the \b word-boundary
|
|
6797
|
+
// fires between 'e' and '-', which rewrites .claude-plugin → .cursor-plugin).
|
|
6798
|
+
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
6799
|
+
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
6800
|
+
content = content.replace(/\.\/\.claude(?![\w-])/g, `./${dirName}`);
|
|
6844
6801
|
content = content.replace(/~\/\.cursor\//g, pathPrefix);
|
|
6845
6802
|
content = processAttribution(content, getCommitAttribution(runtime));
|
|
6846
6803
|
break;
|
|
@@ -9835,14 +9792,14 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
9835
9792
|
const scope = isGlobal ? 'global' : 'local';
|
|
9836
9793
|
installRuntimeArtifacts(runtime, targetDir, scope, _resolvedProfile);
|
|
9837
9794
|
|
|
9838
|
-
// #
|
|
9839
|
-
//
|
|
9840
|
-
//
|
|
9841
|
-
//
|
|
9842
|
-
//
|
|
9843
|
-
//
|
|
9795
|
+
// #1326 — Codex only: remove stale agents/openai.yaml sidecars from managed
|
|
9796
|
+
// gsd-* skill dirs. Prior installs wrote these files so Codex would show a
|
|
9797
|
+
// display name and description in the /skills TUI popup. Recent Codex builds
|
|
9798
|
+
// index BOTH SKILL.md and the sidecar, causing each GSD skill to appear twice
|
|
9799
|
+
// in autocomplete. Cleaning them up fixes the duplication; SKILL.md alone is
|
|
9800
|
+
// sufficient for Codex discovery. User-owned dirs are never touched.
|
|
9844
9801
|
if (isCodex) {
|
|
9845
|
-
|
|
9802
|
+
cleanupCodexSkillMetadataSidecars(path.join(targetDir, 'skills'));
|
|
9846
9803
|
}
|
|
9847
9804
|
|
|
9848
9805
|
// Hermes only: write DESCRIPTION.md for the gsd/ category after layout install
|
|
@@ -12124,8 +12081,7 @@ module.exports = {
|
|
|
12124
12081
|
convertClaudeToGeminiAgent,
|
|
12125
12082
|
convertClaudeAgentToCodexAgent,
|
|
12126
12083
|
generateCodexAgentToml,
|
|
12127
|
-
|
|
12128
|
-
writeCodexSkillMetadataFiles,
|
|
12084
|
+
cleanupCodexSkillMetadataSidecars,
|
|
12129
12085
|
generateCodexConfigBlock,
|
|
12130
12086
|
stripGsdFromCodexConfig,
|
|
12131
12087
|
migrateCodexHooksMapFormat,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: gsd:plan-phase
|
|
3
3
|
description: Create detailed phase plan (PLAN.md) with verification loop
|
|
4
4
|
argument-hint: "[phase] [--auto] [--research] [--skip-research] [--research-phase <N>] [--view] [--gaps] [--skip-verify] [--prd <file>] [--ingest <path-or-glob>] [--ingest-format <auto|nygard|madr|narrative>] [--reviews] [--text] [--tdd] [--mvp]"
|
|
5
|
-
effort:
|
|
5
|
+
effort: max
|
|
6
6
|
allowed-tools:
|
|
7
7
|
- Read
|
|
8
8
|
- Write
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-core",
|
|
3
|
-
"version": "1.5.0
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "GSD Core — a meta-prompting, context engineering, and spec-driven development system for AI coding agents. Loads gsd's operating context into every Gemini CLI session.",
|
|
5
5
|
"contextFileName": "GEMINI.md"
|
|
6
6
|
}
|