Source-bound decision record
35 committed decisions · curriculum/alpha Source generated 2026-07-15T13:03:18Z
#curriculum_shared_map_boundary
curriculum shared map boundary
Unspecified
Decision
Alpha Curriculum owns the student-independent scope, sequence, and teaching-delivery graph. Standards/framework source text remains in stock CASE. Knowledge Components live in the governed Alpha KC registry. Reusable course structure lives in course and course_component rows, while ordered placement, Content targets, renderer choice, and gate behavior live in course_component_member rows. Curriculum excludes Content bodies, Results state, Events, People and Orgs facts, and Policy numbers. Incept may plan and generate writes to these contracts, but it owns none of their storage semantics.
Alternatives and tradeoffs
- Put standards, KCs, components, and membership into one CASE graph
- Let each producer define its own curriculum storage model
- Separate the four concepts and give each one canonical Platform3 ownership
Rationale and consequences
Standards, mastery identity, reusable structure, and course-local placement have different lifecycles and reuse rules. Separating them removes the contradictions that forced producers such as Incept to restate or reinterpret Platform3 storage.
#curriculum_four_store_boundary
curriculum four store boundary
Unspecified
Decision
The target Curriculum storage contract separates four authoritative concepts: (1) standards/frameworks in stock case.cf_document, case.cf_item, and case.cf_association; (2) KCs and their governed graph in alpha.knowledge_component, alpha.kc_prerequisite, and alpha.standard_kc_map; (3) reusable course identity and structure in alpha.course and alpha.course_component; and (4) ordered use of a component or Content target in alpha.course_component_member. Platform renderer capabilities and Policy values remain references to their owning modules, not Curriculum tables. This is an architecture target, not a claim that every table and route is already deployed.
Alternatives and tradeoffs
- Store every concept as a CASE CFItem and association
- Keep KCs in CASE while adding only component/member tables
- Create producer-local sidecars outside Platform3
Rationale and consequences
CASE is the right standards interchange store but cannot express KC governance, reusable course identity, or course-local ordered membership without conflating concepts. The four-store boundary preserves CASE where it is authoritative and adds only the Alpha records required by the live Timeback contract.
#curriculum_reusable_component_member_model
curriculum reusable component member model
Unspecified
Decision
A course is a first-class alpha.course row with stable course_id, subject and grade scope, course_role, name, description, lineage, and lifecycle. course_role is governed as main, hole_filling, remediation, or catalog so Results can use only main-course expected effort in the grade-level denominator. A reusable alpha.course_component carries stable component_id, governed kind, scope_kcs, expected_xp, name, description, lineage, lifecycle, and its reusable subtree structure. expected_xp is student-independent expected effort: an authored or source-backed component value is an expected-XP-bearing leaf/fallback, while course, grade, and track totals derive from expected-XP-bearing leaves and ignore a container value whenever descendants carry expected_xp. An alpha.course_component_member is a distinct row that places exactly one target in an ordered position under a course or component. The member owns position/order, target_ref, renderer_ref, gates, passing_criteria_ref, and on_fail. target_ref points to either a reusable course_component subtree or one immutable Content version through the exact tuple content_id, content_version_id, and content_kind. The same component or exact Content version may be reused by many members without copying identity or producer eligibility evidence. Allowed component kinds are track, grade, course, unit, module, chapter, section, topic, lesson, practice, review, quiz, test, mastery_gate, remediation_pocket, and placeholder. Per-kind schemas are enforced at write time.
Alternatives and tradeoffs
- Represent components and membership as the same row
- Copy a lesson subtree into every course that uses it
- Keep reusable identity separate from each ordered use
Rationale and consequences
Course identity, reusable component identity, and placement have different cardinality: one lesson can appear in many courses, orders, renderers, and gate contexts, while course_role and expected effort determine which course work contributes to progress. Separate course and member rows are the minimum model that preserves reuse and progress semantics without leaking course-local behavior onto the reusable component or Content object or double-counting container and leaf effort.
#curriculum_kc_registry_standard_separation
curriculum kc registry standard separation
Unspecified
Decision
Standards are frozen CASE frameworks: framework identity in case.cf_document, standards in case.cf_item, within-framework containment and standard-to-standard alignment in case.cf_association. A Knowledge Component is not a CASE CFItem. It lives in alpha.knowledge_component with persistent kc_id, lineage_id, kc_kind (substantive, disciplinary, application, or integrative), subject_id, and current-version identity; immutable authored meaning lives in alpha.knowledge_component_version. lineage_id remains the canonical family-grouping primitive. It is complemented, not replaced, by only three explicit lineage transition types in alpha.kc_relation: split_from, merged_into, and supersedes. Source replacedBy associations are retained as migration evidence used to assign lineage_id and typed transition edges. Governed prerequisite edges live in alpha.kc_prerequisite and must form an acyclic graph. The additional applies_to, instantiates, integrates, and interferes_with relation types live in alpha.kc_relation with provenance and confidence and are not constrained as a DAG. KC-to-standard links live in alpha.standard_kc_map with relationship, confidence, provenance, and standards-release identity. KCs have no stored containment tree; named KC sets and groupings are projections or views. Mastery, scope, recommendation, and evidence references resolve to kc_id, never to a standard or lesson id.
Alternatives and tradeoffs
- Use standards as mastery identity
- Store KCs as CASE CFItems with isChildOf and isPartOf associations
- Use lessons as mastery nodes
Rationale and consequences
Standards describe external expectations; KCs are governed atomic masterable units. Separate stores preserve standards interoperability while giving KC identity, kind, immutable meaning, definition quality, DAG enforcement, family grouping, typed transitions, confidence, and provenance a canonical home. lineage_id answers which KCs belong to one refactoring family; split_from, merged_into, and supersedes answer how particular KCs transitioned, so neither duplicates the other. A read-only production source inventory found 5,308 candidate KC CFItems, 3,483 precedes plus 4 needs-first associations, 19,341 exact-match associations, and 659 replacedBy associations retained as lineage-assignment evidence. A separate prototype accepted 4,953 KCs, 3,278 acyclic prerequisite edges, and 705 resolved KC-standard crosswalks after validation. Those are different source/discovered and accepted/valid populations, not fixed migration oracles: every migration must report discovered, accepted, and rejected counts with typed reasons and reconcile them to its named source snapshot.
#curriculum_kc_storage_governance
curriculum kc storage governance
Unspecified
Decision
Platform3 stores KC governance facts; Incept runs the KC steward agent. alpha.knowledge_component owns persistent kc_id, lineage_id, kc_kind, subject_id, and current-version identity. alpha.knowledge_component_version is append-only and owns version_number, status (draft or firm), verb-first definition, positive_examples, negative_examples, boundary_statement, misconceptions, instructional_classification, provenance, confidence, author, and timestamps; every semantic edit creates a new immutable version. alpha.kc_prerequisite remains the separately governed acyclic prerequisite DAG. alpha.kc_relation owns only applies_to, instantiates, integrates, interferes_with, split_from, merged_into, and supersedes edges, each with provenance and confidence; non-prerequisite relations need not be acyclic. A materially different learned performance creates a new kc_id and an explicit lineage edge while retaining the shared lineage_id family. Firming occurs at an explicit course approval, never ordinary publication or first assignment. The approval stores an immutable snapshot of every referenced kc_id and version_id. alpha.kc_firming_event records course_approval or first_genuine_response as the firming reason and source reference; an explicit live_learning signal makes the first genuine response a safety backstop, while trigger mechanics may be implementation-owned. alpha.kc_review_decision stores the reviewed version, one structured decision (accept-as-new, map-to-existing, edit, split, merge, reject/relocate, or defer), the captured edit diff, rationale, provenance, actor, and time. Review fields may be nullable on drafts and need not all be mandatory at initial write. Novelty reconciliation, clone-creep/false-unification/mint-rate monitoring, application-coverage planning, review-queue UI and prioritization, regression-case mining, subject-card management, and steward behavior belong to Incept and are not Platform3 schema or business semantics. Endpoint/blueprint relations, KC selection and ordering, pacing, generation plans, clusters, capstones, connective tissue, and bridges remain course projections or course-bound Content, never KC-registry fields. Aspirational alerting and experiment registries are also out of this storage contract.
Alternatives and tradeoffs
- Keep lineage_id-only, prerequisite-only KC storage and let each producer persist the missing facts privately
- Move novelty reconciliation, review queues, monitoring, clusters, and generation-plan behavior into Platform3
- Store only the governed KC facts and let Incept consume and write them through public APIs
Rationale and consequences
The KC Registry Owner requires durable identity, typed meaning, transitions, review provenance, and a firming boundary, but it assigns steward behavior to an agent. Storing the facts in Platform3 gives every producer and consumer one governed contract without turning Curriculum into an agent runtime or putting course-bound planning into the registry.
#curriculum_member_content_reference_gate_placement
curriculum member content reference gate placement
Unspecified
Decision
Curriculum never stores Content. Only course_component_member crosses from Curriculum identity into a Content use: its target_ref points to either a reusable course_component or one exact Content version identified by content_id, content_version_id, and content_kind, and its renderer_ref resolves to a registered platform capability that accepts that target kind. Content owns release eligibility; Curriculum checks the exact tuple at authoring and again at launch, fails closed for every missing, malformed, unavailable, mismatched, blocked, revoked, unresolved, or otherwise ineligible answer, never substitutes latest, and never copies producer verdicts, quality bars, evidence, serveability, or eligibility. Gate placement also lives only on the member: gates, passing_criteria_ref, and on_fail are course-local behavior. renderer_kind is a temporary compatibility alias only when it resolves to the same registered renderer_ref. The same exact Content version can be gated in one member and non-gated in another.
Alternatives and tradeoffs
- Nest Content inside Curriculum
- Put gate fields on the assessment Content
- Put target, renderer, or gate fields on reusable component identity
Rationale and consequences
The member is the only record whose lifecycle matches an ordered, rendered, possibly gated use. Keeping these fields there prevents Content and reusable components from acquiring contradictory course-specific behavior.
#curriculum_renderer_registry_contract
curriculum renderer registry contract
Unspecified
Decision
Renderer is a registered platform Applications capability, not Content storage and not a Curriculum-owned renderer implementation. The registry exposes renderer id/renderer_ref, accepted Content kinds, integration endpoint, integration protocol, playground URL, implementation pointer, lifecycle state, and verified capability evidence. Course authors choose the renderer only on course_component_member through renderer_ref; writes reject missing, unknown, inactive, or content-kind-incompatible renderer refs. The compatibility field renderer_kind may be accepted and echoed only when it resolves to the same registered renderer.
Alternatives and tradeoffs
- Store renderer implementations or renderer tables inside Curriculum
- Let Content or reusable component identity decide renderer selection for every course use
- Keep renderer_kind as an unchecked display hint
Rationale and consequences
The registry gives course authors a resolvable display capability without making Curriculum a rendering runtime or making Content own course-specific display choice.
#curriculum_five_use_cases
curriculum five use cases
Unspecified
Decision
Curriculum has exactly five use cases: author, place, route, gate, and remediate. Placement is a Policy reference, defaulting to PowerPath bottom-up grade-level mastery placement: start from the lowest or screener-selected likely-mastered grade, test upward, and set entry_track_level to the first grade below the mastery threshold. MAP/RIT may choose the starting point, never final placement. Routing reads the shared component and KC graph. Gate failure returns typed remediation: never_learned to lesson, forgot to review, careless to practice. External systems consume these use cases and add no storage.
Alternatives and tradeoffs
- Make Curriculum a full LMS storage model
- Make Curriculum only a standards map
- Limit Curriculum to the five shared-map use cases
Rationale and consequences
The five verbs are enough to explain every accepted rule and to reject every storage request that belongs in another module.
#curriculum_alias_map_contract
curriculum alias map contract
Unspecified
Decision
This architecture publishes a machine-readable alias-map.json that is also the storage-ownership map. Every public Alpha Curriculum name resolves to exactly one authoritative store or surface command: CASE standards, the KC registry/DAG/crosswalk, course/component identity, component membership, or a named cross-module reference. It distinguishes authoritative rows, derived views, compatibility reads, and the target migration state. No alias moves or copies data, and a derived view is never a second authority.
Alternatives and tradeoffs
- Describe aliases only in the architecture prose
- Let the data dictionary invent aliases later
- Publish a machine-readable alias map from architecture
Rationale and consequences
A generated ownership map makes the four-store boundary testable and prevents downstream docs or producer skill packs from reconstructing storage ownership in prose.
#curriculum_source_shaped_ingest_adapter
curriculum source shaped ingest adapter
Unspecified
Decision
Curriculum Alpha ships one source-shaped TimeBack bulk adapter at POST /alpha/curriculum/v1/ingest/timeback-course-refs for top-level course references and ordered course structure. The producer chain courses -> course_components -> component_resources -> resources normalizes into alpha.course, reusable alpha.course_component rows, and ordered alpha.course_component_member rows; resource bodies first pass through Content so Curriculum receives canonical content_id, content_version_id, and content_kind targets. Source parentage and reusable identity belong to course_component; sort order, target_ref, renderer_ref, and any gate fields belong to course_component_member. The TimeBack adapter requires every lesson component to scope a non-empty producer-authored cluster of active alpha.knowledge_component kc_ids until that source owns an explicit no-KC decision field; Curriculum preserves multi-KC clustering grain rather than imposing one KC per lesson. Separately, direct POST /alpha/curriculum/v1/components sanctions an empty lesson scope only for an exact content-bearing kind=lesson with compatible renderer_ref, scope_kc_refs=[], and kc_scope_decision=authoritative_no_kc. Detail, tree, and next-lesson echo the exact Content target and decision; that lesson contributes no KC-version snapshot or mastery evidence, and Curriculum never synthesizes a similarity, course-root, standards, or Content-tag KC. Missing or unresolved scope still rejects. Source learning-objective or standard evidence is not itself a KC, and the adapter never fabricates a KC mapping. A source quiz label remains a lesson member targeting test Content unless explicit member gate fields make it gating. Content rows, course titles, enrollments, and placement QTI never imply sequence. Course-ref-only imports remain valid anchors, but reconciliation and next-lesson return typed curriculum:lesson_structure_missing until authoritative members and KC scope exist. Validation failures return 400, rejected records return 422, and successful imports materialize readable rows. The reconciliation command is dry-run first, preserves source order, requires the producer-supplied exact Content version without inferring latest, verifies active KCs and Content-owned exact-version release eligibility, and reports source-empty, inactive, ineligible, or KC-unscoped courses rather than fabricating pedagogy.
Alternatives and tradeoffs
- Require migration code to pre-normalize components into CASE
- Accept only Common Cartridge packages
- Create a second TimeBack lesson-structure ingest route instead of extending the existing source-shaped bulk boundary
- Infer course membership and order from Content rows, course titles, placement QTI, or enrollments
- Treat a TimeBack component_resources quiz label as a mastery gate without an owning passing and failure policy
Rationale and consequences
Platform3 owns one normalization contract for reusable structure and ordered membership, while Content owns only flat student-facing artifacts. Producers such as Incept can generate to that contract without carrying a second storage model or fabricating pedagogy from unrelated rows.
#curriculum_module_boundary_rejections
curriculum module boundary rejections
Unspecified
Decision
Curriculum rejects non-Curriculum facts on standards, KC, course, component, and member writes. Content owns questions, stimuli, tests, articles, interactives, media, external links, content assets, and test_spec/test_bank. Results owns mastery, attempts, scores, the XP ledger, student track state, and content effectiveness. Events owns activity moments. People and Orgs owns students, teachers, schools, classes, and enrollments. Thresholds, retake rules, cut scores, rewards, penalties, and the retention model live in the versioned alpha.policy.* registry with ownership split per key (caitd-012). The numbers never live on a course, component, member, KC, or standard row, and no module recreates a key another module owns. Course-generation plans and composition strategy are Incept orchestration; only the resulting validated Platform3 writes are Curriculum data.
Alternatives and tradeoffs
- Allow convenience copies on Curriculum rows
- Hide boundaries in customer docs only
- Reject boundary leaks at write time and document raw-DB rules
Rationale and consequences
A write-time rejection is simpler than documenting exceptions forever. Every object has one home and one module owner.
#curriculum_common_cartridge_transport_only
curriculum common cartridge transport only
Unspecified
Decision
Common Cartridge import/export is a transport over CASE standards, the canonical course/component/member graph, and exact-version Content references. Bundles are produced on demand from a course or component subtree and Content package data, never stored as Curriculum. LMS, LOR, SIS, assessment platforms, and Incept consume author, route, and gate use cases; they do not add clone-specific Curriculum storage.
Alternatives and tradeoffs
- Store Common Cartridge bundles
- Add LMS-specific tables
- Generate transport from canonical Platform3 Curriculum and Content rows
Rationale and consequences
A transport should be reproducible from source rows. If export then re-import needs translation logic or stored bundle state, the storage shape is wrong.
#curriculum_shared_supabase_migration_gate
curriculum shared supabase migration gate
Unspecified
Decision
The four-store change is a verified cutover, not an aspirational documentation rewrite or a permanent dual-write. Migrations run idempotently against shared Supabase under bounded serialization. They create the canonical KC, course, component, member, and source-release stores; copy legacy CASE KCs/components with stable identifiers and provenance; verify counts, DAG acyclicity, crosswalk resolution, reference resolution, and route/read parity; then switch writes to the new authority. Legacy CASE rows remain read-only compatibility evidence until those proofs pass and are retired only in a separately approved cleanup. Downstream data dictionaries and APIs must label current deployed behavior separately from target behavior until the cutover is live.
Alternatives and tradeoffs
- Assume a clean database and delete legacy rows immediately
- Keep CASE and Alpha tables as permanent dual-write authorities
- Publish the target schema as deployed before migration evidence exists
Rationale and consequences
A staged, evidence-gated cutover preserves referential identity and prevents both data loss and the false claim that a newly chosen architecture already exists in production.
#curriculum_teaching_engine_policy_config
curriculum teaching engine policy config
Unspecified
Decision
Teaching-delivery policy lives in the versioned alpha.policy.* registry, and ownership is split per key so exactly one module creates each key. Curriculum is the sole creator of the policies that govern how teaching delivers the map: alpha.policy.mastery_cutoff, placement_weights, xp_award_formula (including repeat-attempt discount and effort-signal weights), spaced_repetition_model, and gate_retake_cooldown. Results is the sole creator of outcome-measurement and reward keys: reward_rules, stuck_attempt_threshold, growth_x_target, proxy_subject_rules, school_year_boundaries/school_day, rit_calculator, 99_level_thresholds, and tenure_buckets. Each module reads the other's keys and recreates none. Every value is a named row carrying policy_version; course, component, member, KC, and standard rows carry only policy references, never the numbers. The policy registry holds no curriculum identity rows. Sibling policy namespaces remain owned by their modules.
Alternatives and tradeoffs
- Hardcode the 89.5/90% cut and the other numbers in each consumer
- Put the numbers on course, component, member, KC, or standard rows
- One module creates the whole alpha.policy.* registry (either Curriculum-owns-all or Results-owns-all)
- Split ownership per key by Brainlift home, read across
Rationale and consequences
The Brainlift assigns teaching-delivery policy (XP, spaced repetition, mastery thresholds, placement weights) to Curriculum and outcome/reward policy to Results; a per-key split by that home is the smallest change that kills every hardcoded cut score AND satisfies one-owner-per-alpha-object, without either module re-creating the other's keys.
#curriculum_mastery_gate_test_bank_reference
curriculum mastery gate test bank reference
Unspecified
Decision
A mastery_gate's gating member target_ref may point at one exact Content test_bank version, not only one exact test version. test_spec (the blueprint) and test_bank (the generated set of equivalent forms) are first-class CONTENT kinds; the entity home is Content and Curriculum stores neither. The universal gate-failure protocol's equivalent form resolves to an UNSEEN member of the gate's exact test_bank version (same blueprint = equivalent), and the retry never re-presents a seen form while unseen forms remain. SELECTION IS DETERMINISTIC AND SURFACE-OWNED, not client-chosen: the engine resolves the instrument for any sitting through one surface operation, GET /alpha/curriculum/v1/gates/{gateId}/next-form?studentId=…, which returns the lowest-index form in the bank the student has not yet seen (stable ascending form_index order, audited) after independently revalidating Content-owned release eligibility for both the exact bank version and the selected exact form version. Either ineligible tuple fails closed; the engine never substitutes a latest bank or form version. EXHAUSTION IS A TYPED CONDITION, not a silent re-show: when every form in the bank has been seen, the operation returns curriculum:bank_exhausted (HTTP 409), never a seen form and never HTTP 200 with an empty body; min_forms (>=5 for grade gates) is sized to the retake budget the alpha.policy.gate_retake_cooldown allows, so exhaustion is the rare signal to author more forms, not a routine path. A gate that targets a single frozen form while the referenced spec's min_forms > 1 is a spec violation rejected at write time. This makes issue #72's same_or_equivalent_form contract deliverable end to end — pick instrument, fail, remediate, draw the next unseen form, all on the surface.
Alternatives and tradeoffs
- Keep gates pointing at one frozen Content test
- Store the bank/blueprint inside Curriculum
- Let the client choose the retry form, or silently re-show a seen form once unseen forms run out
- Reference a Content test_bank, draw the next unseen member deterministically on the surface, and type the exhausted case
Rationale and consequences
The gate machinery is meaningless without a bank to draw an equivalent form from, AND without a pinned rule for which form and what happens when forms run out. A surface-owned deterministic next-unseen draw plus a typed exhausted condition is the smallest contract that makes the documented retry real, reproducible, and dead-end-free while keeping Content the home of test instruments.
#curriculum_kc_authoring_contract
curriculum kc authoring contract
Unspecified
Decision
KC authoring is a documented first-class registry path. POST /alpha/curriculum/v1/kcs creates alpha.knowledge_component identity with persistent kc_id, lineage_id, kc_kind, subject_id, and its first immutable alpha.knowledge_component_version in status=draft; it never creates a CASE CFItem. Editing a KC appends a new version and never mutates a prior version in place. Separate governed writes create prerequisites, typed KC relations, standard crosswalks, course approvals and KC-version snapshots, firming events, and review-decision audit records. A KC version becomes firm only through course approval or the first-genuine-response live_learning backstop; ordinary publication and assignment do not firm it. Mastery, component scope_kcs, gate scope, and learner evidence resolve registry kc_id values, while an approved course resolves the exact firm version captured in its immutable snapshot. Rejections identify unknown or retired identity, invalid kind or relation type, cyclic prerequisite, invalid lifecycle transition, missing firm snapshot, low-definition-quality, or unresolved-standard causes with field-level Problems.
Alternatives and tradeoffs
- Leave KC authoring discoverable only via the CASE surface plus seed data
- Allow KC kinds through the generic /components endpoint
- A first-class /kcs route over the governed KC registry
Rationale and consequences
KCs are referenced throughout teaching and Results, so identity, immutable meaning, kind, lifecycle, review provenance, exact approved version, and graph integrity need one canonical authoring contract rather than a CASE encoding or producer-private state that consumers must reverse-engineer.
#curriculum_authoring_referential_integrity
curriculum authoring referential integrity
Unspecified
Decision
Write-time and lifecycle integrity apply at the owning row. course_component.scope_kcs must resolve to active KC registry rows; parent and subtree refs must resolve without cycles. course_component_member.target_ref must resolve to either an active reusable component or an exact Content tuple {content_id, content_version_id, content_kind} that Content reports currently release-eligible for the governed producer surface. Curriculum repeats that exact-version check at launch and fails closed on malformed, unavailable, mismatched, blocked, revoked, unresolved, or otherwise ineligible answers; it never resolves latest or persists copied producer evidence. renderer_ref must resolve to an active registered renderer that accepts that target kind. renderer_kind may remain only as a compatibility alias to the same renderer. passing_criteria_ref, entry_policy_ref, and exit_policy_ref must resolve in alpha.policy.*. Retirement or release-state change that makes an active member target ineligible causes launch to fail until an explicit exact-version repoint succeeds. Unresolvable or incompatible refs return typed Problems with fieldErrors on every write path.
Alternatives and tradeoffs
- Leave content/renderer/policy refs unvalidated (today's behavior)
- Validate asynchronously via a separate integrity-lint endpoint only
- Validate at write time with the same rigor as KC and parent refs
Rationale and consequences
Each reference is validated where it is owned, and active course trees cannot silently retain dead Content, component, KC, renderer, or Policy targets. That removes producer-side repair logic and false-complete reads.
#curriculum_immutable_standards_source_release
curriculum immutable standards source release
Unspecified
Decision
Every standards import creates or reuses an immutable, checksum-addressed standards source release; it never overwrites source evidence in place. The release records Platform3-minted source_release_id, source authority, official source URL, license URL, a governed rights_status with explicit unknown rather than null, required attribution text, retrieval timestamp, raw-artifact checksum and checksum grain, parser name and version, normalized-artifact reference and checksum, and import actor/time. Each imported CASE framework and standard item is pinned to exactly one source release, and material-level rights exceptions are linked as effective-dated rights records rather than copied onto unrelated rows. Readback must reproduce every source statement losslessly under documented normalization rules and return typed mismatch evidence against the named raw and normalized checksums. A changed source, parser, rights decision, or normalized output creates a new release and preserves prior releases for audit and rollback.
Alternatives and tradeoffs
- Overwrite the existing CASE framework when a source or parser changes
- Keep provenance and rights only in an Incept runbook or producer log
- Store one framework-level license string without parser or checksum evidence
Rationale and consequences
CASE preserves standards structure but does not by itself prove which source artifact, parser, rights decision, and normalization produced a row. An immutable release contract makes import legal status, provenance, identity, and lossless readback Platform3 responsibilities instead of compensating Incept documentation.
#curriculum_ls_pin_item_kc_cardinality
curriculum ls pin item kc cardinality
Unspecified
Decision
For coverage counting in a test_spec blueprint, an item counts toward exactly one PRIMARY KC: one KC cell of the table of specifications. A secondary Q-matrix KC tag is allowed for tracing but is never double-counted toward coverage, and an item that bundles multiple KCs into the coverage count is rejected. A separate standards-breadth check resolves the primary KC through alpha.standard_kc_map; it never treats the standard as the KC identity. Grounded in the LS corpus: diagnostic items must isolate one KC, blueprint cells are counted, and no two items in a form may come from the same mapped standard.
Alternatives and tradeoffs
- Count every KC an item touches
- Forbid any item from referencing more than one KC at all
- One primary KC per coverage item, optional non-counting secondary tag
Rationale and consequences
The corpus is explicit that diagnostic coverage comes from breadth (one clean KC per item), not from depth or bundling. One-primary-KC-per-item is the simplest rule that makes 'covered' enforceable. Where the corpus stops short of a single numeric cardinality, this is the simplest defensible reading, documented as such (not deferred).
#curriculum_ls_pin_comparable_forms
curriculum ls pin comparable forms
Unspecified
Decision
Two forms generated from one test_spec are comparable iff they satisfy the SAME blueprint constraints (identical hard per-domain and FRQ counts, the same DOK band, the same difficulty floor / target mastery-probability, the same rubric design) and, for fixed pre-built forms, have zero item overlap. This is a binary constraint-satisfaction check (pass/fail), not probabilistic equating, grounded in the corpus's five-parallel-forms 'every form satisfies the same blueprint constraints' construction. Honest gap, pinned to the simplest option: the corpus gives no numeric overlap ceiling for SAMPLED forms and no test-information-at-the-cut tolerance, so we pin overlap=0 for fixed forms and set a concrete re-open trigger (the first bank that samples forms) to pin the sampled-form ceiling in the Content test_spec spec.
Alternatives and tradeoffs
- Declare forms equivalent by a shared test_family_id string
- Require full IRT score-equating / test-information matching per gate
- Binary blueprint-constraint satisfaction plus zero overlap for fixed forms
Rationale and consequences
The corpus frames comparable forms as same-blueprint-constraints, not as an equating exercise, for the mastery-test use case. A binary constraint check is the simplest rule that makes 'equivalent form' auditable. The two quantities the corpus is silent on are pinned to the simplest defensible defaults and flagged, not hidden.
#curriculum_ls_pin_passing_rule_loss
curriculum ls pin passing rule loss
Unspecified
Decision
A test_spec passing_rule encodes explicit loss assumptions weighing false pass against false fail, set PER KC / per gateway (not one universal cut), defaulting to STRICT (minimize false pass) for prerequisite gates because of downstream dependency risk. The cut is chosen decision-theoretically (a minimax / loss-function choice over a binomial / beta-binomial short test) and audited by DECISION consistency, not score reliability. A member's passing_criteria_ref may only reference the spec's passing_rule; the global cut value (the >=90% bar, operationalized in the corpus as 22 of 24, a two-error budget) stays in alpha.policy.* config (caitd-012). Honest note: the corpus contains NO '89.5%'; that is a platform rounding convention. The corpus value is 90%, defined per KC-domain, and the numeric loss ratio per gate is the architect's to set within this encoding.
Alternatives and tradeoffs
- One global percentage cut (a hardcoded 89.5/90) for every gate
- A pure score-reliability threshold
- Per-KC explicit-loss cut, default strict, minimax, decision-consistency audited
Rationale and consequences
The corpus is most explicit here: cut scores must declare which error is costlier and tune to it, per context. Encoding the loss in the passing_rule (with the number itself in policy config) is the simplest rule faithful to the corpus that also stops consumers restating cutoffs.
#curriculum_api_write_granularity
curriculum api write granularity
Unspecified
Decision
SHIP per-resource writes for KC identities and immutable versions, prerequisite and typed-relation edges, review decisions, course approvals and KC-version snapshots, firming events, standard crosswalks, courses, reusable components, and ordered members, plus named bulk commands for immutable standards-source-release import, CFPackage/Common Cartridge import, and TimeBack source-shaped ingest. POST /alpha/curriculum/v1/standards-frameworks/import accepts source-shaped framework and standard rows plus the required source-release/rights/provenance envelope, then normalizes standards structure into stock CASE rows and pins every row to the immutable release with platform idempotency. File/directory helpers validate each source artifact, skip-and-warn on non-framework inputs, and delegate to that same endpoint. Do not ship a second KC representation, producer-private KC governance sidecars, steward-agent behavior, per-kind component tables, permanent dual writes, or untracked blind parsed-file loaders.
Alternatives and tradeoffs
- Bulk only
- Per-resource only
- Per-resource plus named bulk commands
Rationale and consequences
Authors need precise row writes while migrations and producers need source-shaped batches. Both paths converge on the same four-store ownership model and immutable standards-release contract, so producer skill packs never become alternate storage implementations.
#curriculum_api_read_shape
curriculum api read shape
Unspecified
Decision
SHIP list, detail, and narrow sub-collection reads for standards frameworks and source releases; KC identities, immutable versions, prerequisite neighborhoods, typed relations, review history, firming events, and course-approval snapshots; standard-KC crosswalks; named KC sets; courses; reusable components; resolved course/member trees; gate contract detail; import jobs; and transport previews. A resolved course tree preserves component identity and exposes each member's order, target, renderer, and gate fields without flattening them onto the component. Platform3 reads expose stored steward outputs but no novelty, queue-priority, monitoring, or generation-plan computation.
Alternatives and tradeoffs
- List only
- List plus detail only
- List, detail, and narrow graph/tree sub-collections
Rationale and consequences
Resolved tree and graph reads replace fragile client traversal while preserving the canonical distinction between standards, KCs, reusable components, and ordered members.
#curriculum_api_query_model
curriculum api query model
Unspecified
Decision
SHIP typed filters, stable sort keys, cursor paging for Alpha convenience lists, CASE offset compatibility where inherited from CASE, and updated_since for polling. Reject unsupported parameters with curriculum:unsupported_query_parameter.
Alternatives and tradeoffs
- No query model
- Arbitrary expression filters
- Typed filters plus paging and updated_since
Rationale and consequences
A small typed query vocabulary is easier to document and makes unsupported filters fail loudly.
#curriculum_api_concurrency
curriculum api concurrency
Unspecified
Decision
SHIP per-resource ETag and If-Match on mutable standard identity, KC identity, course, component, member, crosswalk, approval, and rights-record writes. Standards source releases, KC versions, course-approval KC snapshots, firming events, and review-decision audit rows are immutable: changed evidence creates a new row or version rather than a PATCH. Missing validators return 428 curriculum:precondition_required; stale validators return 412 curriculum:precondition_failed. Bulk import and idempotency conflicts return typed 409.
Alternatives and tradeoffs
- No concurrency
- Bulk-only version tokens
- HTTP validators per mutable resource
Rationale and consequences
ETag/If-Match is the smallest standard mechanism that protects shared curriculum editing without locks.
#curriculum_api_idempotency
curriculum api idempotency
Unspecified
Decision
SHIP Idempotency-Key on POST, PATCH, DELETE, source-shaped ingest, remediation insertion, and transport import. Persist retry state in platform.idempotency_key with module=curriculum and surface=alpha.
Alternatives and tradeoffs
- No idempotency
- Natural unique constraints only
- Shared platform idempotency ledger
Rationale and consequences
Retry safety belongs to the platform, not to every migration script or skill pack.
#curriculum_api_auth
curriculum api auth
Unspecified
Decision
SHIP Platform HS256 bearer JWTs with tenantId, role or roles, and curriculum:read, curriculum:write, curriculum:import, and curriculum:admin scopes. DEFER school, class, and student-scoped claims for Curriculum because shared Curriculum rows are tenant-scoped, not roster-scoped.
Alternatives and tradeoffs
- Role-only JWT
- JWT with roster-scoped claims
- Tenant plus role plus Curriculum scopes
Rationale and consequences
Curriculum has tenant-shared map data. Scopes separate operations without pretending Curriculum rows belong to one school or class.
#curriculum_api_eventing
curriculum api eventing
Unspecified
Decision
SHIP polling with updated_since on list endpoints. DEFER webhooks until three approved Curriculum integrations in 90 days show polling blocks authoring, route, gate, or migration workflows.
Alternatives and tradeoffs
- No change observation
- Polling with updated_since
- Webhooks now
Rationale and consequences
Polling removes enough complexity for the first surface. Webhooks would be a second event-delivery primitive without current evidence.
#curriculum_api_error_envelope
curriculum api error envelope
Unspecified
Decision
SHIP typed RFC 7807 Problems with stable type URIs, code values prefixed curriculum:, requestId, traceId, fieldErrors, and rowErrors for bulk ingest/import. Never return HTTP 200 for a Problem.
Alternatives and tradeoffs
- No error envelope
- Plain RFC 7807 without stable codes
- Typed RFC 7807 with curriculum codes
Rationale and consequences
Typed Problems are the smallest contract that lets an LLM or migration app fix data instead of guessing.
#curriculum_api_tenant_routing
curriculum api tenant routing
Unspecified
Decision
SHIP tenant routing from the verified JWT tenantId on flat /alpha/curriculum/v1 paths. Do not put tenant ids in Curriculum URLs or request bodies.
Alternatives and tradeoffs
- Tenant in URL
- Tenant in header
- Tenant in JWT
Rationale and consequences
The JWT already carries tenantId and every CASE row is tenant-scoped. A second tenant input would add mismatch cases without adding clarity.
#curriculum_api_conformance_evidence
curriculum api conformance evidence
Unspecified
Decision
SHIP local and deployed evidence for: four-store ownership-map coverage; zero authoritative KC CFItems and zero stored KC containment-tree guidance after cutover; KC identity and kc_kind resolution; immutable version append behavior; additive lineage_id plus typed split_from, merged_into, and supersedes transitions; typed non-prerequisite relation provenance/confidence; prerequisite DAG acyclicity; draft-to-firm transition only through course approval or a live_learning first-response backstop; immutable course KC-version snapshots; review-decision diff/rationale audit; standard-crosswalk provenance; reusable component reuse across at least two courses with independent member order/renderer/gate behavior; immutable standards-source-release checksums, rights, parser provenance, and lossless readback; source-ingest materialization; Common Cartridge round trip; auth, errors, idempotency, and concurrency; surface QC; integration; and a skill-pack leak check proving Incept consumes live descriptors rather than copied Platform3 enums, schemas, or steward logic. DEFER official 1EdTech certification claims for Alpha.
Alternatives and tradeoffs
- No conformance evidence
- Claim official certification
- Local executable evidence with no official claim
Rationale and consequences
Executable local and deployed evidence catches the failure modes this loop has already seen.
#curriculum_api_privacy_retention
curriculum api privacy retention
Unspecified
Decision
SHIP tenant-scoped rows, audit provenance, store-specific soft-retire semantics, immutable standards source-release history, redaction from Problems/logs/evidence, and no learner PII beyond typed references required for a personalized course. DEFER age-based retention and public erasure APIs until platform privacy policy names required periods.
Alternatives and tradeoffs
- No privacy/retention rule
- Hard DELETE everything
- Tenant scope, redaction, soft-retire, and deferred retention schedule
Rationale and consequences
Curriculum is mostly shared map data. Soft retirement plus redaction is the simplest safe default until platform-wide retention policy is pinned.
#curriculum_alpha_authoring_subject_registry
curriculum alpha authoring subject registry
Unspecified
Decision
One Platform3 Alpha authoring/catalog subject registry is defined by loop/context/authoring-subject-registry.json, referenced by the owning Content and Curriculum data dictionaries, and consumed through generated runtime, SQL, descriptor, documentation, and skill-pack artifacts. No downstream consumer owns a copied enum or alias map. AP CSA and Computer Science labels fold to computer_science; Social Studies folds to social_studies at write/import time. Economics, history, geography, and civics remain course-grain disciplines rather than subject ids. The Results/HMG reporting subset remains its separately governed exact six, and fastmath remains proxy-only rather than an authoring or HMG subject. Cleanup and retirement code refuses to mutate when its generated registry digest does not match the semantic source.
Alternatives and tradeoffs
- Let each module and producer maintain a copied subject list
- Use an open or free-text subject field
- Publish social_studies as live before the owning descriptor and migration land
Rationale and consequences
A single live descriptor eliminates the repeated six-versus-seven-versus-eight conflict. Owner ruling #187 rejected the autonomous broad expansion; owner issue #607 approved only computer_science for AP CSA, and owner issue #687 approved only social_studies while keeping its four disciplines at course grain and the report subset at six. Future subject families remain typed findings until owner-approved; the closed eight-value descriptor keeps every surface explicit without reopening free-text subjects.
#curriculum_api_list_endpoints
curriculum api list endpoints
Unspecified
Decision
SHIP list endpoints for canonical public collections: standards frameworks and source releases; KCs and versions; prerequisite and typed-relation edges; KC review decisions and firming events; course approvals and KC-version snapshots; standard-KC crosswalks; named KC sets; courses; components by kind; component members; resolved gates; and import jobs. Derived per-kind and tree views may aid discovery but never become a second storage authority. No endpoint implements an Incept review queue, novelty scorer, mint-rate monitor, application-coverage planner, cluster plan, or experiment registry.
Alternatives and tradeoffs
- Write-and-remember
- One list per producer-local object
- Lists that flatten member-owned fields onto reusable components
Rationale and consequences
List endpoints are required for authoring and discovery, but their shapes must preserve the owning store and cannot recreate the old single-CASE-graph model in response payloads.
#caitd-020-governed-test-serve
curriculum governed test identity draft gate launch
Unspecified
Decision
Curriculum preserves the ordinary mastery-gate launch path and adds one Content-authorized test fallback. Published gates always try Content's exact-version release eligibility first; only an authoritative ineligible answer may enter test-serve. A draft gate may enter test-serve only when it already pins a complete exact Content test_bank version. Curriculum passes the exact student person_id to Content, validates at most 50 practice-eligible exact bank members, chooses the same lowest unseen form, and returns it only after Content atomically revalidates the exact bank/form pair and records an immutable authorization receipt. No header or local allowlist can enable this lane. Denied real identities keep the existing draft-gate 422; timeouts, network errors, malformed responses, and target mismatches fail closed.
Alternatives and tradeoffs
- Replace ordinary release eligibility with test-serve for the demo tenant
- Trust a preview query/header without Content authorization
- Cache Content test-serve eligibility or authorization in Curriculum
Rationale and consequences
This extends the existing exact-version bank selection without creating a second gate algorithm or weakening real-student serving. Content remains the material/governance authority and Results remains the seen-form, score, and remediation-state authority.