Adds two pre-gate checks to propose_from_candidate that fire after the
Step 2 capacity check and before the replay gate. No log entry is
written on either refusal — the append-only invariant holds.
Check order at function entry (ADR-0161 §3):
1. Capacity (Step 2) → RefusedAtCapacity
2. Duplicate → RefusedAsDuplicate
3. Dependent_on_pending → RefusedAsDependent
4. Replay gate → auto-reject on regression
New frozen dataclasses:
@dataclass(frozen=True, slots=True)
class RefusedAsDuplicate:
proposal_id: str
existing_state: str # covers all states: pending/accepted/rejected/withdrawn
reason: str = "duplicate"
@dataclass(frozen=True, slots=True)
class RefusedAsDependent:
candidate_id: str
dependent_on: tuple[str, ...] # pending proposal_ids that block
overlapping_lemmas: tuple[str, ...] # normalised lemmas that triggered
reason: str = "dependent_on_pending"
Lemma-overlap rule: case-insensitive exact-match on strip().lower().
Conservative — over-reject rather than admit-with-hidden-dependency.
False positives are recoverable (re-emit after blocker is ratified);
false negatives silently couple ratification choices.
CLI surfaces both outcomes in cmd_teaching_propose and
cmd_teaching_propose_from_exemplars (exit code 1).
Step 2 backpressure tests updated: made pre-populated candidates use
unique objects to avoid triggering the new dependency check, and
updated idempotency assertions to reflect the new RefusedAsDuplicate
return for re-submitted content.
Co-references: ADR-0161 §3, Step 1 PR #296, Step 2 PR #311,
ADR-0057, ADR-0151.
* docs(math): ADR-0163 — path to GSM8K mastery via candidate-graph admissibility (proposed)
Audit reframes the math roadmap entirely.
State of main: every named math capability axis (G1..G5, S1) passes
at 100% with wrong=0 on its controlled lane. binding_graph,
math_versor_arithmetic, math_symbolic_equivalence, math_parser,
math_candidate_parser, math_solver, math_verifier, math_realizer,
math_problem_graph — all landed. The worktrees on disk are stale
forks.
State of GSM8K (50-case train sample): correct=0, refused=50, wrong=0.
Every refusal reason is identical: "candidate_graph: no admissible
candidate for statement: <STATEMENT>".
The reframe: the gap is NOT in operator algebra, NOT in binding graph
internals, NOT in symbolic equivalence. The gap is in
generate/math_candidate_graph.py — the admissibility surface that
turns a natural-language statement into a candidate the downstream
pipeline can consume. The capability axes pass at 100% because they
test statement shapes the candidate-graph already admits. GSM8K
refuses at 100% because its statements span shapes the candidate-graph
has never been taught.
Six-phase plan to lift GSM8K under the thesis "decodes, not generates":
A. Refusal taxonomy (measure before building)
B. Exemplar corpora per shape category (≤20 statements each, ≤3 per round)
C. Contemplation runner ingests exemplars; emits DerivedRecognizer
proposals
D. Operator ratifies through ADR-0161 HITL queue (no new surface)
E. Re-baseline GSM8K train sample. Round 1 exit: correct ≥ 10, wrong = 0.
Round 2: ≥ 25. Round 3: ≥ 35.
F. Scale to public/v1 (200 cases, target correct ≥ 100), then
holdout (measurement-only — never tune against).
Three non-negotiables:
- wrong = 0 at every phase. Auto-rejected by replay gate, not by
operator vigilance.
- No hand-rolled recognizers in generate/. Every recognizer lands
via contemplation → proposal → review corridor.
- Active corpus mutation only via accept_proposal.
Status: proposed. Implementation lands as three PRs starting with
Phase A scaffolding.
Scope discipline: docs-only. No code, no eval changes, no corpus
mutation.
* feat(ADR-0161.1): core teaching queue list|show — read-only queue projection
* fix(ADR-0161.1): restore gap-queue CLI + rename new commands to hitl-queue + R1..R5 refinements
Wires contemplation-enriched DiscoveryCandidates into the ADR-0057 proposal
gate at _load_engine_state(). Proposals land in ProposalLog with
source.kind="contemplation"; operator ratification via existing
core teaching review path unchanged.
`core teaching supersede <old_chain_id> --subject ... --intent ... --connective ...
--object ... --review-date YYYY-MM-DD` is the second corpus mutation surface
(alongside accept_proposal). No replay gate — it's a deliberate operator action
that replaces a hand-authored or previously discovery-promoted chain.
- teaching/supersede.py — `supersede_chain()` orchestrator with pre-checks
(review_date format, intent whitelist, pack-consistency via re-audit,
no double-supersede, no self-supersede, no new-chain-id collision) and
byte-identical rollback on post-audit failure.
- teaching/proposals.py — extended `append_chain_to_corpus` with optional
`superseded_by` kwarg; remains the only function in the codebase that
writes to the active teaching corpus.
- core/cli.py — `core teaching supersede` subcommand wired to the live
`_CORPUS_PATH`; EPILOG updated with example.
- tests/test_supersede.py — 13 tests pin every gate, byte-identical
rollback on rejection, append-only at disk level, audit-and-runtime
parity after supersession, hand_authored provenance with
`supersede(<old_chain_id>)` tag.
Lane state: smoke 67 / cognition 121 / teaching 17 / supersede 13 / audit 23 /
proposals 16 / contemplation 16 / contemplation-wiring 6 / discovery 24 — green.
`core eval cognition`: intent 100% / surface 100% / term 91.7% / versor 100% — unchanged.
The only path by which CORE extends its own active teaching corpus.
Closes ADR-0055 Phase C alongside ADR-0056's cognitive surface.
Three load-bearing calls (recorded in ADR-0057):
1. Replay-equivalence is a precondition, not a permission;
operator --accept remains required.
2. Eligibility = polarity in {affirms, falsifies} AND at least
one source='corpus' evidence pointer AND boundary_clean AND
claim_domain != evaluative (unless --allow-evaluative) AND
proposed_chain complete.
3. Append-only proposal log; corpus history append-only too.
Changes
- teaching/proposals.py — TeachingChainProposal, ReplayEvidence,
ProposalLog (event-sourced replay → current_state), eligibility
predicate, propose_from_candidate, accept/reject/withdraw,
append_chain_to_corpus (the sole corpus-write surface). Uses
TYPE_CHECKING guards to break the circular import with
chat.pack_grounding.
- teaching/replay.py — run_replay_equivalence; swaps _corpus_index
path to a tmp file, runs cognition lane on the active corpus
AND a transient copy with the proposed chain appended, returns
regressed-metrics list; trust-boundary assertion that the active
corpus bytes are byte-identical pre/post.
- teaching/discovery.py — moved chat.pack_grounding /
chat.teaching_grounding imports inside extract_discovery_candidates
to break the cycle (was masked when chat.runtime was the entry
point; surfaced by CLI entry).
- core/cli.py — three new subcommands:
core teaching propose <candidate-jsonl-path> [--allow-evaluative]
core teaching proposals [--state pending|accepted|rejected|withdrawn] [--json]
core teaching review <proposal_id> --accept --review-date YYYY-MM-DD
core teaching review <proposal_id> --reject [--note ...]
core teaching review <proposal_id> --withdraw [--note ...]
- tests/test_teaching_proposals.py — 16 tests covering: every
eligibility gate, proposal_id idempotency, append-only log,
replay-equivalent stays pending, regression auto-rejects with
named regressed metrics, --accept appends one line with typed
Provenance, --accept refused on non-equivalent, state-machine
blocks double-accept, real replay gate runs cognition lane
twice and asserts byte-clean active corpus pre/post.
Invariants preserved
- versor_condition(F) < 1e-6 — C2 touches no algebra path.
- Active corpus bytes byte-identical regardless of replay outcome.
- No clock-time reads, no LLM, no async.
- Proposal-only — accept_proposal is the sole corpus-write path.
Lanes: smoke 67 / cognition 121 / runtime 19 / teaching 17 /
new proposals 16. Cognition eval unchanged.
Open follow-ups (not in scope):
- supersession via operator review action
- cross-pack falsification arbitration (ADR-0056 Call 2 deferred)
- pack-data migration of frame-dependent connectives
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>