Evidence layer how-to
How to keep a calibration evidence layer in ctx so that ctx itself can read it back.
docs/using-ctx-effectively.md recommends storing an agent's calibration as a block, a dedicated "RLHF warnings" block with the 22 axes of the public reference at gottz.de/warnings.md, and loading it every session. The axes are general. The evidence behind them, the incidents with dates, sources and corrections, is project-specific and grows with every session. This page says how to store that evidence without it becoming a block ctx can find but not read.
Why not one growing block
Three limits in ctx decide the shape:
| Limit | Value | Where |
|---|---|---|
| Synthesis reads per source | 1,500 characters, then the source is cut | MaxBlockChars in go/internal/llm/synthesize.go |
| Store guidance | about 1,000 to 1,500 characters per block | docs/operations.md, the backfill section |
| Store ceiling | 50 KB per block | the MCP store tool |
A single evidence block that grows past 1,500 characters is still found by query, but the synthesis model sees only its first 1,500 characters. From then on only get by id reads it whole. Past the embed window it is parked as oversize and leaves the semantic channel. One block per incident keeps every piece inside the window, embeds in seconds, and gives Dream something to link.
Structure
Two kinds of blocks.
One block per incident. Category learnings, type knowledge set explicitly, tags ["rlhf-warnings", "W<n>"], scope and sensitivity as your project wants. Under 1,500 characters. The type is explicit because ctx's title classifier files some titles under audit-trail, its own log type; a W1 block whose title contained "Handover" went there on 2026-10-09 until it was stored again with the type named. Title in a fixed form:
W21 · 2026-10-09 · PR text claimed green gates before the result was read
Axis, date, one line. With the axis as the first token, ctx search W21 (lexical, no LLM) and ctx query land on the right blocks without the index.
One index block. The block your durable instructions point at. It holds pointers only: per axis, one line per incident with the date, the full UUID and five words. Full UUID, because get does not take prefixes. Kept under 1,500 characters so query can read it whole; that is room for about fifteen lines. When an axis outgrows it, the axis gets its own index block and the main index points at that.
Fields of an incident block
Four fields, fixed order, each a short paragraph. The first line names the axis, the context and the pointers.
- Observed. What stood or happened, at byte level. No value words.
- Signal. What revealed it, and whether the signal was external (CI, a test, the user, a source read) or the agent's own check. Over time this line shows which signals work, which is the point of the layer.
- Correction. The commit or action, or "none" when the finding was only noted.
- Interpretation. One sentence, marked as interpretation, never mixed into the three fields above. A positive application of an axis uses the same structure with "Interpretation: positive application".
Pointers are ids that resolve: a ctx block UUID, a commit hash, an issue or pull-request number. Never prose from a transcript.
Example
W21 · 2026-10-09 · PR text claimed green gates before the result was read
Axis: W21, a completion claim wider than what was tested. Context: ku-ops, the
ctx incident, PR GottZ/ctx#46. Pointers: ctx 01a11ea5-…, commit 218d5f84.
Observed: the PR text said "the config gates ... pass with the two new keys".
In the test run only go/ was mounted, so the config package's compose tests
failed for a path reason, and `tail -30` hid the package's FAIL lines. The
result was never read green.
Signal: external. Upstream CI, the Unit Tests job red (a drift guard pinned
45 duration keys, the registry had 47). The agent's own check did not catch it.
Correction: 218d5f84 raises the guard to 47, pushed after a CI-equivalent run
with the repository root mounted and the whole output read.
Interpretation: the claim rested on a partly read result. The external signal
worked, the internal one did not.
Rules that keep it compact
- One block per incident, not per symptom.
- No block without at least one resolvable pointer.
- Observed and Interpretation never share a sentence.
- Nuance lives in the Signal and Interpretation lines, not in length.
Reading it back
- One axis:
ctx search W21, or aquerysuch as "what is on record for W21". - One incident:
ctx get <uuid>from the index or a search hit. - The whole layer:
geton the index block, thengetper line. The index is what a session-start instruction loads.
Related
docs/using-ctx-effectively.mdin this repository, on drift and on loading the calibration each session.- gottz.de/warnings.md, the public 22-axis calibration.
- Claude Haiku 5.5 as the chat backend, the installation the example comes from.
- Issue #1 and GottZ/ctx#46, the incident in the example.