# ADR-0073d — Anchor-lens telemetry, CLI, and tour demo (Plan Phase L1.4) **Status:** Accepted **Date:** 2026-05-19 **Ratified:** 2026-05-19 **Author:** Shay **Phase:** Plan Phase L1.4 (operator-visible anchor lens, falsifiable demo) **Parent:** [ADR-0073](./ADR-0073-anchor-lens-substrate.md) (umbrella) **Builds on:** [ADR-0073a](./ADR-0073a-anchor-lens-content-phase.md), [ADR-0073b](./ADR-0073b-anchor-lens-class-loader.md), [ADR-0073c](./ADR-0073c-anchor-lens-composer-wiring.md) **Pattern:** mirrors ADR-0072 (register R5 — telemetry + tour demo) --- ## Context L1.1–L1.3 built the anchor-lens subsystem inside-out: substrate content (L1.1), pack class + loader (L1.2), composer wiring with surface lift on cognition lemmas (L1.3). The lens is loaded, engages on its intended pack lemmas via the alignment graph, and appends `[lens():]` to the surface when engaged. L1.4 closes the architectural arc by making the lens **operator-observable, operator-driven, and demo-falsifiable** — exactly what R5 did for the register subsystem. After L1.4 the substantive axis is feature-complete enough to compose against the register axis in operator-visible demos and audit pipelines. The load-bearing claim L1.4 ships is the **opposite** of register-tour's: ``` register-tour : per prompt, fix lens, vary register → trace_hash CONSTANT anchor-lens-tour : per prompt, fix register, vary lens → trace_hash DISTINCT ``` Both invariants must continue to hold turn-by-turn. L1.4 packages the second one into a falsifiable demo. --- ## Decision L1.4 ships three artifacts: 1. **Telemetry extension** — `TurnEvent` + `ChatResponse` gain `anchor_lens_id` (loaded pack id, empty for UNANCHORED) and `anchor_lens_mode_label` (engaged mode label this turn, empty when the lens didn't engage on this turn's lemma). 2. **Operator surface** — `core chat --anchor-lens ` CLI flag wires into `RuntimeConfig.anchor_lens_id`. Invalid id fails fast at `ChatRuntime.__init__` (mirrors `--register`). 3. **Narrative demo** — `core demo anchor-lens-tour` walks a fixed 2-prompt sequence under {default_unanchored_v1, grc_logos_v1, he_logos_v1} and asserts the three load-bearing claims. ### Telemetry — `TurnEvent` shape extension ```python # core/physics/identity.py @dataclass(frozen=True) class TurnEvent: ... register_id: str = "" register_variant_id: str = "" # ADR-0073d (L1.4) — operator-visible anchor-lens identity per turn. anchor_lens_id: str = "" anchor_lens_mode_label: str = "" ``` * `anchor_lens_id`: the loaded pack's `lens_id`, or `""` for the in-memory `UNANCHORED` sentinel. Pre-L1.4 callers stay byte-identical (empty string is the default). * `anchor_lens_mode_label`: the engaged `cognitive_mode_label` when the lens fired on this turn's lemma, or `""` when the lens was loaded but did not engage (different lemma than its alignment scope), or when no lens was loaded at all. Reading this pair tells operators three things at a glance: * `(id="", mode="")` — no lens loaded * `(id="", mode="")` — lens loaded, did not engage on this turn * `(id="", mode="