Skip to content

fix: PowerShell hooks audit, script hardening, and workflow fixes - #20

Merged
developzoneio merged 20 commits into
mainfrom
hotfix/ps-hooks-audit
Jul 5, 2026
Merged

fix: PowerShell hooks audit, script hardening, and workflow fixes#20
developzoneio merged 20 commits into
mainfrom
hotfix/ps-hooks-audit

Conversation

@developzoneio

Copy link
Copy Markdown
Owner

This pull request merges the hotfix/ps-hooks-audit branch into main.

Summary of Changes

  • PowerShell Hooks & Installers Hardening:
    • Hardened install.sh/install.ps1 with strict modes, prefix guards, and proper quoting.
    • Resolved bash 3.2 compatibility issues in prompt-router.sh.
    • Added CRLF stripping from jq keyword output.
  • Workflow & Lifecycle Improvements:
    • Ensured correct lifecycle state machine traversal in bug, RCA, and perf workflows.
    • Added Phase 0 bootstrap guarding against missing/malformed Layer-2 contexts.
  • MCP Cleanups:
    • Removed stale references to Microsoft SQL Server/C#/TS stacks in commands/agents.
    • Updated outdated MCP tool names to align with actual tool definitions.
  • Debouncing & Smoke Testing:
    • Fixed UTC timestamp parsing in PowerShell hook debouncing.
    • Integrated macOS into the CI matrix and verified hook behavior.

Notes

Includes REVIEW-TODO.md outlining outstanding findings to review or address pre/post-merge.

developzoneio and others added 20 commits July 2, 2026 11:00
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: 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.
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.
…acts

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.
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.
[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.
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.
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.
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>
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>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@developzoneio
developzoneio merged commit 887ff74 into main Jul 5, 2026
6 checks passed
@developzoneio
developzoneio deleted the hotfix/ps-hooks-audit 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