12ee75a509cbfd8fe2ae2871056fa81777d7dcf5
263 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
caca4d255c |
feat(#985): intel command cutover — commands-only capability, last first-party family (ADR-857 phase 4d-impl-4) (#988)
* feat(#985): intel command cutover — commands-only capability, last first-party family (ADR-857 phase 4d-impl-4) Migrate the intel CLI command family from a hardcoded gsd-tools.cjs case arm to a registry-dispatched Capability (commandFamilies mechanism, #961), mirroring the graphify (#972) and audit (#984) cutovers. New src/intel-command-router.cts exports routeIntelCommand reproducing all 9 subcommands verbatim (incl. the status timeAgo non-raw post-processing), lazily requiring intel.cjs inside the route fn. capabilities/intel/capability.json declares the intel command family; commands-only (skills:[]), declares the existing intel.enabled gate (default false — behavior unchanged). Behavior-CHANGING (dispatch path) but equivalence-proven: CLI output identical; existing intel.test.cjs passes unchanged. Completes the first-party command- family cutover sequence (graphify/audit/intel). Closes #985 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#985): compute expected planningDir via path.join in intel cutover unit tests (Windows CI) The intel-command-cutover unit-mock assertions hardcoded a POSIX `/.planning` expectation while the router builds it with path.join(cwd, '.planning') → backslashes on Windows, so the planningDir-arg assertions (query/status/diff/ snapshot/validate/update/api-surface) failed only on windows-latest CI. Compute the expectation with path.join (cross-platform); production router unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9a03539c2d |
feat(#981): audit-uat + audit-open command cutover — commands-only capability (ADR-857 phase 4d-impl-3) (#984)
Migrate the audit-uat + audit-open CLI commands from hardcoded gsd-tools.cjs case arms to a registry-dispatched Capability (commandFamilies mechanism, #961), mirroring the graphify cutover (#972). New src/audit-command-router.cts exports routeAuditUat/routeAuditOpen, each lazily requiring only its backing module (uat.cjs/audit.cjs) inside the route fn — matching the old per-case lazy loads. capabilities/audit/capability.json declares the two command families; commands-only (skills:[]), no config gate, audit_review cluster untouched. Behavior-CHANGING (dispatch path) but equivalence-proven: CLI output identical; existing audit regression tests pass unchanged. Closes #981 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
77bfd943dd |
feat(#972): graphify command cutover — first capability owning a command family (ADR-857 phase 4d-impl-2) (#975)
Migrate graphify into a Capability that owns its `graphify` command family, dispatched via the registry (#961 mechanism) instead of a hardcoded case. graphify is now an enable/disable plug-in. - src/graphify-command-router.cts: routeGraphifyCommand (standard route*Command), reproduces the removed case EXACTLY (query +--budget, status, diff, build, hidden build snapshot, usage/unknown errors); injectable _graphify test seam. - capabilities/graphify/capability.json: role feature, tier:full, skills:[graphify], config:{graphify.enabled default false}, commands:[{family:graphify, module, router:routeGraphifyCommand}]. - removed case 'graphify' from gsd-tools.cjs; graphify now flows default -> dispatchCapabilityCommand -> commandFamilies.graphify -> router. - regenerated registry (commandFamilies/bySkill/configSchema/profileMembership/ capabilityClusters for graphify); tier:full keeps 4c install/surface a no-op. Equivalence-proven: 8 recording-mock unit tests assert the exact fn+args per subcommand (budget, snapshot-vs-build); 9 subprocess tests assert distinguishing output shapes; existing graphify tests pass unchanged through the new path. Surfaced (not silently accepted): the pre-existing --budget-no-value NaN no-op quirk, preserved for equivalence, filed separately. Closes #972 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
85cfa5dc13 |
feat(#945): unified capability-state resolver (ADR-857 phase 4b) (#946)
Add a read-side query composing the three toggle systems into one
per-capability view. resolveCapabilityState({registry, installedSkills,
surfacedSkills, config, cwd}) reports installed (skills ⊆ resolved install
profile), surfaced (skills ⊆ resolved surface), and per-hook active (no when →
active; non-empty-string when → resolved via _resolveActivationValue; empty/
non-string → inactive), with no forced composite verdict. cmdCapabilityState
does the I/O (resolveProfile + resolveSurface + loadConfig), resolves the
runtime config dir via the canonical getGlobalConfigDir (--config-dir override),
and surfaces resolution failures as warnings rather than a false installed='*'.
Routed as `gsd-tools capability state`.
Additive: install/surface/workflows untouched; consumed by nothing.
Closes #945
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
5670feaef5 |
feat(#918): loop.render-hooks resolver — consume the Capability Registry (ADR-857 phase 3c) (#920)
Add the loop.render-hooks resolver: the first registry-consuming query.
`gsd-tools loop render-hooks <point>` validates the point against the
authoritative canonical 12, reads the registry's materialized byLoopPoint
hooks, filters them by activation, and emits a JSON envelope {point,
activeHooks, rendered} with ordered markdown.
Activation resolves each hook's `when` key by precedence: loadConfig value
(post-cutover federated) -> raw config.json workstream/root single-key lookup
(pre-cutover central override) -> registry configSchema default (so a
default:true capability hook is active out-of-the-box) -> inactive. Guarded
single-value reads only (no merged object built from untrusted keys).
Registry-only: no workflow calls the resolver yet (wiring is the phase-6
cutover). Completes the phase-3 trio.
Closes #918
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
6dbd895028 |
feat(#910): federated config merge in config-loader (ADR-857 phase 3b) (#914)
Build the federated config merge: each capability owns its config-key slice
(ADR-857 decision 3 / ADR-894), and loadConfig merges them defensively. The
registry now emits a full configSchema index ({key:{owner,type,default,
description}}, generator-validated); a new src/federated-config.cts resolves
federated keys defensively (skip central keys -> pending-migration warning,
skip malformed slices -> warning never throw, else type-checked user override
?? default, with nested dotted-path lookup and enum validation); and loadConfig
applies the overlay on every return path.
Wired as a provably-empty no-op channel: every UI-pilot key is still central,
so validKeys is empty and loadConfig returns byte-identical output on all paths
(identity return when the overlay is empty; shared CONFIG_DEFAULTS never
mutated). Registry-only; no key is cut over; nothing in the live loop changes.
Closes #910
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
6edc39c4eb |
chore(#893): remove dead loadConfig from configuration.cts (#912)
`loadConfig` in configuration.cts was superseded by config-loader.cts (ADR-857 phase 2e, #885). Exhaustive grep confirms no caller imports loadConfig from configuration.cjs — all live callers use config-loader.cjs or the core.cjs back-compat re-export. configuration.cts now provides only the pure normalization and defaults primitives (normalizeLegacyKeys, mergeDefaults, migrateOnDisk, CONFIG_DEFAULTS) that config-loader.cts depends on. Updated CONTEXT.md and docs/INVENTORY.md to reflect the narrowed module surface. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
48cc27bd84 |
feat(#903): generate Loop Host Contract from workflow markers (ADR-857 phase 3a-impl-2) (#906)
Replace the inline LOOP_HOST_CONTRACT constant in the Capability Registry generator with a generated-from-workflows contract (ADR-894 §3). The contract is now derived from inert `<!-- gsd:loop-host ... -->` marker blocks in the five step workflows, emitted as the committed gsd-core/bin/lib/loop-host-contract.cjs, and required by gen-capability-registry.cjs — one source of truth, no drift. Drift guards in gen-loop-host-contract.cjs: per-step point ownership (each step must declare exactly its canonical loop points), multiple-block + duplicate-key hard errors, and a word-boundary agent-role cross-check. Contract content is byte-identical to the former constant; registry-only, nothing wired into the live loop. Closes #903 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ad754ca6cd |
feat(#896): Capability Registry generator + UI pilot (ADR-857 phase 3a-impl) (#902)
* feat(#896): Capability Registry generator + UI pilot (ADR-857 phase 3a-impl) First phase-3 code: the Capability Registry generation pipeline, built against the ADR-894 contract and NOT wired into the live loop (registry-only, per the staged-cutover design). - capabilities/ui/capability.json — the UI pilot (ADR-894 worked example): 2 skills, 2 agents, 3 config keys, 2 steps + 1 gate, with `when` activation. - scripts/gen-capability-registry.cjs — --write/--check generator. Hand-rolled schema validation (envelope + role-typed feature/runtime bodies + typed steps/contributions/gates + when + gate-check variants); cross-capability invariants (single ownership; requires exist+acyclic+tier-monotone; config-key ownership exclusive, collision-vs-central as a pending-migration warning); hooks validated against an inline LOOP_HOST_CONTRACT (3a-impl-2 swaps its source to the generated-from-workflows contract); GLOBAL point-ordered consumes-satisfiability; materialized byLoopPoint ordering (produces/consumes topo-sort); emits gsd-core/bin/lib/capability-registry.cjs (role-partitioned indexes + requiresClosure). Prototype-pollution guards (Object.create(null) + inline literal key checks) + fragment.path traversal guard. - gsd-core/bin/lib/capability-registry.cjs — committed generated artifact (mirrors package-identity.cjs: script-generated, tracked, linted, regenerated on build, drift-tested), wired via the new `gen:capability-registry` build step. - tests/capability-registry.test.cjs — 72 tests: schema + invariant + hook + ordering + adversarial (path-traversal, proto-pollution, runtime body, self-consume, cycles, collisions) + committed-file staleness guard. New-CLI-module checklist (INVENTORY 97->98, MANIFEST, ARCHITECTURE), CONTEXT.md "Capability Registry" un-[Planned]'d. Nothing wired into install/surface/loop. Gates: lint, code-review (4 bugs fixed), security-review (path-traversal + prototype-pollution fixed), codex adversarial-review ×3 (8+ findings fixed, confirmed sound), clean-build docker 13190 pass / 0 fail. Closes #896 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#896): CRLF-agnostic --check for capability-registry staleness (Windows) The committed capability-registry.cjs staleness guard failed on Windows CI only: git checks out the committed .cjs as CRLF (autocrlf, no .gitattributes) while the generator emits LF, so the byte-for-byte --check comparison mismatched. Normalize line endings on both sides of the --check comparison (no .gitattributes change, no change to the LF the generator writes). Adds a regression test simulating the Windows CRLF checkout. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
185935379a |
refactor(#888): extract model+effort resolution into model-resolver.cts (#890)
ADR-857 rollout phase 2f — the FINAL core.cts decomposition. Move the model and effort resolution cluster (resolveModelInternal, resolveModelPolicy, resolveTierEntry, _resolveRuntimeTier, resolveModelForTier, resolveGranularityInternal, assertValidGranularityOverride, resolveEffortInternal, resolveFastModeInternal, resolveEffortForTier, nextEffort + VALID_GRANULARITIES/ VALID_EFFORTS/EFFORT_SET + interfaces) out of core.cts into a new leaf module src/model-resolver.cts. core.cts re-exports the 13 public symbols (callers in init/docs/commands unchanged; export= set byte-identical). Cycle-free: model-resolver imports only leaves (config-loader for loadConfig, configuration for defaults, model-profiles + model-catalog for the static tables). Removed 6 now-unused imports from core (verified zero remaining references, none re-exported). This completes the god-module decomposition: core.cts 2271 -> 389 lines (~83%), now a thin re-export spine over seven clean leaves (io, phase-id, roadmap-parser, core-utils, phase-locator, config-loader, model-resolver). New-CLI-module checklist done (.gitignore, eslint, INVENTORY 96->97 + row, manifest, ARCHITECTURE, CONTEXT.md "Model Resolver Module"). Adds tests/model-resolver.test.cjs (81 tests: behavioral + shim-identity + adversarial). Gates: lint, code-review (export set byte-identical; import-removal verified), security-review, codex adversarial-review (all 0 findings; verbatim move). Mac 4303 pass; clean-build docker 13117 pass, 0 fail. Closes #888 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a74e71b049 |
refactor(#885): extract loadConfig cluster into config-loader.cts (#886)
ADR-857 rollout phase 2e — the largest core.cts extraction. Move the configuration-loading subsystem (loadConfig + _getConfigDefault/ _getNestedConfigDefault/CONFIG_DEFAULTS/_deepMergeConfig, isGitIgnored + _gitIgnoredCache, _warnUnknownProfileOverrides + RUNTIME_OVERRIDE_TIERS + the dedup Sets, _resetRuntimeWarningCacheForTests) out of core.cts into a new leaf module src/config-loader.cts. core.cts re-exports the public surface (loadConfig, isGitIgnored, CONFIG_DEFAULTS, RUNTIME_OVERRIDE_TIERS, _resetRuntimeWarningCacheForTests); 12+ callers unchanged. Cycle-free: config-loader imports only leaves (configuration, config-schema, planning-workspace, shell-command-projection, core-utils, model-catalog). All core-internal helpers loadConfig touches moved with it to avoid a cycle. core keeps CANONICAL_CONFIG_DEFAULTS for its model-resolver functions, which now resolve loadConfig via the binding — this unblocks the final model-resolver extraction (2f). core.cts: 1275 -> 792 lines. Repointed tests/config-field-docs.test.cjs (a docs-parity source check) to read the CONFIG_DEFAULTS literal from its new home (config-loader.cjs). New-CLI-module checklist done (.gitignore, eslint, INVENTORY 95->96 + row, manifest, ARCHITECTURE, CONTEXT.md "Config Loader Module"). Adds tests/config-loader.test.cjs (27 tests: behavioral + shim-identity + adversarial config fixtures). Gates: lint, code-review, security-review (prototype-pollution guard confirmed intact), codex adversarial-review (0 findings; byte-identical move). Mac 4115 pass; clean-build docker: full-suite hit the local mirror's known incremental-tsc non-determinism on an unrelated re-exported symbol (findPhaseInternal, from already-merged 2d), but a clean targeted rebuild of the affected file passed 163/0 — CI's clean full matrix is the authoritative gate. Closes #885 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
dd81e3d120 |
refactor(#881): extract phase-locator fs-search into phase-locator.cts (#882)
ADR-857 rollout phase 2d. Move the phase-directory search/location functions (searchPhaseInDir, findPhaseInternal, getArchivedPhaseDirs) + their interfaces (PhaseSearchResult, ArchivedPhaseDir) out of core.cts into a new module src/phase-locator.cts. core.cts re-exports all three (callers unchanged). Cycle-free: phase-locator depends only on leaves (phase-id for token/name matching, core-utils for fs-scan/path helpers, planning-workspace for planningDir) — unblocked by the core-utils leaf (2c). This completes the phase-search split: parsing in phase-id (2a), fs-search in phase-locator. New-CLI-module checklist done (.gitignore, eslint, INVENTORY 94->95 + row, manifest, ARCHITECTURE, CONTEXT.md "Phase Locator Module"). Adds tests/phase-locator.test.cjs (37 tests: behavioral + shim-identity + adversarial phase-dir fixtures). Gates: lint, code-review, security-review, codex adversarial-review (0 findings; verbatim move checksum-verified). Mac 4078 pass; clean-build docker 12972 pass, 0 fail. Closes #881 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
282f145745 |
refactor(#877): extract shared low-level utilities into core-utils.cts (#878)
ADR-857 rollout phase 2c. Move 11 shared low-level utilities out of core.cts into a new leaf module src/core-utils.cts: POSIX path normalization (toPosixPath), filesystem scanning (detectSubRepos, readSubdirectories, getPhaseFileStats, pathExistsInternal), and small pure helpers (generateSlugInternal, extractOneLinerFromBody, filterPlanFiles, filterSummaryFiles, timeAgo, and the private extractCanonicalPlanId). core.cts re-exports the 10 public ones (callers unchanged); extractCanonicalPlanId stays private (exported from the leaf for core's fs-search functions). Cycle-free: core-utils depends only on Node built-ins + already-leafed modules (phase-id for comparePhaseNum, planning-workspace for findContextMdIn). This is the shared leaf that unblocks the phase-locator fs-search extraction (2d) — searchPhaseInDir/findPhaseInternal/getArchivedPhaseDirs can now take their utilities from a leaf instead of from core. New-CLI-module checklist done (.gitignore, eslint, INVENTORY 93->94 + row, manifest, ARCHITECTURE, CONTEXT.md "Core Utilities Module"). Adds tests/core-utils.test.cjs (88 tests: behavioral + shim-identity + adversarial). Gates: lint, code-review, security-review, codex adversarial-review (0 findings). Mac 4041 pass; clean-build docker 12935 pass, 0 fail. Closes #877 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
fa1118afa7 |
refactor(#870): extract ROADMAP.md parsing into roadmap-parser.cts (#876)
ADR-857 rollout phase 2b. Move the 6 ROADMAP.md-parsing functions (stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, getRoadmapPhaseInternal, getMilestoneInfo, getMilestonePhaseFilter) + their interfaces out of core.cts into a new leaf module src/roadmap-parser.cts. core.cts re-exports them (behavior-preserving); ~9 external callers unchanged. Resolves the parse/write straddle: roadmap.cts (ROADMAP.md mutation) now imports its 3 parsing helpers from roadmap-parser.cjs directly instead of reaching through core. roadmap-parser depends only on leaves (phase-id, planning-workspace, shell-command-projection) — cycle-free, enabled by phase 2a. New-CLI-module checklist done (.gitignore, eslint, INVENTORY 92->93 + row, manifest, ARCHITECTURE, CONTEXT.md "Roadmap Parser Module"). Adds tests/roadmap-parser.test.cjs (46 tests: behavioral + shim-identity + adversarial ROADMAP.md fixtures). Adversarial review surfaced a pre-existing fence-blindness bug in getMilestonePhaseFilter (matches phase headings inside fenced code blocks); filed as #875 and left for a separate fix (out of scope for this behavior-preserving extraction). The two fenced-fixture tests characterize the current behavior with a #875 reference and flip when it's fixed. Gates: lint, code-review, security-review, codex adversarial-review (0 correctness findings). gsd-test: clean-build docker + Mac green; the full-suite docker run's "X is not a function" errors on re-exported symbols were local incremental-tsc staleness (verified: clean rebuild of the affected files = 195 pass, 0 fail; Mac = 4001 pass). Closes #870 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2988a21c46 |
refactor(#865): extract pure phase-id helpers from core.cts into phase-id.cts (#868)
ADR-857 rollout phase 2a — the first cut of the core.cts decomposition. Move the 9 pure phase-id parsing/matching helpers (escapeRegex, normalizePhaseName, comparePhaseNum, extractPhaseToken, phaseTokenMatches, phaseMarkdownRegexSource/Exact, getMilestoneFromPhaseId, getPhaseDirFromPhaseId) out of core.cts into a new leaf module src/phase-id.cts. core.cts re-exports them (behavior-preserving); its internal callers resolve the destructured bindings. Cycle-safe by design: phase-id depends on nothing in core, so re-export creates no circular require (the property that made phase-1 io.cts clean). This is the leaf-first ordering — it unblocks the roadmap-parser extraction (2b), which imports phaseMarkdownRegexSource. New-CLI-module checklist: .gitignore, eslint.config.mjs ignores, INVENTORY.md count 91->92 + row, INVENTORY-MANIFEST.json, ARCHITECTURE.md row, CONTEXT.md "Phase Id Module" glossary entry. Adds tests/phase-id.test.cjs (63 behavioral tests incl. shim-identity + adversarial inputs). Gates: lint, code-review, security-review, codex adversarial-review (all 0 findings), and gsd-test-both (14814 pass on Mac + Linux Docker, 0 fail). Closes #865 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a5d82bf98a |
refactor(#859): extract CLI I/O primitives from core.cts into io.cts (#864)
ADR-857 rollout phase 1. Move the CLI I/O primitives — output(), error(), ERROR_REASON, setJsonErrorMode/getJsonErrorMode, and the output() large-payload temp-file spillover helpers (GSD_TEMP_DIR, ensureGsdTempDir, reapStaleTempFiles) — out of the 2271-line core.cts god-module into a new, small src/io.cts. core.cts re-exports them so existing consumers are unaffected (behavior-preserving). Repoint src/profile-pipeline.cts to import output/error/reapStaleTempFiles from io directly; graphify/intel/audit were verified not to import these symbols. Net: the leaf feature modules no longer depend on core just for I/O — the enabling first cut toward Capability extraction. New-CLI-module checklist: .gitignore (bin/lib/io.cjs), eslint.config.mjs ignores, INVENTORY.md count 90→91 + io.cjs row, INVENTORY-MANIFEST.json, ARCHITECTURE.md core.cjs/io.cjs rows, CONTEXT.md "I/O Module" glossary entry. Adds tests/io.test.cjs (28 behavioral tests incl. shim-identity and the @file: spillover branch). Gates: lint, code-review, security-review, codex adversarial-review, and gsd-test-both (14742 pass on Mac + Linux Docker, 0 fail) all green. Closes #859 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1b6bd66f2c |
feat(#770): register Claude Code lifecycle hooks (SubagentStop/Stop/PreCompact/FileChanged) (#821)
* feat(#770): register Claude Code lifecycle hooks (SubagentStop/Stop/PreCompact/FileChanged) Wire three new context-tracking events (SubagentStop, Stop, PreCompact) to gsd-context-monitor so context-headroom warnings surface at model-stop and subagent-finalisation moments — not just on PostToolUse. Add a new FileChanged hook (gsd-config-reload.js) that hot-reloads .planning/config.json context mid-session when the user edits it, injecting a config summary as hookSpecificOutput.additionalContext. Updates plugin manifest hooks.json, managed-hooks-registry, installer-migration-report allowlist, and shell-command-projection cleanup tables. Tests: 21 new assertions in enh-770-claude-hook-events.test.cjs; enh-788 and issue-766 test suites updated. Closes #770 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#770): document newly-registered Claude Code lifecycle hooks Add a Hook coverage table to the Claude Code npm installer section of docs/how-to/install-on-your-runtime.md describing SubagentStop, Stop, PreCompact, and the new FileChanged (gsd-config-reload.js) hook that hot-reloads .planning/config.json mid-session. Also fixes the changeset frontmatter (adds type: Added + pr: 821) so docs-lint can consume the fragment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#770): add gsd-config-reload.js to INVENTORY.md and regenerate manifest The feat commit added hooks/gsd-config-reload.js but did not bump the Hooks count in docs/INVENTORY.md (14→15) or add the new row, and did not regenerate docs/INVENTORY-MANIFEST.json. Both inventory-counts and inventory-manifest-sync tests failed across the full CI matrix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#770): make lifecycle-hook tests deterministic on scoped runner Replace the shared hooks/dist/ ensemble setup (ensureHooksDist / teardownHooksDist) in the Claude hook tests with per-test isolation: pre-populate each test's own tmpDir/.claude/hooks/ with stub files and pass installerMigrations:[] to install() so the first-time-baseline migration does not remove the stubs before the copy step can run. Root cause: hooks/dist/ is gitignored and absent on a fresh npm ci. ensureHooksDist() created it and teardownHooksDist() deleted it, but with --test-concurrency=4 both test files ran concurrently as separate Node.js worker processes sharing the same filesystem. One file's afterEach teardown deleted hooks/dist/ while the other file's install() was copying from it, producing an ENOENT (reproduced 2/10 runs locally). The additional issue: even with pre-placed stubs surviving the copy race, the 000-first-time-baseline migration classified hooks/gsd-*.js as bundled-gsd-hook artifacts, auto-removed them, and the copy step never re-ran (hooks/dist/ absent) — leaving contextMonitorFile missing and all hook registrations silently skipped (the 'got: []' symptom). Fix: pre-populate targetDir/hooks/ per-test (isolated temp dir) AND pass installerMigrations:[] so the baseline scan is skipped. The Qwen suites already used this pattern correctly; the Claude suites are aligned to it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#770): ship gsd-config-reload.js by adding it to build-hooks HOOKS_TO_COPY The #770 feature added hooks/gsd-config-reload.js and registered it in MANAGED_HOOKS, the installer, INVENTORY, and the test EXPECTED_ALL_HOOKS list — but never added it to scripts/build-hooks.js HOOKS_TO_COPY. As a result the hook was never copied into hooks/dist/ during the build, so: - the hook would never ship to users (real production bug — the FileChanged config-reload feature was dead-on-arrival), and - install-minimal-hooks.test.cjs #1755 ("all expected hooks are copied from hooks/dist/ to target", ".js hooks are executable after copy", "manifest contains .js hook entries") failed on any environment with a clean checkout (no pre-existing hooks/dist/): coverage, full test macos-22/macos-24, test ubuntu-24. The failures were masked locally only by a stale hooks/dist/ left from a prior build (build-hooks copies into dist without clearing it). On CI's fresh `npm ci` there is no dist, so the omission surfaced. Fix: add 'gsd-config-reload.js' to HOOKS_TO_COPY so build-hooks stages it into hooks/dist/ alongside the other JS hooks. Verified by removing hooks/dist/ and rerunning the full suite green (0 fail). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#770): make config prototype-pollution beforeEach deterministic on scoped runner Root cause: the #663 and alert-#26 prototype-pollution describe blocks seeded .planning/config.json in beforeEach via a bare runGsdTools('config-ensure-section') whose result was discarded. That command runs in a spawned gsd-tools child; on the scoped CI lane (--test-concurrency=4, config.test.cjs scheduled alongside the heavy install/tarball suites that #770 pulled into the targeted set) the child can be transiently killed under resource pressure (non-zero exit, empty stderr — an OS-level kill, not an app error). The swallowed failure left config.json absent, so the first subtest's readConfig() threw ENOENT opening <tmp>/.planning/config.json. Only 1 of 4 subtests failed, confirming a per-invocation transient, not a deterministic miss; the full suite schedules files differently so config.test.cjs did not collide with those heavy neighbors → passed there. Fix: add ensureConfigReady(tmpDir) which retries config-ensure-section on ANY failure or missing file and throws a clear diagnostic if it still cannot create config.json, then use it in both prototype-pollution beforeEach blocks. Setup is now deterministic under load; the #663/alert-#26 security assertions are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1040fb792e |
feat(#777): register Cursor-native hooks (.cursor/hooks.json) for session-start/post-tool parity (#831)
* feat(#777): register Cursor-native hooks (.cursor/hooks.json) for session-start/post-tool parity - Add gsd-cursor-session-start.js: injects STATE.md presence reminder (or new-project nudge) into Cursor sessions via the sessionStart hook event - Add gsd-cursor-post-tool.js: emits an additional_context nudge when write-class tool calls touch .planning/ files (postToolUse hook event) - Add 'cursor-hooks-json' installSurface to runtime-config-adapter-registry; writeCursorHooksJson/reconcileCursorHooksJson write the canonical { version: 1, hooks: { sessionStart, postToolUse } } JSON shape with idempotent reconciliation that preserves user-owned hook entries - Hook scripts are copied with /gsd:→gsd- rewrite so installed files contain no colon-form slash-command refs (bug-376 invariant) - 20 new tests in tests/cursor-hooks.test.cjs cover all reconciler paths, entry helpers, removal, runtime adapter surface, and hook script behavior - Update CONTEXT.md, ARCHITECTURE.md, installer-migrations.md, and 000-first-time-baseline.cts to include Cursor hooks.json surface Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#777): build hooks/dist on demand in bug-376 test for scoped/windows CI hooks/dist is gitignored and only produced by `npm run build:hooks`. The CI scoped (ubuntu-latest/node-22) and windows (windows-latest/node-24) test jobs do NOT run build:hooks before executing tests, so bug-376's prerequisite suite was failing with "hooks/dist not found" on both legs. Add ensureHooksDist() helper (mirrors bug-3357 pattern) that builds hooks/dist on demand in the before() hooks of prerequisite and Suite 3. Also add ensureHooksDist() call to Suite 3's before() so the snapshot step is also hermetic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2f07443119 |
refactor(#60): make runtime config adapter registry explicit (#795)
* refactor(#60): make runtime config adapter registry explicit Replace scattered inline `runtime === '...'` config-mutation branching in bin/install.js with an explicit, typed adapter registry. The new src/runtime-config-adapter-registry.cts maps each of the 15 supported runtimes to a config intent { installSurface, writesSharedSettings, finishPermissionWriter }; install()/finishInstall() dispatch by resolved intent instead of runtime-name checks (cursor/windsurf/trae collapse to one profile-marker-only branch). Behavior-preserving: the same config files are written for the same runtimes (opencode still writes both settings.json and its permissions; kilo writes only its permissions; codex minimal-mode and opencode GSD_TEST_MODE guards unchanged). Unknown runtimes fail loudly via TypeError, with an Object.hasOwn barrier so prototype-chain keys (__proto__/constructor) also throw rather than returning a bogus intent. Leads the installer-refactor chain (#58 -> #60 -> #56), building on ADR-58. Closes #60 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#60): add changeset for runtime config adapter registry Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#60): register Runtime Config Adapter Registry in CONTEXT.md glossary Per docs/contributor-standards.md, every new Module/seam must get a `### <Name>` entry under the domain glossary. Adds the entry for the runtime-config-adapter-registry seam introduced in this PR (interface, policy boundary, source file, ADR-58 / #60 cross-references). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4056d830bc |
refactor(#651): consolidate verification-status routing into one queryable seam (#755)
* refactor(#651): consolidate verification-status routing into one queryable seam The passed/gaps_found/human_needed verification status was re-encoded as bare strings across three prose surfaces (gsd-verifier emits, execute-phase routes, ship gates), each independently deciding the per-status next action with no parity coupling — the DEFECT.GENERATIVE-FIX class. Give the enum one home: src/verification.cts (-> bin/lib/verification.cjs) exposing `gsd_run query verification.status <phaseDir>` returning a typed {status, next_action, next_command}. ship.md and execute-phase.md now consume the query instead of re-deriving the routing in prose; gsd-verifier.md points at the shared vocabulary as the single emitter (values unchanged). Also fixes the latent broad-grep status misread (DEFECT.FRONTMATTER-SCALAR- BROAD-GREP): execute-phase.md read `grep "^status:"` over the whole report, so a body `status:` line could misroute a valid phase. Extraction is now frontmatter-scoped in one place. A parity test fails if a verifier status gains no route. Lands the two CONTEXT.md DEFECT entries captured on the issue. Closes #651 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#651): set changeset pr to 755 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
cf8bd3cd5e |
fix(#683): auto-degrade phase execution to sequential on worktree base mismatch (#749)
* fix(#683): auto-degrade phase execution to sequential on worktree base mismatch Claude Code forks worktree-isolated executors off the repository default branch (origin/HEAD), not the orchestrator's HEAD. Running /gsd-execute-phase on a branch diverged from the default (unmerged milestone/feature branch) left every executor without the phase's plan files and tripped the worktree-branch-check guard with `exit 42` — 100% reproducible, all OSes. - New module src/worktree-base-ref.cts: HEAD-vs-fork-base drift detection (origin/HEAD with symbolic-ref fallback) and no-clobber worktree.baseRef management, exposed as `worktree base-check` / `worktree set-baseref`. - execute-phase.md: pre-dispatch, for Claude Code with worktrees enabled, auto-degrades the run to sequential on the main tree when a base mismatch is detected, recommending worktree.baseRef:"head". The exit-42 guard stays as a backstop. - Installer: fresh local Claude installs set worktree.baseRef:"head" in .claude/settings.local.json (no-clobber, respecting an explicit shared settings.json value); upgrades print an opt-in notice pointing at `gsd-tools worktree set-baseref`. - Docs: how-to guide, CLI/config reference, planning-config cross-ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#683): auto-apply worktree.baseRef on upgrade; gate fresh+upgrade on use_worktrees Per maintainer direction: on a local Claude Code UPGRADE, set worktree.baseRef:"head" automatically (no opt-in notice) when the project's workflow.use_worktrees is enabled, instead of merely printing a remediation notice. For consistency the FRESH path is now gated the same way: both paths compute worktrees-enabled once (bounded walk-up read of .planning/config.json, default enabled unless workflow.use_worktrees === false) and apply the no-clobber baseRef only when enabled — never overwriting an explicit value in settings.local.json or a shared settings.json. gsd-tools worktree set-baseref remains for manual use. Docs + changeset updated; tests hardened (file-exists assertions, fresh+disabled case, upgrade idempotency). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#683): measure workflow byte-budget on LF, fixing Windows-only CI failure The workflow-size-budget test failed only on Windows: git checks out the .md files as CRLF (no eol=lf in .gitattributes) and byteCount used fs.statSync().size (raw on-disk bytes), counting an extra \r per line. That inflated execute-phase.md — the XL high-water-mark file pinned near its ceiling by the tighten-only ratchet — from 88492 LF bytes to ~90245 on Windows, over the 90000 XL ceiling, while passing on the LF-checkout Mac/Linux runners. The ceilings are explicitly "calibrated against raw `wc -c`" on an LF checkout, so the measurement should be LF-based on every platform. byteCount now reads the file and counts Buffer.byteLength after stripping CR, making the budget platform-independent (a no-op on LF checkouts; verified statSync === normalized for all 88 workflow files). No ceilings changed. Added a regression test asserting CRLF and LF content of the same file count identically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#683): make worktree-base-ref test path mocks Windows-safe (path.join) tests/worktree-base-ref.test.cjs keyed its injected readFile/writeFile mocks (and a few expected `file` values) with forward-slash template literals like `${claudeDir}/settings.local.json`. The module composes those paths with path.join(), which emits backslashes on Windows, so the mock keys never matched the module's lookup → readFile returned null → resolveEffectiveBaseRef / cmdWorktreeBaseCheck / cmdWorktreeSetBaseRef (and the JSONC variants) failed on the Windows full-test runner only (they passed on Mac/Linux, and the install tests passed because they use the real filesystem). The module is correct; only the test fixtures hardcoded '/'. All mock keys and path assertions now use path.join(base, ...) mirroring the module, so they match on every platform (no-op on POSIX). 19 path references across 16 lines. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b79b767629 |
refactor(bin): replace process.exit() in CLI entrypoints + ratchet rule to error (#738) (#741)
Part 2 of 2 of the n/no-process-exit cleanup (completes umbrella #738; part 1 was #739/scripts). Converts the 20 flagged process.exit() calls in the three hand-written gsd-core/bin CLI entrypoints and flips n/no-process-exit to error. - New src/cli-exit.cts -> gsd-core/bin/lib/cli-exit.cjs (ExitError + runMain), the gsd-core-side equivalent of scripts/lib/cli-exit.cjs; registered in .gitignore, eslint ignores, and the inventory manifest like its siblings. - gsd-tools.cjs: 13 apply-prompt-budget exits -> throw ExitError; main()->runMain. - verify-reapply-patches.cjs: 6 exits -> throw ExitError / return verdict; runMain. - check-latest-version.cjs: 1 exit -> return verdict; runMain. - eslint.config.mjs: n/no-process-exit warn -> error. Scope note: the gsd-core/bin/lib/*.cjs modules (core, state, profile-pipeline, roadmap-command-router, adr-parser, ui-safety-gate) are tsc-generated and eslint-ignored (ADR-457), so their process.exit calls were never flagged and are intentionally left untouched. Only the linted hand-written entrypoints are in scope. Exit codes verified unchanged for all three entrypoints. Closes #738 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
11afca2968 |
feat(#656): Research module — content-addressed cache + provider seam + registry-API legitimacy (#664)
* feat(#656): add Research Store module (content-addressed cache, TTL staleness) Content-addressed research cache behind a clock seam: researchKey (sha256, deterministic), putResearch/getResearch ({hit,stale}, never throws), ttlForSource (curated HIGH 30d / MED 7d / web LOW 1d), two-tier resolveStorePath (curated -> ~/.gsd/research-cache, web/synthesis -> project .planning/research/.cache). 28 behavioral + property tests; boundary coverage at ttl-1/ttl/ttl+1. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#656): add Research Provider module (waterfall + confidence + plan) Single source of truth for the Balanced provider waterfall (docs Context7->Ref->Jina, web Exa+Tavily, fallback Perplexity/Brave, Firecrawl scrape-only). classifyConfidence stamps HIGH|MEDIUM|LOW by provider (never throws). providerAvailability maps config flags to usable providers. planResearch checks the Research Store (injected seam) and returns cache-hits + a per-question fetch plan, falling through the waterfall to the always-available websearch terminal. 22 behavioral + property tests. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#656): add Package Legitimacy module (registry-API verdicts, slopcheck optional) Replaces the pip-install-or-degrade slopcheck prose gate with code: classifyPackage (pure, never throws) computes OK|SUS|SLOP from tunable thresholds (minAgeDays 30, minWeeklyDownloads 1000, requireRepo). checkPackages queries injectable npm/PyPI/crates registry adapters (real https with 5s timeout, degraded-not-thrown on failure); slopcheck is one optional adapter that can only escalate severity, never degrade to [ASSUMED]. 34 behavioral + property tests; boundary coverage on age and downloads (limit-1/limit/limit+1). Known follow-up: real npm adapter must add api.npmjs.org last-week downloads fetch (currently null -> unknown-downloads). Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#656): detect Tavily/Ref/Perplexity/Jina provider keys; complete npm downloads adapter config: add tavily_search/ref_search/perplexity/jina availability flags (env var or ~/.gsd/<x>_api_key), mirroring brave_search/exa_search/firecrawl, so the Research Provider waterfall can gate them. package-legitimacy: real npm adapter now fetches api.npmjs.org last-week downloads (bounded, degraded-not-thrown) so weeklyDownloads is populated. +12 config tests; 34 legitimacy tests unchanged. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#656): expose Research seam via gsd-tools query (research-plan, research-store, package-legitimacy) Routes the L2-hybrid surface so agents reach it as CLI: 'query research-store get/put' (cache, HOME-sandboxable), 'query research-plan --input' (cache-hits + fetch plan from planResearch), 'query package-legitimacy check --ecosystem' (async registry verdicts). Commands skip .planning root resolution and appear in top-level usage. 5 behavioral runGsdTools tests; command-contract unchanged (335). Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(#656): document Research module (CONTEXT predicates, ADR-0656, architecture, changeset) Adds GSD-RESEARCH.* + DEFECT.RESEARCH-PROVIDER-PROSE-DRIFT predicates to CONTEXT.md, ADR-0656 recording the L2-hybrid seam decision, a docs/ARCHITECTURE.md Research Module subsection, and an Added changeset fragment (pr:0, backfill on PR). Notes the #657 deferrals (agent collapse + install.js MCP mapping). Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#656): sync inventory for research modules Regenerate INVENTORY-MANIFEST.json and bump docs/INVENTORY.md CLI Modules count 82->85 with rows for research-store/research-provider/package-legitimacy (DEFECT.INVENTORY-DRIFT). Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#656): eslint-ignore generated research .cjs artifacts (ADR-457) research-store/research-provider/package-legitimacy .cjs are tsc-generated from src/*.cts, so they belong in the ESLint ignore block (lint the .cts source, not the emitted .cjs). Fixes tests/551-eslint-bin-lib-coverage. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#656): backfill changeset pr number to #664 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#656): satisfy eslint lint-tests gate Fix 20 eslint errors in the new research files: use helpers.cleanup() instead of raw fs.rmSync() in tests (local/no-raw-rmsync-in-tests, Windows-EBUSY retry budget); drop redundant '| string' union members and unnecessary type assertions; deterministic object normalization in researchKey (no-base-to-string). Logic unchanged; 6180 tests still green. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#656): harden package legitimacy per review (W1/W2/I3/I4) W1: httpsGet now reads statusCode; npm/PyPI/crates map 404 -> exists:false -> SLOP (registry-existence is the #1 slopsquatting defense; previously only npm caught it). Transport made injectable (_setHttpGet) for hermetic 404 tests. W2: suspicious-postinstall is now terminal SLOP independent of the optional slopcheck adapter, and the regex drops the bare https?:// arm (over-fired on esbuild/sharp/node-gyp) for shell-exec/download-exec signatures only. I3: checkPackages now threads version to registry.lookup and adapters verify that specific version exists. I4: moreServerVerdict -> moreSevereVerdict. +11 regression tests (all RED-first); 45 total green. Addresses review by @davesienkowski on #664. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#656): research-store tier coherence + freshness + version TTL (W4/I1/I2/I4) I1: tier now derives from source (curated -> user ~/.gsd, else -> project .planning), not kind, so put-tier and get-tier can't diverge; kind is a key component only. W4: getResearch searches both tiers and returns the freshest (non-stale preferred), never letting a stale curated entry shadow a fresh web one; blank version caps TTL at 1 day (no 30d on version-blind keys). I2: atomic platformWriteSync instead of raw fs.writeFileSync on the shared global path. I4: dropped the dead ttlForSource arm. CLI get now searches both tiers. +5 RED-first regression tests; 38 green. Addresses review by @davesienkowski on #664. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#656): expose classifyConfidence as a CLI route, killing dead code (W3) Adds 'gsd-tools query classify-confidence --provider X [--verified]' so research agents get the confidence tier FROM CODE (provider waterfall + verification lever) instead of asserting it in prose. classifyConfidence previously had no runtime caller. HIGH means 'trusted provider'; --verified raises web results to MEDIUM (verification semantics documented in ADR-0656). +4 behavioral tests. Addresses review by @davesienkowski on #664 (W3). Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#656): close Codex adversarial-review findings (path-traversal, version-age, malformed-cache) HIGH: research key must be 64-hex sha256 (isValidResearchKey) + resolved-path containment check in put/get + CLI validation -> blocks '../../x' arbitrary-file-write. HIGH: package legitimacy now derives publishedAt from the REQUESTED version (npm time[version], PyPI releases[version] upload_time, crates versions[].created_at) so a new malicious version of an old package can't inherit old age and evade 'too-new'. MEDIUM: getResearch validates entry shape (finite fetched_at + positive ttl + required fields) -> malformed cache entry is a miss, not fresh-forever. +regression tests (RED-first); 111 green. Codex adversarial review (required pre-PR gate). Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#656): close code-review correctness findings (1) package-legitimacy CLI now rejects unknown --flags instead of silently consuming the following package as a flag value; only --ecosystem takes a value. (2) crates recent_downloads (90-day) normalized to a weekly figure before the minWeeklyDownloads threshold (was ~13x too lenient). (3) research-plan --input validates parsed JSON is an object with an Array questions before destructuring -> clean usage error instead of an uncaught TypeError on null/bad input. (4) research-store put rejects a flag value that is itself a --flag (no more storing '--source' as content). (5) planResearch skips questions whose text is not a non-empty string instead of emitting question:undefined. +13 RED-first regression tests; 143 green. Code-review gate. Issue #656. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(#657): extract researcher documentation_lookup to shared @-reference 6 researcher agents carried a near-duplicate <documentation_lookup> block; consolidate into gsd-core/references/research-documentation-lookup.md (@-included). Unifies the ctx7 CLI fallback to the safer 'command -v ctx7' guard (drops silent 'npx --yes ctx7@latest' execution in 5 agents). Behavior-preserving dedup; inventory 63->64 references. Phase A of the agent collapse. Issue #657. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(#657): extract researcher philosophy + verification-protocol to shared @-references philosophy and the pitfalls+pre-submission-checklist common-core were near-duplicated in project/phase researchers; consolidate into gsd-core/references/research-{philosophy,verification-protocol}.md (@-included). phase-researcher keeps its 3 extra checklist items inline. Pre-submission domains checklist made agent-agnostic so project-researcher doesn't lose features/architecture coverage. Write-contract intentionally left inline (bug-214 tests assert it verbatim). Inventory 64->66 refs. Behavior-preserving. Phase A. Issue #657. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#657): wire gsd-phase-researcher to the Research seam (Phase B / S1) The phase researcher now CALLS the code seam instead of carrying inline mechanics: provider waterfall -> 'gsd-tools query research-plan' (+ research-store put to cache digests); confidence-tier prose -> 'gsd-tools query classify-confidence'; slopcheck pip-install protocol -> 'gsd-tools query package-legitimacy check'. This makes the Research module a real runtime consumer (validates the seam end-to-end, addresses reviewer S1) and removes the duplicated waterfall/confidence/slopcheck prose. RESEARCH.md output contract, commit step, structured returns, and Phase-A @-includes unchanged. package-legitimacy-gate.test.cjs rewritten prose-grep -> behavioral (asserts the seam invocation). Issue #657. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#657): wire gsd-project-researcher to the seam + add tavily/ref/jina MCP tools (Phase C.1) project-researcher now calls gsd-tools query research-plan / classify-confidence (+ research-store put) instead of the inline provider waterfall + confidence-tier prose (mirrors the phase-researcher rewire; no package-legitimacy — phase-only). Output contract (STACK/FEATURES/ARCHITECTURE/PITFALLS/SUMMARY.md + sections, no-commit, structured returns, Phase-A @-includes) unchanged. Adds mcp__tavily/ref/jina__* to the project/phase/ui researcher tools frontmatter (Balanced provider set) so install.js MCP mapping (C.2) has a consumer. Issue #657. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(#657): cover tavily/ref/jina MCP install handling + frontmatter parity guard (Phase C.2) Investigation: exa/firecrawl have no explicit per-runtime tool-mapping — every mcp__<server>__* except context7 rides the generic passthrough (Copilot lowercases; OpenCode/Cursor/Windsurf/Augment keep as-is; Gemini auto-discovers). tavily/ref/jina are handled identically, no install path broken. Added 12 copilot-install passthrough tests + a mcp-tool-inheritance parity guard (tavily co-declared with exa, jina with firecrawl, ref present across the 3 web researchers) so the MCP set can't drift. No io.github registry ids invented (none sourceable in-repo); documented as a follow-up. 488 tests green. Issue #657. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#657): profiles as source of truth for researcher agents + drift-guard (Phase C.3) scripts/research-profiles.cjs declares each of the 7 researcher agents' identity + contract (name, description, color, tools, required @-includes, required gsd-tools seam calls, output-contract markers). scripts/gen-research-agents.cjs --check validates every committed agent against its profile; --write regenerates ONLY the frontmatter from profiles (body untouched) and is a verified no-op against the current agents (zero diff = fidelity). tests/research-agent-profiles.test.cjs is the DEFECT.GENERATIVE-FIX drift guard. Design note: profiles govern the generatable/contract surface rather than destructively regenerating the disparate operational prose bodies (those were deduped via @-includes in Phase A). scripts/ is not inventoried (no inventory change). Issue #657. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#657): complete agent provider-dispatch + parity guard; align legitimacy field; validate profiles Adversarial-review findings: (HIGH) the seam-wired agents' Step-C dispatch only mapped 6 providers, so a planResearch result of jina/ref/perplexity/brave (reachable via the waterfall fallbacks) had no handling -> agent stall; completed both agents' dispatch to all 9 PROVIDER_WATERFALL ids + a catch-all, and added a parity test asserting agent dispatch stays in sync with research-provider PROVIDER_WATERFALL (DEFECT.GENERATIVE-FIX). (MEDIUM) phase-researcher package-legitimacy JSON example used 'package' but the module returns 'name' -> aligned. (LOW) gen-research-agents checkAgent now returns a clear failure for a malformed profile instead of throwing. +parity/validation tests (RED-first). Issue #657. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#656): make classifyConfidence verification-evidence-driven (W3) Confidence conflated provider authority with claim verification — context7/ref stamped HIGH purely by provider identity, and the only verification lever was a self-set --verified flag. Split into two axes: provider authority (static) + verification evidence (code-computed). HIGH now requires ground-truth corroboration (legitimacyVerdict OK), independent of provider; authority alone caps at MEDIUM; SLOP caps at LOW; the self-reported --verified is demoted to a MEDIUM-only web lever. HIGH = corroborated-against-authoritative-source, not a correctness guarantee. Adds --legitimacy-verdict to the classify-confidence CLI; updates CONTEXT.md predicate + ADR-0656 (tier set unchanged, ADR-consistent). Addresses davesienkowski's W3 review on #664. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#656): bind classify-confidence verdict to code, closing CLI self-grading Adversarial review found the new --legitimacy-verdict flag was caller-supplied, so an agent could self-assert OK->HIGH without any real legitimacy check — reintroducing the exact self-grading hole W3 closes. Remove the free flag; the CLI now computes the verdict via checkPackages only when --package/--ecosystem is given (code-computed, not agent-asserted). Update the stale CLI test (context7 alone -> MEDIUM) and extend the property test to vary legitimacyVerdict. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0e259a589c |
fix(#705): route hardcoded $HOME gsd-tools invocations in agents/commands through gsd_run (#707)
* fix(#705): route hardcoded $HOME gsd-tools invocations in agents/commands through gsd_run The hardcoded `node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" <cmd>` form (fixed for workflows in #621/#637) survived in agent/command surfaces and misresolves on global/shim-only installs. Route every agent-executed invocation through the resolved `gsd_run` launcher in gsd-phase-researcher, gsd-planner (load_graph_context extracted to a shared reference to stay under the planner size budget), import, and graphify. Add a regression guard over agents/ + commands/ + gsd-core/references/ bash blocks. User-facing display messages and docs are intentionally left untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#705): use repo changeset fragment format (type: Fixed, pr: 707) The hand-written fragment used the standard changesets package format (package: bump) which lacks the type:/pr: frontmatter the repo's docs-required lint consumes (fail_malformed_fragment / missing_type). Regenerated via scripts/changeset/new.cjs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
463cffd894 |
chore(#604): rename get-shit-done/ runtime directory to gsd-core/ (#615)
* chore(#604): rename get-shit-done/ runtime directory to gsd-core/ Renames the installed runtime directory `get-shit-done/` to `gsd-core/` so the on-disk name matches the package (`@opengsd/gsd-core`), repo, and binary (`gsd-tools`). The npm package name and binary are unchanged; npx/npm consumers are unaffected. Mechanical (bulk, ~90% of the diff): - `git mv get-shit-done gsd-core` - Swept path/identifier references across the repo via `perl -pe 's/get-shit-done(?!-\w)/gsd-core/g'`. The negative lookahead preserves the five legitimate slug variants that are NOT the directory: get-shit-done-{OLD,cc,classic,cli,redux} (old package/repo names). - Build/manifest wiring: package.json (bin, files, coverage globs), tsconfig.build.json (outDir), ~86 .gitignore build-output entries, stryker.config.mjs, scan-ignore files, install.js path strings. - Frozen (not rewritten): CHANGELOG.md history; translated docs (README.<locale>.md and docs/{ja-JP,ko-KR,pt-BR,zh-CN}/). New logic (review here): - src/installer-migrations/003-rename-get-shit-done-to-gsd-core.cts: a proper ADR-0008 installer migration. On upgrade it walks the legacy `~/.claude/get-shit-done/` tree, classifies each file via the prior install manifest, and emits remove-managed / backup-and-remove for managed files while PRESERVING unknown user-added files. Symlink-safe (skips a symlinked root and symlinked entries; bounds-checks every path under configDir). The framework rolls back on install failure. Emptied dirs may remain (framework has no recursive dir-removal primitive) — documented. - scripts/lint-legacy-dir-name.cjs: CI regression guard forbidding the bare `get-shit-done` directory token (split token to avoid self-match; case- insensitive; `(?!-\w)` lookahead allows the slug variants; allowlists CHANGELOG, translated docs, and `gsd-allow-legacy-name` marker lines). Wired into the lint-tests CI job. - Restored scripts/lint-package-identity-drift.cjs detection regexes (the mechanical sweep had wrongly rewritten the old-name patterns it exists to detect) and marked them as intentional legacy references. - TDD tests for the migration and the guard; do.md slash-command guard regex tightened so a `/gsd-core/bin` path segment is not mistaken for a command; changeset + docs/installer-migrations.md row added. Breaking: the installed runtime path moves `~/.claude/get-shit-done/` -> `~/.claude/gsd-core/`. Migration 003 removes the stale legacy dir's managed files (preserving user files) on upgrade. Users with custom hooks/configs hardcoding the old path must update them. Closes #604 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): unsweep pending changesets + allowlist injection-example docs CI fixes for the rename PR: - Do not sweep pending .changeset/*.md (ephemeral release-note fragments, like CHANGELOG); reverted those body edits so 5 pre-existing malformed fragments (missing type/pr) no longer enter the PR diff and trip docs-lint. Allowlisted .changeset/ in the legacy-name guard accordingly. - Allowlisted TEST-EXAMPLES.md and docs/explanation/security-model.md in prompt-injection-scan.sh: they contain intentional injection examples / security-model prose; the path-reference rewrites are kept. CodeQL alerts on this PR are pre-existing (alert lines unchanged by this PR; none in the new migration/guard) and are out of scope for the rename. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): resolve CodeQL alerts surfaced on this PR The rename diff touched files carrying pre-existing CodeQL findings; per the no-pre-existing-dismissal rule, fixing every surfaced alert rather than waving them off. All behavior-preserving: - scripts/ci-test-scope.cjs: build the config-path match from string .includes() instead of a RegExp over an arg-derived value (js/regex-injection). - src/profile-output.cts: escape backslashes before pipe-escaping desc/safeName so the table-cell escape is complete (js/incomplete-sanitization). - tests/{bug-2643,bug-2808,docs-parity-live-registry}: two-pass HTML-comment strip so a bare/unclosed `<!--` cannot survive (js/incomplete-multi-character-sanitization). - tests/inline-plan-threshold: drop the no-op `\s`->`\s` identity replace, keep the meaningful POSIX-class conversion (js/identity-replacement). Verified: build:lib green; the touched test files + ci-test-scope + profile-output suites pass; lint:legacy-name clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): correctly resolve remaining CodeQL alerts (regex-injection + sanitization) The prior commit's fixes for two alerts were ineffective: - ci-test-scope.cjs js/regex-injection: the alert is the CLI-arg-derived `file` reaching static regex `.test(file)` calls (not the config rule). Removed ALL regex over file/t — startsWith/includes/=== string checks + an isWindowsHint helper — so there is no regex sink for the tainted value. - js/incomplete-multi-character-sanitization (3 test files): a single `.replace(/<!--...-->/g,'')` can let `<!--` re-form. Replaced with a fixpoint loop (replace until stable) plus a final bare-opener strip. Verified: no regex over file/t remains; ci-test-scope + the 3 test suites pass; lint:legacy-name clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): make ci-test-scope + comment-strippers regex-free to clear CodeQL CodeQL flags the regex PATTERNS syntactically (regex-injection on the --files arg split; incomplete-multi-character-sanitization on the <!--...--> replace), so loop fixes do not satisfy it. Made these paths regex-free: - ci-test-scope.cjs splitFiles: char-by-char separator tokenizer (no /[,\\s]+/). - 3 test files: indexOf/slice HTML-comment stripper (no .replace(/<!--/)). Behavior preserved; ci-test-scope + the 3 suites pass; guard clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): unblock security base64 scan on the large rename diff The security job hit its 10m timeout: base64-scan.sh choked on the binary test fixture tests/feat-3594-parser-property-style.test.cjs (embedded NUL/ non-UTF8 bytes -> thousands of bogus blobs + "ignored null byte" warnings), and the ~800-file rename diff is slow to scan regardless. - scripts/base64-scan.sh: skip binary-by-content files (grep -Iq .) — they can't carry base64-obfuscated *text* and feeding NUL bytes through the per-line scanner is pathologically slow. collect_files already filtered binary *extensions*; this catches binary *content* in text extensions. - .github/workflows/security-scan.yml: raise the security job timeout 10m->30m to accommodate very large diffs (the scan itself is unchanged). Verified locally: scan skips the fixture, 0 "ignored null byte" warnings, 0 findings, exit 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): sweep get-shit-done refs introduced by merging next The branch was updated with next (#614/#384/#618 etc.), which reference the get-shit-done/ dir (still named that on next). Swept the stale references in the merged files to gsd-core so the rename stays consistent and lint:legacy-name passes: - commands/gsd/discuss-phase.md (runtime-launcher shim paths) - src/core.cts (getAgentsDir layout comments) - tests/bug-384-agents-runtime-aware.test.cjs (require path to runtime lib) Verified: guard 0 violations; build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): exclude gsd-core/ path segments from bug-3683 command cross-ref invariant The #614 runtime-launcher shim added to discuss-phase.md references `${_GSD_RUNTIME_ROOT}/gsd-core/bin/...`. bug-3683's REF_PATTERN excluded path-y refs only via lookbehind, but `}` precedes `/gsd-core/` in the shim, so it mis-read the directory path as a dangling `/gsd-core` command ref (same class as the #604 bug-2954 fix). Added a trailing `(?![\w-]*\/)` so `/gsd-<x>/...` path segments are not treated as slash-command references. Verified locally on BOTH platforms before pushing: - mac (node 26) full suite: 0 failures - gsd-test-runner (linux, node22 image) full suite: 0 failures - bug-3683 + bug-2954 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): lazily resolve findProjectRoot in gsd-tools (harden flaky CI) CI intermittently failed state.test's gsd-tools subprocess with "findProjectRoot is not a function" (flip-flopping across legs; not reproducible on mac full suite, gsd-test linux full suite, test:unit, or state.test x8). findProjectRoot is a re-export from core.cjs (sourced from project-root.cjs); binding it via destructure at module-load can be undefined under a load-ordering edge. Resolve it lazily at call time via a small wrapper so the lookup happens after core.cjs is fully initialized. Verified green on BOTH platforms before pushing: - mac (node 26) full suite: 0 failures - gsd-test-runner (linux, node22) full suite: 0 failures - state.test.cjs: 106/106; gsd-tools loads cleanly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): allowlist verification-patterns.md placeholder examples in secret scan The rename git-mv'd references/verification-patterns.md into gsd-core/, pulling it into the secret-scan diff. It documents stub/placeholder RED-FLAG env-var examples (illustrative Stripe test-key / database-URL / API-key placeholders) — not real credentials. Added it to .secretscanignore with the strict annotation, mirroring the existing gsd-core/workflows/plan-phase.md exception. Verified locally: secret-scan-lint --strict OK; secret-scan --diff origin/next exits 0 with 0 findings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
91f5bd7cb1 |
feat(#607): rebuild get-shit-done-cc → gsd-core migration (per-package cache + installer auto-cleanup + --dry-run) (#611)
* feat(#607): rebuild get-shit-done-cc → gsd-core migration Leftover get-shit-done-cc installs poisoned the shared update cache, causing a permanent false "update available". Rebuild the migration so a stale old install is both harmless and actively removed. - Per-package update cache filename (gsd-update-check-<slug>.json) in the shared ~/.cache/gsd dir, single-sourced via package-identity; writers stamp package_name and readers reject foreign/absent lineage. Multi- runtime visibility preserved (same shared dir + filename across runtimes). - New get-shit-done/bin/lib/legacy-cleanup.cjs seam: detects code-file references to the old package + the legacy fixed-name cache across home runtime dirs; installer auto-cleans on every install; --dry-run previews and mutates nothing. User hooks and dev-preferences are never touched. - update.md cache-clear globs gsd-update-check*.json across ALL supported runtimes (adds cursor/windsurf/augment/trae/qwen/hermes/codebuddy/cline). - Fix worker MODULE_NOT_FOUND post-install (ship managed-hooks-registry.cjs + degrade gracefully) so the per-package cache is always written. - Diataxis how-to: docs/cleanup-get-shit-done-cc.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#607): add changeset for PR #611 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#607): set USERPROFILE alongside HOME in dry-run install test for Windows os.homedir() reads USERPROFILE on win32, so HOME-only isolation let the spawned installer scan the real runner home on windows-latest. Set both. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
3bb2f8f1c5 |
docs: rebrand to GSD Core and restructure docs with Diataxis (#605)
* chore: wire docs/agents config into AGENTS.md Agent skills section
Add the `## Agent skills` discovery block pointing the engineering
skills at the existing docs/agents/{issue-tracker,triage-labels,domain}.md
files (issue tracker, triage label mapping, single-context domain docs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: rebrand to GSD Core and restructure docs with Diataxis
Reorganise the root README and docs/ around the Diataxis framework
(tutorials, how-to guides, reference, explanation), add new how-to
guides and schema references (STATE.md / CONTEXT.md / PLAN.md /
planning artifacts), and cross-link the whole set. Update the lone
legacy gsd-build reference to open-gsd; keep internal get-shit-done/
filesystem paths unchanged (directory rename tracked separately in
open-gsd/gsd-core#604). Regenerate the ja-JP, ko-KR, pt-BR and zh-CN
localised trees to mirror the new structure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: backfill changeset PR number (#605)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
0c1084ba88 |
fix(#48): verify-only worktree_branch_check + orchestrator cwd-drift guard (#590)
Closes #48. Makes the canonical worktree_branch_check fragment verify-only/fail-closed (exit 42, no git reset self-recovery), adds an orchestrator fail-closed collection rule and a cwd-drift guard at execute_waves entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b88d6d6ef1 |
refactor(#588): consolidate duplicated worktree_branch_check into one canonical fragment (#589)
Extracts the fail-closed worktree branch-check guard into a single canonical fragment (get-shit-done/references/worktree-branch-check.md) and repoints all five sites at it; orchestrator embeds the runnable block at dispatch. All safety invariants preserved; adversarially reviewed; full matrix green. Closes #588. |
||
|
|
faf329ecb9 |
fix(#260): enforce worktree absolute-path safety via PreToolUse hook
Closes #260 Moves the step-0b absolute-path guard from prose instructions to a harness-enforced PreToolUse hook (gsd-worktree-path-guard.js). Hard-blocks Edit/Write/MultiEdit calls whose absolute path resolves outside the active worktree root. |
||
|
|
628e1e2f32 |
feat(#78): complete documentation and release MVP Vertical Slice mode
Closes #78 Completes the Vertical MVP Slice Mode feature. Core implementation was already on `next`; this PR adds the missing COMMANDS.md docs, CHANGELOG entries, INVENTORY row, --tdd CLI fix, and changeset fragment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
2ba6b69d53 |
feat(#49): provider-neutral model policy presets
* feat(#49): provider-neutral model policy presets Adds model_policy config surface with known-provider presets (openai/anthropic/google/qwen) and generic provider escape hatch. model_policy.runtime_tiers resolves before legacy model_profile_overrides. reasoning_effort is stripped for unsupported runtimes. Closes #49 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#49): replace unregistered /gsd-settings-advanced token in docs docs-parity-live-registry enforces every /token in docs/*.md maps to a live command. /gsd-settings-advanced is a workflow filename, not a registered command — use /gsd:settings instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#49): update INVENTORY.md count and manifest for config-types.cjs inventory-counts and inventory-manifest-sync tests require the headline count and INVENTORY-MANIFEST.json to reflect every file in bin/lib/. config-types.cjs (new module added by feat(#49)) was missing from both. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
0a12b06381 |
feat(#39): milestone-prefixed phase IDs (M-NN convention) + migration tool + validation (#565)
* feat(#39): milestone-prefixed phase IDs (M-NN convention) + migration tool + validation - Add getMilestoneFromPhaseId() / getPhaseDirFromPhaseId() helpers to core.cjs - Fix isDirInMilestone to match M-NN-style dirs (02-01-setup) against M-NN ROADMAP headings - Extend heading regex to tolerate [bracket-token] scope prefix on phase headings - Add W021 validation rule for milestone prefix mismatch - Add gsd-tools roadmap validate + roadmap upgrade --convention milestone-prefixed - Add phase_id_convention config field (null default, backwards-compatible) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#39): address 4 Codex review findings in milestone-prefixed phase ID implementation - getMilestoneFromPhaseId: tighten regex to require a digit after the hyphen (rejects '1-' and '1-abc') - isDirInMilestone: use convention-aware regex — only capture M-NN segments when ROADMAP itself uses hyphenated phase IDs, preventing legacy dirs like '01-02-setup' from being misread as phase '1-02' - checkW021: add UNPREFIXED_PHASE_RE path so unprefixed headings (### Phase 1:) also fire W021 when convention is milestone-prefixed - roadmap-upgrade: remove isMigratedDirName dir-name check (false-positive for legacy dirs); config + ROADMAP heading checks at lines 194 and 212 are sufficient Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: update changeset pr reference to #565 * fix(#39): restore phaseDirNameRe 2-digit minimum; add roadmap-upgrade to inventory - validate.cjs: \d{1,} → \d{2,} to keep single-digit prefix rejection per W005 contract - docs/INVENTORY.md: 79 → 80, add roadmap-upgrade.cjs row - docs/INVENTORY-MANIFEST.json: regenerated (roadmap-upgrade.cjs entry) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
6fc84528e5 |
fix(#448): resolve UI safety gate helper against the GSD install dir (#539)
* fix(#448): resolve UI safety gate helper against the GSD install dir The §5.6 UI Design Contract Gate (and autonomous §3a.5) resolved ui-safety-gate.cjs via `git rev-parse --show-toplevel`, i.e. the consuming project's git root — which has no bin/lib. The node call failed, its exit code was conflated with "no UI", and the gate silently no-opped so frontend phases skipped the UI-SPEC prompt. Resolve the helper against the GSD install dir via RUNTIME_DIR (the same idiom §1 uses for gsd-tools), with git-toplevel and $HOME/.claude fallbacks. When the helper genuinely can't be found, fail OPEN with a stderr warning (assume UI present) rather than silently skipping. Tests: bug-3706 structural guard now requires RUNTIME_DIR resolution and forbids the consuming-project GSD_REPO_ROOT anchor; a new behavioral test resolves and runs the helper from a temp consuming project (no bin/lib) with RUNTIME_DIR set. autonomous-ui-steps updated to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#448): add changeset fragment for PR #539 * fix(#448): add get-shit-done/bin/lib/ to UI gate probe and deploy helper there The installer copies get-shit-done/ to the target but not root bin/lib/, so the helper was never found for installed users. Placing ui-safety-gate.cjs in get-shit-done/bin/lib/ ensures the installer deploys it, and probing that path first makes the gate work correctly in installed runtimes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: update changeset to cover get-shit-done/bin/lib/ deployment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: update inventory for ui-safety-gate.cjs in get-shit-done/bin/lib/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
c81d5fcb2c | docs(#523): rebrand public docs as GSD Core | ||
|
|
79002a00cb |
chore(#518): rename npm package + bin to @opengsd/gsd-core (#519)
* chore: rename npm package + bin to @opengsd/gsd-core (functional) - package.json: name @opengsd/get-shit-done-redux → @opengsd/gsd-core, bin key get-shit-done-redux → gsd-core, repository/homepage/bugs URLs - package-lock.json: regenerated (npm install --package-lock-only) - tests/**, scripts/**, bin/**, .github/**, agents/**, commands/**, get-shit-done/bin/**, get-shit-done/workflows/**: applied the 4-rule replacement (scoped npm ref, GitHub repo path, bin/clone invocations) per #505 single-source refactor Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: sweep live references to @opengsd/gsd-core Update all live documentation (README.md + translations, docs/**, CONTRIBUTING.md, VERSIONING.md, SECURITY.md, CONTEXT.md, docs/CANARY.md) to reflect the renamed package and repository. Rules applied: - @opengsd/get-shit-done-redux → @opengsd/gsd-core (scoped npm name) - open-gsd/get-shit-done-redux → open-gsd/gsd-core (GitHub repo) - GSD-redux/get-shit-done-redux → open-gsd/gsd-core (stale badge org) - bare bin/clone refs → gsd-core CHANGELOG.md, docs/adr/**, docs/RELEASE-*.md, docs/research/**, and .changeset/** are preserved byte-identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: add negative lookbehind to slash-command regex in bug-2954 test The extractSlashReferences regex matched /gsd-core inside npm package URLs (@opengsd/gsd-core), producing a false /gsd:core command reference. Adding a negative lookbehind (?<![a-z]) excludes matches preceded by a letter, so only standalone /gsd-<cmd> and /gsd:<cmd> tokens are found. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#518): add changeset for package rename Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#518): update package-identity expectations to the renamed coordinates The rebase regenerated the seam to @opengsd/gsd-core (bin gsd-core, repo open-gsd/gsd-core). The #498 seam tests assert deriveIdentity against the REAL package.json, so their expected literals must follow the rename. The drift-lint unit test is left as-is — its SEAM is a self-consistent fixture and its stale-literal detection cases would shift if altered; the live-repo scan in it already passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
6f2520786d |
feat(#498): single Package Identity seam for /gsd:update + fix runtime undefined-name bug (#499)
* feat(#498): generated package-identity seam derived from package.json Introduce a single source for GSD's published-package coordinates: scripts/generate-package-identity.cjs (pure deriveIdentity + formatManualInstall + render) emits the generated get-shit-done/bin/lib/package-identity.cjs with values baked from package.json at build time. Baking is required because the installed tree carries only a synthetic {"type":"commonjs"} package.json, so a runtime require('package.json').name resolves to undefined (#378). Reconciles Wired into npm run build; a parity test fails CI if the committed file drifts from package.json. Refs #498 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#498): repoint update worker + check-latest-version at the seam - check-latest-version.cjs sources PACKAGE_NAME from the package-identity seam instead of a re-typed literal (single source; #2992's constant guarantee is preserved since the seam bakes from package.json). - gsd-check-update-worker.js no longer does require('../package.json').name (resolved to undefined in the installed tree → background update check silently broken, #378). It now delegates the latest-version lookup to checkLatestVersion(), collapsing the duplicated npm-view call onto the single deterministic adapter and inheriting its typed {ok,version,reason} surface. - Move the PR #3102 Windows shell-gate contract test onto execNpm (where the spawn now lives) and assert the worker no longer spawns npm directly. - Rewrite the #378 contract: worker must NOT use require(package.json).name and must delegate; check-latest-version PACKAGE_NAME is single-sourced from the seam. Fixes #378-class runtime breakage. Refs #498 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#498): changeset for package-identity seam + update-check fix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(#498): drift-guard lint — value-check GSD coordinate literals against the seam scripts/lint-package-identity-drift.cjs scans the runtime/code surface (bin/, hooks/, scripts/, get-shit-done/) and asserts every GSD package name and GitHub repo slug literal equals the Package Identity seam's current value. Passes today; fails the moment a repoint isn't propagated (rename package.json, regenerate the seam, and stale literals are reported until updated). This is the second adapter that makes the seam real and a repoint mechanically safe. Enforced via tests/issue-498-identity-drift-lint.test.cjs (scanRepo === []) under npm test; also exposed as `npm run check:identity-drift`. Refs #498 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(#498): update-context projection — port update.md resolution to a tested seam Add get-shit-done/bin/lib/update-context.cjs: a pure, injected-fs port of update.md's ~280-line get_installed_version bash. resolveUpdateContext() reproduces the full precedence cascade (preferred fast-path -> local probe -> global probe via env overrides then $HOME -> LOCAL-if-distinct -> scope cascade -> UNKNOWN) and returns the 4-field contract { installedVersion, scope, runtime, gsdDir }. The fs is injected so every branch is finally testable without a live multi-runtime install. Expose it as `gsd-tools update-context [--config-dir <d>] [--runtime <r>] --json`. Purely additive — update.md is unchanged in this commit; the workflow swap follows separately. Refs #498 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(#498): swap update.md resolution to the update-context projection Replace ~280 lines of inline runtime/scope/config-dir bash in update.md's get_installed_version step with a call to `gsd-tools update-context --json` (60 lines: derive PREFERRED_* from execution_context, resolve gsd-tools.cjs, parse the 4-field JSON). Behavior is unchanged — the projection reproduces the same cascade — but the logic is now tested in update-context.cjs instead of untestable bash-in-markdown. Relocate the #3608 antigravity-first-class contract onto the projection (RUNTIME_DIRS order, inferPreferredRuntime, envRuntimeDirs) plus a behavioral test; keep the execution_context path-classification assertion on update.md. Re-point install.test's custom-config-dir assertion (kilo.jsonc/KILO_CONFIG) to update-context.cjs where that detection now lives. Full root suite: 2022 pass / 0 fail. Refs #498 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#498): record Update Context Module in CONTEXT.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#498): CI — avoid bare gsd-tools in update.md; register new CLI modules - update.md update-context invocation: resolve the PATH gsd-tools shim into a variable and call "$GSD_TOOLS" (never a bare `gsd-tools` command) — satisfies the #2851 workflow-bare-gsd-tools guard. - Register package-identity.cjs and update-context.cjs in docs/INVENTORY.md (CLI Modules 76 -> 78 + rows) and regenerate docs/INVENTORY-MANIFEST.json, fixing inventory-counts and inventory-manifest-sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#498): make update-context + parity tests OS-agnostic (Windows CI) Two Windows-only test failures, both test-portability (production code is fine — the real-fs CLI integration test passed on Windows): - update-context resolver tests + bug-3608 behavioral test used POSIX path-string keys in their fake fs, but the resolver builds lookups via path.join/resolve (backslash + drive letter on Windows) → keys never matched → everything resolved to UNKNOWN/claude. Normalize fake-fs keys and gsdDir comparisons through path.resolve so they match on both platforms. - package-identity parity test compared render() (LF) to the committed file, which Windows git checks out as CRLF (no .gitattributes eol rule). Normalize line endings before comparing, matching the repo convention (autonomous-decomposition, bug-3707). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#498): update.md backup must use GSD_DIR (adversarial-review finding) The get_installed_version rewrite emits GSD_DIR but dropped the probe-loop variables LOCAL_DIR/GLOBAL_DIR. The backup_custom_files step still read those, so RUNTIME_DIR went empty for every LOCAL/GLOBAL install and detect-custom-files was skipped — and since the update then runs a clean install that wipes managed dirs (commands/gsd, get-shit-done), user-added files could be deleted without the intended backup. Set RUNTIME_DIR="$GSD_DIR" directly (the resolved config dir; empty for UNKNOWN scope, which still skips the backup). Add a structural regression (tests/issue-498-update-backup-runtime-dir.test.cjs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#503): re-point Antigravity .agent detection at the #498 projection #499 moves the runtime/scope detection cascade out of update.md inline bash into get-shit-done/bin/lib/update-context.cjs. The #503 regression test asserted on the inline RUNTIME_DIRS array, which no longer exists, so it would fail against the projected update.md even though the .agent guarantee is preserved. Rewrite it to verify the surviving surfaces: - behavioral: resolveUpdateContext resolves a LOCAL ./.agent install to the antigravity runtime (the original root cause, now covered by adding ['antigravity', '.agent'] to the projection RUNTIME_DIRS table) - update.md prose classifier still maps /.agent/ -> antigravity - the post-update cache-clear for-dir loop still includes .agent Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#498): finish de-hardcoding consumers + close adversarial-review parity gaps Restore the consumer de-hardcoding that is the point of the seam, and close the parity gaps an adversarial review (codex) found in the update-context projection. De-hardcode the repo slug + install command in the changeset tooling — #516 only single-sourced the package NAME, leaving 'open-gsd/get-shit-done-redux' hardcoded in scripts/changeset/cli.cjs and github-release-notes.cjs. Route both through the seam's repoSlug/packageName so a rename is a regenerate, not a hand edit. The drift-lint real scan now reports zero divergent coordinate literals. Projection parity vs the old inline bash, as ONE consistent rule (trustedVersionAt) applied on every path: - expand a leading ~/ in preferredConfigDir before the fast path (the bash ran expand_home first; a custom --config-dir ~/foo otherwise fell to UNKNOWN) - trust a version only when BOTH VERSION and the update.md marker exist — fast path AND LOCAL/GLOBAL cascade; a partial dir falls to 0.0.0 keeping scope - apply the same same-path dedup to the 0.0.0 fallback so a partial install probed from cwd===home is not misdetected as LOCAL Adds regression tests for tilde expansion, VERSION-only (cascade + fast path), and the cwd===home partial-install dedup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
fbd555c2ce |
fix(#520): register package-identity.cjs in inventory (regression from #516) (#521)
#516 added get-shit-done/bin/lib/package-identity.cjs without regenerating the inventory, breaking inventory-manifest-sync and inventory-counts on next. Regenerate docs/INVENTORY-MANIFEST.json and bump docs/INVENTORY.md CLI-module count 76 -> 77 with the new row. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
bf68ad4d93 |
fix(tests): deterministic concurrency via injectable clock seam; delete flaky racing tests (#459)
* feat(#453): add deterministic clock seam to lock modules Introduces get-shit-done/bin/lib/clock.cjs exporting realClock with now() (Date.now) and sleep() (Atomics.wait). acquireStateLock, writeStateMd, and readModifyWriteStateMd in state.cjs each accept an optional trailing clock param (default: realClock). withPlanningLock in planning-workspace.cjs gains the same seam. No production behavior change — all callers that omit the param continue to use realClock. Adds tests/helpers/clock.cjs (makeFakeClock) and tests/clock-seam.test.cjs with 20 deterministic in-process tests covering: lock serialization, timeout throw at maxWaitMs boundary, stale-lock takeover, lock released on error path, withPlanningLock timeout recovery, exit-cleanup integration, readModifyWriteStateMd call-site coverage (7 cmd*), and roadmap analyze behavioral assertion (50 phases, no elapsed-time gate). Deletes/converts per research verdicts: removes 11 source-grep/elapsed-time/ non-deterministic-concurrent tests across concurrency-safety.test.cjs, locking-bugs-1909-1916-1925-1927.test.cjs, and bug-1974-context-exhaustion- record.test.cjs. All deleted tests have deterministic replacements in clock-seam.test.cjs or surviving barrier-based tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#453): update module inventory for clock.cjs; make EEXIST-retry assertion behavioral Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#453): satisfy lint-tests — allow-test-rule annotation on readFileSync/includes runtime output check Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: CI Rebase Check <ci@gsd-redux> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
47bba87d90 |
fix: restore sdk query families in gsd-tools (#353)
* fix: restore sdk query families in gsd-tools * chore: add changeset * docs: sync query router inventory |
||
|
|
b99dd64e02 |
fix(#341): restore next-based test gate regressions (#342)
* fix: use active-workstream resolver exported by store module * fix: wire verify codebase-drift alias and sync inventory docs * chore: add changeset for next gate regression fixes * fix: normalize changeset fragment metadata for docs-lint |
||
|
|
4fa13cf9de | refactor(#190): collapse SDK sync bridge and remove synckit seam | ||
|
|
bac141b120 | docs: sync inventory and handsync allowlist after #189 | ||
|
|
22e9c1de62 | refactor(#181): migrate workstream inventory builder to sdk/src/workstream (#250) | ||
|
|
59bcdf03b6 |
refactor(#180): migrate STATE.md Document Module to sdk/src/state (#249)
* refactor(#180): migrate state document module to sdk/src/state * test(#180): ratchet lint allowlists for state module relocation |
||
|
|
5434c8c4cb | docs: update inventory surfaces for semver compare module | ||
|
|
6177e3a5f5 |
fix(4): retire cooperating-sibling for phase.*, introduce generator + I/O adapter, fix cmdPhaseComplete (#154)
* test(4): reproduce non-idempotent phase complete + unclamped percent in CJS CLI RED regression tests for issue #4: - T1: double invocation of cmdPhaseComplete double-increments **Completed Phases:** in STATE.md body (blind parseInt+1 instead of deriving from ROADMAP) - T2: progress percent can exceed 100% when Completed Phases > Total Phases The CJS path (bin/lib/phase.cjs:cmdPhaseComplete) has the bug; the SDK path (phase-lifecycle.ts:phaseComplete, fixed in ~PR#3520) already derives completed_phases from ROADMAP Complete-row count, making it idempotent. References: - Issue #4 (open-gsd/get-shit-done-redux) - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - /tmp/adr-3524-review-findings.md (architectural justification) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(4): add sdk/scripts/gen-phase-lifecycle-policy.mjs generator + freshness check placeholder Generates phase-lifecycle-policy.generated.cjs from sdk/src/query/phase-lifecycle-policy.ts. All functions in phase-lifecycle-policy.ts are pure transforms (no I/O), directly serializable via Function.prototype.toString(). The GSDError dependency is replaced with a lightweight stub that throws plain Error objects — CJS callers that need process.exit(1) behavior catch these and delegate to error(). This is the "I/O adapter pattern" from ADR-3524 Section 4 applied to pure helpers. References: - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - /tmp/adr-3524-review-findings.md (architectural justification) - Issue #4 (open-gsd/get-shit-done-redux) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(4): add sdk/scripts/gen-phase.mjs generator Generates phase.generated.cjs from sdk/src/query/phase.ts. Only pure helpers (isCanonicalPlanFile, describeNonCanonicalPlans) are generated; async query handlers (findPhase, phasePlanIndex) are I/O-bound and remain per-side per ADR-3524 Section 4. References: - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - /tmp/adr-3524-review-findings.md (architectural justification) - Issue #4 (open-gsd/get-shit-done-redux) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(4): add sdk/scripts/gen-phase-lifecycle.mjs + core idempotency fix logic Generates phase-lifecycle.generated.cjs providing two pure functions that are the root-cause fix for issue #4: 1. deriveProgressFromRoadmap(roadmapContent): counts Complete rows in ROADMAP progress table — makes completed_phases idempotent (derived from ground truth instead of blind +1). Direct transcription of the SDK's "Root cause 1 fix" block in phase-lifecycle.ts (~line 1644). 2. clampPercent(completed, total): percent capped at 100 — prevents >100% progress when Completed Phases exceeds Total Phases. Design note: the full phase lifecycle mutations (add, insert, remove, complete) are inherently async and I/O-bound. Per ADR-3524 Section 4 ("I/O stays per-side"), those are NOT generated. Only the pure-computation kernel is extracted, following the I/O adapter pattern: pure logic shared; each side (CJS sync, SDK async) supplies its own I/O adapter. The pure functions are defined in the generator as real JS functions and serialized via Function.prototype.toString() — same technique as gen-project-root.mjs — rather than embedded in template literals (which would require double-escaping all regex backslashes). References: - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - /tmp/adr-3524-review-findings.md (architectural justification) - Issue #4 (open-gsd/get-shit-done-redux) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(4): emit phase.generated.cjs, phase-lifecycle.generated.cjs, phase-lifecycle-policy.generated.cjs Three generated CJS artifacts from their respective generator scripts: - phase.generated.cjs (1.7K): isCanonicalPlanFile + describeNonCanonicalPlans from sdk/src/query/phase.ts - phase-lifecycle.generated.cjs (3.6K): deriveProgressFromRoadmap + clampPercent — the idempotency+clamp fix for issue #4 - phase-lifecycle-policy.generated.cjs (7.0K): 14 pure phase naming/directory helpers from sdk/src/query/phase-lifecycle-policy.ts Run to regenerate: node sdk/scripts/gen-phase.mjs node sdk/scripts/gen-phase-lifecycle.mjs node sdk/scripts/gen-phase-lifecycle-policy.mjs References: - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - Issue #4 (open-gsd/get-shit-done-redux) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(4): migrate bin/lib/phase.cjs cmdPhaseComplete to use generated helpers Replace the blind-increment + unclamped percent bug in cmdPhaseComplete with the idempotent ROADMAP-derived approach: read freshly-updated ROADMAP, call deriveProgressFromRoadmap() from phase-lifecycle.generated.cjs, fall back to existing value when ROADMAP is unavailable. clampPercent() prevents >100%. Root cause fix for issue #4: the original parseInt(completedRaw) + 1 on every call made phase complete non-idempotent; the missing Math.min(100, ...) clamp allowed Progress to exceed 100%. I/O adapter pattern (ADR-3524 §4): pure computation in generated module; CJS supplies sync readFileSync; SDK supplies async readFile. Same logic, two adapters. Closes: Tests in 4-phase-complete-cjs-regression.test.cjs go GREEN. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(4): add freshness checks + npm scripts for phase generated artifacts (D4/D6) Add check-phase-fresh.mjs, check-phase-lifecycle-fresh.mjs, and check-phase-lifecycle-policy-fresh.mjs (same pattern as check-project-root-fresh.mjs: import buildXCjs() from the generator, regenerate in-memory, byte-compare to committed file, exit 1 if stale). Add gen:phase, check:phase-fresh, gen:phase-lifecycle, check:phase-lifecycle-fresh, gen:phase-lifecycle-policy, check:phase-lifecycle-policy-fresh to sdk/package.json. Note: gen:phase-lifecycle / check:phase-lifecycle-fresh do not require 'npm run build' because the generator defines pure functions directly rather than importing dist. Update shared-module-handsync-allowlist.json: reclassify phase.cjs justification to reflect that it now consumes phase-lifecycle.generated.cjs for cmdPhaseComplete. The *.generated.cjs files are excluded by the lint scanner (excludes *.generated.cjs) so no new allowlist entries are required for the generated artifacts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(4): add freshness-check CI steps for phase, phase-lifecycle, phase-lifecycle-policy Add three drift-check steps to .github/workflows/test.yml following the same pattern as the existing freshness checks (ubuntu-latest + node 24 only): - SDK generated phase artifact drift check - SDK generated phase-lifecycle artifact drift check - SDK generated phase-lifecycle-policy artifact drift check These guard against editors modifying the generated *.cjs files directly. They run check-phase-fresh.mjs, check-phase-lifecycle-fresh.mjs, and check-phase-lifecycle-policy-fresh.mjs respectively (added in D4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(4): amend ADR-3524 — phase * I/O adapter pattern for issue #4 (D8) Append a 2026-05-23 amendment to docs/adr/3524-cjs-sdk-hard-seam.md documenting the Phase * cooperating-sibling retirement: three new generator scripts extract pure-computation helpers from phase.ts / phase-lifecycle.ts / phase-lifecycle-policy.ts, cmdPhaseComplete migrates to deriveProgressFromRoadmap + clampPercent for idempotency, freshness checks + CI steps added. Clarifies what is NOT generated (async I/O-bound mutation handlers stay per-side per Section 4) and notes open drift bugs #6 and #26 for traceability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(4): add changeset fragment for cmdPhaseComplete fix Refs #4 * fix(154): use canonical /gsd:plan-phase form in phase-lifecycle-policy.ts Replaces the retired /gsd-plan-phase slash command reference with the canonical colon-namespaced /gsd:plan-phase in the TS source template string that feeds the generated CJS roadmap entry helper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(154): regenerate phase-lifecycle-policy.generated.cjs after slash-namespace fix Regenerated via node sdk/scripts/gen-phase-lifecycle-policy.mjs after fixing /gsd-plan-phase → /gsd:plan-phase in the TS source. Generated file now contains the canonical colon form. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(154): cross-platform frontmatter regex anchor in 4-phase-complete-cjs-regression.test.cjs Replaces /^---\n/ with /^---\r?\n/ so the frontmatter extraction helper in the regression test tolerates Windows CRLF line endings (autocrlf=true checkout leaves \r before \n, causing /^---\n/ to never match). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(154): add new generated CJS modules to INVENTORY.md and regenerate manifest Adds three missing rows to the CLI Modules table: - phase-lifecycle-policy.generated.cjs - phase-lifecycle.generated.cjs - phase.generated.cjs Bumps the headline count from 74 to 77 to match the filesystem. Also regenerates docs/INVENTORY-MANIFEST.json via node scripts/gen-inventory-manifest.cjs --write. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(154): regenerate phase-lifecycle-policy.generated.cjs with hyphen form Root cause: commit 6cd701f4 regenerated the CJS artifact but at that point sdk/dist/query/phase-lifecycle-policy.js already had the correct /gsd-plan-phase (hyphen) form while sdk/src/query/phase-lifecycle-policy.ts still had /gsd:plan-phase (colon). The generator uses Function.prototype.toString() on the compiled dist, so the CJS picked up the wrong string from the stale TS source that was compiled into dist at some earlier point. Fix: correct the TS source to /gsd-plan-phase and re-run gen-phase-lifecycle-policy.mjs so that buildPhaseRoadmapEntry in the CJS emits the hyphen form, satisfying the bug-3584-runtime-slash-emitters.test.cjs assertion at line 179. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(154): restore /gsd:plan-phase canonical form in phase-lifecycle-policy Commit 0c3a9c75 incorrectly reverted the slash-namespace fix by misreading sdk/dist/ (a build artifact in hyphen form for non-Claude runtimes) as the authoritative source. The canonical form for Claude-facing source is /gsd:plan-phase (colon-namespaced). Fix: revert TS source back to /gsd:plan-phase, rebuild dist, regenerate phase-lifecycle-policy.generated.cjs. Fixes bug-2543-gsd-slash-namespace test failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(154): update INVENTORY.md CLI Modules count to 79 after rebase onto main Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(154): use hyphen form /gsd-plan-phase in persisted phase section template - sdk/src/query/phase-lifecycle-policy.ts: use /gsd-plan-phase (routable hyphen form) in the phase scaffold template that gets persisted to ROADMAP.md; bug-3584 requires persisted artifacts use the hyphen form - docs/INVENTORY.md: add missing runtime-name-policy.cjs row in CLI Modules table - tests/4-phase-complete-cjs-regression.test.cjs: add maxRetries/retryDelay to rmSync calls to satisfy Windows parity ratchet (baseline was 95) - Regenerate phase-lifecycle-policy.generated.cjs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
63396dbb16 |
enh(#142): centralize runtime alias canonicalization seam (#143)
* fix(#142): canonicalize runtime aliases across cjs and sdk * fix(#142): satisfy hand-sync and inventory parity gates * test(#1974): remove record-session lock contention in context monitor spec * test(config): retry transient config-ensure-section failures * docs(context): capture PR #143 CI reliability findings * fix(#142): bump CLI Modules inventory headline to 76 (runtime-name-policy + runtime-slash) docs/INVENTORY.md had the two new .cjs rows listed but the headline count stayed at 75; fs count is 76. inventory-counts.test.cjs caught the drift. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
41210f014e |
fix(5): decision-coverage gate parses <action> XML tag bodies for D-NN citations (#155)
* test(5): add failing test for decision IDs inside <objective>/<tasks>/<task>/<action> XML bodies Regression test for issue #5 — the translation gate (check.decision-coverage-plan) is blind to D-NN citations placed inside XML tag bodies by gsd-planner. Maintainer acceptance criteria (verbatim, issue #5): "Gate parses <action> tag bodies for decision ID citations; regression test with XML-tag plan body covers all decision IDs." Five new test cases added to the 'XML tag body citation parsing (issue #5)' suite: 1. RED: five decisions cited only in <objective>/<action> bodies → gate fails (before fix) 2. Non-canonical tag <comment> → must NOT count (negative control, passes) 3. Plain prose under undesignated heading → must NOT count (negative control, passes) 4. Self-closing <action/> → no crash, D-NN not covered (passes) 5. D-NN in <objective> body → should count (also fails before fix, GREEN after) Tests 1 and 5 are the load-bearing RED cases. All others are negative controls. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(5): extend extractPlanSections to scan <objective>/<tasks>/<task>/<action> for D-NN citations Closes #5. Root cause: extractPlanSections() in check-decision-coverage.ts collected only front-matter (must_haves/truths/objective) and body lines under designated markdown headings. The gsd-planner spec (agents/gsd-planner.md line 66) says 'Task actions reference the decision ID they implement (e.g., "per D-03")' and emits citations inside <action> tag bodies — a location the gate could not see. Fix: add extractXmlTagBodies() helper that matches the four canonical planner XML tags (<objective>, <tasks>, <task>, <action>) via a deliberately narrow regex (no XML parser library — D2 design decision). The helper output is appended to the designated string inside extractPlanSections(), making any D-NN citation inside those tag bodies count toward coverage. Maintainer acceptance criteria (verbatim, issue #5): "Gate parses <action> tag bodies for decision ID citations; regression test with XML-tag plan body covers all decision IDs." Self-closing tags (<action/>) are safely ignored — the capturing group does not match. Non-canonical tags (<comment>, <note>, etc.) are not in the alternation and are ignored by design. The CJS surface for check-decision-coverage.ts does NOT have a generator (gen-decisions.mjs covers decisions.ts, not this gate). No CJS artifact exists for this module. Per the generator framework established in PR #154 (ADR-3524), a CJS migration is a follow-up; this PR focuses on the TS fix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(5): clarify in gsd-planner.md that decision-coverage gate reads XML tag bodies Adds a parenthetical note to the existing self-check bullet (line 66) explaining which locations the gate scans so the planner's own guidance and the gate's behavior are explicitly aligned. Refs #5. No behavior change — documentation truthing only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(5): add changeset fragment for decision-coverage XML body fix Refs #5. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(5): extract Interface Context for Executors into reference file to pass planner-decomposition gate gsd-planner.md was 49446 chars after the XML-tag clarification added in this PR, exceeding the 48K threshold enforced by tests/planner-decomposition.test.cjs. Extracted the "Interface Context for Executors" section (~2137 chars) into get-shit-done/references/planner-interface-context.md, leaving a one-line pointer in gsd-planner.md. New normalized size: 47310 chars (1842 chars under threshold). Refs #5 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(5): register planner-interface-context.md in INVENTORY.md and manifest - Bump References headline from 61 to 62 to match filesystem count - Add planner-interface-context.md row in Modular Planner Decomposition table - Update footnote from 61 to 62 top-level references - Regenerate docs/INVENTORY-MANIFEST.json via gen-inventory-manifest.cjs --write Fixes inventory-counts and inventory-manifest-sync CI failures caused by the extraction commit (32e8950f) adding a new reference file without updating the inventory artefacts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
5414da2ce5 |
fix(6): retire validate.ts/verify.cjs cooperating-sibling, fix W007/phaseVariants/W006 drift via generator (#156)
* test(6): reproduce W007 + phaseVariants + W006 drift between CJS verify and SDK validate Adds tests/6-validate-cjs-drift-regression.test.cjs with 5 RED tests covering the three drift items from issue #6 between verify.cjs (Check 8) and validate.ts (Check 8): 1. W007 activeDiskPhases — verify.cjs uses diskPhases (includes archived) for W007; archived phase "1" absent from current ROADMAP fires false W007. validate.ts: activeDiskPhases (active phasesDir only) correctly excludes archives. 2. phaseVariants() normalization — ROADMAP says "01A", disk has "1A-foo". verify.cjs parseInt("01A")=1 → padded "01" (drops letter suffix) → miss. validate.ts phaseVariants("01A") = {"01A","1A","01A"} → "1A" matched. Both W006 and W007 fire as false positives in verify.cjs. 3. W006 letter-suffix padding mismatch — ROADMAP says "3B", disk has "03B-foo". verify.cjs parseInt("3B")=3 → padded "03" (drops "B") → diskPhases.has("03B") missed. W006 and W007 fire as false positives. All 5 tests RED on origin/main. Will turn GREEN after generator + verify.cjs migration. References: - Issue #6 (open-gsd/get-shit-done-redux) — maintainer acceptance criteria: "Port all three items to verify.cjs; add parity tests confirming identical output for all three cases on both paths" - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - PR #154 (issue #4) — precedent for the generator pattern Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(6): add sdk/scripts/gen-validate.mjs generator Extracts phaseVariants() from sdk/dist/query/validate.js via brace-balanced source-text parsing (phaseVariants is a closure inside validateHealth, not a module export, so Function.prototype.toString() is unavailable). Emits get-shit-done/bin/lib/validate.generated.cjs with three pure helpers: - phaseVariants(phase): normalized Set of padded/unpadded/letter-suffix variants - buildRoadmapPhaseVariants(content): {roadmapPhases, roadmapPhaseVariants} - buildNotStartedPhaseVariants(content): Set of unchecked-phase variants These three helpers directly address the three drift items in issue #6. Follows the gen-phase-lifecycle-policy.mjs extraction pattern from PR #154. References: - Issue #6 (open-gsd/get-shit-done-redux) - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - PR #154 (issue #4) — generator pattern precedent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(6): add sdk/scripts/check-validate-fresh.mjs freshness check Mirrors check-phase-lifecycle-policy-fresh.mjs from PR #154: imports buildValidateCjs() directly, regenerates in-memory, and diffs against the committed validate.generated.cjs. Exits 1 if stale (CI gate). References: - Issue #6 (open-gsd/get-shit-done-redux) - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - PR #154 (issue #4) — precedent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(6): emit validate.generated.cjs from validate.ts Generated by: node sdk/scripts/gen-validate.mjs Exports three pure helpers extracted from sdk/src/query/validate.ts Check 8: - phaseVariants(phase): Set of normalized variants {"01A","1A"} etc. - buildRoadmapPhaseVariants(content): {roadmapPhases, roadmapPhaseVariants} - buildNotStartedPhaseVariants(content): Set of unchecked-phase variants Freshness check: node sdk/scripts/check-validate-fresh.mjs → FRESH References: - Issue #6 (open-gsd/get-shit-done-redux) - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - PR #154 (issue #4) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(6): migrate verify.cjs to consume validate.generated.cjs helpers (GREEN) Check 8 in verify.cjs now uses three generated helpers from validate.generated.cjs: 1. buildRoadmapPhaseVariants(roadmapContent) — replaces hand-rolled roadmapPhases Set. Produces both roadmapPhases (raw, for W006 message) and roadmapPhaseVariants (all variants, for W007 membership check). Fixes false W007 for letter-suffix phases with padding mismatch. 2. activeDiskPhases — now uses collectDiskPhases() WITHOUT forEachArchivedPhaseToken. W007 iterates activeDiskPhases, not diskPhases, so archived phases absent from current ROADMAP no longer trigger false W007. 3. buildNotStartedPhaseVariants(roadmapContent) — replaces raw+parseInt-padded notStartedPhases population. Uses phaseVariants() expansion so zero-padded letter-suffix unchecked entries (e.g. "03B") correctly suppress W006 for their un-padded counterpart ("3B") and vice versa. 4. phaseVariants() in W006 loop — replaces parseInt-padded disk-existence check. "3B" now matches disk dir "03B-foo" via variant expansion. Also updates test fixture for drift item 1 to use two milestone archives (v1.0 + v1.1), accurately reproducing the scenario where forEachArchivedPhaseToken walks ALL archives while getActiveMilestoneArchiveDir returns only the most recent one. All 5 tests GREEN. Confirmed RED on pre-fix code (git stash test). References: - Issue #6 (open-gsd/get-shit-done-redux) — maintainer acceptance criteria: "Port all three items to verify.cjs; add parity tests confirming identical output" - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - PR #154 (issue #4) — generator pattern precedent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci(6): wire validate freshness check into test workflow Adds 'SDK generated validate artifact drift check' step to .github/workflows/test.yml, mirroring the pattern used by all PR #154 generator freshness checks. Runs on ubuntu-latest/node-24 only (same as other artifact drift checks). Placement: after workstream-name-policy check, before Shared Module hand-sync drift check. References: - Issue #6 (open-gsd/get-shit-done-redux) - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - PR #154 (issue #4) — precedent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(6): wire gen:validate into sdk/package.json, root package.json, and allowlist sdk/package.json: adds gen:validate and check:validate-fresh npm scripts. package.json: adds check:validate-fresh script (mirrors other check:*-fresh entries). scripts/shared-module-handsync-allowlist.json: updates verify.cjs justification to note that Check 8 W006/W007 helpers are now generated from validate.ts via gen-validate.mjs (issue #6), with freshness check at check-validate-fresh.mjs. References: - Issue #6 (open-gsd/get-shit-done-redux) - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(6): amend ADR-3524 — validate.ts now uses generator pattern Adds 2026-05-23 amendment section to docs/adr/3524-cjs-sdk-hard-seam.md documenting: - Generator/artifact/freshness-check/CI paths - Three drift items resolved (W007 activeDiskPhases, phaseVariants normalization, W006 unchecked-phase variant skip) - phaseVariants extraction technique (brace-balanced source-text parsing) - Parity test coverage (5 tests, RED→GREEN) - Allowlist classification preserved (cooperating-sibling) References: - Issue #6 (open-gsd/get-shit-done-redux) - ADR-3524 (docs/adr/3524-cjs-sdk-hard-seam.md) - PR #154 (issue #4) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(6): add changeset fragment for validate.ts/verify.cjs generator migration Touches get-shit-done/bin/lib/validate.generated.cjs and verify.cjs which match USER_FACING_PREFIXES. Required by the fix-template checklist + the changeset-lint CI workflow. Refs #6 #156 * docs(6): register validate.generated.cjs in INVENTORY + manifest INVENTORY parity test demanded a row for the new generated CJS surface and a matching entry in INVENTORY-MANIFEST.json. Headline count bumped from 74 → 75. Refs #6 --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |