Skip to main content

PRD 005: PostToolUse Context-Aware Directive Re-injection

Design Document

This is a Product Requirements Document — a design specification. It describes intended behavior, not necessarily current implementation. Refer to the architecture docs for the current state.


Problem​

Directives and protocols are injected once at session start (and on compact/clear), but Claude's attention to them degrades as context fills. By 50–60% context usage, recall measurably drops. By 70–75%, the model is at its least intelligent — exactly when auto-compact fires. There is no mechanism to refresh this content mid-session.


Goal​

Add a PostToolUse hook that re-injects directives and protocols at three token-budget thresholds calibrated to the context degradation curve, keeping the model behaviorally aligned throughout long sessions.


Degradation Research​

Source: docs/research/context-and-degredation.md

  • 30–49% — Model is sharp; session-start directives are still well-attended
  • 50–60% — Context rot begins; accuracy and recall measurably degrade
  • 70–75% — "Consider compacting now" zone; quality is at its lowest
  • 80–83.5% — Auto-compact fires

Thresholds​

Three injections with compressed cadence (each window shorter as risk increases):

InjectionThresholdGapRationale
First30%—Pre-degradation; model maximally receptive
Second52%22%Just into rot zone; catch it early
Third70%18%Deep in rot; last refresh before compaction zone

All three configurable via env var: LESSONS_REINJECT_THRESHOLDS=30,52,70


What Gets Re-injected​

Both directives and protocols — same set as session-start. They have equal drift exposure. Output format mirrors session-start-lesson-protocol.mjs: directives section first, protocols section second, grouped by tag.


Context Percentage Source​

Three layers, tried in order:

  1. input.context_window?.used_percentage from PostToolUse stdin — statusline receives this field; PostToolUse may too (needs empirical verification on first run)
  2. Parse transcript JSONL at input.transcript_path: scan for most recent <system_warning>Token usage: X/Y entry and compute X/Y * 100
  3. Fallback: tool-call count — increment a counter per session, re-inject every N calls (default: LESSONS_REINJECT_TOOL_COUNT=20)

State Tracking​

Temp file for tracking which thresholds have fired and the tool call count:

{ "fired": [30], "toolCount": 7 }

Location: $TMPDIR/lessons-<sessionHash>-reinject.json — consistent with the existing dedup/seen state convention.

Reset: session-start-reset.mjs already cleans session temp state on clear/compact — no extra wiring needed, as long as the file uses the same $TMPDIR convention.


Files​

ActionFile
Createhooks/posttooluse-directive-reinject.mjs
Edithooks/hooks.json — add PostToolUse entry, matcher .+

Open Questions​

  • Does PostToolUse stdin actually include context_window.used_percentage? Needs a quick empirical dump on first run.
  • Should state live in $TMPDIR or /var/run? The former is consistent with existing conventions but may get cleaned mid-session on some systems.
  • Should re-injection suppress the PreToolUse dedup so directives can fire again at tool-use time too, or let them remain deduped?

Verification​

  1. Smoke-test the hook directly with mocked stdin at 31% usage — confirm output contains directives and protocols
  2. Smoke-test with 25% — confirm no output (threshold not crossed)
  3. Smoke-test with 31% twice — confirm second call produces no output (threshold already fired)
  4. Run node --test 'tests/unit/**/*.test.mjs' — all unit tests must pass
  5. Manual: run a long session and confirm re-injection appears in context near the threshold points