Commit Graph

3921 Commits

Author SHA1 Message Date
Tom Boucher
c28cccbf85 Merge pull request #1595 from davesienkowski/feat/1592-plan-drift-precheck
feat(#1592): add plan:pre codebase-drift pre-check before planner runs
2026-06-23 10:56:29 -04:00
Tom Boucher
1b95762661 Merge pull request #1568 from behruznassre/fix/1514-retired-phase-total-phases
fix(#1514): exclude retired/folded phases from progress.total_phases
2026-06-23 10:55:41 -04:00
Tom Boucher
afabb7c526 Merge pull request #1616 from open-gsd/fix/1614-antigravity-flat-skills
fix(#1614): install Antigravity skills flat
2026-06-23 10:54:27 -04:00
Tom Boucher
ce7fffffc4 test(#1614): classify Antigravity skills as flat 2026-06-23 10:40:55 -04:00
Tom Boucher
6db580d694 chore(#1614): backfill changeset PR number 2026-06-23 10:22:57 -04:00
Tom Boucher
da2de3a183 Merge branch 'next' into fix/1514-retired-phase-total-phases 2026-06-23 10:22:18 -04:00
Tom Boucher
cbd21092a9 fix(#1614): install Antigravity skills flat 2026-06-23 10:21:06 -04:00
Tom Boucher
610720d163 Merge pull request #1611 from open-gsd/claude/loving-cannon-745e64
feat(#1602): deterministic coverage-metadata UAT routing for verify-work
2026-06-23 09:26:09 -04:00
Tom Boucher
50eb6176fb chore(#1602): add changeset for #1611
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:43:22 -04:00
Tom Boucher
ba96c70b14 feat(#1602): deterministic coverage-metadata UAT routing for verify-work
Add an optional structured `coverage:` block to SUMMARY.md frontmatter and a
deterministic classifier that `verify-work` consumes to route deliverables to
auto-pass vs human-UAT — replacing the rejected #1598/#1599 post-hoc heuristic.

- New `src/coverage.cts` (→ bin/lib/coverage.cjs) parses the nested coverage
  block (extractFrontmatter can't — its `-` items are scalars-only; this is a
  focused parser, sibling of parseMustHavesBlock), validates each entry, and
  classifies into auto_passed vs present. Frozen MODE/PRESENT_REASON/ERROR_CODE
  typed-IR surface. Exposed via `uat classify-coverage --summary <f>`.
- Auto-pass is the narrow proven case only: strict-boolean human_judgment:false
  AND non-empty all-`pass` verification AND zero validation errors. Everything
  else — judgment, empty/failing verification, malformed entry — routes to the
  human (fail-safe). A malformed block falls back to legacy prose extraction and
  surfaces an error; an absent block is byte-identical to pre-#1602.
- execute-plan create_summary populates the block (fail-safe default
  human_judgment:true); verify-work extract_tests consumes it; create_uat_file
  marks auto-passed entries `source: automated`.
- Templates (summary + 3 variants), CONTEXT.md predicate + glossary, INVENTORY,
  eslint/gitignore registration, and Diataxis docs (COMMANDS reference +
  USER-GUIDE explanation) updated.
- Behavioral tests via the CLI (no source-grep); parser-robustness regressions
  for the null-entry/comment-header/mis-indent cases found in adversarial review.

Closes #1602

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:43:22 -04:00
Rezolv
652142521b enhance(#1549): validate PR-title issue-ref convention at open time (#1576)
* enhance(#1549): validate PR-title issue-ref convention at open time

The release changelog is title-driven: release.yml generates "What's Changed"
from PR titles, then format-github-release-notes.cjs buckets each line by its
conventional-commit prefix and relies on a `(#<issue>)` in the title to render
the issue link. Both rules were enforced only socially, so titles like
`fix(core): ...` (no issue link) and `[security] fix(...): ...` (leading tag
defeats the `^fix` bucket anchor -> mis-filed under Enhancement) silently broke
the changelog, landing on the maintainer as release-time cleanup.

Extract the title matcher into one shared module consumed by BOTH the changelog
classifier and a new PR-title CI gate, so a title that passes the gate cannot
mis-bucket in the changelog (single source of truth).

- scripts/lib/conventional-title.cjs (new): classifyBucket + evaluatePrTitle +
  the anchored regexes. One matcher, two consumers.
- scripts/release-notes/format-github-release-notes.cjs: classifyTitle now
  delegates to classifyBucket (behavior preserved; existing tests green).
- .github/workflows/pr-title-validator.yml (new): runs evaluatePrTitle on
  pull_request opened/edited/reopened/synchronize, for ALL authors (the drift
  came from member PRs). Trusted base-ref checkout; WARN_ONLY knob for rollout.
- tests/conventional-title.test.cjs (new): bucket + gate cases incl. the
  leading-tag mis-bucket (backfills the untested classifyTitle case) and a
  cross-check that the classifier delegates to the shared matcher.
- CONTRIBUTING.md: document the `type(#<issue>):` rule and no-leading-tag.

Claude-Session: https://claude.ai/code/session_01UMV5Qr3H4oFikbuiEauGQk

* fix(#1549): check out the PR in pr-title-validator so the new matcher resolves

The workflow checked out the base branch (next) as a trusted policy source, but
the shared matcher (scripts/lib/conventional-title.cjs) is introduced by this PR
and does not exist on next yet — so require() failed and validate-title errored
on its own introducing PR. Check out the PR's merge ref instead: the matcher
under review is present, the check is self-consistent, and a fork pull_request
runs read-only with no secrets, so running the PR's own pure-string regex is
safe.

* fix(#1549): move conventional-title.cjs out of installed scripts/lib/

bin/install.js bundles every file under scripts/lib/ into the user-installed
payload (the changeset CLI's dependencies), and install.test.cjs (#935) asserts
that exact set. The new matcher is release/CI tooling that must NOT ship to
users, so placing it in scripts/lib/ both broke the install manifest test and
would have shipped dead code. Relocate it next to its consumer in
scripts/release-notes/ (which the installer does not copy) and update the three
require paths (classifier, workflow, test) + the CONTRIBUTING reference.

install.test.cjs now 125/125; conventional-title + release-notes suites green;
lint:ci clean.

* fix(#1549): load title matcher from trusted base ref, not PR code

Addresses review (Solvely-Colin + trek-e): the gate checked out the PR
merge ref and require()'d evaluatePrTitle from PR-controlled code, so any
future PR could edit conventional-title.cjs to return { valid: true } and
wave its own malformed title through — a self-bypassable required check.

Load the matcher from a base-branch checkout instead (ref:
github.event.pull_request.base.ref), the same trusted-policy-source pattern
pr-target-validator.yml already uses. The PR can change its title but not
the ruler that measures it. An existsSync bootstrap guard skips the check
when the matcher isn't on the base branch yet (the introducing PR); every
PR after merge is fully gated. This keeps the single shared matcher (#1549's
whole point) rather than forking the regex into the workflow.

Also per review:
- add tests/conventional-title.property.test.cjs (fast-check): any
  `type(#n): summary` round-trips to valid; evaluatePrTitle/classifyBucket
  are total functions (never throw).
- pin the `fix(#):` zero-digit boundary as missing-issue-ref.

Claude-Session: https://claude.ai/code/session_01VqUHNQCh71pEqjo96zkgQL

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-22 22:44:44 -04:00
Tom Boucher
fcf44ac02a Merge pull request #1604 from open-gsd/docs/1603-adr-1239-opencode-binding
docs(#1603): add opencode host-plugin binding to adr-1239
2026-06-22 22:33:49 -04:00
Tom Boucher
1985d4a4de docs(#1603): add opencode host-plugin binding to adr-1239 2026-06-22 22:29:48 -04:00
Dave
d1f7ba82f2 feat(#1592): add plan:pre codebase-drift pre-check before planner runs
Add a non-blocking, warn-only codebase-drift gate at plan:pre so a stale
STRUCTURE.md is surfaced before /gsd:plan-phase spawns the planner, instead
of being discovered mid-execution by the existing execute:wave:post gate.
Gated on a dedicated workflow.plan_drift_precheck toggle (default true),
independent of schema_drift_gate. Never blocks planning, never spawns the
mapper agent at plan time.

Review feedback (#1595):
- Use a documented conventional-commit type (feat, not enhance) per
  CONTRIBUTING.md / gsd-validate-commit.sh.
- Normalize the plan_drift_precheck command references to the colon prose
  form (/gsd:plan-phase, /gsd:map-codebase) to match plan-phase.md §5.65;
  registry regenerated from capability.json.
- Make the test temp dirs hermetic: drain mkdtemp dirs in an after() hook
  via the helpers.cleanup() budget (local/no-raw-rmsync-in-tests-compliant).

Closes #1592

Claude-Session: https://claude.ai/code/session_016JBiXEAofvB3prJim29XMS
2026-06-22 20:30:59 -04:00
Behruz Nassre Esfahani
0271231910 test(#1514): add fast-check property + all-retired boundary for retired parser
Per review (test-standard items):
- Property test (RULESET.TESTS.property-based-testing): extractRetiredPhaseNumbers
  is the parsing core, so add a fast-check property — k of n checklist phases
  struck → exactly the k canonical keys returned, across randomized phase counts
  and numeric/zero-padded/project-code ID forms. Exposed via a `_`-prefixed test
  seam (mirrors the existing _setLockProbes seams), no public API surface added.
- Boundary (RULESET.TESTS.boundary-coverage): all-retired case (k === n) →
  total_phases 0, via state json.

No production behavior change; the exclusion logic is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:22:29 -07:00
Behruz Nassre Esfahani
c6edeb3eb3 Merge branch 'next' into fix/1514-retired-phase-total-phases 2026-06-22 17:13:25 -07:00
Tom Boucher
8c467bc313 Merge pull request #1601 from open-gsd/docs/1600-phase-cd-plugin-skill-assessment
docs(#1600): Phase C+D — per-platform plugin skill model assessment (all N/A)
2026-06-22 19:26:26 -04:00
Tom Boucher
d108932136 docs(#1600): Phase C+D — per-platform plugin skill model assessment (all N/A)
Resolves TBD entries in the skill-mapping matrix provision/consumption
table. All 15 non-Claude platforms assessed as N/A — none have a
documented plugin skill-provision model. File-copy install path
handles skill provision for all runtimes. Updates claude row to
reflect Phase B-provide merged status. Closes epic #1258 acceptance.

Closes #1600
2026-06-22 19:22:18 -04:00
Tom Boucher
38fe706767 Merge pull request #1597 from open-gsd/feat/1596-plugin-skills-provision
feat(#1596): ship GSD skills via .claude-plugin/plugin.json
2026-06-22 19:16:20 -04:00
Tom Boucher
f68814d2d0 fix(#1596): remove gen:plugin-skills from prepack — stdout pollutes npm pack 2026-06-22 18:20:52 -04:00
Tom Boucher
bf52edf2e2 chore(#1596): backfill changeset pr number 1597 2026-06-22 18:12:55 -04:00
Tom Boucher
da4a86d8c1 feat(#1596): ship GSD skills via .claude-plugin/plugin.json
Phase B-provide of epic #1258. Adds a build-generated skills/ dir +
a skills manifest field so plugin-installed GSD exposes gsd-core:<skill>
the native Claude Code way. Closes the gap where plugin-only installs
lacked the skill surface because bin/install.js never ran.

- scripts/gen-plugin-skills.cjs: build step converting commands/gsd/*.md
  to skills/gsd-<stem>/SKILL.md via convertClaudeCommandToClaudeSkill
- .claude-plugin/plugin.json: add "skills": "./skills/"
- package.json: add skills to files, gen:plugin-skills to build chain
- tests/issue-766-plugin-manifest.test.cjs: Section H conformance
  (manifest field + dir + frontmatter + count parity) + C2 skills symlink
- docs/adr/766-*.md: dated amendment adding skills surface row
- .changeset/rapid-bears-hum.md: type Added
- skills/: 69 generated gsd-<stem>/SKILL.md files (build-committed)

Closes #1596
2026-06-22 18:07:35 -04:00
Tom Boucher
69de3bca23 Merge pull request #1594 from open-gsd/docs/1593-skill-mapping-converter-methodology
docs(#1593): ADR for cross-runtime skill mapping + converter methodology
2026-06-22 17:12:05 -04:00
Tom Boucher
860179ee5d docs(#1593): ADR for cross-runtime skill mapping + converter methodology
Phase A of epic #1258. Adds the single authoritative ADR documenting
the per-runtime skill mapping + converter transform-contract catalog
that ADR-3660 (layout) and ADR-1508 (module ownership) each carry a
third of. Ships a companion reference matrix projecting all 16
runtimes from their capability descriptors.

- docs/adr/1593-skill-mapping-converter-methodology.md (new ADR)
- docs/reference/skill-mapping-matrix.md (new reference page)
- docs/adr/README.md (index row + status fixes: 3660, 1016 Proposed->Accepted)
- docs/adr/1016-runtime-capability-descriptor.md (header Proposed->Accepted;
  the ConverterName enum is already code-enforced per ADR-857 phase 5e)

Doc-only. No code, no behavior change. Closes #1593.
2026-06-22 16:27:41 -04:00
Tom Boucher
3aa528d6e3 Merge pull request #1590 from open-gsd/chore/1589-worktree-test-escape-class
test: complete regex-escape class in worktree-safety assertion (#1589)
2026-06-22 14:34:52 -04:00
Tom Boucher
cd56500d20 test: complete regex-escape class in worktree-safety assertion (#1589)
CodeQL alert #41 (js/incomplete-sanitization) flagged the partial
escape class /[-]/g at tests/worktree-safety.test.cjs:645 — it only
escaped hyphen-minus, leaving 13 other regex metacharacters (notably
backslash) unescaped. The canonical class /[.*+?^${}()|[\]\\]/g is
what every sibling escape in the test suite already uses
(bug-2839, bug-2760, 4-phase-complete, phase6-capstone-conformance).

Today dormant: the flag array is a hardcoded [a-z-] literal, so the
expanded class is a no-op for the four existing flags and the regexes
they produce are byte-identical. The fix prevents future drift — a
contributor adding e.g. '--output=file' would have silently introduced
a regex wildcard.

All 69 tests in the file pass. No user-facing behavior change.

Fixes #1589
2026-06-22 14:07:33 -04:00
Tom Boucher
6ae95e6e09 Merge pull request #1438 from behruznassre/feat/1173-wire-agent-converters-descriptor
feat(#1173): wire the 8 agent converters into the descriptor-driven install path
2026-06-22 13:15:30 -04:00
Tom Boucher
3a06b4888a Merge branch 'next' into feat/1173-wire-agent-converters-descriptor 2026-06-22 13:04:46 -04:00
Tom Boucher
23e363c581 Merge pull request #1418 from behruznassre/fix/1394-gemini-skill-tool-exclusion
fix(#1394): exclude Skill/SlashCommand from the Gemini agent tool converter
2026-06-22 13:04:42 -04:00
Tom Boucher
675956ff17 Merge branch 'next' into fix/1394-gemini-skill-tool-exclusion 2026-06-22 12:52:37 -04:00
Tom Boucher
5bc08eddb8 Merge pull request #1587 from open-gsd/fix/1586-perf-407-liveness-probe-determinism
fix(#1586): pin withPlanningLock liveness probe in perf-407 for determinism
2026-06-22 12:51:17 -04:00
Tom Boucher
7013406f0b fix(#1586): pin withPlanningLock liveness probe in perf-407 for determinism
#1531/#1532 replaced withPlanningLock's mtime-staleness with PID-liveness
(process.kill(pid,0)). perf-407 plants pid: process.pid + 1 and relied on the
old mtime model to force the retry/sleep path; under the new model that pid's
liveness is environment-dependent, so the retry path was taken on some runners
and skipped on others (sleepCallCount: 0 precondition failure) — flaky CI red
on next that blocks the merge queue.

Pin the planted holder live via the _setLockProbes seam that #1532 added, and
_resetLockProbes() in afterEach. The retry/sleep path is now exercised
deterministically on every runner. No assertion weakened; no variable renamed.
perf-316 is unaffected (its worker writes the parent's own, always-live pid).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 12:33:13 -04:00
Tom Boucher
79d0e79c61 Merge branch 'next' into fix/1394-gemini-skill-tool-exclusion 2026-06-22 12:15:57 -04:00
Tom Boucher
5c8509cf7b Merge pull request #1410 from behruznassre/fix/1400-agent-skills-stdout-flush
fix(#1400): flush agent-skills block via writeAllSync instead of process.exit(0)
2026-06-22 12:15:53 -04:00
Tom Boucher
1a16fa7ecc Merge branch 'next' into fix/1400-agent-skills-stdout-flush 2026-06-22 12:06:09 -04:00
Tom Boucher
e9829e2754 Merge pull request #1532 from open-gsd/fix/1531-core-lock-liveness
fix(#1531): PID-liveness staleness model for the two core-path file locks
2026-06-22 12:05:53 -04:00
Tom Boucher
b1297db50c Merge branch 'next' into fix/1531-core-lock-liveness 2026-06-22 11:56:04 -04:00
Behruz Nassre Esfahani
cc362e474f test(#1394): assert Gemini tool exclusion via exported converter, not internal
The back-merge of next carried #1559/#1565 (audit installer compatibility
exports), which removed convertGeminiToolName from bin/install.js's
module.exports. This PR's regression test destructured it from
require('../bin/install.js'), so after the merge it was undefined →
"TypeError: convertGeminiToolName is not a function" across all test lanes.

Rewrite the regression to assert the user-visible behavior through the
still-exported convertClaudeToGeminiAgent: a Skill/SlashCommand/AskUserQuestion
tools entry must not appear in the emitted Gemini frontmatter (the lowercase
fallback would emit invalid tool names that abort agent load — #1394/#3362),
while mapped tools (Read→read_file, WebFetch→web_fetch) survive. Folds the
dropped AskUserQuestion/ask_user coverage into the behavior test and removes
the internal-function import, so the test no longer depends on a private export
#1559 intentionally pruned. The exclusion fix itself is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 08:01:28 -07:00
Tom Boucher
2085a4ebeb Merge pull request #1574 from open-gsd/fix/1529-gsd-new-project-emits-claude-style-proje
fix(#1529): emit runtime-native instruction file from new-project
2026-06-22 10:54:01 -04:00
Behruz Nassre Esfahani
384b8e9d8b Merge branch 'next' into fix/1400-agent-skills-stdout-flush 2026-06-22 07:32:47 -07:00
Tom Boucher
248c056532 chore(#1574): regenerate workflow-size baseline for new-project prose growth
The copilot path correction (.github/copilot-instructions.md) lengthened
the new-project.md instruction-file prose by 16 bytes past the prior
baseline. Regenerated; growth is justified by the more accurate path.
2026-06-22 10:09:43 -04:00
Tom Boucher
b2c0086c1b fix(#1574): resolve review — copilot instruction file is .github/copilot-instructions.md
GitHub Copilot reads repository-wide instructions only from
.github/copilot-instructions.md (confirmed via GitHub Docs), not a root
copilot-instructions.md. Aligns getProjectInstructionFile with the installer
(runtime-config-adapter-registry installSurface 'copilot-instructions') and
cites the docs source in the doc-comment.
2026-06-22 09:56:15 -04:00
Tom Boucher
bf9bd1f4e0 fix(#1529): emit runtime-native instruction file from new-project 2026-06-22 09:32:29 -04:00
Tom Boucher
90f123434d Merge branch 'next' into fix/1394-gemini-skill-tool-exclusion 2026-06-22 07:56:58 -04:00
Behruz Nassre Esfahani
317ac8bd35 test(#1514): rename regression test to fix- prefix (regression-test-names lint)
New tests/bug-NNNN-*.test.cjs files are banned by the lint-regression-test-names
ratchet (fold into the owning module or use the fix- prefix). Matches the
fix-1445 precedent for the same total_phases subsystem.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:41:10 -07:00
Behruz Nassre Esfahani
2bfe026737 chore(#1514): add changeset fragment (Fixed)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:35:43 -07:00
Behruz Nassre Esfahani
e3acdd89cb fix(#1514): exclude retired/folded phases from progress.total_phases
A retired/folded phase (struck through in ROADMAP, marked [x], with a
directory but no completion artifact) was counted in the total_phases
denominator via max(phaseDirs.length, roadmapPhaseCount), yet could never
satisfy the numerator (no SUMMARY → never "completed"), freezing shipped
milestones below 100% (e.g. 5/6 = 83%).

Both STATE counting paths now read the current-milestone ROADMAP scope and
exclude retired phases from BOTH the disk phase-dir set and the heading
count, so a retired phase counts toward neither denominator nor numerator:
  - buildStateFrontmatter (`state json`)
  - cmdStateSync (`state sync --verify` / rebuild) — previously re-derived the
    inflated denominator and reported "no drift", per the issue.

Retired detection (extractRetiredPhaseNumbers) is scoped to the lines that
canonically mark a phase retired — a checklist entry (`- [x] …`) or a phase
heading — and within those, only a struck span whose SUBJECT is the phase
(`~~**Phase 04: Delta**~~`). So struck prose, a struck goal line, and the fold
target ("folded into Phase 05") are not misread as retired.

Phase matching uses the canonical phase-id helpers (normalizePhaseName +
extractPhaseToken), so numeric, decimal, and project-code IDs (PROJ-42) match
consistently across ROADMAP tokens and on-disk dir names.

Scope boundaries (separate, pre-existing concerns left unchanged):
  - `roadmap analyze` (src/roadmap.cts) intentionally trusts the [x] checkbox
    (incl. externally-completed phases) — a different reporting surface.
  - cmdStateSync does not apply the milestone phase-dir filter (so 999.x /
    other-milestone dirs can still affect its count); that is the #1445 /
    milestone-filter axis, independent of retired phases.

Same counting family as #549 / #500 / #1445.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:29:05 -07:00
Tom Boucher
91d29be124 Merge pull request #1567 from open-gsd/chore/sync-next-version-1.6.0-rc.2
chore: sync next package version to 1.6.0-rc.2
2026-06-22 01:22:31 -04:00
github-actions[bot]
f20dc691c1 chore: sync next package version to 1.6.0-rc.2 2026-06-22 05:22:23 +00:00
Joe
e12a2abfd8 feat(#441): add /gsd-capture --list-seeds for seed listing and audit (#722)
* feat(#441): add /gsd-capture --list-seeds for seed listing and audit

Seeds (.planning/seeds/SEED-NNN-slug.md) could only be created (--seed),
enriched (--enrich), or auto-surfaced at /gsd-new-milestone. There was no way
to browse or audit parked seeds on demand. This adds a read-only listing,
following the established --list → workflow pattern (per the approved scope on

- gsd-tools `list-seeds [status]` (cmdListSeeds in src/commands.cts): scans the
  seeds dir, returns { count, seeds[], summary } JSON with each seed's id,
  slug, status, scope, trigger_when, planted, title. Optional case-insensitive
  status filter. User-controlled content is sanitized (sanitizeForDisplay) and
  every path validated (requireSafePath); read-only. Independent of
  audit.scanSeeds, which only returns unimplemented seeds for the milestone surface.
- /gsd-capture --list-seeds routes to a new read-only list-seeds workflow that
  renders the seed table.

Closes #441

* chore(#441): point changeset fragment at PR #722

* test(#441): allowlist list-seeds test in prompt-injection scan

The test asserts that list-seeds neutralizes injection payloads
(<system>, [INST]) embedded in seed content, so the fixtures legitimately
contain those patterns — same as the sibling security tests already on the
allowlist.

* fix(#441): use canonical /gsd:capture colon form in list-seeds workflow

Claude-facing source (commands/, agents/, gsd-core/workflows/, ...) must use
the /gsd:<cmd> colon form per ADR/CONTEXT.md; the hyphen /gsd-<cmd> form is
retired there (enforced by bug-2543-gsd-slash-namespace.test.cjs). The new
list-seeds workflow used the hyphen form.

* docs(#441): sync help full.md + INVENTORY for --list-seeds

Adds the --list-seeds entry to the help reference (help/modes/full.md, per
bug-2954 argument-hint↔help parity) and registers the new list-seeds workflow
in docs/INVENTORY.md (88→89) and the generated INVENTORY-MANIFEST.json.

* docs(#441): add --list-seeds how-to + drop phantom statuses

Addresses CHANGES_REQUESTED on PR #722 (two documentation blockers):

- USER-GUIDE.md Seeds section (how-to): extend the task to cover
  auditing parked seeds on demand via --list-seeds, including the
  status filter — kept task-oriented per Diataxis how-to mode.
- CLI-TOOLS.md (reference): drop phantom statuses implemented|rejected
  from the list-seeds filter vocabulary; the system only produces
  dormant|active|triggered (src/audit.cts scanSeeds). Reference must
  be factually accurate and complete.

* fix(#441): guard non-scalar status frontmatter in cmdListSeeds

A seed with a bare `status:` line (extractFrontmatter yields {}) or a
`status: [a, b]` value (yields an array) crashed the whole audit list:
`(fm.status || 'dormant').toLowerCase()` throws a TypeError on a non-string.
Coerce every frontmatter read through a `fmStr` helper (mirrors the existing
`typeof fm.id === 'string'` guard), so a non-scalar status falls back to
dormant and non-scalar scope/trigger_when/title can no longer leak a raw
array/object into the JSON contract. Title is now capped symmetrically.

Adds regression coverage for empty and array `status:` and non-scalar fields.

Refs #441

* docs(#441): align list-seeds workflow status vocabulary

The load_seeds step listed `implemented` as an example status filter, but the
real seed vocabulary is dormant|active|triggered (src/audit.cts scanSeeds);
`implemented` has no producer. Matches the earlier CLI-TOOLS.md correction.

Refs #441

* refactor(#441): extract pure deriveSeedIdentity; match raw status in list-seeds

Pull the seed_id/slug derivation out of cmdListSeeds into a pure, exported
deriveSeedIdentity(stem, rawFmId) so the parsing contract can be property-tested
in-process (review minor #1). No behavior change.

Filter comparison now matches the raw lowercased status (both sides already
normalized) instead of sanitizeForDisplay(status); sanitization is for output,
not matching (review nit #3).

* test(#441): add fast-check property coverage and count=1 boundary for list-seeds

Adds tests/list-seeds.property.test.cjs with four fast-check properties over
deriveSeedIdentity (never-throws, string-only contract, canonical id->seed_id/slug
invariant, filename-prefix fallback) per RULESET.TESTS.property-based-testing
(review minor #1).

Adds an N==1 status-filter boundary case to list-seeds.test.cjs (review minor #2).

* chore(#441): sync runtime launcher snippet into list-seeds workflow

Propagate the current _runtime-launcher.snippet.sh (with non-Claude
runtime home probes) into the new list-seeds.md workflow via
scripts/sync-runtime-launcher.cjs, satisfying bug-891 (E) propagation.

* test(#441): record list-seeds.md in workflow size baseline (#1074)

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-22 00:59:41 -04:00