Skip to content

fix(context-guard): describe the 0.5.0 audience split in the reader contract - #3040

Merged
kyle-sexton merged 2 commits into
mainfrom
claude/pocock-steering-course-00zkvd
Aug 19, 2026
Merged

fix(context-guard): describe the 0.5.0 audience split in the reader contract#3040
kyle-sexton merged 2 commits into
mainfrom
claude/pocock-steering-course-00zkvd

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Closes #2973

Summary

reference/reader-contract.md's zone-crossing hooks section described the advisory injection as it worked before the 0.5.0 audience split — as one undifferentiated block carrying "a minimal generic continuation tree plus a presence-gated pointer to session-flow:workflow's router". Since 0.5.0 the menu has been operator-channel only, so the reference told every reader of the seam that the model receives an exit menu it has not received for many versions. Surfaced by the fresh-context verifier during AI Hero course lane 2 (#2900).

Fix

The section now states what each channel actually carries, and why:

  • Model channel (additionalContext) — the determination plus the counter-steer (the reading is a measurement rather than an instruction; real degradation shows up in the model's own output, never in a zone word; keep working the task in hand), plus the durable-note addendum in dumb.
  • Operator channel (systemMessage) — the same crossing plus the continuation menu that is the human's call (continue / /clear / handoff-then-/clear, with the hand-written resume note as the standalone-install fallback / /compact) and the presence-gated router pointer.
  • Neither the menu nor the router pointer ever reaches the model channel — with the I23 rationale stated where the split is described (claude-config, reference/criteria.md): a menu injected into model context manufactures the model's own initiative to stop, summarize, or hand off, and I23's Remediate clause prescribes exactly this shape — counter-steer plainly stated, reassurance rather than an exit menu.
  • The model channel says continuation is the operator's call, never that the operator has seen the menu, since no documented hook behavior tells a hook whether an operator is present.

Verification

  • Verified against the hook's actual emission, not its header summary: hooks/zone-crossing-inject.sh builds guidance (model) and operator (operator) separately and emits both via hook::emit_channels — the rewritten prose tracks those two strings, including the dumb-only addendum and the option-3 manual fallback.
  • Third acceptance criterion swept: grep over the whole contract for injection/menu/channel prose found the content described in exactly one place (the section rewritten here). The other injection mentions cover hysteresis and the compaction marker and are accurate — no other section describes pre-0.5.0 behavior.
  • bash plugins/context-guard/hooks/zone-crossing-inject.test.sh — PASS=53 FAIL=0 (hook untouched; run because this doc is its contract).
  • bash scripts/validate-plugins.sh — all manifests + catalog validated; generate-catalog.mjs --check in sync.
  • bash scripts/check-changelog-parity.sh --check / --check-bump origin/main / --check-order — all green.
  • bash scripts/check-changed-skills.sh origin/main — no changed skills under plugins/*/skills/ (this is a plugin-level reference/ file).
  • bash scripts/check-cross-plugin-source-drift.sh — exit 0.
  • npx markdownlint-cli2 on both changed markdown files — 0 issues; typos clean.
  • Version bump context-guard 0.7.14 → 0.7.15 (patch — documentation accuracy, no behavior change).

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4


Generated by Claude Code

…ontract

The zone-crossing hooks section still called the advisory injection "a minimal
generic continuation tree plus a presence-gated pointer to
session-flow:workflow's router" — one undifferentiated block, which is the
shape from before 0.5.0 moved the menu off the model channel. Every reader of
the seam was therefore told the model receives an exit menu it has not
received since 0.5.0.

The section now states what each channel actually carries: additionalContext
gets the determination plus the counter-steer (and the durable-note addendum
in dumb), systemMessage gets the same crossing plus the continuation menu and
the router pointer, and neither the menu nor the pointer ever reaches the
model channel. The I23 rationale sits where the split is described, including
why the model channel says continuation is the operator's call but never that
the operator has seen the menu.

Verified against hooks/zone-crossing-inject.sh's actual emission rather than
its header summary. Swept the rest of the contract for pre-0.5.0 injection
prose: the other injection mentions describe hysteresis and the compaction
marker, and are accurate. Documentation only; no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4
@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 44s —— View job


Security Review — /review:security-review

Result: Skip gate triggered — no security review performed.

All three changed files are documentation/metadata, with no security-relevant surface:

  • plugins/context-guard/.claude-plugin/plugin.json — version bump only (0.7.140.7.15), no permission or config changes.
  • plugins/context-guard/CHANGELOG.md — changelog entry only.
  • plugins/context-guard/reference/reader-contract.md — prose rewrite of the zone-crossing hooks section describing what the existing, unmodified hooks/zone-crossing-inject.sh emits on each channel. No hook, script, or workflow logic is touched.

No injection surfaces, authz/access-control logic, credential/token handling, or GitHub Actions workflow changes are present in this diff. Per the skill's skip gate ("change has no security-relevant surface after reading the diff"), stopping here with no findings.

The new entry replaced the 0.7.14 heading instead of being inserted above it,
so that release's notes were absorbed into 0.7.15 — caught by
changelog-parity-gate's absorbed-heading check, which is exactly the defect it
exists to find. The heading is back and both sections own their own notes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4
@github-actions

Copy link
Copy Markdown

Last security-reviewed head: ddf5bc6d8d575c862bcac7013d30df06f3de6723. On the next push, the relevance gate compares only the commits since this SHA; delete this comment to force a full re-review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ddf5bc6d8d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/context-guard/CHANGELOG.md
@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 2m 2s —— View job


Code review complete

  • Gather context (PR diff, CLAUDE.md, REVIEW.md)
  • Review changed files for correctness/maintainability
  • Post findings (inline + summary)

No blocking findings. This is a documentation-only fix and it holds up against the implementation it describes.

What I checked:

  • Diffed plugins/context-guard/reference/reader-contract.md's rewritten zone-crossing section against the actual string construction in hooks/zone-crossing-inject.sh (the guidance and operator variables) rather than trusting the PR's own description. The model-channel prose (measurement-not-instruction, counter-steer, dumb-zone durable-note addendum) and operator-channel prose (4-option menu, session-flow:workflow router pointer, "(if installed)" hedges) both match the emitted strings, including the option-3 handoff/resume-note bundling.
  • Grepped reader-contract.md for menu/systemMessage/additionalContext/session-flow:workflow — confirmed the rewritten section is now the only place in that file describing this content (matches the PR's third-acceptance-criterion sweep claim).
  • Checked plugins/context-guard/README.md's own channel description (lines 27–30) — already accurate, no drift there to compound.
  • Version bump (0.7.140.7.15) in plugin.json matches the new ## [0.7.15] CHANGELOG heading, and the second commit (1fb7ab8, "restore the 0.7.14 changelog heading") correctly fixes a heading-absorption defect from the first commit — verified the current CHANGELOG.md has both ## [0.7.15] and ## [0.7.14] as distinct sections with their own content.
  • The I23 (claude-config, reference/criteria.md) shorthand path (full path is actually plugins/claude-config/skills/audit-instructions/reference/criteria.md) is copied verbatim from the pre-existing comment in hooks/zone-crossing-inject.sh:13 (an untouched file) — not new imprecision introduced by this PR, so not flagging it.

Nothing else in the diff (the plugin.json version bump, the CHANGELOG entry) raised concerns — both are consistent with repo conventions.

Branch: claude/pocock-steering-course-00zkvd

@github-actions

Copy link
Copy Markdown

Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count.

@kyle-sexton
kyle-sexton merged commit 30c686e into main Aug 19, 2026
48 checks passed
@kyle-sexton
kyle-sexton deleted the claude/pocock-steering-course-00zkvd branch August 19, 2026 18:29
kyle-sexton added a commit that referenced this pull request Aug 19, 2026
…zone interplay (#3042)

Closes #2995

## Summary

The reader contract stated that no auto-compaction threshold is
documented and stopped there, which reads as "the trigger is opaque".
The *default* is unpublished, but the trigger is operator-tunable — and
none of the surfaces that tune it appeared anywhere in the plugin.
Consumers tuning zone bands therefore had no guidance on how the bands
relate to the auto-compact trigger, and the PostCompact marker's
`trigger` field had documented values but no consumer guidance.

Documentation only; no behavior change. context-guard 0.7.15 → 0.7.16.

## Fix

**`reference/reader-contract.md` — the four auto-compact config
surfaces.** Verified 2026-08-17 against two independent pools (the
official settings reference and the shipped binary's schema strings,
v2.1.233), and re-verified 2026-08-19 against the live settings,
env-vars, and model-config pages:

| Surface | Kind | What it does |
|---|---|---|
| `autoCompactWindow` | `settings.json` | Tokens, `100000`–`1000000`;
**no numeric default** (model-tuned when unset). Written by
`/autocompact`; `--autocompact` sets it for one launch and, unlike the
command, isn't preempted by a higher-priority settings scope. |
| `CLAUDE_CODE_AUTO_COMPACT_WINDOW` | environment | Same units;
**highest precedence**. Plain integer only — `500k` reads as `500` and
clamps to the 100K minimum. |
| `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` | environment | Percentage (1–100)
of the auto-compact window at which compaction triggers; **lower-only**.
Applies to subagents too. |
| `autoCompactEnabled` / `DISABLE_AUTO_COMPACT` | `settings.json`
(default `true`) / environment | Off switch. (`DISABLE_COMPACT` is
recorded as binary-strings-sourced and **unconfirmed by docs** as of
2026-08-19.) |

**The bands-below-the-trigger rule, with rationale.** Whatever the
trigger resolves to, `dumb` should be reached first: auto-compact offers
no steering hook, so a firing means the boundary decision was reached
too late.

**Comparisons are normalized into the percentage shape, never
occupancy.** The contract already forbids equating its two zone shapes;
the new guidance honors that explicitly. A configured window is a fill
threshold in the percentage shape's input-token accounting, not the
token shape's occupancy. Worked example: a 400,000-token window on a
1M-class model puts the trigger at **40% of the full window — inside the
shipped `smart` band (≤ 50)**, so auto-compact fires while every zone
still reads green. Recorded with the docs' own consequence:
`used_percentage` **always** measures against the model's full context
window, so a lowered window stops being visible in the percentage at
all.

**Only the diagnostic half of the house stance is adopted.** The
prescription that usually travels with it (disable auto-compact) is
explicitly rejected: unattended cloud and autonomous sessions have no
human at the boundary, and a degraded continuation beats a hard stall at
the window. Auto-compact stays enabled as the last-resort safety net
beneath the instrumented ladder.

**The Boris §64 rot figure (300–400k;
`CLAUDE_CODE_AUTO_COMPACT_WINDOW=400000`) is recorded as a named
practitioner anchor, never an adopted number** — carrying its own
amendment, that the calibration is Opus 4.7-era and the Opus 5 prompting
guide (verified 2026-08-08) removes its degradation premise. Consistent
with claim-ladder bucket ii.

**The marker's `trigger` field now has consumer guidance.** Consumers
deliberately do *not* differentiate on it — evidence degradation is
trigger-independent, holding identically for a steered `/compact` and an
auto-compact. The field is captured anyway so the stance is falsifiable:
it is the observable for the track-on-event condition recorded in
`docs/upstream/aihero-course.md` lane 3.

**A dated refinement to the adjacent "no documented threshold"
paragraph.** Upstream is now more specific than the "when approaching
context limits" phrasing that paragraph quotes: an unconfigured session
compacts **at** the model's context limit, with five enumerated
earlier-firing exceptions — including Sonnet 5 at "about 967K tokens by
default". The conclusion is unchanged (no percentage default is
published, so the bands stay declared judgment defaults), but the
trigger is now documented as model- and environment-dependent, which is
why no single band set is correct everywhere.

**`README.md`** gains a pointer naming the four surfaces, the
below-the-trigger rule, and the full-window percentage caveat, deferring
to the contract as SSOT.

## Verification

Every claim about shipped behavior was checked against the hooks'
**emitted strings**, not their header comments:
`hooks/post-compact-mark.sh` regex-captures `manual|auto`, defaults
`unknown`, and writes the marker unconditionally (no boundary-timed
carve-out), matching what the contract now states. Every upstream claim
was checked against a live fetch of the primary page, not from memory.

Full local gate, re-run after each of the three commits:

- `scripts/validate-plugins.sh` — all manifests + catalog pass
- `generate-catalog.mjs --check` / `generate-cheatsheet.mjs --check` —
both in sync
- `check-changelog-parity.sh` all four modes — `--check`,
`--check-order`, `--check-bump origin/main`, `--check-preserved
origin/main` (**40 headings compared, all preserved** — the entry was
inserted above `## [0.7.15]`, not over it)
- `check-changed-skills.sh origin/main` — no skills touched
- `markdownlint-cli2` on every touched file — 0 issues
- `typos` on `plugins/context-guard/` — clean
- Test suites of everything the change could affect:
`zones-inline-drift.test.sh` 11/0, `context-zone.test.sh` 73/0,
`statusline-tee.test.sh` 47/0, `statusline-shim.test.sh` 36/0 — **167
passed, 0 failed**. The drift suite matters here specifically: the
reader contract's operable-floor values are inlined by consumers, and
none of them were altered.

### Review rounds

Four findings raised across three review passes, all verified against
primary sources before acting, all fixed and resolved:

1. *(Codex, P2)* The worked example equated occupancy with the trigger's
input-token accounting — breaking the contract's own "never equate them
without normalizing" rule. Fixed in `a4168554`.
2. *(Claude, Important)* The surface table omitted
`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`. Confirmed against a live fetch and
added in `a4168554`.
3. *(Claude, Important)* The refinement named three of upstream's five
default-threshold exceptions, dropping Sonnet 5's. Fixed in `7c88cee5`.
4. *(Claude, Nit)* `auto-compact` split across a soft line break
rendered as "auto- compact". Fixed in `7c88cee5`, along with a
pre-existing instance of the same defect (`hard` / `-stops`) a few lines
above.

## Related

- Refs #2901 (lane 3, the verification that produced verdicts C1–C3)
- Refs #2973 (adjacent reader-contract drift fix, merged as #3040)
- Refs #2994 (the durable AI Hero course roadmap this item sits in)

---
🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

context-guard: reader-contract advisory-injection section predates the 0.5.0 audience split (aihero lane 2)

2 participants