🔒 Security / safety
-
hook-canary.shcould never go back to green: one blind event pinned it red forever. It
branched on the existence of a.blindmarker and never compared it against the good heartbeat,
so a single legitimate empty-payload invocation reported BLIND permanently — measured with good
heartbeats 107 seconds newer while the hooks were receiving 900+ byte payloads. An instrument
stuck red gets ignored, which is the same end state as no instrument.A blind marker is now CURRENT unless a well-formed marker is followed by a strictly
newer good heartbeat. The first attempt at this fix cleared too eagerly and the release
review caught it: EQUAL timestamps read green (same second proves no ordering), and a
truncated marker like20260909_15sorts BELOW a real stamp and so read green too. -
A recovered hook now reports
RECOVERED, notALIVE, and is counted separately. For
backup-before-edit.shandsnapshot-before-tool.sh-- which exit 0 silently on an empty
payload -- this report is the only place intermittent starvation could ever surface, so
folding "was blind, then recovered" into a green line hid it. Exit code stays 0: the hook IS
working now, and saying otherwise is the false positive that gets an instrument ignored. -
Known limit: clock skew is out of reach by construction. No pair of timestamps can reveal
a non-monotonic clock, so a heartbeat written after a clock step can still read as a recovery. -
Hook budgets raised 30s → 60s (
PreToolUse) and 30s → 120s (SessionStart). v3.9 raised them from 5s after
establishing that a late refusal is discarded. Two measurements bound the headroom and
they disagree, so both are stated: in-hook instrumentation puts the slowest hook body at
557 ms, while the worsthook_successduration across 250 transcripts is 8.2 s.
Against the pessimistic figure 60s is a ~7x margin — the same ratio 30s gave against the
4,620 ms that motivated raising it — so this is headroom, not a fix. SessionStart was
already 120s before this arc; only the four PreToolUse entries changed here.
📉 Corrections to what v3.9 claimed
- v3.9's closing claim that "the real fix is the ~20 forking
echo | greppipelines" is
withdrawn. In-hook instrumentation measuredprotect-bash.sh's entire body at 375 ms median
(max 465 ms) and itsINPUT=$(cat)stdin read at 13 ms. Removing ~35 forks would buy roughly
250 ms against a 60,000 ms budget. The refactor was planned, priced against real data, and
dropped — it would have rewritten the matching core of a 313-line delete guard for an
unmeasurable gain.
🧰 Release process
-
The Nexus pack now has one home and one shape, enforced by a script and a test.
Across eleven releases it landed in six shapes and four places: v3.5 and v3.5.3 shipped
EMPTY directories,nexus-v3.8/holds a zip named 3.8.1, and v3.9 split in two with the
real pack on a network share while the canonical home held onlyUPLOAD-NOTES.txt. Every
one passed review because nothing asserted the shape.scripts/build-nexus-pack.sh
produces the pack and refuses to report success on an incomplete one;
tests/test_nexus_pack.pyasserts the shape. -
The pack's zip is downloaded from the GitHub release, never rebuilt. The installable
bundle is built by CI from the tag with payload guards; a locally rebuilt zip could differ
silently from the one the release page serves. -
The changelog and description are passed in, not generated. Extracting the
## <tag>
block from CHANGELOG.md yielded 147 lines of raw markdown where the pasteable version was
63 — the Nexus box renders a flat list, so it is a rewrite, not an extract.
📚 Documentation
- PyFFI/PyNifly recipes moved out of the always-loaded
CLAUDE.mdinto the path-scoped
skyrim-nifskill. The HARD LIMITS stay inCLAUDE.md— a "never do X" rule must not sit
behind a loading condition.CLAUDE.mddrops 2,200 chars.
⚠ Known, not fixed
- A hook run from the wrong directory resolves a plausible-but-wrong install root and silently
allows.protect-bash.shderives its root from its own location; a byte-identical copy run from
elsewhere returns zero bytes (= allow) for a command the same file at its real location correctly
denies.hook-canary.shstill reports such a hook ALIVE, because it is running and receiving its
payload. The obvious hardening ("deny unless the root looks like a Skyrim install") would break
this repo, which carries these hooks and is not a game install.