Commit Graph

20 Commits

Author SHA1 Message Date
Tom Boucher
96806003c5 fix(#3229): shared model catalog source of truth for agent profiles + runtime tier defaults (#3230)
* docs(adr): add ADR-0003 model catalog module

* fix(#3229): add shared model catalog as source of truth for agent profiles and runtime tier defaults

Research / design (ADR-0003):
- Existing drift came from 4 independent model truths:
  1. CJS model-profiles.cjs
  2. SDK config-query.ts stale copy (18 agents)
  3. settings-advanced.md runtime tier table
  4. session-runner Claude-only profile map
- New design: one machine-readable Model Catalog Module in sdk/shared/
  that both packages ship and consume.

Implementation:
- sdk/shared/model-catalog.json — canonical source of truth for:
  - full 33-agent registry
  - per-agent golden (quality) alias + balanced/budget aliases
  - adaptive derivation from routingTier
  - agent→phaseType map
  - agent→dynamic-routing default tier map
  - runtime tier defaults for all supported runtimes
- get-shit-done/bin/lib/model-catalog.cjs — CJS adapter over the catalog
- sdk/src/model-catalog.ts — SDK adapter over the same catalog
- CJS model-profiles.cjs now re-exports derived data from model-catalog.cjs
- SDK config-query.ts now re-exports MODEL_PROFILES/VALID_PROFILES from
  model-catalog.ts instead of maintaining its own list
- sdk/src/query/helpers.ts runtime list now comes from the catalog (fixes hermes drift)
- sdk/src/session-runner.ts Claude profile→model-id mapping now resolves via catalog
- docs/CONFIGURATION.md + settings-advanced.md runtime tables updated to match catalog

Behavior changes:
- resolve-model now covers every shipped agent file on disk (33 agents)
- unknown-agent fallback is profile-semantic, not hardcoded sonnet:
  quality→opus, budget→haiku, balanced/adaptive→sonnet, inherit→inherit
- Group B runtimes remain known runtimes but do not get built-in tier defaults

Tests (RED→GREEN):
- root tests: shipped agent files must equal MODEL_PROFILES keys
- sdk tests: shipped agent files must equal MODEL_PROFILES keys
- direct fix assertion: gsd-code-reviewer resolves to opus under quality with no unknown_agent
- runtime defaults parity test: settings-advanced.md + CONFIGURATION.md tables must match catalog
- helper tests: hermes included in SUPPORTED_RUNTIMES and getRuntimeConfigDir()

Closes #3229

* chore(changeset): update #3229 changeset pr field to 3230

* fix(ci): update inherit fallback expectations and inventory parity for model catalog
2026-05-08 21:25:37 -04:00
Tom Boucher
918f987a19 feat(#2982): extend no-source-grep lint to catch var-binding readFileSync.includes() (#2985)
* feat(#2982): extend no-source-grep lint to catch var-binding readFileSync.includes()

The base lint (scripts/lint-no-source-grep.cjs) only catches
readFileSync(...).<text-method>() chained directly. The much more
common var-binding form escapes it:

  const src = fs.readFileSync(p, 'utf8');
  // 50 lines later
  if (src.includes('foo')) {}        // ← still grep, lint missed it

Scan of the test suite found ~141 files using this pattern.

Implementation built TDD per #2982 with structured-IR assertions:

  scripts/lint-no-source-grep-extras.cjs
    - detectVarBindingViolations(src) — pure detector, two passes:
      pass 1 collects vars bound from readFileSync, pass 2 finds any
      <var>.<includes|startsWith|endsWith|match|search>( on those vars.
    - detectWrappedAssertOkMatch(src) — flags
      assert.ok(<expr>.match(...)) which escapes the assert.match rule.
    - VIOLATION enum exposes stable codes for tests to assert on.

  scripts/lint-no-source-grep.cjs
    - Wires the new detectors into the existing per-file check; one
      additional violation row per file with the first 3 sample tokens.

  tests/bug-2982-lint-var-binding.test.cjs
    - 13 tests, all assertions on typed VIOLATION enum / structured
      records. Covers all 5 text-match methods, multi-var, no-bind,
      string literal (must NOT trigger), wrapped assert.ok(.match),
      and assert.match (must NOT double-flag).

Migration backlog (#2974 expanded scope):

  - 42 files annotated `// allow-test-rule: source-text-is-the-product`
    (legitimate — they read .md/.json/.yml files whose deployed text
    IS the product)
  - 3 files annotated `// allow-test-rule: pending-migration-to-typed-ir [#2974]`
    (read .cjs/.js source — clear migration debt)
  - 95 files annotated `pending-migration-to-typed-ir [#2974]` with
    `Per-file review may reclassify as source-text-is-the-product
    during migration` (mixed — manual review under #2974)

After this lands the lint reports 0 violations on main; new
violations in PRs surface immediately.

Closes #2982
Refs #2974

* test(#2982): fix truncated test name per CR

The label ended with a bare '(' from a copy-paste mishap. Now reads
'does NOT flag .matchAll(...) — matchAll is not match, so
assert.ok(.matchAll(...)) is not flagged'.

* chore(#2982): add changeset fragment for PR #2985

* chore(#2982): add changeset fragment for PR #2985
2026-05-01 19:50:10 -04:00
Tom Boucher
7b0a8b6237 fix: normalize phase numbers in stats Map to prevent duplicate rows (#2220)
When ROADMAP.md uses unpadded phase numbers (e.g. "Phase 1:") and
the phases/ directory uses zero-padded names (e.g. "01-auth"), the
phasesByNumber Map held two separate entries — one keyed "1" from the
ROADMAP heading scan and one keyed "01" from the directory scan —
doubling phases_total in /gsd-stats output.

Apply normalizePhaseName() to all Map keys in both the ROADMAP heading
scan and the directory scan so the two code paths always produce the
same canonical key and merge into a single entry.

Closes #2195

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 14:59:35 -04:00
Tom Boucher
2703422be8 refactor(tests): standardize to node:assert/strict and t.after() per CONTRIBUTING.md (#1675)
* refactor(tests): standardize to node:assert/strict and t.after() per CONTRIBUTING.md

- Replace require('node:assert') with require('node:assert/strict') across
  all 73 test files to enforce strict equality (no type coercion)
- Replace try/finally cleanup blocks with t.after() hooks in core.test.cjs
  and hooks-opt-in.test.cjs per the test lifecycle standards
- Utility functions in codex-config and security-scan retain try/finally
  as that is appropriate for per-function resource guards, not lifecycle hooks

Closes #1674

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* perf(tests): add --test-concurrency=4 to test runner for parallel file execution

Node.js --test-concurrency controls how many test files run as parallel child
processes. Set to 4 by default, configurable via TEST_CONCURRENCY env var.
Fixes tests at a known level rather than inheriting os.availableParallelism()
which varies across CI environments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): allowlist verify.test.cjs in prompt-injection scanner

tests/verify.test.cjs uses <human>...</human> as GSD phase task-type
XML (meaning "a human should verify this step"), which matches the
scanner's fake-message-boundary pattern for LLM APIs. This is a
false positive — add it to the allowlist alongside the other test files
that legitimately contain injection-adjacent patterns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 14:29:03 -04:00
Tibsfox
9d90c7b420 feat(hooks): add check-commit guard for commit_docs enforcement (#1395)
Add check-commit command to gsd-tools that acts as a pre-commit guard.
When commit_docs is false, rejects commits that stage .planning/ files
with an actionable error message including the unstage command.

Recreated cleanly on current main — previous version carried stale
shared fixes that are now upstream.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 16:11:00 -07:00
Tom Boucher
4afc5969ef Merge pull request #1477 from Tibsfox/fix/stats-verification-status-1459
fix(stats): require verification for Complete status, add Executed state
2026-04-01 17:26:49 -04:00
Tibsfox
3321d48279 fix(stats): require verification for Complete status, add Executed state
Phases with all summaries but no passing VERIFICATION.md now show as
"Executed" instead of "Complete", preventing false progress reporting.

Adds determinePhaseStatus() helper used by both cmdStats() and
cmdProgressRender(). Also fixes duplicate phase directory accumulation
in cmdStats() — plans/summaries from directories sharing the same
phase number are now summed instead of silently overwritten.

New statuses: Executed (summaries done, no verification), Needs Review
(verification exists with human_needed status).

Closes #1459

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 12:18:03 -07:00
Tibsfox
c1fd72f81f fix(branching): capture decimal phase numbers in commit regex
The phase-extraction regex /(\d+)-/ only matched the last integer
segment before a dash, so decimal phases like 45.14 were misresolved
to phase 14 — silently switching to the wrong branch.

Closes #1402

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:42:19 -07:00
Tom Boucher
9ddd6c1bdc fix: create strategy branch before first commit, not at execute-phase (#1278)
When branching_strategy is "phase" or "milestone", the branch was only
created during execute-phase — but discuss-phase, plan-phase, and
new-milestone all commit artifacts before that, landing them on main.

Move branch creation into cmdCommit() so the strategy branch is created
at the first commit point in any workflow. execute-phase's existing
handle_branching step becomes a harmless no-op (checkout existing branch).

Also fixes websearch test mocks broken by #1276 (fs.writeSync change).

Closes #1278

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:08:06 -04:00
Tom Boucher
02254db611 fix: resolve ProviderModelNotFoundError on non-Claude runtimes (#1156)
Extend resolve_model_ids to accept "omit" value: returns empty string
so non-Claude runtimes (OpenCode, Codex, Gemini, etc.) use their
configured default model instead of unresolvable Claude aliases.

- resolve_model_ids: "omit" short-circuits before alias resolution
- model_overrides still respected (checked first) for explicit IDs
- Installer sets resolve_model_ids: "omit" in ~/.gsd/defaults.json
  for non-Claude runtimes during install
- 4 new tests covering omit behavior and override passthrough
- Fix websearch test mocks for fs.writeSync output change (#1276)

Closes #1156

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 00:57:20 -04:00
j2h4u
104a39e573 fix(lifecycle): extract one-liner from summary body when not in frontmatter
The summary template puts the one-liner as a `**bold**` line after the
`# Phase N` heading, but `cmdSummaryExtract` and `cmdMilestoneComplete`
only checked frontmatter `one-liner` field — which is often empty.

Adds `extractOneLinerFromBody()` to core.cjs as a fallback that parses
the first `**...**` line after the heading.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 21:29:25 +05:00
Tom Boucher
e84999663c feat(discuss-phase): cross-reference pending todos against phase scope
Add a cross_reference_todos step to discuss-phase that surfaces relevant
backlog items before scope-setting decisions are made.

Implementation:
- New 'todo match-phase <N>' CLI command (commands.cjs) that scores
  pending todos against a phase's ROADMAP goal using three heuristics:
  keyword overlap, area match, and file path overlap
- New cross_reference_todos step in discuss-phase.md between
  load_prior_context and scout_codebase
- CONTEXT.md template gains 'Folded Todos' subsection in <decisions>
  and 'Reviewed Todos (not folded)' subsection in <deferred>

Design:
- No AI call for matching — pure keyword/area/file heuristics for speed
- Silent skip when todo_count is 0 or no matches (no workflow slowdown)
- Auto mode folds all todos with score >= 0.4 automatically
- Scoring: keywords (up to 0.6), area match (0.3), file overlap (0.4)

Tests: 5 new tests covering empty state, keyword matching, unrelated
todo exclusion, area matching, and score sorting.

Closes #1111
2026-03-18 12:25:04 -04:00
Colin Johnson
0fde04f561 fix(stats): correct git and roadmap reporting (#1066) 2026-03-15 19:31:28 -06:00
ashanuoc
944df19926 feat: add /gsd:stats command for project statistics
Add a new `/gsd:stats` command that displays comprehensive project
statistics including phase progress, plan execution metrics, requirements
completion, git history, and timeline information.

- New command definition: commands/gsd/stats.md
- New workflow: get-shit-done/workflows/stats.md
- CLI handler: `gsd-tools stats [json|table]`
- Stats function in commands.cjs with JSON and table output formats
- 5 new tests covering empty project, phase/plan counting, requirements
  counting, last activity, and table format rendering

Co-authored-by: ashanuoc <ashanuoc@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 21:58:09 -06:00
Ethan Hurst
c9e73e9c94 fix(quick-1): remove HIGH severity test overfitting in 4 test files
- frontmatter.test.cjs: delete FRONTMATTER_SCHEMAS describe block (4 tests asserting on lookup table shape)
- core.test.cjs: replace 12 per-profile per-agent matrix tests with 1 structural validation test
- commands.test.cjs: URL test uses URL.searchParams.get() instead of raw string includes
- state.test.cjs: date assertion uses before/after window to handle midnight boundary safely
2026-02-26 05:49:31 +10:00
Ethan Hurst
c027fe6542 test(07-02): add cmdCommit and cmdWebsearch tests for commands.cjs
cmdCommit tests (5 cases) with real git repos via createTempGitProject:
- commit_docs=false skip, .planning gitignored skip, nothing-to-commit
- Successful commit with hash and git log verification
- Amend mode with commit count verification

cmdWebsearch tests (6 cases) with fetch mocking and stdout interception:
- No API key returns available=false, no query returns error
- Successful response with correct result shape
- URL parameter construction verification
- API error (non-200) handling, network failure handling

Covers requirements CMD-04, CMD-05.
2026-02-26 05:47:33 +10:00
Ethan Hurst
d4ec9c69a9 test(07-01): add utility function tests for commands.cjs
Add 14 new tests across 5 describe blocks:
- cmdGenerateSlug: normal text, special chars, numbers, hyphens, empty input
- cmdCurrentTimestamp: date, filename, full, default format
- cmdListTodos: empty dir, multiple, area filter match/miss, malformed
- cmdVerifyPathExists: file, directory, missing, absolute, no-arg
- cmdResolveModel: known agent, unknown agent, default profile, no-arg

Covers requirements CMD-01, CMD-02, CMD-03.
2026-02-26 05:47:33 +10:00
TÂCHES
0176a3ec5b fix: map requirements-completed frontmatter in summary-extract (#627) (#716)
Add requirements_completed field to summary-extract output, mapping
from SUMMARY frontmatter requirements-completed key. Enables
/gsd:audit-milestone cross-check to receive data from SUMMARY source.

Re-applied from #631 against refactored codebase (commands.cjs + split tests).

Co-authored-by: Colin Johnson <Solvely-Colin@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:55:22 -06:00
TÂCHES
3704829aa6 fix: clamp progress bar percent to prevent RangeError crash (#715)
When orphaned SUMMARY.md files cause totalSummaries > totalPlans, the
progress percentage exceeds 100%, making String.repeat() throw RangeError
on negative arguments. Clamp percent to Math.min(100, ...) at all three
computation sites (state, commands, roadmap).

Closes #633

Co-authored-by: vinicius-tersi <vinicius-tersi@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:51:28 -06:00
Tyler Satre
fa2e156887 refactor: split gsd-tools.test.cjs into domain test files
Move 81 tests (18 describe blocks) from single monolithic test file
into 7 domain-specific test files under tests/ with shared helpers.
Test parity verified: 81/81 pass before and after split.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 10:30:54 -05:00