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)
7.4 KiB
Phase 6 — Comparative Demo: CORE vs In-System Baseline
Date: 2026-05-17
Corpus: evals/forward_semantic_control/public/v2_phase6_demo/cases.jsonl (8 cases)
Runner: evals/forward_semantic_control/phase6_demo.py
Report: evals/forward_semantic_control/results/phase6_demo_report.json
Contract tests: tests/test_phase6_demo.py (17 passing)
What this demo shows
Three head-to-head conditions where adding the Phase 2-5 mechanisms
(inner-loop admissibility, margin gate, typed refusals) produces
behavior that the in-system baseline (boundary-only, ADR-0023) cannot
produce. The "baseline" is the same CORE codebase with
inner_loop_admissibility=False — a true ablation, not a comparison
to a transformer LLM or any external system.
Why the baseline is in-system
A transformer-LLM comparison would be:
- Non-deterministic — directly violating CLAUDE.md's anti-stochastic stance.
- Unable to be CI-enforced — every run would produce different outputs.
- An apples-to-oranges comparison — the LLM has access to its training corpus; CORE has access only to the curated pack.
The in-system ablation is the honest comparison because it isolates the contribution of the mechanism itself, not the corpus, prompt, or sampling differences.
Headline
| metric | value |
|---|---|
all_three_conditions_pass |
true |
| C1 replay determinism (baseline) | 8/8 stable across 5 reruns |
| C1 replay determinism (CORE) | 8/8 stable across 5 reruns |
| C2 baseline emits forbidden | 3/3 |
| C2 baseline admits forbidden | 0/3 (inadmissibility is visible but ignored) |
| C2 CORE corrects-or-refuses | 3/3 |
| C2 CORE rejection in trace | 3/3 |
| C3 baseline typed refusals | 0/3 |
| C3 baseline emitted inadmissible | 3/3 |
| C3 CORE typed refusals | 3/3 with RefusalReason.INNER_LOOP_EXHAUSTION |
Condition 1 — Replay determinism
Both baseline and CORE produce byte-identical
hash_admissibility_trace(...) outputs across 5 reruns on the same
case. This is preserved, not added, by Phase 2-5.
What CORE adds on top: the trace now also folds
refusal_reason into its payload when present (ADR-0024 Phase 2),
so a refusal event itself is replayable evidence, not just an
exception type at runtime.
This is the load-bearing precondition for everything that follows: without determinism, "the rejection appeared in the trace" is not a verifiable claim, just a probabilistic one.
Condition 2 — Traced rejection
On three adversarial cases the boundary geometrically picks the
forbidden token (meaning / reason / question). Both legs see
the same field state, same vocabulary, same persona.
Baseline behavior (ADR-0023): boundary picks the forbidden
token. The admissibility verdict for that selection is
admitted = False — the inadmissibility is visible in the trace
but the walk emits the forbidden anyway. This is the "silent
emit" failure mode: the rejection is observable but not actionable.
CORE behavior (ADR-0024 + ADR-0026): inner-loop overrides
boundary, selects the blade-aligned expected token (or refuses on
sub-δ margin), and the forbidden token appears in
rejected_attempts. The rejection is now causally responsible
for the selection difference between baseline and CORE — not just
observable.
This is the falsifiable mechanism-isolation claim of ADR-0024.
Condition 3 — Coherent refusal
On three no-admissible-path cases (all candidates score negative under the chosen blade), the two configurations produce qualitatively different outputs:
Baseline: never raises a typed refusal. Emits an inadmissible
candidate (admitted = False) or fails with an untyped ValueError.
The refusal — if it happens — carries no RefusalReason and no
rejected_attempts evidence.
CORE: raises InnerLoopExhaustion with
reason = RefusalReason.INNER_LOOP_EXHAUSTION, carrying the full
rejected_attempts list. The refusal is:
- Typed — callers can pattern-match on
RefusalReason. - Replayable — the reason is folded into the trace hash.
- Evidenced —
rejected_attemptsshows which candidates were considered and what scores they received.
This is the "honest refusal" architectural commitment of CLAUDE.md made concrete and CI-enforced.
Three-condition compositionality
C1 + C2 + C3 together are the load-bearing claim: CORE is
- deterministic (C1)
- self-aware of bad selections (C2)
- capable of refusing rather than emitting bad selections (C3)
Without C1, the other two are anecdotes. Without C2, C3 is unfalsifiable (every refusal could be a hidden bug). Without C3, C2's "rejection in trace" can still be ignored by the walk.
This is what distinguishes CORE from a wrapped LLM at the mechanism level.
Threats to validity (what this does NOT claim)
- Not LLM benchmarking. This demo does not compare CORE against a transformer model on any natural-language benchmark. Doing so would require accepting non-determinism and is out of scope.
- Not a generality claim. The corpus is 8 hand-curated cases from the cognition pack, geometrically constructed to exercise each condition. A larger natural corpus is exercised in Phase 5; the Phase 6 corpus is intentionally focused for narrative clarity.
- Not a performance claim. CORE's inner-loop adds latency over boundary-only (see Phase 2 corpus report). Phase 6 demonstrates capability gain, not throughput parity.
- Not a soundness proof. C1's "byte-identical across 5 reruns"
is empirical determinism, not formal. Formal replay determinism
is the responsibility of the
trace_hashcontract incore/cognition/trace.pyand is exercised elsewhere.
What this enables next
- A future "honest refusal in chat" PR can plumb
InnerLoopExhaustion.reasonintoChatResponse.refusal_reasonand Phase 6's C3 contract will already enforce that the typed reason is preserved end-to-end. - A future replay-debug UI can render
rejected_attemptsper step, and Phase 6's C2 contract guarantees that the data is there to render. - Phase 5's δ=0.4 falsifiability gate composes with C2 here: any
new corpus case that surfaces a margin below δ where margin-mode
refusal is wrong would simultaneously fail C2's
core_corrects_or_refusespredicate, surfacing the architectural finding rather than silently being patched.
Files
evals/forward_semantic_control/public/v2_phase6_demo/cases.jsonl— 8 demo casesevals/forward_semantic_control/phase6_demo.py— comparative runnerevals/forward_semantic_control/results/phase6_demo_report.json— full per-case reporttests/test_phase6_demo.py— 17 contract tests (C1: 4, C2: 6, C3: 6, headline: 1)docs/evals/phase6_comparative_demo.md— this note
Six-phase ADR-0024 chain — closing summary
| Phase | Deliverable | Status |
|---|---|---|
| 1 | Pack-grounded fixture rewrite + architectural finding | ✅ 3940290 |
| 2 | Typed refusals + trace fold + RefusalReason enum |
✅ 310793a |
| 3 | ADR-0026 ranked-with-margin (δ=0.4) | ✅ 639e107 |
| 4 | ADR-0025 rotor / frame admissibility (sibling module) | ✅ 542e13d |
| 5 | Stratified 5-family mechanism-isolation corpus + benign EXHAUSTION_CEILING corpus | ✅ b664984 |
| 6 | Three-condition head-to-head demo (replay / traced rejection / coherent refusal) | ✅ this commit |
Total: 8 commits, ~58 contract tests added, 3 ADRs (0024 / 0025 / 0026) moved to Accepted with implementation + characterization evidence.