Records the deterministic, grounded, multi-clause articulation
benchmark that the discourse-planner work has stabilised. Mirrors
the format of teaching_loop_bench.md so the four sub-benches in
benchmarks/articulation.py have a load-bearing reference document.
Headline:
* 20 independent ChatRuntime instances × 4 prompts (EXPLAIN /
PARAGRAPH / COMPOUND / WALKTHROUGH) produce 4 unique surfaces —
byte-identical determinism on the articulation path with
RuntimeConfig(discourse_planner=True).
* Every visible token traces to a pack lemma, pack gloss, reviewed
teaching-chain entry, or fixed-template connective from the
closed five-entry _MOVE_CONNECTIVE table. No synthesis.
* discourse_planner sub-bench:
cases: 4
articulate_sentence_rate: 1.0
disclosure_sentence_rate: 0.0
multi_sentence_rate: 1.0
* Compound prompt ("What is truth, and why does it matter?") emits
6 distinct grounded sentences with cross-part fact dedup, no
anchor repetition.
* Walkthrough mode walks the teaching-chain edge graph up to 3 hops,
cycle-safe, final hop as CLOSURE; no chain ⇒ degrades to ANCHOR +
SUPPORT rather than fabricating steps.
Doc explains the partitioned predicate contract
(articulate + disclosure + unarticulate = 1.0, total and disjoint)
so future readers know why ``multi_sentence_rate`` alone is not the
headline.
Companion docs cross-linked: discourse_runtime_baseline_2026-05-19.md
(lane-level delta table), the two new isolation lanes
(compound_intent_decomposition, walkthrough_chain), and the
partitioned multi_sentence_response contract.
Extends ``generate/intent.py:_RULES`` with three new expository
patterns so the upstream subject-extraction gap that the dedup
revealed is closed:
* ``^explain\s+`` → DEFINITION
* ``^(write|compose|draft) (a )?(short|brief)?
paragraph (about|on)\s+`` → DEFINITION
* ``^paragraph (about|on)\s+`` → DEFINITION
Rules placed AFTER the NARRATIVE family so ``Tell me about X`` and
``Describe X`` continue to route to NARRATIVE. Subject extraction
re-uses ``_normalize_subject`` so articles and trailing punctuation
are stripped: ``Explain the parent.`` → subject ``parent``.
``ResponseMode`` is untouched and remains orthogonal: the same prompts
still classify as ``EXPLAIN`` / ``PARAGRAPH`` independently.
20 new tests pin: each rule's expected subject, response-mode
preservation, NARRATIVE/EXAMPLE/existing-DEFINITION rules unchanged.
Lane re-measurement (multi_sentence_response, 21 cases):
flag off: multi=0.1429, primed_multi=0.0000, conn=0.5385, grounded=0.8571
flag on : multi=0.9048, primed_multi=1.0000, conn=0.8462, grounded=0.8571
Combined lift over the original (pre-wiring) baseline:
* multi_sentence_rate: +70pp on the substantive predicate
* primed_multi_sentence_rate: +50pp (0.5 → 1.0 post-classifier)
* connective_present_rate: +74pp (0.10 → 0.85)
* grounded_rate: +39pp (0.47 → 0.86)
Cognition eval byte-identical: public 100/100/91.7/100, holdout
100/100/83.3/100 — these prompts aren't in cognition cases, and the
new rules don't perturb any rule that fires for cognition prompts.
Conversational thread coherence unchanged.
docs/evals/discourse_runtime_baseline_2026-05-19.md updated with the
full delta table; the planner is now load-bearing across the warm
and cold pack/teaching paths and the lane measures real capability
rather than punctuation artifacts.
Three shareable demo / benchmark writeups modeled on the existing
`docs/evals/phase6_comparative_demo.md` treatment, each accompanied
by an asciinema-rendered GIF for at-a-glance viewing on the repo page.
- docs/evals/anti_regression_demo.md — three-gate defense; per-gate
table; honesty paragraph about the synthetic regression in S2 (real
ReplayEvidence shape via documented run_replay= kwarg); sample run
output; falsifiable claims index.
- docs/evals/learning_loop_demo.md — headline before/after; CORE-vs-
pretraining comparison table; trust-boundary code snippet showing
the _CORPUS_PATH swap; per-scene table; full sample run; subject-
selection rationale (pack-resident ∧ no active chain ∧ deterministic
intent classification).
- docs/evals/teaching_loop_bench.md — what's byte-identical and why
it matters per artifact; 100-run reference numbers (unique=1 across
all five artifacts; mean=1.849s p50=1.838s p95=1.851s); pairing
paragraph with ADR-0045 (read vs write determinism).
GIF captures (rendered with asciinema 3.2.0 + agg 1.8.1, github-dark
theme, JetBrains Mono):
- docs/evals/assets/anti_regression.gif (120K, 944x843)
- docs/evals/assets/learning_loop.gif (332K, 944x1039)
- docs/evals/assets/teaching_loop_bench.gif (64K, 860x1000)
Raw .cast files preserved alongside the GIFs for re-rendering at
different themes / speeds / sizes without re-recording.
README.md — added writeup-link column to the Inter-Session Memory
three-demo table.
Closes the 6-phase ADR-0024 chain with a focused comparative demo
that distinguishes CORE (inner-loop + margin + typed refusals) from
the in-system boundary-only baseline (ADR-0023 ablation).
Three conditions, all passing under contract tests:
C1. Replay determinism
baseline: 8/8 stable across 5 reruns
CORE: 8/8 stable across 5 reruns
CORE additionally folds refusal_reason into trace hash so
refusal events are replayable evidence.
C2. Traced rejection
baseline emits forbidden: 3/3 (admits=False but walk continues)
CORE corrects-or-refuses: 3/3
CORE rejection in trace: 3/3
Demonstrates that inner-loop is causally responsible for the
selection difference between baseline and CORE.
C3. Coherent refusal
baseline typed refusals: 0/3 (never raises typed refusal)
baseline emits inadmissible: 3/3
CORE typed refusals: 3/3 (all INNER_LOOP_EXHAUSTION)
Demonstrates that typed refusal with rejected_attempts evidence
is new in CORE, not present in boundary-only.
Why in-system baseline (not LLM):
A transformer-LLM comparison would be non-deterministic by
construction, could not be CI-enforced, and would be apples-to-
oranges (different corpus / training / sampling). The honest
comparison is the ablation: same codebase with the Phase 2-5
additions disabled.
Files:
evals/forward_semantic_control/phase6_demo.py
evals/forward_semantic_control/public/v2_phase6_demo/cases.jsonl (8 cases)
evals/forward_semantic_control/results/phase6_demo_report.json
tests/test_phase6_demo.py (17 passing)
docs/evals/phase6_comparative_demo.md
Tests: 1085 passed, 2 skipped (+17 from Phase 5 baseline).
This closes the ADR-0024 6-phase chain:
Phase 1 — pack-grounded fixture + architectural finding (3940290)
Phase 2 — typed refusals + trace fold (310793a)
Phase 3 — ADR-0026 ranked-with-margin (639e107)
Phase 4 — ADR-0025 rotor / frame admissibility (542e13d)
Phase 5 — stratified 5-family mechanism-isolation (b664984)
Phase 6 — comparative demo (this commit)
Authors a 20-case corpus stratified across five geometric failure-mode
families and a separate 10-case benign corpus for the
EXHAUSTION_CEILING lane:
A. near_forbidden_correct_endpoint (6 cases, gaps 0.002 to 0.55)
B. near_equal_admissible (5 cases, diffs ≤ 0.01)
C. no_admissible_path (3 cases, honest refusal)
D. multi_step_admissibility (3 chained cases)
E. heterogeneous_relation (3 chained cases, blade-switching)
phase5_runner runs each case under BOTH threshold and ADR-0026 margin
modes and reports per-family pass_rate, refusal_rate, and (for Family
A) rejection_traced_rate + boundary_overridden_rate.
Headline:
pass_rate_threshold = 1.00 (20/20)
pass_rate_margin = 1.00 (20/20)
mechanism_isolated = true (both modes, all five families)
replay determinism = byte-identical across 3 reruns
Family C refuses with RefusalReason.INNER_LOOP_EXHAUSTION in both
modes (load-bearing evidence for ADR-0024 Phase 2 typed refusals).
Family B refuses under margin mode (validates ADR-0026 δ=0.4 gate).
Benign inner-loop corpus for EXHAUSTION_CEILING ≤ 0.05 gate:
boundary_only: exhaustion 0.00, pass 1.00
null_control: exhaustion 0.00, pass 1.00
inner_loop_t0: exhaustion 0.00, pass 1.00
inner_loop_tpos: exhaustion 0.00, pass 1.00 (threshold 0.25)
Geometric finding documented while authoring the benign corpus:
23 of 85 pack tokens have negative self-cga_inner under Cl(4,1).
Tokens with self-score ≤ 0 cannot serve as single-token expected
endpoints in threshold mode — the algebra's Lorentzian signature
forbids this geometrically. Phase 5 benign corpus draws expected
endpoints from the 62-token positive-self-score subset. This is
consistent with Phase 4 characterization: no static threshold
delivers separation_quality ≥ 0.8 — the margin lane survives
because margin compares differences, not absolute scores.
Files:
evals/forward_semantic_control/public/v2_phase5/cases.jsonl
evals/forward_semantic_control/public/inner_loop_benign/cases.jsonl
evals/forward_semantic_control/phase5_runner.py
evals/forward_semantic_control/phase5_mine.py
evals/forward_semantic_control/results/phase5_report.json
evals/forward_semantic_control/results/phase5_benign_inner_loop_report.json
tests/test_phase5_corpus.py (20 passing)
docs/evals/phase5_stratified_findings.md
Tests: 1068 passed, 2 skipped (+20 from Phase 4 baseline).