* feat(#2995): post-install path audit for workflow-invoked scripts
Catches the gap class surfaced by #2994: a workflow references a script
via ${GSD_HOME}/<path> that ships in the npm tarball but is not copied
to the user's config dir at install time. Unit tests don't catch it
because they resolve the script via path.join(__dirname, '..', 'scripts',
…) — the source layout, not the deployed layout.
Implementation built TDD per #2995, vertical slices with structured-IR
assertions:
scripts/audit-workflow-script-paths.cjs
- Pure auditWorkflowScriptPaths({ workflowsDir, repoRoot,
installedPrefixes }) returns { ok, findings: [{ workflow, path,
kind }] } via the AUDIT_FINDING enum.
- Two finding kinds: MISSING_FROM_REPO (typo / file deleted) and
NOT_INSTALLED (#2994 class — first segment outside installed
prefixes).
- Tolerates ${GSD_HOME:-...} default-fallback syntax.
tests/bug-2995-post-install-script-paths.test.cjs
- 9 tests across 3 suites:
• Pure-function pass and per-finding-kind detection (5 tests on
synthetic fixtures).
• Real workflow audit (2 tests asserting the actual repo's
get-shit-done/workflows/ has no NEW gaps and KNOWN_GAPS stays
consistent with audit findings).
• Enum shape lock + extractReferences edge cases.
- All assertions on typed AUDIT_FINDING enum / structured records;
zero raw text matching.
- KNOWN_GAPS is a Set keyed on `workflow|path|kind` strings;
currently contains the #2994 entry. The companion test fails if
a KNOWN_GAPS entry no longer matches a real finding (forces the
allow-list to shrink as gaps fix).
The audit immediately catches #2994's gap on `reapply-patches.md`. The
allow-list contains exactly that entry; new gaps fail CI; #2994's fix
will remove the entry as part of the same PR.
Closes#2995
Refs #2994
* chore(#2995): add changeset fragment for PR #2996
* chore(#2995): add changeset fragment for PR #2996
* fix(#2995): emit both NOT_INSTALLED + MISSING_FROM_REPO; clean up fixture leak (CR)
CodeRabbit on PR #2996 found two issues:
1. (Low value) auditWorkflowScriptPaths short-circuited on NOT_INSTALLED,
masking MISSING_FROM_REPO for the same ref. Removed the `continue` so
both findings emit in one run; added a regression test.
2. (Low value) bug-2995 test created tmpRoot in before() but never wrote
into it; per-fixture mkdtempSync dirs leaked. Rooted fixture repos
under tmpRoot so the after() cleanup actually frees them.