ADR-0071: Read-Only Pattern-Composition Instruments (View Supply / Diversity / Grounding)
September 22, 2026 · View on GitHub
Status
accepted
Date
2026-07-03
Context
Memory-distillation pipelines across the field are converging on the same
three-layer-plus-distill structure — surveyed in arXiv 2512.13564, "Memory
in the Age of AI Agents," and formalized as a write–manage–read loop in
arXiv 2603.07670. This repo's differentiator
against that convergence is the views layer
(ADR-0019). An audit
found that differentiator is mostly unused: of the 7 shipped views, only 2
are actually queried — self_reflection by distill-identity (distill.py)
and constitutional by amend-constitution (constitution.py). The other
five — noise, communication, reasoning, social, technical (former
insight batch axes and the former ingest noise gate) — are orphaned
definitions consumed by nothing.
The audit also found documentation asserting a mechanism that does not
exist: distill.py's comment and docs/CODEMAPS/ (architecture.md,
core-modules.md) claimed a query-time noise-view filter and per-pattern
view telemetry fields (last_view_matches, last_classified_at) that are
not present in code. This is not stale wording around a real mechanism — it
is documentation describing a live mechanism that is fiction.
Three open observations were sitting without data to resolve them. First, an
echo-chamber / register-collapse effect forming at the distill stage
(handoff 2026-06-22) —
ADR-0057,
ADR-0058, and
ADR-0060 corrected course on it,
but the effect itself was never quantified. Second, review 2026-06-27
finding M3: insight silently drops singleton patterns, and a rescue lane
for them needs a floor chosen from the real distribution, not a guess.
Third, review 2026-06-27 finding M2: epistemic_counts.observed is
structurally zero after ADR-0060 and gets misread as "no external
grounding," when it is in fact an artifact of the schema change.
The production generation model had swapped to gemma4:e4b five days
earlier
(ADR-0069),
which means any behavior change made now, without a baseline, would be
unattributable to the swap versus the change itself. The operator's
response was to sequence the work: instrument first, intervene second —
quantify the current state, then choose interventions from the readings
rather than from intuition.
Decision
- Add
core/view_metrics.py. A new read-only pattern-composition instrument module; nothing else in the pipeline changes behavior. - Measure view supply for the two consumed views only. Compute
cosine-to-seed distributions — pass rate at the view threshold,
p50/p90/max — restricted to
CONSUMED_VIEWS = (self_reflection, constitutional). Do not measure the five unconsumed views: a distribution over an orphaned seed measures seed staleness, not corpus structure, and its reading changes no downstream action (signal-first). - Measure seed-independent diversity as the echo-chamber detector.
Compute pairwise-cosine homogeneity (mean/p50/p90) plus the cluster
structure
insightwould see, using the same threshold/min/max AND the samegated-row exclusion asinsight(codex review P2); skip cluster stats aboveCLUSTER_STATS_MAX_N=500(mirrorsinsight.FULL_RECLUSTER_WARN_N, kept as a literal to avoid an import cycle and cross-checked by a test). Rising homogeneity + supply concentration + inward grounding together read as register collapse, without any hand-written axis. - Measure grounding composition. Tally provenance
source_type, the ADR-0050 epistemic tally (carrying the explicit caveat thatobserved == 0is structural post-ADR-0060, per review 2026-06-27 M2), and the legacy gated count. - Wire the instruments into three call sites, not into behavior.
distill --dry-runlogs the instruments for the would-be-added set — post-dedup, so skipped duplicates are not counted (codex review P3) — through a new public paraminstrument_views(deliberately not namedview_registry, to respect the ADR-0060 regression test that pins the gate-era threading out ofdistill's signature).report --patterns(new opt-in flag) renders the same instruments over the whole live pool.insightlogs dropped singletons with their nearest consumed view vianearest_view(extract_insightgainsinstrument_views). - Keep every instrument observability-only. Distributions and
compositions are for the operator; none feed gates, ranking, retrieval,
or promotion — the same shape as AKC ADR-0015 Decision 2 (visibility
without intervention), consistent with this repo's
ADR-0050 /
ADR-0051 rejections of metric
write-back. Instrument output carries a fixed ambiguity note (empty/low
supply = missing patterns OR a stale seed, undecidable by the instrument
itself), per the AKC ADR-0016 lesson that a systematically biased
instrument is worse than none. Instruments also degrade instead of
raising — a malformed or wrong-dimension embedding row is skipped with a
WARNING, never allowed to abort the host command (python-reviewer
CRITICAL, 2026-07-03) — and bound their own memory (pairwise stats run on
a deterministic stride sample above
PAIRWISE_STATS_MAX_N=3000). - Correct the stale documentation in the same change. Fix the
distill.pycomment, theinsight.pymodule docstring, the Data Flow section ofarchitecture.md, and the schema example incore-modules.md.
Note (2026-08-29, ADR-0101): the dissolution mandate — every new instrument states a consumption plan — was added as this ADR's balancing rule; ADR-0101 names this ADR as the rule's eventual home.
注記(2026-09-22, ADR-0112): the decision-shadow reading of ADR-0112 is an instrument in this ADR's sense (read-only over the skill-selection record, thresholds set from readings) but its producer is not free of mechanism: it issues its own Ollama calls under a time budget and, in swap mode, evicts the generation model from the shared daemon. The "existing state, no mechanism" premise here does not cover it; ADR-0112 names the side effect. See ADR-0112.
Alternatives Considered
Measure all 7 views as instrument axes
Rejected by the operator during planning: the 5 orphaned seeds have no consumer, so a coverage distribution over them answers no action — it mostly measures how stale the seed text is (a signal-first violation). Their disposition (prune / reseed / promote clusters into new views with consumers) is deferred to the intervention phase, with readings in hand.
Implement interventions directly, without instruments first
Rejected: a rare-singleton rescue lane, a seed rewrite, or view pruning would have their floors and designs chosen blind (the numeric-cap anti-pattern), and — five days after the gemma4:e4b swap — any resulting behavior change would be unattributable to the intervention versus the model swap.
Adopt an embedding-drift monitoring stack (evidently, whylogs)
Rejected: the pandas/scikit-learn dependency footprint conflicts with the requests+numpy-only policy, and reference-vs-current drift primitives answer a different question than per-view membership against fixed seed embeddings. The needed aggregation is ~100 lines over already-stored vectors — search-first verdict: Build.
Re-wire the orphaned topic views into insight batching
Rejected: this re-litigates the settled move from predefined view axes to bottom-up embedding clustering. If topical views return, they should be grown from observed cluster structure and given a consumer first — a candidate for the intervention phase, shaped like the AKC cycle's Promote phase.
Keep a production-run instrument (log instruments on every scheduled distill)
Deferred, not rejected: phase 1 scopes instruments to distill --dry-run
and the opt-in report flag. Longitudinal wiring into every scheduled run
is a cheap follow-up once the readings prove useful.
Consequences
Positive
- First corpus baseline is immediately actionable — live pool n=1463:
self_reflectionsupply 1108/1463 (76%) andconstitutional986/1463 (67%) pass at threshold 0.55 with near-identical distributions (p50=0.58, p90=0.64 both), while corpus pairwise cosine mean/p50=0.554 — i.e. the view threshold sits at the corpus homogeneity floor andtop_kdoes all the real selection. - A same-day control experiment (three unrelated texts: floor 0.33–0.46 vs. corpus, 0.42–0.52 vs. seeds) shows the homogeneity is genuinely ~0.1–0.2 above the embedding-model floor — a real echo signature, not nomic-embed anisotropy — and that 0.55 does reject truly foreign text.
- Grounding composition surfaced
external_reply=0among live patterns and 195 legacy gated rows. - These readings give the intervention phase calibrated evidence (floor / corpus mean / view top ≈ 0.68–0.77).
- Singleton visibility (importance distribution + nearest consumed view) turns the M3 rescue-lane design into a data-driven choice.
- Documentation no longer claims fictional mechanisms.
Negative
- ~330 new lines plus tests to maintain.
- The
CLUSTER_STATS_MAX_Nmirror constant can drift frominsight.FULL_RECLUSTER_WARN_N— guarded by a test, not by an import. - Instruments themselves improve nothing: if the readings are ignored, the only value delivered is honest docs.
- Operators must resist wiring these numbers into gates; the module docstring, this ADR, and the output's ambiguity note are the only guard against that.
Neutral / Follow-ups
report --patternsis opt-in and costs two seed embeddings via Ollama.- Scheduled production runs are byte-identical in behavior — instruments fire only on the dry-run and opt-in paths.
- The ADR-0060 regression test still pins the gate-era signature out of
distill.
References
- arXiv 2512.13564 — "Memory in the Age of AI Agents"
- arXiv 2603.07670 — "Memory for Autonomous LLM Agents": formalizes agent memory as a write–manage–read loop
- AKC ADR-0015 — Decision 2, visibility without intervention (github.com/shimo4228/agent-knowledge-cycle)
- AKC ADR-0016 — a systematically biased instrument is worse than none (github.com/shimo4228/agent-knowledge-cycle)
- ADR-0019 — the views layer this repo differentiates on
- ADR-0031
- ADR-0050 — epistemic tally consumed by the grounding-composition instrument
- ADR-0051 — prior rejection of metric write-back
- ADR-0056
- ADR-0060 — gate-era
distillsignature; source ofepistemic_counts.observed == 0 - ADR-0069 — production model swap that made uninstrumented interventions unattributable
- review 2026-06-27 — M2 (
epistemic_counts.observed), M3 (singleton drop)