Skip to content

fix(activation): a pending receipt gates dispatch not reads; preflight consumes the nonce; deliver identity, not the body #893

Description

@thewrz

This was written agentically; verify its assertions:

North star

Few turns. A parallel-issues root paid six tool calls of activation ceremony before its first real call on 2026-09-23: two denied reads, an ack, two checks, one preflight. The receipt is the only proof against the #722 class (six hours and 261.8M tokens with no plugin loaded), so the receipt stays and the ceremony goes.

Why (verified on origin/main b63f8ca, rollout 01a0ce6d on jacobs)

  • agentkit/hooks/pre-tool-use.sh:52-64 arms the gate on the session receipt; workflow-activation.py:336-352 denies every non-inspection call while the record is pending. The root's first two reads of the skill were denied.
  • The receipt needs its own ack tool call, then agent-preflight.sh runs check anyway.
  • workflow-activation.py:233-256 embeds the whole 66 KB SKILL.md in additionalContext. On Codex it arrived as a 10,616-char developer message with the rest spilled to /tmp/hook_outputs/; zero copies of ### Step 2b reached the model through the hook, while the native skill injection delivered the full body separately. Claude Code spills hook context over 10,000 chars to a file too.
  • Research 2026-09-23 (Claude Code and Codex hooks docs): both harnesses fail open on hook timeout/crash/bad JSON; Codex does not enforce a PreToolUse deny for apply_patch (PreToolUse deny (exit 2 + permissionDecision JSON) is not enforced for apply_patch - hook fires, write proceeds openai/codex#27833); Claude holds the first response until SessionStart hooks finish. A deny-until-ack gate on reads therefore buys nothing it can rely on.

What (decision B, operator 2026-09-23)

  1. A pending receipt gates dispatch-class calls only: spawn tools, create-issue-worktree.sh, worktree-commit.sh, chain-advance.sh, git push, git worktree add, gh pr create|ready|merge. Reads, edits, apply_patch, and inspection proceed.
  2. agent-preflight.sh --activation-nonce NONCE promotes the receipt inside the first call every workflow already makes; a wrong nonce exits 1 before env-contract.txt is written. Delivery names that preflight line instead of a separate ack.
  3. Delivery is the preflight line plus skill= version= hash= identity, under 1,500 characters; SessionStart re-delivery has the same shape. The body copy is gone.
  4. bench/activation-ordering.sh proves on both harnesses that hook context arrives before the model's first tool call, and counts denied calls before and after.

Testing

  • tests/test-workflow-activation.sh (new) fails on origin/main at the pending-read and body-delivery assertions, passes on the branch
  • tests/test-agent-preflight.sh pins wrong-nonce → exit 1, no contract written
  • probe: ORDER=context-before-first-call on codex 0.155.1 and claude 2.1.280; denied calls 0 and 0
  • tests/run-tests.sh green; no size-ceiling raise
  • CI green

🤖 Co-authored by Claude Fable 5.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/hooksSessionStart, PreToolUse, PostToolUse, StopbugSomething isn't workingp1Blocks public releasereliabilityCorrectness of the guards and helpers

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions