* fix(sdk): extractCurrentMilestone Backlog leak + state.begin-phase flag parsing
Closes#2422Closes#2420
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#2444,#2445): scope stopped_at extraction to Session section; filter stale phase dirs
- buildStateFrontmatter now extracts stopped_at only from the ## Session
section when one exists, preventing historical prose elsewhere in the
body (e.g. "Stopped at: Phase 5 complete" in old notes) from overwriting
the current value in frontmatter (bug #2444)
- buildStateFrontmatter de-duplicates phase dirs by normalized phase number
before computing plan/phase counts, so stale phase dirs from a prior
milestone with the same phase numbers as the new milestone don't inflate
totals (bug #2445)
- cmdInitNewMilestone now filters phase dirs through getMilestonePhaseFilter
so phase_dir_count excludes stale prior-milestone dirs (bug #2445)
- Tests: 4 tests in state.test.cjs covering both bugs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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>
* feat(state): add programmatic gates for STATE.md consistency
Adds four enforcement gates to prevent STATE.md drift:
- `state validate`: detects drift between STATE.md and filesystem
- `state sync`: reconstructs STATE.md from actual project state
- `state planned-phase`: records state after plan-phase completes
- Performance Metrics update in `phase complete`
Also fixes ghost `state update-position` command reference in
execute-phase.md (command didn't exist in CLI dispatcher).
Closes#1627
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(state): By Phase table regex ate next section when table body was empty
The lazy [\s\S]*? with a $ lookahead in byPhaseTablePattern would
match past blank lines and capture the next ## section header as table
body when no data rows existed. Replaced with a precise row-matching
pattern ((?:[ \t]*\|[^\n]*\n)*) that only captures pipe-delimited
lines. Added regression assertion to verify row placement.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: correct STATE.md progress counter fields during plan/phase completion (#1589)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: re-run CI with Windows pointer lifecycle fix in main
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
cmdStateBeginPhase replaced the entire ## Current Position section with only
Phase and Plan lines, destroying Status, Last activity, and Progress fields.
cmdStateAdvancePlan then failed to update these fields since they no longer
existed.
Now begin-phase updates individual lines within Current Position instead of
replacing the whole section. Also adds updateCurrentPositionFields helper so
advance-plan keeps the Current Position body in sync with bold frontmatter
fields.
Fixes#1365
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test suite modernization:
- Converted all try/finally cleanup patterns to beforeEach/afterEach hooks
across 11 test files (core, copilot-install, config, workstream,
milestone-summary, forensics, state, antigravity, profile-pipeline,
workspace)
- Consolidated 40 inline mkdtempSync calls to use centralized helpers
- Added createTempDir() helper for bare temp directories
- Added optional prefix parameter to createTempProject/createTempGitProject
- Fixed config test HOME sandboxing (was reading global defaults.json)
New CONTRIBUTING.md:
- Test standards: hooks over try/finally, centralized helpers, HOME sandboxing
- Node 22/24 compatibility requirements with Node 26 forward-compat
- Code style, PR guidelines, security practices
- File structure overview
All 1382 tests pass, 0 failures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
syncStateFrontmatter() rebuilds YAML frontmatter from the body on every
writeStateMd() call. If an agent removes or omits the Status: field from
the body, buildStateFrontmatter() defaults to 'unknown', overwriting a
previously valid status (e.g., 'executing').
Fix: read existing frontmatter before stripping, and preserve its status
value when the body-derived status would be 'unknown'. This makes
frontmatter self-healing — once a status is set, it persists even if the
body loses the field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactoring:
- Extract stateReplaceFieldWithFallback() to state.cjs as single source of truth
for the try-primary-then-fallback pattern that was duplicated inline across
phase.cjs, milestone.cjs, and state.cjs
- Replace all inline bold-only regex patterns in cmdPhaseComplete with shared
stateReplaceField/stateExtractField helpers — now supports both **Bold:**
and plain Field: STATE.md formats (fixes the same bug as #924)
- Replace inline bold-only regex patterns in cmdMilestoneComplete with shared helpers
- Replace inline bold-only regex for Completed Phases/Total Phases/Progress
counters in cmdPhaseComplete with stateExtractField/stateReplaceField
- Replace inline bold-only Total Phases regex in cmdPhaseRemove with shared helpers
Security:
- Fix command injection surface in isGitIgnored (core.cjs): replace execSync with
string concatenation with execFileSync using array arguments — prevents shell
interpretation of special characters in file paths
Tests (7 new):
- 5 tests for stateReplaceFieldWithFallback: primary field, fallback, neither,
preference, and plain format
- 1 regression test: phase complete with plain-format STATE.md fields
- 1 regression test: milestone complete with plain-format STATE.md fields
854 tests pass (was 847). No behavioral regressions.
`cmdStateAdvancePlan` expected separate `Current Plan` and
`Total Plans in Phase` fields, but the current STATE.md template
uses a single compound field: `Plan: X of Y in current phase`.
Now tries legacy separate fields first, then falls back to parsing
the compound format. Preserves the compound format when writing back
(replaces only the plan number). Also handles `Last activity`
(lowercase) field name from current template.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phases listed in ROADMAP.md but not yet planned (no directory on disk)
were excluded from total_phases, causing premature milestone completion.
Now uses Math.max(diskDirs, roadmapCount) via filter.phaseCount.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract getMilestonePhaseFilter() from milestone.cjs closure into core.cjs
as a shared helper. Apply it in buildStateFrontmatter and cmdPhaseComplete
so multi-milestone projects count only current milestone's phases instead
of all directories on disk.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add toPosixPath() helper to normalize output paths to forward slashes
- Use string concatenation for relative base paths instead of path.join()
- Apply toPosixPath() to all user-facing file paths in init.cjs output
- Use array-based execFileSync in test helpers to bypass shell quoting
issues with JSON args and dollar signs on Windows cmd.exe
Fixes 7 test failures on Windows: frontmatter set/merge (3), init
path assertions (2), and state dollar-amount corruption (2).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --cwd <path> / --cwd=<path> support so sandboxed subagents running
outside the project root can target a specific directory. Invalid paths
return a clear error. Tests ported to tests/state.test.cjs (the old
monolithic test file was split into domain files on main).
Closes#622
Co-Authored-By: Colin Johnson <colin@solvely.net>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>