The SDK records a bare, upgrade-stable binary name only when a PATH entry
realpath-matches the execPath it is handed. From the module-relative
entrypoint that can only succeed under npm, which symlinks its bin entry
straight at it; a wrapper-based install never can, because a script that
invokes a file does not resolve to that file. So every wrapper install --
Nix, a shim, a generated .cmd -- recorded an absolute path that moves on
upgrade, and a session-start hook that cannot execute fails silently.
`setup hooks` now resolves gitea-axi on PATH itself and hands that
location to the SDK, so the bare name is recorded. A candidate qualifies
only if it resolves to the running entrypoint -- by realpath for a
symlink, or by naming it in its text for a wrapper, following the chain,
since a Nix install is two hops. A same-named binary that is some other
program does not qualify, and the absolute entrypoint path stands as the
fallback exactly as before.
The related defect on the same line goes too: the SDK recognises its hook
by finding the marker inside the recorded command, so an entrypoint path
without "gitea-axi" in it made a re-run append a duplicate rather than
update in place. `setup hooks` now prunes duplicates by matching the
exact command it records, which is independent of that command's shape
and cannot mistake another tool's hook for its own.
With the coupling gone, package.nix no longer renames its build tree; the
fast tier runs from /build/source and its idempotency test passes there.
The help text's instruction to re-run hooks after an upgrade is deleted,
having become false.
Verified against the built Nix binary and a globally npm-installed pack:
both record the bare name, both fall back to the absolute path when the
name is absent, an impostor on PATH is refused, and re-runs leave one
entry. Decision recorded as ADR 0019; ADR 0009's addendum is amended.
Grilling task 0042's mitigation found its framing too narrow. Recording an
absolute entrypoint path is one defect; the deeper one is that `setup` is
write-only, and an operator whose agent configuration is generated
declaratively cannot let it write at all. On such a machine both halves fail —
`setup hooks` against a read-only settings file, and `setup` on an unhandled
filesystem error — and the Agent Skill gets hand-copied into the operator's own
configuration, where it silently drifts from the package that ships it.
Three tasks follow:
- 0043 records the bare binary name, resolved through PATH, so the hook
survives an upgrade on any wrapper-based install. Fixes the marker-substring
coupling with it and drops the derivation's build-tree rename.
- 0044 reports an unwritable target as a structured error naming no cause,
rather than crashing.
- 0045 adds the declarative install path: a stable Skill location, the Skill
and hook specification exposed as Nix-consumable attributes, one committed
hook specification read by both the expression and the test suite, and a
home-manager module wiring them. Blocked by 0043, whose bare name the
specification declares.
CONTEXT.md gains the four terms this settled and amends `setup` and
`SessionStart hook`, which described the imperative path as the only one.
Entries for unbuilt work name the task that lands them, so the glossary does
not assert behaviour the code lacks.
The re-run-after-upgrade help text this branch added stays as it is: accurate
until 0043 removes it, which that task carries as a criterion.