Development record
2026-08-01 — itd-105 / spc-21, hook-ordering assumption DISCLOSED and defensively mitigated rather than verified. spc-21's Approach asserts "ordering within one event's hook list is preserved by the harness, so the binary-backed session hooks run after the bootstrap in the same event". That is an assumption about the harness, and nothing in this repository verifies it; if it is false, a fresh install's first session runs `abcd hook prompt-router-reset` and `abcd hook session-start` before any binary exists, and the intent's AC ("never a raw shell 'No such file or directory'") is missed by the two commands the bootstrap is supposed to protect. A PATH fallback is forbidden by the intent's Decisions and is NOT added. Instead both commands gain an inline existence check in `hooks/hooks.json` — the same shape as the `PreToolUse` guard entry's existing inline wrapper, which turns a bad exit code into a clean message — so if the assumption turns out to be false the failure is at least a plain-language message and a non-blocking exit 2 rather than a raw shell error, with no fallback binary resolution anywhere. This mitigates the SYMPTOM; the assumption itself still wants verifying against the real harness, and the maintainer should treat it as open. In the same file the bootstrap entry gains `"timeout": 240` (seconds): the three curl budgets plus the tag resolution total 180s worst case, which the harness's 60s default would have killed mid-run — which is precisely how the trap bug above and an orphaned temp dir would both have been reached in production. Deferred, deliberately, and bounded: two racers can both proceed when one breaks the other's ten-minute-stale lock, which last-write-wins resolves into a verified binary either way; and the trap is installed just after the lock is taken, leaving a signal window bounded by that same stale-lock recovery. Both are cheaper to state than to fix, and neither can install an unverified binary.
2026-08-01 — itd-105 / spc-21, hook-ordering assumption DISCLOSED and defensively mitigated rather than verified. spc-21's Approach asserts "ordering within one event's hook list is preserved by the harness, so the binary-backed session hooks run after the bootstrap in the same event". That is an assumption about the harness, and nothing in this repository verifies it; if it is false, a fresh install's first session runs abcd hook prompt-router-reset and abcd hook session-start before any binary exists, and the intent's AC ("never a raw shell 'No such file or directory'") is missed by the two commands the bootstrap is supposed to protect. A PATH fallback is forbidden by the intent's Decisions and is NOT added. Instead both commands gain an inline existence check in hooks/hooks.json — the same shape as the PreToolUse guard entry's existing inline wrapper, which turns a bad exit code into a clean message — so if the assumption turns out to be false the failure is at least a plain-language message and a non-blocking exit 2 rather than a raw shell error, with no fallback binary resolution anywhere. This mitigates the SYMPTOM; the assumption itself still wants verifying against the real harness, and the maintainer should treat it as open. In the same file the bootstrap entry gains "timeout": 240 (seconds): the three curl budgets plus the tag resolution total 180s worst case, which the harness's 60s default would have killed mid-run — which is precisely how the trap bug above and an orphaned temp dir would both have been reached in production. Deferred, deliberately, and bounded: two racers can both proceed when one breaks the other's ten-minute-stale lock, which last-write-wins resolves into a verified binary either way; and the trap is installed just after the lock is taken, leaving a signal window bounded by that same stale-lock recovery. Both are cheaper to state than to fix, and neither can install an unverified binary.
Frontmatter
| id | dcn-2608010000000007 |
|---|---|
| type | decision |
| date | 2026-08-01 |
.abcd/work/decisions/dcn-2608010000000007-itd-105-spc-21-hook-ordering-assumption.md
open on GitHub ↗ · commit history ↗
mentions4
- iss-323hook salvage entries invoke bootstrap.sh with no timeout budget below its 180s curl worst case so a slow-link provision is killed and suppressed for ten minutes
- iss-2609012039107700GHSA-gx3m-3224-qqcv (CWE-426, advisory severity medium): the hook shims' PATH fallback executes an unverified `abcd` as the guard and rules loader. In `hooks/hooks.json`, UserPromptSubmit, PreToolUse and PreCompact carry `if [ -z "$g" ] && command -v abcd; then g=abcd; fi` and SessionEnd `exec abcd hook session-end` when the plugin-root binary is missing; PreToolUse's fence passes exit 0/1/2 through, so a foreign binary exiting 0 approves every shell command. SessionStart has no PATH rung and fails closed. Reproduced at v0.7.0 with the shipped PreToolUse command, a failing bootstrap and a hijack directory first on PATH: the planted binary received `guard hook` plus the tool input and its exit 0 was accepted as armed. The rung was introduced by iss-254 (20968bdf, v0.5.1), is pinned as desired behaviour by `internal/surface/cli/hooks_selfprovision_test.go:TestBinaryHooksFallBackToAPathBinary`, and is a documented contract (docs/how-to/install.md: "the plugin root first, then an abcd on PATH … the install one-liner restores it"; brief 04-surfaces/01-ahoy.md section 9; brief 05-internals/03-configuration.md). The record contradicts itself: the 2026-08-01 decisions [dcn-2608010000000005](../../decisions/dcn-2608010000000005-itd-105-spc-21-implementation-the-plugin.md) and [dcn-2608010000000007](../../decisions/dcn-2608010000000007-itd-105-spc-21-hook-ordering-assumption.md) (itd-105 / spc-21) state "Rejected: a PATH fallback in the hook commands (spc-21 forbids it)" and "A PATH fallback is forbidden by the intent's Decisions and is NOT added"; iss-254 later added it with no DECISIONS entry reversing that.
- itd-105A fresh plugin install just works: the hook binary survives install and every update
- spc-21a-fresh-plugin-install-just-works-the-hook-binary-survives