core/docs/plans/curriculum-license-loop-2026-07-25.md
Shay 55a3f79f18 docs(arc): curriculum-license-loop plan of record + tier division of work
Two prior arcs closed on "the binding constraint is ratified curriculum volume,
not machinery." Verified against code 2026-07-25: the machinery half is wrong in
a load-bearing way, and the correction is the reason this arc exists.

evals/curriculum_serve/practice/ does not exist. seal_ledger exists only for
deduction (evals/deduction_serve/practice/runner.py:96), yet
chat/curriculum_serve_license.py's own docstring names
evals.curriculum_serve.practice.runner.seal_ledger as "the only writer" of the
artifact it reads -- that writer was never built, and
chat/data/curriculum_serve_ledger.json does not exist. And the ceremony stops
short: RatificationReceipt.pending_stages = (arena_queue_entry, ledger_reseal).

So the pipeline is author -> ratify -> ??? -> ??? -> license -> serve. Authoring
curriculum today cannot earn a license at ANY volume. Content is necessary and
currently insufficient. The build is still modest, because oracle.py is already
an independent decision procedure usable as the gold tether and ADR-0263 already
extracted the sealers for exactly a third instance.

Two measured facts shape the content work. `refuted` has NEVER been produced --
the physics lane gold is entailed 14 / unknown 12 / declined 6, and no corpus row
can express it. And bands key on (domain, operator_family), so negative content
must live INSIDE operator_family "modal" with a row-level polarity marker; a
separate modal_negative family would create a new band at n=0 instead of adding
refuted volume to the target band -- the easiest available way to waste the whole
authoring effort.

DIVISION-OF-WORK.md records the tiering criterion and why it is not difficulty.
The evidence does not support a prestige split: in the same 48h window Sonnet 5
found the DEFAULT_SINK bug that wrote outside the repository and did the
six-worktree public_demo archaeology, while Opus 5 mis-diagnosed the CGA hot path
by multiplying a microbenchmark by a call count. Model tier was not the variable;
verification discipline was.

The split is therefore: would an error here be caught by a gate, or is it silent?
Two units are silent-failure BECAUSE THEY ARE THE GATE -- the negative-curriculum
epistemology (wrong => corpus encodes falsehoods and wrong=0 still passes, since
lane gold and the oracle both read the same rows) and the volume-honesty invariant
(wrong => conservative_floor cannot tell 657 independent facts from 16 asked 41
times, so the ledger clears theta=0.99 having earned nothing). Those get Opus.
Everything loud goes to the cheapest competent tier.

Also records the ordering correction: the epistemology ADR must precede the
generator, or the generator gets built twice.

[Verification]: docs-only; no code paths touched. Every referenced path confirmed
to exist and all five line citations confirmed accurate at HEAD (gold.py:1163,
curriculum_serve/runner.py:71, discovery_yield.py:67-70, entail.py:125,
curriculum_surface.py:184). Pre-push gate runs on push.
2026-07-25 15:09:09 -07:00

186 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Curriculum License Loop — Plan of Record
**Date:** 2026-07-25 · **Status:** ACTIVE · **Base:** main @ `0a26787f`
(post ADR-governance pin, PR #114)
Successor to `generalization-arc-2026-07-24.md`, which closed its Tier F/O/S
arc, and to the assessment-verification arc (PR #113). Division of work across
tiers: `docs/handoff/curriculum-license-loop-2026-07/DIVISION-OF-WORK.md`
(binding — read it before starting any unit).
---
## 1. Why this arc exists
Two arcs closed on the same stated conclusion: *the binding constraint is
ratified curriculum volume, not machinery.* That conclusion is half right, and
the wrong half is load-bearing. Verified 2026-07-25 by reading code:
1. **`evals/curriculum_serve/practice/` does not exist.** The deduction analogue
(`evals/deduction_serve/practice/{gold,runner}.py`) is what folds
`run_practice` over a corpus into per-band `ClassTally`. Curriculum has no
producer.
2. **`seal_ledger` exists only for deduction**
(`evals/deduction_serve/practice/runner.py:96`).
`chat/curriculum_serve_license.py`'s own docstring names
`evals.curriculum_serve.practice.runner.seal_ledger` as "the only writer" of
the artifact it reads. **That writer was never built**, and
`chat/data/curriculum_serve_ledger.json` does not exist.
3. **The ceremony stops short.** `teaching/ratification.py::RatificationReceipt`
carries `pending_stages = ("arena_queue_entry", "ledger_reseal")` — it grows
the corpus and explicitly neither practices it nor reseals the ledger.
So the pipeline is `author → ratify chain → ??? → ??? → license → serve`, with
two unbuilt segments. **Authoring curriculum today cannot earn a license at any
volume.** Content is necessary and currently insufficient.
The build is nonetheless *modest*, because the expensive pieces already exist:
`evals/curriculum_serve/oracle.py` is already an independent decision procedure
usable as the gold tether, and ADR-0263 already extracted the sealers
(`core/ratified_ledger.py`) for precisely a third instance.
## 2. Two measured facts that shape the content work
- **`refuted` has never been produced.** The physics lane gold is
`entailed 14 / unknown 12 / declined 6` — zero refuted. The verdict exists in
`CurriculumDecision.verdict`; no corpus row can express it.
- **Bands key on `(domain, operator_family)`**
(`chat/curriculum_surface.py:184`). Negative content must therefore live
*inside* `operator_family: "modal"` with a row-level polarity marker. A
separate `modal_negative` family would create a **new band at n=0** instead of
adding refuted volume to the target band. This is the easiest available way to
waste the entire authoring effort.
## 3. Decisions taken (2026-07-25, Shay)
| Decision | Choice |
|---|---|
| Leading track | Close the license loop (not articulation, not substrate) |
| First content target | **physics · modal** — n=9, closest of eleven bands (24× short) |
| `refuted` representation | **Explicit negative edges**, polarity inside the existing family |
| Reseal trigger | **Explicit `core proposal-queue reseal` verb**, never automatic on `ratify` |
Reseal is explicit because `AGENTS.md` forbids ratification automation, and
because an auto-reseal per ratified row would churn the ledger's
`content_sha256` on every append.
## 4. Phases
### Phase A — Negative-curriculum epistemology *(design only; gates everything)*
ADR deciding how an explicitly-taught negative coexists with the open-world
reading. Binding constraints: absent edge ⇒ UNKNOWN (never "no"); explicit
negative edge ⇒ REFUTED; polarity is a row-level field or `intent` value, never
a new `operator_family`; `evals/curriculum_serve/oracle.py` must learn polarity
**independently** of the serving path, since that independence is the whole
evidentiary value.
Touches: `teaching/domain_chains/physics_chains_v1.jsonl` schema,
`teaching/curriculum_premises.py`,
`teaching/ratification.py::validate_admissible`,
`evals/curriculum_serve/oracle.py`, lane contract anti-recall probes.
**Done when:** ADR Accepted; schema shape fixed; no code written.
### Phase B — The volume-honesty invariant *(the anti-gaming gate)*
Failing tests written **before** any producer code. The generator must emit at
most one committed case per distinct `(relation, direction, polarity,
question-shape)` tuple and must never paraphrase-pad. S6 already proved
`conservative_floor` cannot distinguish "657 independent facts" from "16 facts
asked 41 times," so an unbounded generator would clear the Wilson floor without
earning anything.
**Done when:** tests exist, fail for the right reason, and fail under mutation
(pad the generator ⇒ red).
### Phase C — The producer *(implementation against Phase B)*
`evals/curriculum_serve/practice/{__init__,generator,runner}.py`, mirroring
`evals/deduction_serve/practice/` topology exactly — producer in `evals/`,
artifact next to its reader in `chat/data/`.
Reuse, do not rewrite: `evals/curriculum_serve/oracle.py` (gold tether),
`core/learning_arena/engine.py::run_practice`,
`core/ratified_ledger.py::{seal_artifact, tally_dict, write_sealed_ledger}`,
`core/reliability_gate::{Action, Ceilings, ClassTally, license_for}`,
`chat/curriculum_surface.py` (solver under test).
**Done when:** Phase B tests green; `wrong=0` on the physics lane.
### Phase D — Close the ceremony's `pending_stages`
`core proposal-queue reseal` verb; `ratify_chain` records the enqueue.
Files: `teaching/ratification.py`, `core/cli_proposal_queue.py`.
**Done when:** the verb produces a SHA-sealed
`chat/data/curriculum_serve_ledger.json`, and
`curriculum_serve_license()` reads a real (still-unearned) ledger rather than an
absent one.
### Phase E — Honesty seams *(parallel, no dependency on AD)*
- **E1 — audit-ledger R7.** Defer the single sink emission to the pipeline serve
boundary (`chat/runtime.py::finalize_turn_surface`; spec at
`docs/specs/runtime_contracts.md:448-456`). Repairs the stale surface and
stale `trace_hash` together. Default sink is `None`, so no deployment risk.
Independent of the `hash_surface`-on-`TurnEvent` ruling.
- **E2 — `assert_corpus_sound` name collision.** Two functions, one name:
`evals/deduction_serve/practice/gold.py:1163` (no args, practice corpus) vs
`evals/curriculum_serve/runner.py:71` (`domain, cases`, lane contract). Must
land **before** Phase C adds a third caller.
- **E3 — ADR status vocabulary normalization.** 27 unparseable status lines plus
`draft`/`ratified`/`active`/`accepted.` variants across 312 files; then widen
`tests/test_adr_status_governance.py` to a closed vocabulary.
### Phase F — Content *(the actual constraint)*
Author + ratify physics · modal volume: ≈219 distinct taught modal relations for
the entailed bucket, plus matching refuted/unknown. Measure with
`teaching.ratification._count_chains` and
`evals/vocab_trigger_instrument.py --compare` admissions delta.
Relations must be **true**. Drafting candidates is delegable; ratification is
Shay's.
### Phase G — The flag *(Shay only)*
Only after a band clears θ=0.99 at n≥657 with a genuine outcome mix: *propose*
flipping `curriculum_serving_enabled`. On earned evidence, never on proximity.
## 5. Deferred, decision-gated *(a ruling, not effort)*
- **Band-level capability index** — 25 near-1.0 entries would inflate
`coverage_geomean` against the index's own anti-gaming property. Needs a
scoring decision before any schema change.
- **Discovery-yield stratification** — blocked on new persisted per-source
counters in the `engine_state` manifest
(`teaching/discovery_yield.py:67-70` reads one monotonic `turn_count`).
- **`hash_surface` on `TurnEvent`** — self-verifying telemetry vs replay-only.
Current contract states recomputation is unsupported *and correct to be so*.
## 6. Off this arc's critical path
- **Articulation depth (Phase 5 / core_logos).** `EntailmentTrace` carries five
opaque BDD node keys; `generate/proof_chain/entail.py:125-190` decides via
`is_tautology`. There are no intermediate steps, so nothing is dropped and the
stated trigger describes an event that cannot occur. CORE's decision procedure
is **complete but not explanatory.** Next deliverable is a *feasibility study
for proof-term extraction over the ROBDD* — a derivation calculus with a real
soundness surface, not a data-structure refactor. Highest intellectual value,
least schedulable.
- **Substrate.** `CORE_BACKEND=rust` bit-exact parity is now answerable locally
(cargo 1.97.1 + registry cache present; `core-rs/tests/test_crdt_hash_parity.rs`
exists; needs `PYO3_PYTHON=/opt/homebrew/bin/python3.12`). Then Rust typestate
(`UnverifiedClaim` → `VersorClaim`) — zero determinism cost, greenfield. The
real hot path is `cga_inner``geometric_product`, 33,986 calls/turn, ~73% of
turn time. **Not** the diagonal shortcut — not bit-exact (954/4000 in f32).
**Refuted, do not revisit:** `.metal` kernel, MLX fusion, bf16.
- **Math.** Phase 4.2 case-first is falsified on a count — cases
0000/0001/0148/0082 affect 1 and 2 cases out of 500. Only remaining lever is
the `q:complex` decomposition study (101 cases, one label). Low expected value.
## 7. Dependencies
- **A gates B** (polarity shapes the generator's enumeration — building C before
A means building it twice).
- **B gates C** (tests first; the invariant *is* the specification).
- **E2 lands before C.** E1/E3 fully parallel.
- **C + D gate F's measurement**, and F gates G.
- Phase 6 items independent and parallel throughout.
## 8. Non-goals
- Flipping any flag without explicit ratification.
- Adding band entries to the capability index before the scoring decision.
- Paraphrase-padding practice volume to reach the Wilson floor.
- Renumbering or overwriting Accepted ADRs.