* fix(3785): case-insensitive depends_on resolution in phase resolver
planMap, canonicalToId, and shortFormToId in phasePlanIndex used strict
Map.has() with no case normalization. A depends_on ref in mixed/lowercase
against an uppercase-suffix plan ID (e.g. '20-01-auth' → '20-01-Auth')
dropped the DAG edge, assigning the dependent plan to wave 1 instead of
wave 2. Fix: normalize all keys and lookup values to lowercase so the
three-tier resolution is case-insensitive. Adds regression test (#3785).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(changeset): add PR 3798 changelog fragment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(3785): detect case-fold collisions; apply lowercase-both to CJS path
- Add collision guard in both sdk/src/query/phase.ts (phasePlanIndex)
and get-shit-done/bin/lib/phase.cjs (cmdPhasePlanIndex): when two plan
IDs in the same phase are identical after toLowerCase(), throw/error
immediately with a clear message naming both files instead of silently
overwriting one in planMap and misrouting depends_on edges.
- Apply the same lowercase-both normalization (#3785) to cmdPhasePlanIndex
in phase.cjs, which was missing from the original PR — planMap and
canonicalToId keys are now lowercased on write; dep strings are
lowercased before lookup.
- Add regression tests to tests/phase.test.cjs: case-insensitive
resolution test (runs on all platforms) and collision-detection test
(skipped on macOS/Windows where FS is case-insensitive).
- Update changeset to describe both the SDK and CJS fixes.
Identified via Codex adversarial review of PR #3798.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(3785): address review — KNOWN GAP comment for CJS shortFormToId, canonical-casing tests, depends_on output normalization
- F1: Reword changeset for accuracy (plannerID drift trigger; two-tier CJS gap honest).
Add KNOWN GAP comment in phase.cjs before Kahn's loop noting CJS lacks shortFormToId
(tracked as follow-up parity gap, out of scope for #3785).
- F2: Add strict planA.id === '20-01-Auth' assertions in both SDK (vitest) and CJS (node --test)
tests — a future regression that silently lowercases stored IDs would now fail the test.
- F3: Normalize depends_on output to canonical plan IDs in both SDK phase.ts and CJS phase.cjs.
User-typed '20-01-auth' in depends_on resolves to '20-01-Auth' in output via planMap lookup.
Add planB.depends_on === ['20-01-Auth'] assertions in both test suites.
- F5: Add seenLower guard-scope comment (full-ID collisions only; shared-prefix collisions
handled by first-write-wins from sorted planFiles).
- F6: Add ASCII-safe toLowerCase comment at first call site in both SDK and CJS.
- F7: Add intentional-separation comment on seenLower vs planMap in both SDK and CJS.
Reviewers: gsd-code-reviewer (MN-01/NT-1) + sonnet adversarial.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(3785): cover case-insensitive depends_on resolution branches
Add 4 focused test cases exercising branches introduced by #3785:
- All-uppercase depends_on ref resolving to lowercase plan ID via planMap
- External cross-phase dep preserved as-is in Pass 3 output (planMap miss)
- Mixed-case short canonical prefix resolving via canonicalToId
- Plans with undefined/empty depends_on emit empty array correctly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>