Development record
2026-08-01 — itd-105 / spc-21 implementation: the plugin provisions its own binary from a committed POSIX-sh `hooks/bootstrap.sh` wired as the FIRST `SessionStart` hook, per spc-21's Decisions (fast path on an executable binary, darwin/linux × amd64/arm64 gate, atomic `mkdir` lock with a 10-minute stale break, same-origin SHA-256 against the release `checksums.txt`, atomic rename install, `.binary-meta` provenance). Three judgment calls a reviewer should see. (1) The script's TEST-ONLY overrides are TWO env vars, not one: `ABCD_BOOTSTRAP_BASE_URL` (download base, as spc-21 names) plus `ABCD_BOOTSTRAP_API_URL` (release-commit lookup base) — the release-sha resolution hits a different URL shape than the asset download, and folding both behind one variable would have made the fixture server lie about which endpoint was called. Both mirror `ABCD_BIN_TARGET`'s pattern. (2) `release_sha` is read from the commits API (`/commits/<tag>`), NOT the releases API's `target_commitish`: this repo tags annotated (`git tag -a`) and creates the release from an existing tag, so `target_commitish` is the default branch name rather than a commit — a value that would have made the skew notice compare a branch name against a SHA. A value that is not 40 hex is recorded as `unknown` and renders nothing. (3) spc-21's CI-smoke line ("the existing smoke job additionally runs `sh hooks/bootstrap.sh` against a fixture server on both OS runners") is satisfied WITHOUT touching workflow YAML: the script-driving test lives in `internal/surface/cli/bootstrap_test.go`, which `go test ./...` already runs on both legs of ci.yml's ubuntu+macos `check` matrix, and it shells out to the committed script against an `httptest` fixture exactly as the AC describes. A bespoke workflow step would have duplicated that coverage while adding background-process management to CI. Rejected: a PATH fallback in the hook commands (spc-21 forbids it); signature verification (out of scope, same-origin checksums are the accepted bar); guessing `plugin_sha` from `git rev-parse` when the plugin root is not a commit-stamped cache directory (the meta file never guesses).
2026-08-01 — itd-105 / spc-21 implementation: the plugin provisions its own binary from a committed POSIX-sh hooks/bootstrap.sh wired as the FIRST SessionStart hook, per spc-21's Decisions (fast path on an executable binary, darwin/linux × amd64/arm64 gate, atomic mkdir lock with a 10-minute stale break, same-origin SHA-256 against the release checksums.txt, atomic rename install, .binary-meta provenance). Three judgment calls a reviewer should see. (1) The script's TEST-ONLY overrides are TWO env vars, not one: ABCD_BOOTSTRAP_BASE_URL (download base, as spc-21 names) plus ABCD_BOOTSTRAP_API_URL (release-commit lookup base) — the release-sha resolution hits a different URL shape than the asset download, and folding both behind one variable would have made the fixture server lie about which endpoint was called. Both mirror ABCD_BIN_TARGET's pattern. (2) release_sha is read from the commits API (/commits/<tag>), NOT the releases API's target_commitish: this repo tags annotated (git tag -a) and creates the release from an existing tag, so target_commitish is the default branch name rather than a commit — a value that would have made the skew notice compare a branch name against a SHA. A value that is not 40 hex is recorded as unknown and renders nothing. (3) spc-21's CI-smoke line ("the existing smoke job additionally runs sh hooks/bootstrap.sh against a fixture server on both OS runners") is satisfied WITHOUT touching workflow YAML: the script-driving test lives in internal/surface/cli/bootstrap_test.go, which go test ./... already runs on both legs of ci.yml's ubuntu+macos check matrix, and it shells out to the committed script against an httptest fixture exactly as the AC describes. A bespoke workflow step would have duplicated that coverage while adding background-process management to CI. Rejected: a PATH fallback in the hook commands (spc-21 forbids it); signature verification (out of scope, same-origin checksums are the accepted bar); guessing plugin_sha from git rev-parse when the plugin root is not a commit-stamped cache directory (the meta file never guesses).
Frontmatter
| id | dcn-2608010000000005 |
|---|---|
| type | decision |
| date | 2026-08-01 |
.abcd/work/decisions/dcn-2608010000000005-itd-105-spc-21-implementation-the-plugin.md
open on GitHub ↗ · commit history ↗
mentions3
- 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