* feat(teaching): add curriculum-sourced proposal builder * test(teaching): cover curriculum proposal construction * test(evals): add curriculum loop closure contract * test(evals): add curriculum loop closure runner * test(evals): add canonical curriculum loop closure report * ci(lanes): pin curriculum loop closure lane * docs(adr): add ADR-0104 curriculum sourced proposals * docs(adr): register ADR-0104 and seven pinned lanes * docs(teaching): mark curriculum source activation * fix(ci): pin curriculum_loop_closure SHA to runner output * fix(ci): register curriculum_loop_closure in CLAIMS.md generator
91 lines
4.5 KiB
Markdown
91 lines
4.5 KiB
Markdown
# ADR-0104 — Curriculum-Sourced Teaching Proposals
|
|
|
|
**Status:** Accepted
|
|
**Date:** 2026-05-22
|
|
**Accepted:** 2026-05-22
|
|
**Author:** CORE agents + reviewers
|
|
**Depends on:** ADR-0094, ADR-0095
|
|
|
|
---
|
|
|
|
## Acceptance evidence
|
|
|
|
Accepted after curriculum-sourced proposals were wired through the reviewed teaching pipeline with a deterministic, SHA-pinned closure lane:
|
|
|
|
- `teaching/from_curriculum.py` converts curriculum-authored `PACK_MUTATION_CANDIDATE` findings into `PackMutationProposal` records carrying `ProposalSource(kind="curriculum", source_id=<curriculum_id>, ...)`.
|
|
- `evals/curriculum_loop_closure/runner.py` and `evals/curriculum_loop_closure/contract.md` define the closure lane; `evals/curriculum_loop_closure/results/v1_dev.json` is the canonical report.
|
|
- `tests/test_curriculum_proposals.py` exercises curriculum-to-proposal conversion, source-tagging, replay determinism, identity defense, telemetry redaction, and review-path parity with operator/miner proposals.
|
|
- `scripts/verify_lane_shas.py` pins `curriculum_loop_closure` and includes it in the lane verifier.
|
|
|
|
---
|
|
|
|
## Context
|
|
|
|
ADR-0094 reserved `ProposalSource(kind="curriculum")` as a sealed provenance value. ADR-0095 then proved that non-operator proposal sources can exist without gaining pack-write authority, coherence authority, or a parallel reviewer.
|
|
|
|
Curriculum ingestion is the next non-operator source. A curriculum may contain structured lessons, examples, and pack-extension candidates. Those candidates are not trusted merely because they came from a curriculum. They must enter the same proposal log and reviewed-teaching path as operator and miner proposals.
|
|
|
|
---
|
|
|
|
## Decision
|
|
|
|
Introduce `teaching/from_curriculum.py`, a structural sibling of `teaching/from_miner.py`, to translate curriculum-authored `ContemplationFinding(kind=PACK_MUTATION_CANDIDATE)` values into `PackMutationProposal` candidates.
|
|
|
|
Curriculum proposals carry:
|
|
|
|
```python
|
|
ProposalSource(
|
|
kind="curriculum",
|
|
source_id=<curriculum_id>,
|
|
emitted_at_revision=<git_sha_or_revision>,
|
|
)
|
|
```
|
|
|
|
### Hard constraints
|
|
|
|
1. **Single review path.** Curriculum-sourced proposals enter the same review path as operator and miner proposals. No alternate reviewer and no auto-acceptance.
|
|
2. **Default status `speculative`.** Curriculum-sourced proposals are never coherent at emission.
|
|
3. **Identity-pack defense at construction.** A curriculum item whose subject or proposed action trips the identity-override detector is rejected before it can become a proposal.
|
|
4. **Replay-equivalence pre-gate.** A pluggable checker can reject curriculum proposals whose proposed mutation changes non-target turn `trace_hash` values.
|
|
5. **Deterministic emission.** Proposal IDs are SHA-256 derived from `(curriculum_id, finding_canonical, emitted_at_revision)` and truncated to 16 hex chars.
|
|
6. **Redacted telemetry.** Proposal-emitted events carry only proposal ID, source serialization, and epistemic status. Raw content is excluded.
|
|
|
|
---
|
|
|
|
## Invariants
|
|
|
|
`curriculum_proposal_replay_equivalence` — every curriculum-sourced proposal that reaches review eligibility must preserve non-target replay hashes under the proposed mutation.
|
|
|
|
`curriculum_proposal_single_review_path` — no code path may promote a curriculum-sourced proposal to coherent outside the allowed review/store path.
|
|
|
|
---
|
|
|
|
## Lane
|
|
|
|
`evals/curriculum_loop_closure/` proves:
|
|
|
|
- positive: legitimate curriculum item -> proposal emitted with curriculum provenance
|
|
- negative: identity override in subject -> rejected at construction
|
|
- negative: identity override in action -> rejected at construction
|
|
- negative: replay-equivalence failure -> rejection log entry
|
|
- negative: wrong finding kind -> typed error, no proposal
|
|
- determinism: same findings and revision -> identical proposal stream
|
|
|
|
---
|
|
|
|
## Consequences
|
|
|
|
- Curriculum import can now feed the learning loop without receiving trust elevation.
|
|
- Domain/course authors can supply structured proposal candidates while preserving review authority boundaries.
|
|
- The proposal-source lattice now has three active kinds: operator, miner, curriculum.
|
|
- SHA-pinned lane coverage expands from six to seven lanes.
|
|
|
|
---
|
|
|
|
## PR Checklist
|
|
|
|
- Capability added: curriculum-sourced proposal construction.
|
|
- Invariants proved: `curriculum_proposal_replay_equivalence`, `curriculum_proposal_single_review_path`.
|
|
- Lane proving it: `evals/curriculum_loop_closure/`.
|
|
- Hidden normalization / stochastic fallback / approximate recall / unreviewed mutation: none.
|
|
- Trust boundary: curriculum content can propose; only review/store can accept or promote.
|