Skip to content

fix: stop $Matches clobber in subagent-retro.ps1 spec-ID extraction - #18

Merged
developzoneio merged 3 commits into
hotfix/ps-hooks-auditfrom
fix/retro-ps1-matches-clobber
Jul 2, 2026
Merged

fix: stop $Matches clobber in subagent-retro.ps1 spec-ID extraction#18
developzoneio merged 3 commits into
hotfix/ps-hooks-auditfrom
fix/retro-ps1-matches-clobber

Conversation

@developzoneio

Copy link
Copy Markdown
Owner

Summary

  • Fixes .ai-docs/01-bug-subagent-retro-ps1-matches-clobber.md.
  • Get-IndexSpecs in hooks/powershell/subagent-retro.ps1 ran the spec-ID regex -match before the in-progress literal -match, so the second match overwrote $Matches and every in-progress spec was reported under the literal ID "in-progress" instead of its real ID.
  • The retro-staleness check then looked for <specDir>/in-progress/05-retro.md, which never exists, so every in-progress spec was reported stale under the wrong ID.
  • Swapped the match order (in-progress first, ID regex last) to match the already-correct sister hooks prompt-router.ps1:167 and spec-gate.ps1:145.
  • Stacked on fix: rename reserved $input variable in PowerShell hooks #17 (fix/ps-hooks-input-var-collision) - that fix was required for this one's stdin-piping verification to even reach the affected code path.

Test plan

  • Fixture index with FEAT-123 | ... | in-progress and no 05-retro.md -> PowerShell hook now reports FEAT-123 (previously reported in-progress)
  • Fixture index with an additional RCA-77 | ... | in-progress row -> RCA row correctly skipped
  • PowerShell output matches hooks/bash/subagent-retro.sh output exactly for the same fixture
  • grep -nP "[^\x00-\x7F]" hooks/powershell/*.ps1 install/*.ps1 - no matches (pure ASCII preserved)
  • .\scripts\validate.ps1 - all checks pass
  • CHANGELOG [Unreleased] entry added

🤖 Generated with Claude Code

Assigning parsed hook JSON to $input (PowerShell's automatic pipeline
variable) throws a non-terminating ParameterBindingException whenever
the hook receives real piped/redirected stdin - i.e. on every actual
Claude Code invocation. The assignment silently fails, $input stays
unbound, and the hook exits before reading any input. Renamed to
$hookInput in subagent-retro.ps1, prompt-router.ps1, and spec-gate.ps1.

Discovered while reproducing the 01-bug-subagent-retro-ps1-matches-clobber
fixture; that bug was unreachable until this one was fixed.
Get-IndexSpecs ran the spec-ID regex -match before the in-progress
literal -match, so the second match overwrote $Matches and every
in-progress spec was reported under the literal ID "in-progress"
rather than its real ID. The retro-staleness check then looked for
<specDir>/in-progress/05-retro.md, which never exists, so every
in-progress spec was reported stale under the wrong ID.

Swap the match order (in-progress first, ID regex last) to match
the already-correct sister hooks prompt-router.ps1:167 and
spec-gate.ps1:145.

Verified against a fixture index (FEAT-123 in-progress, RCA-77
in-progress) - PowerShell now reports FEAT-123 and skips the RCA
row, matching hooks/bash/subagent-retro.sh output exactly.
@developzoneio
developzoneio merged commit 5408c7c into hotfix/ps-hooks-audit Jul 2, 2026
4 checks passed
developzoneio added a commit that referenced this pull request Jul 5, 2026
* fix: rename reserved $input variable in PowerShell hooks (#17)

Assigning parsed hook JSON to $input (PowerShell's automatic pipeline
variable) throws a non-terminating ParameterBindingException whenever
the hook receives real piped/redirected stdin - i.e. on every actual
Claude Code invocation. The assignment silently fails, $input stays
unbound, and the hook exits before reading any input. Renamed to
$hookInput in subagent-retro.ps1, prompt-router.ps1, and spec-gate.ps1.

Discovered while reproducing the 01-bug-subagent-retro-ps1-matches-clobber
fixture; that bug was unreachable until this one was fixed.

* fix: stop $Matches clobber in subagent-retro.ps1 spec-ID extraction (#18)

* fix: rename reserved $input variable in PowerShell hooks

Assigning parsed hook JSON to $input (PowerShell's automatic pipeline
variable) throws a non-terminating ParameterBindingException whenever
the hook receives real piped/redirected stdin - i.e. on every actual
Claude Code invocation. The assignment silently fails, $input stays
unbound, and the hook exits before reading any input. Renamed to
$hookInput in subagent-retro.ps1, prompt-router.ps1, and spec-gate.ps1.

Discovered while reproducing the 01-bug-subagent-retro-ps1-matches-clobber
fixture; that bug was unreachable until this one was fixed.

* fix: stop $Matches clobber in subagent-retro.ps1 spec-ID extraction

Get-IndexSpecs ran the spec-ID regex -match before the in-progress
literal -match, so the second match overwrote $Matches and every
in-progress spec was reported under the literal ID "in-progress"
rather than its real ID. The retro-staleness check then looked for
<specDir>/in-progress/05-retro.md, which never exists, so every
in-progress spec was reported stale under the wrong ID.

Swap the match order (in-progress first, ID regex last) to match
the already-correct sister hooks prompt-router.ps1:167 and
spec-gate.ps1:145.

Verified against a fixture index (FEAT-123 in-progress, RCA-77
in-progress) - PowerShell now reports FEAT-123 and skips the RCA
row, matching hooks/bash/subagent-retro.sh output exactly.

* fix: same-line in-progress detection parity in spec-gate.sh

bash used two independent file-wide greps for the in-progress marker
and a spec ID, letting a legend/header line combine with an unrelated
done row. spec-gate.ps1 and prompt-router.sh already require both on
the same line; spec-gate.sh now matches.

* fix: align feature.md and refactor.md subagent field names with contracts

sd-spec-architect and sd-code-explorer read TASK/SPEC/IMPACT, not the
TASK_TYPE/SPEC_REF/IMPACT_REF/TICKET_DATA names feature.md sent them.
Both refine invocations were also missing the required SPEC path, and
the TEMPLATE field was bare instead of the full template filename.
refactor.md's characterization-test loop sent sd-implementer an
unrecognized TASK_TYPE field instead of TASK_DETAILS/SPEC_REF/
WORKFLOW_TYPE. Reviewer invocations, which own TASK_TYPE/SPEC_REF as
their own contract, are unchanged.

* fix: make /sd:spec validate plan/tasks check type-aware

The in-progress file-presence rule exempted only RCA from requiring
01-plan.md/02-tasks.md, but bug and perf workflows never produce those
artifacts either (their artifact set is spec, decisions, artifacts,
retro). Validate failed every correctly executed bug/perf spec.
Narrowed the requirement to feature and refactor, and fixed the same
overgeneralized claim in docs/usage.md's resume heuristic.

* fix: parse debounce timestamp as UTC in subagent-retro.ps1

[datetime]::Parse of a "...Z" string returns Kind=Local, converted
from UTC to local time. Subtracting that from a UTC now skewed $age
by the machine's UTC offset -- wrongly suppressing reminders on
UTC+N machines, never suppressing on UTC-N. Verified on a UTC+7
machine under Windows PowerShell 5.1 (the hooks' target runtime):
old code showed a 420-minute skew immediately after save; fixed code
shows ~0.

* fix: apply per-workflow keyword defaults in prompt-router.ps1

PS only fell back to built-in defaults when project-config.json was
absent or unparseable, then silently skipped any workflow whose
keyword list was null once a config file existed. A valid config
that omitted workflow.keywords (or one workflow's entry) lost
keyword routing hints on Windows while bash kept emitting them from
defaults, matching bash's per-workflow "// []" + empty-list fallback
semantics. Verified against both a config missing workflow.keywords
entirely and one overriding a single workflow, under Windows
PowerShell 5.1.

* fix: replace stale MCP tool names in agents, skills, and explore command

* fix: remove hardcoded MSSQL/C#/TS stack references from agents and commands

* fix: code-explorer impact-map returns analysis instead of self-appending

* fix: walk full lifecycle state machine in bug/rca/perf workflows

* fix: guard workflow Phase 0 against missing or malformed Layer-2 context

* refactor: dedup rules copy-pasted from skills into agent/command bodies

* docs: fix README/ROADMAP/usage drift after v1.3.0+ shipped

* refactor: derive validate.sh/ps1 install counts from source tree

* feat: add macOS to CI matrix and hook smoke tests

Also fixes two PowerShell-7-only debounce bugs in subagent-retro.ps1
that the new smoke test caught: an unresolvable Split-Path parameter
set, and re-Parse-ing an already-converted [datetime] as local time.

* fix: harden install.sh - strict mode, prefix guard, quoting

* fix: strip CRLF from jq keyword output in prompt-router.sh

Some jq builds (observed with Windows jq.exe) emit CRLF for join("\n")
output even from LF-only input, so every keyword but a workflow's last
kept a trailing \r and never matched, silently dropping keyword-routing
hints whenever project-config.json defined workflow.keywords. Fixture
in scripts/smoke-hooks.sh/.ps1 now sets workflow.keywords so this path
is actually exercised.

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

* fix: make prompt-router.sh bash 3.2 compatible

macOS runners execute hooks with system bash 3.2, which has no
associative arrays: declare -A silently failed and every string
subscript arithmetic-evaluated to 0, so the router emitted
'- /sd:0' instead of '- /sd:bug' and the macOS smoke test failed.
Replace the two associative arrays with parallel indexed arrays.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: add review findings TODO before merge

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@developzoneio
developzoneio deleted the fix/retro-ps1-matches-clobber branch July 28, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant