fix(setup): record the session-start hook as a search-path name (task 0043) #52
Reference in New Issue
Block a user
Delete Branch "task-0043-hook-records-bare-binary-name"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Task:
.claude/tasks/0043-hook-records-bare-binary-name.mdSummary
The SessionStart hook recorded the entrypoint's absolute path on every wrapper-based install, so it broke on upgrade — silently, since a hook that cannot execute simply does not run.
setup hooksnow resolvesgitea-axionPATHitself and hands that location to the SDK, whose realpath test then succeeds and records the bare, upgrade-stable name.A candidate qualifies only if it resolves to the running entrypoint: by realpath for a symlink (npm's shape), or by naming the entrypoint in its text for a generated wrapper, following the chain — a Nix install turns out to be two hops,
bin/gitea-axi→bin/.gitea-axi-wrapped→dist/main.js.A same-named binary that is some other program is refused, and the absolute entrypoint path stands as the fallback exactly as before.
The related defect on the same line goes with it.
The SDK recognises its own hook by finding the marker inside the recorded command, so an entrypoint path without
gitea-axiin it made a re-run append a duplicate instead of updating in place.setup hooksnow prunes duplicates by matching the exact command it records — independent of that command's shape, and unable to mistake another tool's hook for its own.Consequently
package.nixno longer renames its build tree, and thesetuphelp text no longer tells the user to re-run hooks after an upgrade.The decision is ADR 0019; ADR 0009's addendum and the spec's "Resolved verification item" section are amended, both having recorded conclusions this change supersedes.
Deviations
The first cut accepted any executable named
gitea-axionPATH.That passed the SDK's check only because the path handed over trivially matched itself, making the check a tautology, and would have recorded a bare name for a binary shadowing this one — against the criterion's "resolves to the running program".
Hence the stricter symlink-or-wrapper rule, which in turn required following the wrapper chain once the real Nix binary showed it was two hops rather than one.
An early pruning predicate was
recorded === command || recorded.includes("gitea-axi"), which reintroduced the exact substring coupling the task removes and could have deleted an unrelated tool's hook.It is now exact-equality only; that suffices, because a re-run records an identical command and the upgrade case is handled by the bare name being stable.
Criterion 6 was met by driving the built binary rather than by a test, since no test tier installs a wrapper.
Against
result/bin/gitea-axi: onPATH→gitea-axi; offPATH→ the store entrypoint path; impostor onPATH→ fallback; re-runs in both modes → exactly one entry.A globally
npm install-ed pack recordsgitea-axithrough its symlinkedbin.Review
Risk
Overall: MEDIUM
setup hooksplus a newsrc/hooks.ts; no other caller imports either symbol.settings.json/hooks.json, which re-running restores.test/hooks.test.tscovers resolution and pruning directly;test/setup.test.tsdrives the CLI for the on-PATH, off-PATHand impostor branches.setupis an explicit one-off install command, not on any request path.Unaddressed findings
Standards — prose rationale is restated in five places (ADR 0019, the spec,
CLAUDE.md,hooks.ts,setup.ts).Kept: each audience reads exactly one of them, and the two source comments are the ones a reader hits without knowing an ADR exists.
Standards —
pruneDuplicateManagedHookscasts throughHookSettings/HookGroup/HookEntryrather than narrowing once.Kept: the input is arbitrary parsed JSON from a user-owned file, and a validating parser for a shape the SDK owns would be a larger commitment than this change warrants.
All other findings were fixed in the diff.