ADR-0066 P3.1 + P3.2. Conversation now reads as a thread: turns
carry structured summaries of their predecessors and (optionally)
prefix new pack/teaching surfaces with deterministic backreferences.
P3.1 — chat/thread_context.py.
TurnSummary(turn_index, intent_tag_name, subject, grounding_source,
chain_id, corpus_id) — frozen, structured-fields-only.
ThreadContext — bounded FIFO (default MAX_THREAD_TURNS=8) with
snapshot(), recent_for_subject(), recent_subjects(), clear().
recent_for_subject() excludes ungrounded tiers (oov/partial/none)
by default — those are not strong-enough anchors.
ChatRuntime.thread_context is owned at construction.
_push_thread_summary runs at end-of-turn on BOTH stub and walk
paths. Teaching-grounded turns carry chain_id + corpus_id so
downstream composers (P3.2) can detect same-chain reference.
Cold-start intent classification now runs unconditionally (was:
gated on sink attachment) so thread context captures subject
regardless of sink state.
P3.2 — chat/anaphora.py.
thread_anaphora_prefix(ctx, subject, intent_name, source) returns
a deterministic prefix when:
- current turn is pack/teaching tier
- a prior pack/teaching turn on the same subject exists
- the prior intent differs from the current intent
Format (structural-fields-only — no prose):
"(Recalling turn N: chain <chain_id>.) " # prior was teaching
"(Recalling turn N: <subject> grounded pack.) " # prior was pack
Opt-in via RuntimeConfig.thread_anaphora=False. Default off keeps
every existing surface byte-identical.
Live verification (with thread_anaphora=True + seeded context):
> What is light? # following a "Why does light exist?" teaching turn
[pack] (Recalling turn 0: chain cause_light_reveals_truth.)
light — pack-grounded (en_core_cognition_v1): cognition.illumination;
logos.core; perception.clarity. No session evidence yet.
32 new tests passed. Curated lanes green. Cognition eval
byte-identical to pre-ADR baseline.