* chore(evals, cli): contract standardization + bench --json stdout cleanliness
End-of-session shippability pass. Three concrete fixes:
1. core/cli.py — bench --json no longer pollutes stdout
Several bench paths call scripts.run_pulse.run_pulse which prints
verbose [pulse] traces unconditionally to stdout, breaking jq /
programmatic consumers of --json output.
New _bench_stdout_guard() redirects stdout → stderr for the
duration of the bench run when --json is set. Operator still sees
the pulse trace (on stderr), but --json consumers get a clean JSON
document on stdout. Applied to all four bench paths: cost,
articulation, default suite, and --suite all.
Verified: core bench --suite determinism --json now produces
parseable JSON; human path still shows 1140 [pulse] lines.
2. evals/{frontier_compare,realizer_guard}/contract.md (new)
core/contemplation/contract.md (new)
Each new contract follows the established pattern (37 contracts
already exist under evals/<lane>/contract.md):
- What it measures
- Why it matters (structural win)
- How to run
- How to read the output
- Pass criteria table
- When it has failed and why
- Runner / module layout
Coverage:
- frontier_compare: both Lane A (CORE-only suites) and Lane B
(cross-provider prompt_battery) with explicit guardrails
against mixing — operator asks for the wrong lane combination,
runner exits 2 with helpful error.
- realizer_guard: C1/C2 articulation safety boundary — synthetic
illegal candidates rejected directly by check_surface AND
former-bug runtime prompts now produce legal articulations.
- contemplation (ADR-0080): not under evals/ since it's runtime
infrastructure that consumes eval reports — contract lives at
core/contemplation/contract.md. Documents the read-only +
SPECULATIVE-only + deterministic-replay invariants and the
shared DiscoveryCandidateSink plumbing convergence (ADR-0080).
3. evals/CLAIMS.md — Tier 2 rows added
- frontier_compare Lane A: determinism.primary_score, max_versor_condition
- frontier_compare Lane B: prompt_battery.primary_score (CORE adapter),
cross-provider artifact persistence
- realizer_guard: all_claims_supported
- contemplation: SPECULATIVE-only invariant, deterministic replay,
additive sink path, no pack mutation (all CI-pinned by tests)
Verification
------------
$ core test --suite smoke -q
67 passed in 27.22s (no regression)
$ uv run pytest -q tests/test_contemplation_loop.py \
tests/test_contemplation_pipeline_convergence.py \
tests/test_frontier_compare_cross_provider.py
27 passed in 4.87s
$ core bench --suite determinism --json 2>/dev/null | jq .results[0].passed
true (was: JSONDecodeError on prior [pulse] pollution)
* feat(evals/ui): report viewer renders Lane B cross-provider + pass-rate chart
Stop-hook caught that #62 only covered contracts — the 929-line
report_viewer.html was never audited against the new cross-provider
report shape from #61. Two real gaps:
1. Lane-aware observation drawer
The drawer hardcoded Lane A (CORE-native) fields: surface,
grounding_source, anchor_lens_mode_label, versor_condition.
Lane B (cross-provider) observations carry different fields:
provider, model, elapsed_ms, error_type, error_message.
Loading a cross-provider report rendered only the surface row
with empty `grounding` — the provider + model + timing data
was unreachable without expanding "Show raw JSON".
Fix: detect Lane B (presence of `obs.provider`) and render the
appropriate field set. Lane A still renders identically (now
also surfaces trace_hash + register_id when present, which were
silently buried in the raw JSON before).
2. Pass-rate chart per suite
The summary strip showed one aggregate Primary % across all
suites, with no way to see WHICH suite is dragging the score.
Multi-suite runs (e.g. --suite all) had to expand each panel
individually to find the failing one.
Fix: new .passrate-chart element below the summary strip,
one horizontal bar per suite showing passed/total. All-pass =
solid green, all-fail = solid red, partial = green/red split
at the pass fraction. CSS only — no new dependencies.
3. SUITE_PREAMBLES gains the prompt_battery entry so the sidebar
shows the "side-by-side surface evidence across providers"
description when loading a Lane B report.
Verified
--------
- Brace/paren/div balance unchanged (308/308 / 380/380 / 54/54)
- One <script> tag pair preserved
- Generated a real Lane B report via
`python -m evals.frontier_compare --provider core --suite prompt_battery`
for visual confirmation
Out of scope (noted for future PR)
----------------------------------
Sampled 3 `core demo` targets:
- register-tour: clean schema (all_claims_supported, claims, grid)
- audit-tour: both scene_1_* keys AND an empty scenes:[] array — inconsistent
- anti-regression: no all_claims_supported key, uses all_gates_held instead
Demo schema standardization deserves its own PR — operator tooling
would benefit from a uniform top-level success field across demos.
* docs(evals) + chore(demos): systematic audit + uniform success field
Stop-hook caught two real gaps after the contract+UI PR:
- demos had divergent success-field names (all_gates_held vs
learning_loop_closed vs claim_supported vs nested claims_supported)
- no systematic look at the 48 eval directories had been done
Both addressed concretely; remaining work captured in audit doc
rather than vaguely deferred.
1. Demo schema standardization — uniform all_claims_supported field
----------------------------------------------------------------------
All 9 ``core demo`` targets now emit a top-level
``all_claims_supported: bool`` field. Existing per-demo fields
(``all_gates_held``, ``learning_loop_closed``, ``claim_supported``,
nested ``claims_supported``) are preserved for backwards compat —
the new field is an alias derived from the demo's existing success
signal, not a replacement.
Operator tooling and the CI gate can now target
``all_claims_supported`` without knowing each demo's idiomatic
field name.
Files touched:
- evals/anti_regression/run_demo.py — adds AND of all_gates_held +
active_corpus_byte_identical
- evals/learning_loop/run_demo.py — adds AND of learning_loop_closed +
active_corpus_byte_identical
- scripts/publish_pack_measurements.py — adds AND of the three
entries in the nested claims_supported dict
- evals/long_context_cost/comparison_runner.py — adds alias for
claim_supported (singular)
The 5 demos already using ``all_claims_supported`` (audit-tour,
register-tour, anchor-lens-tour, orthogonality-tour, articulation)
are unchanged.
Verified across all 9 demos:
audit-tour : True
register-tour : True
anchor-lens-tour : True
orthogonality-tour : True
pack-measurements : True ← new alias
anti-regression : True ← new alias
learning-loop : True ← new alias
articulation : True
long-context-comparison : True ← new alias
2. docs/EVAL_AUDIT_2026-05-20.md — systematic 48-lane audit
------------------------------------------------------------
Replaces the "future PR" deferral with a concrete document.
Contains:
- Method (what was inspected for each lane).
- Summary (40/48 have contract.md; 18/48 have saved results;
empty results/ ≠ broken — most lanes regenerate on demand).
- Cross-provider relevance triage:
* 9 lanes are cross-provider-relevant and could benefit
from the prompt_battery-style adapter pattern (cognition,
english_fluency_ood, hebrew_fluency, koine_greek_fluency,
grammatical_coverage, inference_closure, multi_step_reasoning,
discourse_paragraph, foundational_*_ood, etc.).
* 29 lanes are CORE-only by design (versor closure, anchor
lens, identity divergence, provenance, etc.) — wiring
providers would be category-erroneous.
- Demo schema standardization status (this PR closes that).
- UI/UX coverage matrix.
- 5 concrete follow-up items, each focused enough for a single
PR, none requiring architectural change.
Regenerated reports
-------------------
evals/long_context_cost/results/comparison_v1.json and
evals/results/phase2_pack_measurements.json now contain the new
all_claims_supported field (auto-regenerated when validating the
schema change).
evals/frontier_compare/results/sample_core_promptbattery.json
added as a reference Lane B report so the new viewer always has
something to load on first open.
487 lines
19 KiB
Python
487 lines
19 KiB
Python
"""Learning-loop demo — CORE learning a new chain from a cold turn.
|
|
|
|
The thesis (the demo's headline claim):
|
|
|
|
> CORE, asked a question it cannot ground, emits structured evidence
|
|
> that a reviewed chain would have helped. An operator authors a
|
|
> proposal from that evidence. The replay-equivalence gate confirms
|
|
> the chain would not regress the cognition lane. The operator
|
|
> accepts. The **same prompt now produces a deterministic
|
|
> teaching-grounded surface** — and CORE will produce that same
|
|
> surface for that prompt every time, replayably, with full
|
|
> provenance back to the operator's accept.
|
|
|
|
No LLM provider has this loop. Continuous pre-training is the
|
|
nearest analog and is fundamentally different: opaque gradient
|
|
updates over uncurated data without per-fact provenance, without
|
|
operator review, without a replay-equivalence gate, without an
|
|
audit trail that lets you ask "why did the model say this today
|
|
that it would not have said yesterday?"
|
|
|
|
Five scenes, each on a real ``ChatRuntime`` against the live active
|
|
corpus. The active corpus file bytes are byte-identical pre/post —
|
|
the demo writes only to a transient corpus, then swaps ``_CORPUS_PATH``
|
|
to that transient for the "after" turn. The same swap pattern the
|
|
replay-equivalence gate uses (``teaching.replay._swap_corpus_path``).
|
|
|
|
S1. Cold turn. The runtime cannot ground the prompt.
|
|
S2. Discovery emission. A ``DiscoveryCandidate`` is emitted to the
|
|
attached sink — structured evidence, not a mutation.
|
|
S3. Operator-authored proposal. A complete proposal is built from
|
|
the candidate's structure plus operator-provided connective /
|
|
object / corpus-evidence pointer. The replay-equivalence gate
|
|
runs (real ``teaching.replay.run_replay_equivalence``) and
|
|
confirms no regression.
|
|
S4. Operator accept against a *transient* corpus. The active corpus
|
|
on disk is untouched; the accepted chain is written to a tmp
|
|
file. Audit + runtime both honour the transient corpus.
|
|
S5. Same prompt, now teaching-grounded. The deterministic
|
|
teaching-grounded surface contains the new chain's subject /
|
|
connective / object. Identical for any replay of the same
|
|
prompt against the same corpus state.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import tempfile
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from chat import teaching_grounding as _tg
|
|
from chat.runtime import ChatRuntime
|
|
from teaching.discovery import DiscoveryCandidate, EvidencePointer
|
|
from teaching.proposals import (
|
|
ProposalLog,
|
|
accept_proposal,
|
|
propose_from_candidate,
|
|
)
|
|
|
|
|
|
# The single prompt that drives every scene. CAUSE intent, subject
|
|
# ``narrative`` — pack-resident lemma but no ``(narrative, cause)``
|
|
# chain in the active corpus today, guaranteeing the cold-turn path.
|
|
#
|
|
# History: the original demo used ``thought`` as the cold subject; the
|
|
# cognition saturation v2 curriculum unit (commit ``a0edbb4``) added
|
|
# ``cause_thought_reveals_meaning`` to the active corpus, so the
|
|
# (thought, cause) cell is no longer cold. ``narrative`` is the new
|
|
# cold exemplar — same thematic shape, same connective + object.
|
|
_DEMO_PROMPT: str = "Why does narrative exist?"
|
|
_DEMO_SUBJECT: str = "narrative"
|
|
|
|
# Operator-authored proposal payload. The (narrative, cause) cell is
|
|
# unoccupied; the operator proposes the chain
|
|
# narrative reveals meaning
|
|
# affirming evidence is the existing corpus chain
|
|
# cause_creation_reveals_meaning (creation reveals meaning)
|
|
# both endpoints are pack-resident.
|
|
_OPERATOR_CONNECTIVE: str = "reveals"
|
|
_OPERATOR_OBJECT: str = "meaning"
|
|
_OPERATOR_EVIDENCE_REF: str = "cause_creation_reveals_meaning"
|
|
|
|
|
|
_VERBOSE = True
|
|
|
|
|
|
def _say(*args: Any, **kwargs: Any) -> None:
|
|
if _VERBOSE:
|
|
print(*args, **kwargs)
|
|
|
|
|
|
def _print_header(title: str, claim: str) -> None:
|
|
_say()
|
|
_say("─" * 72)
|
|
_say(f" {title}")
|
|
_say("─" * 72)
|
|
_say(f" CLAIM: {claim}")
|
|
_say()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Sinks + helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _BufferSink:
|
|
"""Discovery candidate sink that retains every emitted line."""
|
|
|
|
def __init__(self) -> None:
|
|
self.lines: list[str] = []
|
|
|
|
def emit(self, line: str) -> None:
|
|
self.lines.append(line)
|
|
|
|
|
|
def _active_bytes() -> bytes:
|
|
return _tg._CORPUS_PATH.read_bytes() if _tg._CORPUS_PATH.exists() else b""
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scene outputs
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class SceneResult:
|
|
scene: str
|
|
claim: str
|
|
detail: dict[str, Any]
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
return {"scene": self.scene, "claim": self.claim, "detail": self.detail}
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class DemoReport:
|
|
prompt: str
|
|
before_surface: str
|
|
before_grounding_source: str
|
|
after_surface: str
|
|
after_grounding_source: str
|
|
scenes: tuple[SceneResult, ...]
|
|
learning_loop_closed: bool
|
|
active_corpus_byte_identical: bool
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
# ``all_claims_supported`` is the canonical cross-demo success
|
|
# field — see anti_regression/run_demo.py for the convention.
|
|
return {
|
|
"prompt": self.prompt,
|
|
"before": {
|
|
"surface": self.before_surface,
|
|
"grounding_source": self.before_grounding_source,
|
|
},
|
|
"after": {
|
|
"surface": self.after_surface,
|
|
"grounding_source": self.after_grounding_source,
|
|
},
|
|
"scenes": [s.as_dict() for s in self.scenes],
|
|
"learning_loop_closed": self.learning_loop_closed,
|
|
"active_corpus_byte_identical": self.active_corpus_byte_identical,
|
|
"all_claims_supported": (
|
|
self.learning_loop_closed
|
|
and self.active_corpus_byte_identical
|
|
),
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scenes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _scene1_cold_turn(rt: ChatRuntime, sink: _BufferSink) -> tuple[SceneResult, Any]:
|
|
_print_header(
|
|
"S1. Cold turn — runtime cannot ground the prompt",
|
|
"Active corpus has no (thought, cause) chain. The runtime "
|
|
"falls through to the universal insufficient-grounding "
|
|
"disclosure. Identity / safety / ethics gates still run.",
|
|
)
|
|
response = rt.chat(_DEMO_PROMPT)
|
|
_say(f" prompt : {_DEMO_PROMPT}")
|
|
_say(f" surface : {response.surface}")
|
|
_say(f" grounding_source : {response.grounding_source}")
|
|
_say(f" discovery candidates : {len(sink.lines)} (emitted post-turn)")
|
|
return SceneResult(
|
|
scene="S1_cold_turn",
|
|
claim="No teaching chain for (thought, cause) — runtime returns the disclosure.",
|
|
detail={
|
|
"prompt": _DEMO_PROMPT,
|
|
"surface": response.surface,
|
|
"grounding_source": response.grounding_source,
|
|
"discovery_candidates_emitted": len(sink.lines),
|
|
},
|
|
), response
|
|
|
|
|
|
def _scene2_discovery_emission(sink: _BufferSink) -> tuple[SceneResult, dict[str, Any]]:
|
|
_print_header(
|
|
"S2. Discovery candidate — structured evidence, not a mutation",
|
|
"The runtime emits a DiscoveryCandidate (ADR-0055 Phase B) "
|
|
"documenting that a reviewed (thought, cause) chain WOULD have "
|
|
"grounded this turn. Contemplation (ADR-0056 Phase C1) "
|
|
"enriches with pack/corpus evidence pointers. Active corpus "
|
|
"is byte-identical — emission writes to the sink only.",
|
|
)
|
|
if not sink.lines:
|
|
raise RuntimeError("expected at least one discovery candidate from S1")
|
|
import json as _json
|
|
payload = _json.loads(sink.lines[0])
|
|
_say(f" candidate_id : {payload['candidate_id'][:16]}…")
|
|
_say(f" trigger : {payload['trigger']}")
|
|
_say(f" proposed_chain : {payload['proposed_chain']}")
|
|
_say(f" polarity : {payload['polarity']}")
|
|
_say(f" claim_domain : {payload['claim_domain']}")
|
|
_say(f" pack_consistent : {payload['pack_consistent']}")
|
|
_say(f" boundary_clean : {payload['boundary_clean']}")
|
|
_say(f" evidence (pack-only) : "
|
|
f"{[e for e in payload['evidence']]}")
|
|
return SceneResult(
|
|
scene="S2_discovery_emission",
|
|
claim=(
|
|
"DiscoveryCandidate is structured evidence: it never mutates "
|
|
"the active corpus. Phase C is the only path to mutation."
|
|
),
|
|
detail={
|
|
"candidate_id": payload["candidate_id"],
|
|
"proposed_chain": payload["proposed_chain"],
|
|
"polarity": payload["polarity"],
|
|
"evidence": payload["evidence"],
|
|
},
|
|
), payload
|
|
|
|
|
|
def _scene3_propose(log_path: Path, candidate_id: str) -> tuple[SceneResult, Any]:
|
|
_print_header(
|
|
"S3. Operator-authored proposal — replay-equivalence gate runs",
|
|
"From the discovery candidate's evidence, the operator authors "
|
|
"a complete chain: narrative reveals meaning. Affirming evidence "
|
|
"is the existing corpus chain cause_creation_reveals_meaning. "
|
|
"The real replay gate (teaching.replay.run_replay_equivalence) "
|
|
"runs the cognition public split twice — active corpus vs. "
|
|
"transient-with-appended-chain — and reports no regression.",
|
|
)
|
|
# Construct the operator-augmented candidate. This is the operator
|
|
# contribution: connective, object, and an affirming-source evidence
|
|
# pointer to a corpus chain that already encodes the relevant
|
|
# semantic shape.
|
|
augmented = DiscoveryCandidate(
|
|
candidate_id=candidate_id,
|
|
proposed_chain={
|
|
"subject": _DEMO_SUBJECT, "intent": "cause",
|
|
"connective": _OPERATOR_CONNECTIVE,
|
|
"object": _OPERATOR_OBJECT,
|
|
},
|
|
trigger="would_have_grounded",
|
|
source_turn_trace="",
|
|
pack_consistent=True,
|
|
boundary_clean=True,
|
|
polarity="affirms",
|
|
claim_domain="factual",
|
|
evidence=(
|
|
EvidencePointer(
|
|
source="corpus",
|
|
ref=_OPERATOR_EVIDENCE_REF,
|
|
polarity="affirms",
|
|
epistemic_status="coherent",
|
|
),
|
|
),
|
|
)
|
|
log = ProposalLog(log_path)
|
|
proposal = propose_from_candidate(augmented, log=log)
|
|
rec = log.find(proposal.proposal_id) or {}
|
|
ev = rec.get("replay_evidence") or {}
|
|
_say(f" proposal_id : {proposal.proposal_id}")
|
|
_say(f" proposed_chain : {proposal.proposed_chain}")
|
|
_say(f" evidence (corpus ref) : {_OPERATOR_EVIDENCE_REF}")
|
|
_say(f" replay baseline : {ev.get('baseline')}")
|
|
_say(f" replay candidate : {ev.get('candidate')}")
|
|
_say(f" regressed_metrics : {ev.get('regressed_metrics')}")
|
|
_say(f" replay_equivalent : {ev.get('replay_equivalent')}")
|
|
_say(f" state : {rec.get('state')}")
|
|
if rec.get("state") != "pending":
|
|
raise RuntimeError(
|
|
f"expected pending state but got {rec.get('state')!r}; "
|
|
f"regressed metrics: {ev.get('regressed_metrics')}"
|
|
)
|
|
return SceneResult(
|
|
scene="S3_propose_replay_pass",
|
|
claim=(
|
|
"Real replay gate confirms no metric regression — the "
|
|
"proposal moves to pending. Operator --accept still required."
|
|
),
|
|
detail={
|
|
"proposal_id": proposal.proposal_id,
|
|
"proposed_chain": proposal.proposed_chain,
|
|
"replay_evidence": ev,
|
|
"state": rec.get("state"),
|
|
},
|
|
), proposal
|
|
|
|
|
|
def _scene4_accept_against_transient(
|
|
log_path: Path,
|
|
proposal_id: str,
|
|
) -> tuple[SceneResult, Path]:
|
|
_print_header(
|
|
"S4. Operator accept — transient corpus, active corpus untouched",
|
|
"accept_proposal writes one JSONL line to a TRANSIENT corpus "
|
|
"(copy of active + new chain). The active corpus file bytes "
|
|
"are byte-identical pre/post. Provenance on the new entry: "
|
|
"adr-0057:discovery_promoted:<review_date>.",
|
|
)
|
|
log = ProposalLog(log_path)
|
|
tmp_dir = Path(tempfile.mkdtemp(prefix="learning_loop_demo_"))
|
|
transient = tmp_dir / "cognition_chains_v1.jsonl"
|
|
if _tg._CORPUS_PATH.exists():
|
|
shutil.copyfile(_tg._CORPUS_PATH, transient)
|
|
else:
|
|
transient.write_text("", encoding="utf-8")
|
|
|
|
active_before = _active_bytes()
|
|
transient_lines_before = len(transient.read_text(encoding="utf-8").splitlines())
|
|
|
|
chain_id = accept_proposal(
|
|
proposal_id,
|
|
log=log,
|
|
corpus_path=transient,
|
|
review_date="2026-05-18",
|
|
operator_note="learning-loop demo (transient corpus only)",
|
|
)
|
|
active_after = _active_bytes()
|
|
transient_lines_after = len(transient.read_text(encoding="utf-8").splitlines())
|
|
|
|
_say(f" appended chain_id : {chain_id}")
|
|
_say(f" transient corpus path : {transient}")
|
|
_say(f" transient lines before : {transient_lines_before}")
|
|
_say(f" transient lines after : {transient_lines_after}")
|
|
_say(f" active corpus byte-eq : {active_before == active_after}")
|
|
if active_before != active_after:
|
|
raise RuntimeError(
|
|
"demo invariant broken: accept_proposal mutated the active corpus"
|
|
)
|
|
return SceneResult(
|
|
scene="S4_accept_against_transient",
|
|
claim=(
|
|
"accept_proposal is the sole corpus-write surface. Pointing "
|
|
"it at a transient path leaves the active corpus byte-identical."
|
|
),
|
|
detail={
|
|
"chain_id": chain_id,
|
|
"transient_corpus": str(transient),
|
|
"transient_lines_before": transient_lines_before,
|
|
"transient_lines_after": transient_lines_after,
|
|
"active_corpus_byte_identical": active_before == active_after,
|
|
},
|
|
), transient
|
|
|
|
|
|
def _scene5_replay_now_grounded(transient: Path) -> SceneResult:
|
|
_print_header(
|
|
"S5. Same prompt — now deterministically teaching-grounded",
|
|
"With the runtime's corpus path swapped to the transient corpus, "
|
|
"the same prompt now returns a teaching-grounded surface "
|
|
"containing the operator-accepted chain: "
|
|
"narrative reveals meaning. Identical bytes for any replay of "
|
|
"the same prompt against this corpus state.",
|
|
)
|
|
# ADR-0064 — the cognition corpus is one of several registered
|
|
# teaching corpora; surface composers now consult
|
|
# ``_all_chains_index`` instead of ``_corpus_index`` alone. We
|
|
# rewrite the registry entry's path for the duration of the swap
|
|
# and clear every teaching cache so the aggregator re-reads the
|
|
# transient corpus.
|
|
real_path = _tg._CORPUS_PATH
|
|
original_specs = _tg.TEACHING_CORPORA
|
|
swapped_specs = tuple(
|
|
_tg.TeachingCorpusSpec(
|
|
corpus_id=s.corpus_id,
|
|
path=transient if s.corpus_id == _tg.TEACHING_CORPUS_ID else s.path,
|
|
pack_id=s.pack_id,
|
|
)
|
|
for s in original_specs
|
|
)
|
|
try:
|
|
_tg._CORPUS_PATH = transient # type: ignore[assignment]
|
|
_tg.TEACHING_CORPORA = swapped_specs # type: ignore[misc]
|
|
_tg.clear_teaching_caches()
|
|
rt2 = ChatRuntime()
|
|
response = rt2.chat(_DEMO_PROMPT)
|
|
finally:
|
|
_tg._CORPUS_PATH = real_path # type: ignore[assignment]
|
|
_tg.TEACHING_CORPORA = original_specs # type: ignore[misc]
|
|
_tg.clear_teaching_caches()
|
|
|
|
surface = response.surface
|
|
grounding = response.grounding_source
|
|
_say(f" prompt : {_DEMO_PROMPT}")
|
|
_say(f" surface : {surface}")
|
|
_say(f" grounding_source : {grounding}")
|
|
|
|
# Falsifiable assertions for the demo's headline claim.
|
|
contains_subject = _DEMO_SUBJECT in surface.lower()
|
|
contains_connective = "reveal" in surface.lower() # humanised
|
|
contains_object = "meaning" in surface.lower()
|
|
is_teaching_grounded = grounding == "teaching"
|
|
|
|
if not (contains_subject and contains_connective and contains_object and is_teaching_grounded):
|
|
raise RuntimeError(
|
|
f"demo invariant broken: same-prompt surface did not become "
|
|
f"teaching-grounded (surface={surface!r}, grounding={grounding!r})"
|
|
)
|
|
|
|
return SceneResult(
|
|
scene="S5_replay_now_grounded",
|
|
claim=(
|
|
"The same prompt now produces a deterministic teaching-"
|
|
"grounded surface containing the accepted chain's "
|
|
"subject / connective / object."
|
|
),
|
|
detail={
|
|
"surface": surface,
|
|
"grounding_source": grounding,
|
|
"contains_subject": contains_subject,
|
|
"contains_connective_reveals": contains_connective,
|
|
"contains_object_meaning": contains_object,
|
|
},
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Public entry point
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def run_demo(*, emit_json: bool = False) -> dict[str, Any]:
|
|
"""Run all five scenes and return a structured report."""
|
|
global _VERBOSE
|
|
_VERBOSE = not emit_json
|
|
|
|
active_bytes_before = _active_bytes()
|
|
rt = ChatRuntime()
|
|
sink = _BufferSink()
|
|
rt.attach_discovery_sink(sink)
|
|
rt.attach_contemplation(enabled=True)
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
log_path = Path(tmpdir) / "demo_proposals.jsonl"
|
|
s1, _before_response = _scene1_cold_turn(rt, sink)
|
|
s2, candidate_payload = _scene2_discovery_emission(sink)
|
|
s3, proposal = _scene3_propose(log_path, candidate_payload["candidate_id"])
|
|
s4, transient = _scene4_accept_against_transient(log_path, proposal.proposal_id)
|
|
s5 = _scene5_replay_now_grounded(transient)
|
|
|
|
active_bytes_after = _active_bytes()
|
|
report = DemoReport(
|
|
prompt=_DEMO_PROMPT,
|
|
before_surface=s1.detail["surface"],
|
|
before_grounding_source=s1.detail["grounding_source"],
|
|
after_surface=s5.detail["surface"],
|
|
after_grounding_source=s5.detail["grounding_source"],
|
|
scenes=(s1, s2, s3, s4, s5),
|
|
learning_loop_closed=(
|
|
s1.detail["grounding_source"] == "none"
|
|
and s5.detail["grounding_source"] == "teaching"
|
|
),
|
|
active_corpus_byte_identical=(active_bytes_before == active_bytes_after),
|
|
)
|
|
|
|
if _VERBOSE:
|
|
_say()
|
|
_say("═" * 72)
|
|
_say(" BEFORE / AFTER (single deterministic prompt, one accept between)")
|
|
_say("═" * 72)
|
|
_say(f" prompt : {report.prompt}")
|
|
_say(f" before : [{report.before_grounding_source}] {report.before_surface}")
|
|
_say(f" after : [{report.after_grounding_source}] {report.after_surface}")
|
|
_say()
|
|
_say(f" learning_loop_closed : {report.learning_loop_closed}")
|
|
_say(f" active corpus byte-identical : {report.active_corpus_byte_identical}")
|
|
_say()
|
|
|
|
return report.as_dict()
|
|
|
|
|
|
__all__ = ["run_demo"]
|