strain · git:20260909.0eb3b37 · 2026-09-09 · sha256 d1a705dfe75260c7
strain git:20260909.0eb3b37A
Immutable. This exact content is served forever at /api/v1/blob/d1a705dfe75260c7.
--- name: strain description: >- Read how loaded the current session has become and report it as a tier, so a conversation gets wrapped before its answers start degrading. Use whenever someone asks how the session is holding up, whether it is time to wrap or start fresh, why the agent feels sluggish or forgetful; whenever a strain tick fires; and as a standing habit on long multi-hour work. Triggers on: session strain, is this session too long, should we wrap, context is filling up, running out of context, start a fresh session, why is the agent getting worse, session health, strain check, strain tier. Counts the real context size where the host exposes one, and counts behaviour where it does not. Not for short one-pass tasks — there is nothing to measure. --- # Strain A long session degrades before it fails. The context window fills, the same problem comes back a third time, an earlier answer turns out to be wrong — and the work keeps going, because nothing in the loop is watching the loop. Strain is the thing that watches: a small, countable reading of how loaded this conversation has become, reported on a schedule, in a form the user can act on. The failure this exists to prevent is not a crash. It is **silence** — the agent that never mentions the session has gone bad, and lets the user find out from the output. ## The one rule **One session, one reading.** Strain measures a single conversation, identified by its session id. Two agents working the same project in two windows are two sessions and their counts never add up. If you cannot tell which session a number belongs to, it is not a measurement. ## When to run the check Run it when any of these happen — not on a feeling that it might be time: 1. **A strain tick fires.** On hosts with hooks, a tick arrives every N tool calls (default 10) and says so explicitly. Run the check before continuing the work. 2. **A hard signal lands** (see the table below) — a factual error caught, a regression introduced, a revert of your own work, a context compaction. 3. **The task list changes shape** — a new side task, a goal switch, a task that balloons past the one it was supposed to serve. 4. **The user asks** how the session is doing, or whether to wrap. On a host with no hooks, 2–4 still work. That is the cooperative half, and it is weaker: say so rather than implying the check is firing on its own when it is not. ## What to count Two inputs, and they are not equally strong. ### Context occupancy — measured, when the host allows it Some hosts publish a per-session transcript carrying token usage. Where that exists, the context reading is a real number, not an impression: - **current** = the input side of the most recent turn (`input + cache_read + cache_creation` — cached tokens are still context the model is carrying) - **baseline** = the same sum on the first turn: what the boot alone cost before any work happened. System prompt, tool schemas, project instructions, skills. It is the floor the session can never get back under, and it is usually larger than people expect. The tick directive quotes both when it can. When it cannot, it says so — and then you count behaviour instead. **Never quote a context number you did not measure.** ### Behavioural signals — always available | Signal | Weight | How it is counted | |---|---|---| | Distinct subjects touched | soft | count of side tasks + goal switches in the task list | | A side task that balloons past the main one | soft | it spawned sub-tasks of its own | | A problem that came back | soft | it was already solved once this session | | Critical / blocking tasks open | soft | count them | | A second main goal appeared | soft | more than one thing claims to be the point | | **A factual error that ESCAPED to the user** | **hard, escaped** | one per error; record it | | **A regression that reached shipped work** | **hard, escaped** | one per regression; record it | | **An error you caught and fixed pre-delivery** | **hard, caught** | recorded, tiers nothing | | **A revert of your own work** | **hard** | escaped if the user saw the churn | | **A context compaction** | **hard** | the host compacted; the session has run long | Soft signals are colour, not ladder. Hard signals are ABSOLUTE — the same on any window size — but only the ones that **escaped** move the tier. A caught-and-fixed error is a working immune system, not exhaustion: say so, record it, and let a repeat of the same class earn a *pattern note* instead of a tier. Record every hard signal when it happens, so the counters know what you know: ``` bash "$CLAUDE_PLUGIN_ROOT/scripts/strain-signal.sh" <kind> --caught|--escaped ``` A compaction is not a fresh start — it is the clearest evidence there is that the session has run long. ## The tiers Fill is the PRIMARY signal: what fraction of the detected context window is occupied. The default bands are **40 / 60 / 75 / 85%**, calibrated to the window the plugin detects at boot (a 200k and a 1M session get different absolute budgets from the same bands — no per-host table). The tick computes and PROPOSES the tier; your job is to confirm it or adjust it with what the counters cannot see. | Tier | Fill band | Also reached by | What it means | |---|---|---|---| | **Healthy** | under 40% | — | carry on | | **Mid** | 40–60% | — | fine, but the end is in sight | | **High** | 60–75% | 1 escaped signal, or 1 compaction | wrap after the current thread | | **Warning** | 75–85% | 2+ escaped signals, or 2+ compactions | wrap now; new work should start fresh | | **Danger** | 85%+ | Warning-band fill **plus** an escaped signal | stop and hand off | The bands are printed in every readout and are configurable; retune them to your setup. **Escalate only on evidence, never on momentum.** Ticks accumulating is not evidence; a previous high reading is not evidence. Fill crossing a band and fresh escaped signals are the only ladders — and strain DECAYS: when the proposal comes in lower than the carried tier and nothing new happened, record the lower tier. Floors from compactions and escaped signals hold; everything else is allowed to relax. (The old "continuing past a Warning ⇒ Danger" rule is deleted — it pinned Danger at a measured 33% fill, three sessions running.) ## How to report it Match the shape to the tier. The point is that the user can act without asking follow-ups. - **Healthy** — one line, or nothing at all if the user did not ask. Do not pad. - **Mid / High** — the counts, and a suggestion to wrap soon: > 🟡 High — context 136k/200k (68%), of which 70k was the boot. 1 main goal, 3 side > tasks. Suggest finishing the current thread and wrapping. - **Warning / Danger** — the counts, **which hard signals fired and whether they escaped**, why it matters, and a recommendation to wrap now: > 🔴 Warning — context 158k/200k (79%), 2 compactions, 1 escaped regression. The fix > shipped broken and the window is nearly full. Recommend wrapping and starting > fresh; I will write the handoff first. Then **record it**, so the next tick carries it forward instead of starting over: ``` bash "$CLAUDE_PLUGIN_ROOT/scripts/strain-level.sh" <Healthy|Mid|High|Warning|Danger> ``` An unrecorded tier is how this reading silently sits at its first value forever while every check around it runs correctly. ## Wrapping When the tier says wrap, wrap — and mark it, because that is the only thing that resets the counters: ``` bash "$CLAUDE_PLUGIN_ROOT/scripts/strain-wrap.sh" --label "what was finished" ``` A wrap means the work is actually closed: the handoff is written, the tests are green, the thing is done. Recording that a session felt heavy and declaring it finished are different claims — do not let one imply the other. Strain says *when* to hand off. It does not do the handing off. Its companion for that is **[Throughline](../../../handoff-skill/throughline)**, whose task track is also the cleanest source for the behavioural counts above: one main goal anchor, every other task tagged. If you use both, strain reads what throughline already records. ## Honest limits - **Hooks are per-host.** Where they exist, the check fires whether or not the agent remembers. Where they do not, it is a discipline the agent has to keep — weaker, and worth naming out loud rather than papering over. - **Thresholds are guesses** until you retune them. They came from one agent-and-user pair over a long run; yours will differ. - **The soft signals are judgement calls.** Counting them honestly is the whole job; a tier that is always Healthy is not a healthy session, it is a broken check.