Skip to content

Stale-hook notice from gateway version headers (0.18.0) - #33

Open
davidcrowe wants to merge 2 commits into
mainfrom
feat/hook-stale-notice
Open

davidcrowe wants to merge 2 commits into
mainfrom
feat/hook-stale-notice

Conversation

@davidcrowe

Copy link
Copy Markdown
Collaborator

Why

A production tenant is running hook 0.4.0 while main is 0.16.0, and two hardline/tamper denies ran anyway on that stale hook (hook.enforcement_diverged) — a hook that is behind can silently stop enforcing.

What

The hook now reads X-ACP-Latest-Version / X-ACP-Min-Good-Version on every hook response (PreToolUse /govern/tool-use, PostToolUse /govern/tool-output, SessionStart /govern/attest), plus latestVersion / minGoodVersion in the attest body, and compares them with its own version.

When it is behind, it says so:

  • [ACP] governance hook v0.4.0 is outdated (v0.17.0 current). Run: acp-update
  • below the minimum: [ACP] governance hook v0.4.0 is below the minimum supported v0.12.0 — denies may not be enforced. Run: acp-update

The line goes to stderr and rides the event's existing notice channel (PreToolUse allow funnel, PostToolUse systemMessage, SessionStart additionalContext), appended to any notice already there — never replacing it, still one stdout JSON object per run.

Frequency

At most once per 24h across all events, via ~/.acp/.stale-notice (epoch ms of the last notice). The marker is written before the notice is emitted; every failure (unwritable ~/.acp, garbage marker) → no notice, never noise.

Scope

Advisory only. The hook does not download or execute anything, adds no await before the verdict, and modifies no file except the marker. Missing or unparseable version values compare as equal, so a gateway that does not send the headers yet produces no notice at all. It depends on the gateway PR that adds the response headers and attest fields.

Also

  • Version bump to 0.17.0 (bin/govern.mjs, plugin.json, .claude-plugin/marketplace.json).
  • lib/staleNotice.mjs is the canonical copy for tests (govern.mjs stays self-contained, same pattern as vendor-patterns / receipt / attestation); test/stale-notice.test.mjs covers semver ordering incl. garbage, header parsing, the three text states, and the marker TTL / rewrite / throwing-fs cases. node --test test/: 199 pass, 0 fail.

A production tenant ran hook 0.4.0 while main was 0.16.0 and two
hardline/tamper denies ran anyway (hook.enforcement_diverged). The hook
now reads X-ACP-Latest-Version / X-ACP-Min-Good-Version on every hook
response (and latestVersion / minGoodVersion in the attest body) and,
when it is behind, says so once per 24h: one stderr line plus the same
text on the event's existing notice channel (PreToolUse allow funnel,
PostToolUse systemMessage, SessionStart additionalContext), appended to
any existing notice. Advisory only: no download, no execution, no await
before the verdict, no file touched except ~/.acp/.stale-notice.
@davidcrowe
davidcrowe force-pushed the feat/hook-stale-notice branch from 93a0ba0 to c40a654 Compare September 14, 2026 22:37
@davidcrowe davidcrowe changed the title Stale-hook notice from gateway version headers (0.17.0) Stale-hook notice from gateway version headers (0.18.0) Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant