Curriculum · Alpha · Data dictionary · v2.1.0

TimeBack Curriculum Alpha Data Dictionary

One field-level authority for immutable standards releases, governed KCs, reusable course structure, ordered members, and exact-version Content references. A cold builder should need no Incept documentation or source code.

Target: four-store authorityDeployed compatibility: CASE-backed read onlyIssue #777Source be42157dbbd7

Contract state

Target authority is not a deployment claim

Target: The four-store model in this dictionary is the accepted authority after verified cutover.

Compatibility: The deployed Curriculum API currently reads CASE-backed components. Component GET fails closed rather than selecting one edge when storage has multiple active parents. POST /components creates initial placement; an isolated PATCH /components/{componentId} with parent_component_id + position atomically replaces the component's one active containment parent while preserving component/edge identity and every non-containment reference. Generic association POST and TimeBack ingest cannot add a second active parent; ingest also cannot replace a parent. A one-object lesson may carry one direct target. A producer-authoritative no-KC lesson is represented only by kind=lesson with an exact eligible target_ref, a compatible registered renderer_ref, scope_kc_refs=[], and kc_scope_decision=authoritative_no_kc; detail, tree, and next-lesson repeat that exact decision, and the lesson contributes no KC-version snapshot or mastery evidence. Missing or unresolved KC scope still fails closed. For a many-item lesson, create one stable ordered child practice/review component per Content item with parent_component_id=lesson, matching KC scope, target_ref, and renderer_ref; discover all targets through the lesson tree or GET /components?parentId=lesson. This is a one-for-one compatibility adapter to future member rows, not authority to add another member store. curriculum.component_sample remains only so deployed conformance probes can verify that compatibility read.

Cutover: The #707 KC registry cutover is complete. This pending-cutover rule applies only to still-unshipped course/component/member targets: do not advertise them as deployed until migrations, reconciliation, parity, and live readback pass.

Dual authority: Forbidden. Legacy CASE KC rows and source crosswalks are immutable migration evidence only; active KC identity, versions, graph, standards links, scope, remediation, reads, and lifecycle operations use the registry-native stores.

Four stores, one owner per concept

Curriculum owns student-independent scope, sequence, KC identity, reusable course structure, and ordered teaching delivery. It references but never stores Content bodies, Results state, Events, People and Orgs facts, Policy numbers, or Incept steward behavior.

curriculum_shared_map_boundary

explicitly outside Curriculum storage

Steward agent, novelty reconciliation, review queues, monitoring, generation plans, clusters

Incept

curriculum_kc_storage_governance

Alias, subject, renderer, and Policy registries

Alias/ownership map

Every public name resolves to exactly one authoritative store, derived view, compatibility read, or surface command. Aliases never move/copy data, and a view is never a second authority.

loop/curriculum/artifacts/alpha/architecture/site/alias-map.json

curriculum_alias_map_contract

Authoring subjects

Generate owning Curriculum artifacts from the machine-readable registry, but consume the deployed descriptor at runtime; do not copy this list into clients or confuse it with the six-subject Results/HMG reporting subset. Economics, history, geography, and civics remain course-grain disciplines rather than subject ids.

Deployed: mathreadinglanguagesciencecomputer_sciencevocabularywritingsocial_studies

curriculum_alpha_authoring_subject_registry

Renderer capabilities

Only course_component_member selects renderer_ref. Writes reject missing, unknown, inactive, or content-kind-incompatible capabilities; renderer_kind is compatibility only when it resolves to the same registry row.

renderer_refaccepted_content_kindsintegration_endpointintegration_protocolplayground_urlimplementation_pointerlifecycle_stateverified_capability_evidence

curriculum_renderer_registry_contract

Versioned Policy references

Every value is a versioned alpha.policy row. Curriculum identity rows store references only, never thresholds, weights, rewards, calendars, cut scores, or retake numbers.

Curriculum creates: alpha.policy.mastery_cutoff · alpha.policy.placement_weights · alpha.policy.xp_award_formula · alpha.policy.spaced_repetition_model · alpha.policy.gate_retake_cooldown

Results creates: alpha.policy.reward_rules · alpha.policy.stuck_attempt_threshold · alpha.policy.growth_x_target · alpha.policy.proxy_subject_rules · alpha.policy.school_year_boundaries · alpha.policy.school_day · alpha.policy.rit_calculator · alpha.policy.99_level_thresholds · alpha.policy.tenure_buckets

curriculum_teaching_engine_policy_config

Standards acquisition, rights, admission, and counsel

Only green may proceed. CASE transport is never content-use permission.

source Selection Owner: The producer selects a source and supplies evidence under its runbook; Platform3 does not infer a source or silently broaden scope.

acquisition Rule: Acquisition records what was retrieved and from where. Acquisition alone never admits rows for generation, publication, or customer use.

transport Rights Rule: Successful CASE transport proves syntax/interchange only. It is not permission to copy, transform, generate from, publish, or redistribute source content.

scope Rule: Every admission names an exact authority + subject/grade/jurisdiction/version row scope. A partial source must be named as a partial scope; it may not use the unqualified authority/framework name.

rights Precedence: The most-specific material-grain rights record effective at evaluation time wins. A missing, expired, conflicting, or unknown record resolves to unknown and cannot inherit a broader permitted record.

counsel Rule: Counsel owns disputed ownership, contract interpretation, permissions, and risk exceptions. A technical operator cannot turn yellow/red into green; the counsel decision is stored as a new effective-dated rights record with evidence_ref and counsel_decision_ref.

fail Closed Rule: Only green may enter an active standards/KC/course workflow. Yellow and red return typed Problems and never degrade to warnings.

Deterministic outcomes

GREEN: All in-scope rows are bound to one immutable release; readback is lossless; effective material rights are permitted or public_domain; required attribution exists; the declared row scope is non-empty and exact. Generation/adoption may proceed for that scope only.

YELLOW: Evidence, scope, rights, attribution, or readback is incomplete/disputed but not conclusively prohibited. Quarantine: no generation, publication, or customer use until a new decision resolves every finding.

RED: Any effective material is prohibited, the source/scope is misrepresented, lossless evidence is irrecoverable, or counsel rejects use. Reject the release for adoption; preserve audit evidence.

Policy references

  • Curriculum — Durable release, rights, binding, readback, and mismatch semantics
  • Incept — Producer acquisition procedure and source selection only
  • 1EdTech — CASE transport/content shape only; no content-use grant

Author · place · route · gate · remediate

Use case

author

API: Per-resource KC, version, relation, review, course, component, member, approval, firming, crosswalk, and import writes under /alpha/curriculum/v1

Author into the owning table; semantic KC edits append immutable versions, optional source_kc_id remains provenance only, and every graph/scope/remediation reference uses an exact active tenant-owned registry kc_id validated before any write. A direct POST /components with a caller-supplied component_id that matches a soft-retired CASE component reactivates that same identity only as draft: preserve its original created_at, advance its CASE version, clear retired_at, and reactivate a supplied same-id retired containment edge transactionally. If a surviving active child is reattached to the recreated parent through a new edge id, the active exact child/parent pair supersedes retired same-pair history for current tree, routing, and publication validation while the tombstone remains immutable audit history. An isolated component PATCH with parent_component_id + position locks and replaces exactly one active containment edge, preserves stable component/edge identity plus every other incident reference, and rejects zero/multiple parents, cycles, cross-tenant parents, or an occupied target position without partial writes; generic association POST refuses a second active parent, and TimeBack ingest may replay initial placement but cannot replace or add a parent. It never returns 201 without durable active readback. KC clustering remains producer/learning-scientist-owned and non-empty multi-KC lesson scope is preserved. The only sanctioned empty lesson scope is direct kind=lesson authoring with an exact eligible target_ref, compatible renderer_ref, scope_kc_refs=[], and kc_scope_decision=authoritative_no_kc; Curriculum echoes that decision and never synthesizes a KC.

curriculum:validation_failedcurriculum:reference_unresolvedcurriculum:kc_semantics_conflictcurriculum:precondition_requiredcurriculum:precondition_failed

curriculum_kc_authoring_contract

Use case

place

API: GET /alpha/curriculum/v1/tracks/{trackId}/placement-candidates?studentId=…&subjectId=…

Resolve the track entry_policy_ref. PowerPath starts at the lowest or screener-selected likely-mastered grade, tests upward, and returns the first grade below the Policy mastery threshold. MAP/RIT may choose only the starting point, never final placement.

curriculum:not_foundcurriculum:reference_unresolved

curriculum_five_use_cases

Use case

route

API: GET /alpha/curriculum/v1/courses/{courseId}/next-lesson?studentId=…&subjectId=…

Choose the first ordered lesson/review/practice/mastery-gate frontier before applying publication filters; require the course root, that candidate, and every containment ancestor to be published; require the exact current graph to have an immutable approval with a complete firm, active KC-version snapshot; and revalidate the candidate's exact Content version through Content-owned release eligibility. Draft, retired, changed, unapproved, incomplete, unavailable, malformed, mismatched, or ineligible state fails closed and never exposes a later node. Read student state from Results; Curriculum never stores or recomputes the student's realized path.

curriculum:not_foundcurriculum:lesson_structure_missingcurriculum:course_not_routablecurriculum:content_not_eligiblecurriculum:content_unavailablecurriculum:content_version_mismatchcurriculum:invalid_content_bank_member_contract

curriculum_five_use_cases

Source-shaped ingest and Common Cartridge

new-course-ingest

API: POST Content /tenants/{tenantId}/alpha/content/imports/qti-package; for one primary object GET then PATCH /alpha/curriculum/v1/components/{lessonId}; for multiple items POST /alpha/curriculum/v1/components once per targeted child practice/review component; GET /alpha/curriculum/v1/components/{lessonId}/tree or GET /alpha/curriculum/v1/components?parentId={lessonId}

Provision a new course in owner order: import/admit its standards framework and use the returned framework identity in course standards_framework_refs; have the owning author create each missing governed registry KC with POST /alpha/curriculum/v1/kcs using stored_as=alpha.knowledge_component, definition, and optional provenance-only source_kc_id; retry only with the original Idempotency-Key or exact canonical kc_id, because source_kc_id never resolves an identity; capture and verify the returned canonical kc_id through GET /alpha/curriculum/v1/kcs/{kcId}; use only that exact UUID in prerequisite, scope, remediation, standards-map, and Content-attribution references. Write prerequisite ordering through POST /alpha/curriculum/v1/kc-prerequisites; write other typed graph semantics to alpha.kc_relation and standards links to alpha.standard_kc_map; never create KC containment. Import Content, then route each returned exact Content tuple {content_id, content_version_id, content_kind} from Curriculum after Content-owned release-eligibility succeeds. stored_as is a governed-authority assertion, not a physical-store selector. Migrated CASE/source ids are immutable evidence, never active read/retire aliases or UUID reference values. An ordinary scoped lesson preserves the producer-authored non-empty KC cluster and may carry many scope_kc_refs. When the producer explicitly decides that one content-bearing lesson has no authored KC attribution, POST /alpha/curriculum/v1/components sanctions only kind=lesson with the exact eligible target_ref, compatible renderer_ref, scope_kc_refs=[], and kc_scope_decision=authoritative_no_kc; Curriculum never substitutes a similarity, course-root, standards, or Content-tag binding. The accepted target cardinality is one course_component_member per ordered Content use. Until member routes are deployed, the CASE component compatibility adapter uses one stable targeted child practice/review component per additional item under the lesson; repeatedly PATCHing the lesson target replaces the prior direct target and is forbidden for many-item attachment. Standards import never mints KCs, Content kc-tags never encode lesson membership, and the frozen Content curriculum_node_id archive is not a current routing index.

Readback: The deployed compatibility BFF reads every targeted child from the lesson tree or parent-filtered component list; the target BFF reads member rows after verified cutover. Content /kc-tags may independently prove weighted item-to-KC attribution. GET Content items?curriculum_node_id=… remains historical archive compatibility only, has no automatic TTL, and receives no new placements.

curriculum_member_content_reference_gate_placement

timeback-source-ingest

API: POST /alpha/curriculum/v1/ingest/timeback-course-refs

Normalize only the courses → course_components → component_resources → resources boundary into course, reusable component, and ordered member rows. Resources pass through Content first, whose handoff must supply content_id, content_version_id, and content_kind; missing, malformed, mismatched, or ineligible exact versions fail closed and Curriculum never infers latest. Every lesson preserves its authored non-empty active kc_id cluster, including multi-KC scope; learning_objectives, standards, standard, and other standards-shaped evidence return curriculum:adapter_rejected and must use POST /alpha/curriculum/v1/standards-frameworks/import; course ingest never writes CASE standards or fabricates KCs; a source quiz is not a gate without explicit member gate fields.

Readback: Assembly writes deterministic identities as draft, verifies the exact intended tree, publishes components bottom-up, and publishes the course root last. Same-run retry upserts the same identities. Successful complete imports materialize readable course/component/member rows and become structurally routable only after root-last publication; launch still requires a matching immutable course approval, a complete firm active KC-version snapshot, and fresh exact Content eligibility. Source-empty or KC-unscoped known anchors return 409 curriculum:lesson_structure_missing; draft, retired, partially published, unapproved, or snapshot-incomplete graphs return 409 curriculum:course_not_routable and never skip forward.

curriculum_source_shaped_ingest_adapter

common-cartridge

API: POST /alpha/curriculum/v1/transports/common-cartridge/import and POST /alpha/curriculum/v1/transports/common-cartridge/preview

Import/export is transport over CASE standards, the course/component/member graph, and exact-version Content references. Produce bundles on demand and never store a cartridge as Curriculum authority.

curriculum_common_cartridge_transport_only

Gate, blueprint, and form rules

powerpath-100-lesson-mastery-v2

A PP100 gate binds one exact Content adaptive bank and this exact versioned policy. Curriculum owns placement and remediation routing but does not select a fixed form, choose a question, store a score, or decide mastery. Results freezes the exact eligible Content pool at attempt start, deterministically selects an unseen question from the current score band, applies the governed score walk, pauses the same attempt for remediation after 30 responses in one session, and resumes with score and history intact. Mastery and spaced-retrieval eligibility occur only at exactly 100; errors can set the score back but can never make mastery mathematically unreachable. At scores 90–99 only Content-calibrated hard questions at standardized-test rigor are eligible, with no downward fallback unless the owning blueprint marks the lesson prerequisite.

curriculum_mastery_gate_test_bank_reference

test-bank-next-form

This fixed-form route remains for non-PP100 gates and immutable historical PP100 v1 attempts. PP100 v2 never calls it: Results resolves the gate's exact pp100_adaptive_shards bank directly through the governed Content pool contract. Ordinary fixed-form learners launch only a published, complete gate whose exact bank and candidate form versions pass Content-owned release eligibility. Published gates always try that ordinary path first, and only an authoritative ineligible answer may enter Content's governed test-serve lane; timeouts, network errors, malformed answers, and target mismatches fail closed. Content's typed invalid-bank graph answers (content.invalid_test_bank_member_contract or content.bank_kc_attribution_incomplete) map immediately to HTTP 409 curriculum:invalid_content_bank_member_contract and can never enter test-serve, be treated as empty membership, or skip to a later form. A targetless draft rejects before Content reads, but a draft gate with a complete exact test_bank target may launch only when the exact student person_id is authorized by Content's demo-only grant plus live People & Orgs test/synthetic reality. Both fixed-form lanes evaluate at most 50 immutable exact members, require practice_eligible relationship and referenced-item scopes, and choose the same lowest-index unseen form. Test-serve additionally requires Content to atomically revalidate the exact bank/form membership and append an immutable authorization receipt before Curriculum returns serve_mode=test plus receipt/grant ids. A denied real identity keeps curriculum:invalid_gate_contract for a draft gate. Empty/oversized membership remains curriculum:invalid_gate_contract, no eligible forms remains curriculum:content_not_eligible, and only an eligible set with zero unseen forms is curriculum:bank_exhausted. Curriculum never substitutes latest, accepts a preview flag, or caches the test authorization.

curriculum_mastery_gate_test_bank_reference · curriculum_governed_test_identity_draft_gate_launch

primary-kc-coverage

Each blueprint item counts toward exactly one primary KC; secondary Q-matrix tags do not count twice. Standards breadth resolves the primary KC through alpha.standard_kc_map, never by substituting a standard for KC identity.

curriculum_ls_pin_item_kc_cardinality

comparable-forms-and-passing-loss

Fixed forms are comparable only when every hard blueprint constraint matches and item overlap is zero. passing_rule carries per-KC/gateway loss assumptions and defaults strict for prerequisite gates; numeric cuts stay in alpha.policy and the source corpus says 90%, not 89.5%.

curriculum_ls_pin_comparable_forms · curriculum_ls_pin_passing_rule_loss

Write-time referential integrity

  • Every non-empty scope_kc_refs value and remediation_entries.kc_ref resolves exactly to an active tenant-owned alpha.knowledge_component.kc_id before any write; retired, cross-tenant, missing, CASE, source, or standards ids return a typed failure with no partial persistence. Multi-KC authored clusters are preserved. Registry-native POST /kcs is owner-only, accepts only stored_as=alpha.knowledge_component with definition, optional provenance-only source_kc_id, and no CASE fields; callers use its returned kc_id.
  • An empty lesson scope is accepted only for a direct kind=lesson component with an exact eligible target_ref, a compatible registered renderer_ref, scope_kc_refs=[], and kc_scope_decision=authoritative_no_kc. Detail, tree, and next-lesson echo the decision and exact target; the lesson contributes an empty kc_version_snapshot and no mastery evidence. Omitted/unresolved scope and every inferred similarity, course-root, standards, or Content-tag binding remain forbidden.
  • Parent/component/member references resolve tenant-locally without cycles.
  • POST /components inserts a never-seen caller identity or transactionally reactivates the matching soft-retired CASE identity as draft while preserving created_at and advancing version; a successful 201 is never a normalization-only receipt and must be immediately readable with its new ETag.
  • At authoring and ordinary launch, Content targets resolve through Content-owned release eligibility for the exact {content_id, content_version_id, content_kind}; every unavailable, malformed, mismatched, blocked, revoked, unresolved, or otherwise ineligible answer fails closed, and Curriculum never substitutes latest or stores copied producer evidence. The only launch exception is Content's demo-only governed test-serve lane: Curriculum supplies the exact student person_id, Content independently requires an active grant plus People & Orgs reality=test/synthetic, and no form is returned until Content appends an exact bank/form authorization receipt.
  • renderer_ref resolves to an active capability accepting the target kind.
  • entry_policy_ref, exit_policy_ref, and passing_criteria_ref resolve to versioned alpha.policy rows.
  • Retirement that strands an active member is rejected unless the reference is repointed atomically.

curriculum:reference_unresolved

curriculum_authoring_referential_integrity

Allowed values

rights_status

unknownpermittedrestrictedprohibitedpublic_domain

admission_outcome

greenyellowred

standards_material_grain

releaseframeworkstandardstatementattachment

readback_mismatch_code

nonemissing_source_rowunexpected_source_rowtext_changedidentifier_changedorder_changedrelationship_changedrights_scope_changedraw_checksum_mismatchnormalized_checksum_mismatchparser_version_mismatch

kc_kind

substantivedisciplinaryapplicationintegrative

kc_write_stored_as_assertion

alpha.knowledge_component

kc_version_status

draftfirm

kc_relation_type

applies_toinstantiatesintegratesinterferes_withsplit_frommerged_intosupersedes

kc_source_identity_mapping_kind

preserved_uuiddeterministic_uuid_v5adopted_registry_source_kc_id

kc_case_migration_entity_type

knowledge_componentprerequisitestandard_kc_maplineage

kc_case_migration_disposition

acceptedrejected

kc_case_migration_rejection_reason

source_retiredunresolved_tenantduplicate_source_identitymissing_definitionmissing_subject_idinvalid_kc_kindtarget_identity_collisionunresolved_kc_endpointself_loopcycle_sccnot_kc_standard_pairunresolved_standard_bindingambiguous_standard_binding

kc_review_decision

accept-as-newmap-to-existingeditsplitmergereject/relocatedefer

firming_reason

course_approvalfirst_genuine_response

standard_kc_relationship

exactbroadernarrowersupports

course_role

mainhole_fillingremediationcatalog

component_kind

trackgradecourseunitmodulechaptersectiontopiclessonpracticereviewquiztestmastery_gateremediation_pocketplaceholder

kc_scope_decision

authoritative_no_kc

target_kind

course_componentcontent_version

component_target_repoint_operation

repointattach

publication_status

draftpublishedretired

gate_remediation_reason

never_learnedforgotcareless

alpha.subject

mathreadinglanguagesciencecomputer_sciencevocabularywritingsocial_studies

Tables and fields

25 tables/views and 280 explicit fields. Each Alpha rule traces to architecture; CASE bodies trace to the upstream spec.

authoritative_target_pending_verified_cutover

alpha.standards_source_release

Immutable checksum-addressed evidence for one retrieved source scope.

curriculum_immutable_standards_source_release

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
source_release_idUUID
required
Platform3-minted immutable release identity derived from the canonical source scope, checksums, parser, and release evidence.
Constraint: Primary key with tenant_id; never derived solely from a mutable publisher URL. An exact replay reuses this id without writing; changed evidence mints a new id.
8a17f30a-2a39-4a4c-b44e-643c3342756d
source_systemTEXT
required
Stable importing/source-system namespace used with source_framework_id to serialize one release lineage.incept
source_framework_idTEXT
required
Publisher/source identity shared by every immutable release in one framework lineage.TX-TEKS-MATH-G3
source_authorityTEXT
required
Publisher or authority responsible for the source.Texas Education Agency
official_source_urlURI
required
Official retrieval origin.https://tea.texas.gov/academics/curriculum-standards/teks
declared_scopeJSONB
required
Exact jurisdiction, subject, grade, edition, and row-membership scope admitted by this release.
Constraint: Canonical object requires jurisdiction, subject, non-empty grades, edition, rowFilter, expectedRowCount, sourceRowKeysSha256, isPartial, and officialFrameworkName. expectedRowCount and the sorted source-row-key hash cover every binding, including the framework binding. Both must match exactly; partial scopes require an honest partial_display_name distinct from officialFrameworkName.
{"jurisdiction":"TX","subject":"math","grades":[3],"edition":"2024","rowFilter":"strand=3.4","expectedRowCount":130,"sourceRowKeysSha256":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","isPartial":true,"officialFrameworkName":"Texas Essential Knowledge and Skills for Mathematics"}
partial_display_nameTEXT
nullable
Honest customer-visible name for a partial source.
Invalid when: Null when declared_scope is narrower than the official source, or equal to the unqualified authority/framework name for a partial scope.
Texas Math Grade 3 — Strand 3.4 only
license_urlURI
nullable
Publisher license or terms location; null never means permission.https://tea.texas.gov/terms
release_rights_statusTEXT enum
required
enum: rights_status
Release-level default rights classification.
Constraint: unknown is explicit; material overrides are resolved separately.
unknown
required_attributionTEXT
nullable
Exact attribution required when use is permitted.Source: Texas Education Agency
retrieved_atTIMESTAMPTZ
required
Timestamp of raw retrieval.2026-07-16T12:00:00Z
raw_artifact_sha256TEXT
required
Lowercase SHA-256 of the immutable raw artifact at raw_checksum_grain.
Constraint: Exactly 64 lowercase hex characters.
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
raw_checksum_grainTEXT
required
What exact bytes the raw checksum covers.downloaded_zip_bytes
parser_nameTEXT
required
Named parser used for normalization.tea-teks-json
parser_versionTEXT
required
Immutable parser version.2.1.0
normalized_artifact_refURI or opaque ref
required
Immutable normalized artifact location/reference.artifact://standards/8a17f30a/normalized.json
normalized_artifact_sha256TEXT
required
SHA-256 of normalized bytes under normalization_profile.
Constraint: Exactly 64 lowercase hex characters.
abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789
normalization_profileTEXT
required
Versioned, lossless normalization rules used for readback.case-lossless-v1
source_policy_refURI
required
Versioned source-selection/admission policy evidence used for this release.https://github.com/andymontgomery-byte/incept-skill-pack/blob/main/docs/standards-source-acquisition.md#v1
imported_byTEXT
required
Authenticated actor/application reference.app:incept
imported_atTIMESTAMPTZ
required
Server-assigned immutable insert time.2026-07-16T12:01:00Z
supersedes_source_release_idUUID
nullable
Immediately prior immutable release in this source_system + source_framework_id lineage.
Relationship: Self-FK alpha.standards_source_release within tenant; null only for the first known release or an exact historical replay response.
c30e0884-40a8-42e0-a969-c6fe8bdb1767
is_frozenBOOLEAN
required
Storage guard proving this release and its bound CASE rows are append-only.
Constraint: Always true. UPDATE and DELETE fail; changed reimport inserts a new superseding release.
true
authoritative_target_pending_verified_cutover

alpha.standards_source_row_binding

Lossless, one-release binding from each admitted source row/material to its CASE row.

curriculum_immutable_standards_source_release

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
binding_idUUID
required
Immutable binding identity.
Constraint: Primary key with tenant_id.
f1ad17e0-2508-4e7a-95df-93ffb5aa3399
source_release_idUUID
required
Owning immutable release.
Relationship: Many-to-one alpha.standards_source_release.
8a17f30a-2a39-4a4c-b44e-643c3342756d
source_row_keyTEXT
required
Stable locator inside the raw artifact.
Constraint: Unique with tenant_id + source_release_id.
sheet=Grade3,row=142
material_grainTEXT enum
required
enum: standards_material_grain
Rights/readback evaluation grain.standard
source_locatorJSONB
required
Lossless file/page/sheet/row/path locator.{"file":"math-g3.xlsx","sheet":"Grade3","row":142}
source_identifierTEXT
required
Publisher identifier exactly as supplied.3.4A
case_resource_typeTEXT
required
Bound CASE type.
Constraint: cf_document, cf_item, or cf_association.
cf_item
case_resource_idUUID
required
Tenant-scoped CASE row id.
Relationship: Polymorphic FK validated against case_resource_type.
657b306e-4bd8-4938-bf1f-0951334e6a67
raw_statementTEXT
required
Exact source statement before normalization.Represent multiplication facts by using arrays.
normalized_statementTEXT
required
Normalized CASE statement under normalization_profile.Represent multiplication facts by using arrays.
ordinal_pathINTEGER[]
required
Source order path preserved for lossless replay.[3,4,1]
binding_sha256TEXT
required
Hash over release id, locator, identifiers, statements, order, and CASE target.
Constraint: 64 lowercase hex characters.
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
created_atTIMESTAMPTZ
required
Immutable insert time.2026-07-16T12:01:00Z
authoritative_target_pending_verified_cutover

alpha.standards_rights_record

Effective-dated material-level permission/admission decision with counsel provenance.

curriculum_immutable_standards_source_release

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
rights_record_idUUID
required
Append-only rights decision identity.
Constraint: Primary key with tenant_id.
08d93f35-c236-4c26-a147-d86e04007cf5
source_release_idUUID
required
Release whose material is evaluated.
Relationship: Many-to-one alpha.standards_source_release.
8a17f30a-2a39-4a4c-b44e-643c3342756d
material_grainTEXT enum
required
enum: standards_material_grain
Specificity of this decision.statement
material_refTEXT
required
Release id or source_row_key at the declared grain.
Constraint: Must resolve inside source_release_id.
sheet=Grade3,row=142
rights_statusTEXT enum
required
enum: rights_status
Permission classification; unknown is explicit and fail-closed.permitted
admission_outcomeTEXT enum
required
enum: admission_outcome
Deterministic operational result.
Constraint: Only green permits adoption/generation.
green
permission_basisTEXT
required
Plain-language legal/contract/public-domain basis.Official public standards with attribution permitted by publisher terms.
allowed_usesTEXT[]
required
Explicit permitted uses; absence never implies use.["store","display","generate"]
required_attributionTEXT
nullable
Material-specific attribution overriding release default.Source: Texas Education Agency
effective_fromTIMESTAMPTZ
required
Inclusive decision start.2026-07-16T12:00:00Z
effective_toTIMESTAMPTZ
nullable
Exclusive decision end; null is open-ended.
Constraint: Must be later than effective_from; overlapping records at the same grain are rejected.
null
source_policy_refURI
required
Versioned admission policy applied.https://platform3-andymontgomery-9773s-projects.vercel.app/curriculum/alpha/architecture/#caitd-019-standards-source-release
evidence_refURI or opaque ref
required
Immutable terms/license/evidence reference.artifact://rights/tea-terms-2026-07-16.pdf
decided_by_roleTEXT
required
Role accountable for the decision.
Constraint: operator or counsel; operator cannot grant an exception.
counsel
counsel_decision_refURI or opaque ref
nullable
Required for disputed rights, contracts, or risk exceptions.
Invalid when: Null when permission_basis depends on counsel interpretation or an exception.
legal://decision/2026-184
created_atTIMESTAMPTZ
required
Immutable decision time.2026-07-16T12:10:00Z
authoritative_target_pending_verified_cutover

alpha.standards_readback_evidence

Immutable comparison proving release-bound CASE rows reproduce the source losslessly.

curriculum_immutable_standards_source_release

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
evidence_idUUID
required
Immutable check identity.
Constraint: Primary key with tenant_id.
25f4d781-aa71-4605-9288-53e9a6b8cf2f
source_release_idUUID
required
Checked release.
Relationship: Many-to-one alpha.standards_source_release.
8a17f30a-2a39-4a4c-b44e-643c3342756d
binding_idUUID
nullable
Specific row binding; null only for release aggregate evidence.
Relationship: Nullable FK alpha.standards_source_row_binding.
f1ad17e0-2508-4e7a-95df-93ffb5aa3399
raw_sha256TEXT
required
Recomputed raw bytes hash.0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
normalized_sha256TEXT
required
Recomputed normalized bytes hash.abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789
readback_sha256TEXT
required
Hash of canonical source-shaped readback.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
mismatch_codeTEXT enum
required
enum: readback_mismatch_code
Typed result; none is the only passing value.none
mismatch_detailJSONB
nullable
Expected/actual locator and bounded redacted diff for failures.
Constraint: Required when mismatch_code != none; no secrets or unrestricted source dumps.
null
checked_atTIMESTAMPTZ
required
Check execution time.2026-07-16T12:20:00Z
checker_versionTEXT
required
Deterministic readback checker version.standards-readback-v1
authoritative

case.cf_document

Verbatim CASE framework identity and body; Curriculum adds no alternate standards text store.

curriculum_kc_registry_standard_separation

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
idUUID
required
Tenant-scoped internal row identity.
Constraint: Primary key with tenant_id.
91a39ab0-82b7-45f4-98fc-a1f6405a93f9
identifierTEXT
required
Release-scoped CASE identifier; publisher identity remains in the source release and row binding.
Constraint: Derived with source_release_id, so a changed reimport cannot collide with or overwrite the prior framework.
91a39ab0-82b7-45f4-98fc-a1f6405a93f9
source_release_idUUID
required
Immutable standards release owning this framework row.
Relationship: FK alpha.standards_source_release within the tenant mapping.
8a17f30a-2a39-4a4c-b44e-643c3342756d
supersedes_source_release_idUUID
nullable
Prior release named by the owning release; exposed beside the CASE body for historical readback.
Relationship: FK alpha.standards_source_release; null on the first release.
c30e0884-40a8-42e0-a969-c6fe8bdb1767
is_frozenBOOLEAN
required
Prevents UPDATE or DELETE of a release-bound framework.
Constraint: True for standards frameworks; changed packages must use a new release-scoped identifier.
true
bodyJSONB
required
Lossless CASE CFDocument JSON.{"title":"Texas Math Grade 3","creator":"TEA"}
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; a new source release does not mutate the prior body.null
authoritative

case.cf_item

Verbatim CASE standard statement. KCs and course components must never be stored here after cutover.

curriculum_kc_registry_standard_separation

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
idUUID
required
Tenant-scoped internal standard row identity.
Constraint: Primary key with tenant_id.
657b306e-4bd8-4938-bf1f-0951334e6a67
identifierTEXT
required
Release-scoped CASE standard identifier; publisher code and source identifier remain losslessly bound as source evidence.
Constraint: Derived with source_release_id, so the same publisher code in a changed release creates a new row.
657b306e-4bd8-4938-bf1f-0951334e6a67
cf_document_idUUID
required
Owning release-specific CASE framework row.
Relationship: Many-to-one case.cf_document within tenant.
91a39ab0-82b7-45f4-98fc-a1f6405a93f9
source_release_idUUID
required
Immutable release shared with the owning CASE framework and exact source-row binding.
Relationship: FK alpha.standards_source_release within the tenant mapping.
8a17f30a-2a39-4a4c-b44e-643c3342756d
is_frozenBOOLEAN
required
Prevents UPDATE or DELETE of the historical standard row.
Constraint: True for imported standards; removal from a later release does not mutate or retire this historical row.
true
bodyJSONB
required
Lossless CASE CFItem JSON containing the publisher statement.{"fullStatement":"Represent multiplication facts by using arrays."}
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp.null
authoritative

case.cf_association

Within-framework containment and standard-to-standard alignment only.

curriculum_kc_registry_standard_separation

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
idUUID
required
Tenant-scoped association identity.
Constraint: Primary key with tenant_id.
e490c62a-a421-452e-b947-ef683875ec0b
association_typeTEXT
required
CASE relationship for standards only.
Constraint: May express standards containment/alignment; must not represent KC prerequisites, KC lineage, KC-standard maps, course structure, or members.
isChildOf
origin_node_idUUID
required
Origin CASE standard/framework row.
Relationship: Typed CASE endpoint.
657b306e-4bd8-4938-bf1f-0951334e6a67
destination_node_idUUID
required
Destination CASE standard/framework row.
Relationship: Typed CASE endpoint.
91a39ab0-82b7-45f4-98fc-a1f6405a93f9
bodyJSONB
required
Lossless CASE CFAssociation JSON.{"associationType":"isChildOf"}
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp.null
authoritative

alpha.knowledge_component

Persistent, course-independent masterable identity; meaning lives in immutable versions.

curriculum_kc_registry_standard_separation · curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
updated_atTIMESTAMPTZ
required
Server-assigned last mutable-row update time.
Constraint: Changes only through a successful If-Match write.
2026-07-16T12:00:00Z
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; null means active.
Constraint: Ordinary reads and reference validation require retired_at IS NULL.
null
kc_idUUID
required
Persistent KC identity.
Constraint: Primary key with tenant_id; never a CASE, lesson, standard, or course id.
49371a30-9796-4bbd-a523-bd38a876e415
source_kc_idTEXT
nullable
Optional producer semantic identity retained as creation provenance and migration evidence.
Constraint: Unique with tenant_id among active rows when present; 1–512 characters (at most 2048 UTF-8 bytes). Never accepted as an identity resolver by create, read, version, retire, graph, scope, remediation, or Content-attribution operations; callers capture and use the returned canonical kc_id UUID. A retry uses the original Idempotency-Key or exact canonical kc_id, never source_kc_id.
kc:math:3:oa:ccss.math.content.3.oa.1
lineage_idUUID
required
Stable family grouping across splits/merges/supersession.
Constraint: Does not replace typed lineage edges.
a0f70fc9-3a25-4537-a0ed-a05078774032
kc_kindTEXT enum
required for authored KCs; nullable only for #707 legacy adoption
enum: kc_kind
Governed performance kind.
Constraint: NULL means only unclassified legacy adoption pending #898; it requires publication_status=draft and is never publishable or accepted from an authoring API.
substantive
subject_idTEXT
required
Live Platform authoring subject-registry value.
Constraint: Resolve from the owning descriptor; current deployed values are documented but consumers must not copy the enum.
math
current_version_idUUID
required for governed KCs; nullable only for the retained unclassified #707 legacy row
Latest appended semantic version, draft or firm.
Relationship: DEFERRABLE INITIALLY DEFERRED composite FK (tenant_id, kc_id, current_version_id) to alpha.knowledge_component_version (tenant_id, kc_id, version_id), so a KC cannot select another KC's version and the identity plus first immutable version can be created atomically.
7c3f9e18-d244-41b2-9727-79fb4c97f11a
publication_statusTEXT enum
required
enum: publication_status
Identity discovery lifecycle, separate from version firming.draft
authoritative

alpha.knowledge_component_version

Immutable authored KC meaning and reviewable definition quality.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
version_idUUID
required
Immutable version identity.
Constraint: Primary key with tenant_id; unique with tenant_id + kc_id as the composite current-version FK target.
7c3f9e18-d244-41b2-9727-79fb4c97f11a
kc_idUUID
required
Persistent KC identity.
Relationship: DEFERRABLE INITIALLY DEFERRED many-to-one FK alpha.knowledge_component; paired with knowledge_component.current_version_id for atomic first-version creation.
49371a30-9796-4bbd-a523-bd38a876e415
version_numberINTEGER
required
Monotonic per-KC version number.
Constraint: >=1; unique with tenant_id + kc_id.
1
statusTEXT enum
required
enum: kc_version_status
Draft/firm semantic lifecycle.
Constraint: Only a firming event may change draft to firm; all other fields remain immutable.
draft
definitionTEXT
required
Verb-first observable performance definition, independent of any course.Represent multiplication facts using equal-size arrays.
positive_examplesJSONB
required
Examples that satisfy the boundary.["Build 4 rows of 6 and state 4 × 6 = 24."]
negative_examplesJSONB
required
Near misses/non-examples.["Count objects without representing equal groups."]
boundary_statementTEXT
nullable
What is deliberately excluded from this KC.Does not require interpreting division remainders.
misconceptionsJSONB
nullable
Known course-independent misconceptions.["Swaps number of groups and group size without preserving the model."]
instructional_classificationJSONB
nullable
Course-independent instructional metadata; never pace/order/cluster fields.{"knowledgeType":"procedure"}
provenanceJSONB
required
Sources and authoring chain for the definition.{"source":"review:math-owner-42"}
confidenceNUMERIC(4,3)
required
Confidence in [0,1].
Constraint: 0 <= confidence <= 1.
0.94
author_refTEXT
required
Authenticated human/application author.person:math-owner
created_atTIMESTAMPTZ
required
Immutable authored time.2026-07-16T13:00:00Z
immutable_migration_evidence

alpha.kc_case_migration_snapshot

Immutable named source snapshot and reconciliation proof for the one-time #707 cutover.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
snapshot_idUUID
required
Deterministic identity for the captured source hash.
Constraint: Primary key.
114bc4fb-6ac4-5be8-b93e-e3e96687d245
migration_keyTEXT
required
Single idempotency key for this operator migration.
Constraint: Unique; issue-707-case-kc-registry-v1.
issue-707-case-kc-registry-v1
snapshot_nameTEXT
required
Human-readable immutable name including the capture time.
Constraint: Unique.
issue-707-case-kc-registry-v1@2026-07-20T12:00:00.000Z
source_systemTEXT
required
Exact copied authority at cutover.case.cf_item
captured_atTIMESTAMPTZ
required
Database capture time inside the SERIALIZABLE transaction.2026-07-20T12:00:00Z
source_txidBIGINT
required
PostgreSQL transaction id that captured and applied the source snapshot.123456
source_hash_sha256TEXT
required
SHA-256 over the sorted entity/source-key/source-row hashes.
Constraint: 64 lowercase hex.
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
code_commit_shaTEXT
required
Merged, gated repository commit that executed the operator.
Constraint: 40 lowercase hex and reachable from origin/main at apply time.
0123456789abcdef0123456789abcdef01234567
discovered_countsJSONB
required
Per-entity discovered counts from this snapshot.{"knowledge_component":6290,"prerequisite":3565}
accepted_countsJSONB
required
Per-entity accepted counts.{"knowledge_component":6280,"prerequisite":3559}
rejected_countsJSONB
required
Per-entity totals and typed-reason counts.{"knowledge_component":{"total":10,"reasons":{"source_retired":9,"missing_subject_id":1}}}
reference_proofJSONB
required
Source-row retention, scope/evidence resolution, current-version, and DAG proof.{"source_case_rows_retained":true,"prerequisite_dag":true,"new_orphaned_kc_references":0}
statusTEXT
required
Atomic run state.
Constraint: applying or completed; only applying→completed is legal.
completed
completed_atTIMESTAMPTZ
nullable
Commit-bound completion timestamp.2026-07-20T12:03:00Z
immutable_migration_evidence

alpha.kc_source_identity

Immutable #707 migration evidence mapping a captured source id to the canonical registry UUID.

curriculum_kc_registry_standard_separation · curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
source_systemTEXT
required
Source namespace.case.cf_item
source_tenant_refTEXT
required
CASE tenant reference exactly as captured.demo
source_kc_idTEXT
required
CASE KC id exactly as captured, including semantic ids.
Constraint: Unique with tenant_id + source_system.
kc:math:3:equal-groups
kc_idUUID
required
Canonical registry identity.
Relationship: FK alpha.knowledge_component.
49371a30-9796-4bbd-a523-bd38a876e415
mapping_kindTEXT enum
required
enum: kc_source_identity_mapping_kind
Whether the source id was preserved, deterministically remapped, or adopted from an existing operational source_kc_id crosswalk.adopted_registry_source_kc_id
source_snapshot_idUUID
required
Snapshot that admitted this mapping.
Relationship: FK alpha.kc_case_migration_snapshot.
114bc4fb-6ac4-5be8-b93e-e3e96687d245
source_row_sha256TEXT
required
Hash of the exact migration payload for this KC.
Constraint: 64 lowercase hex.
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
immutable_migration_evidence

alpha.kc_case_migration_ledger

One immutable accepted/rejected disposition for every discovered #707 source object.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
snapshot_idUUID
required
Owning named snapshot.
Relationship: FK alpha.kc_case_migration_snapshot.
114bc4fb-6ac4-5be8-b93e-e3e96687d245
entity_typeTEXT enum
required
enum: kc_case_migration_entity_type
Reconciled population.prerequisite
source_tenant_refTEXT
required
Source CASE tenant reference.demo
source_idTEXT
required
Source CFItem/CFAssociation id.0a77d011-0f33-4e2e-8aac-5b59860e1fc1
tenant_idUUID
nullable
Resolved target tenant; null only for unresolved_tenant rejection.11111111-1111-4111-8111-111111111111
source_row_sha256TEXT
required
Hash of source_payload.
Constraint: 64 lowercase hex.
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
source_payloadJSONB
required
Minimal exact migration fields, not an unrestricted copyrighted source dump.{"association_type":"precedes","origin_id":"kc-a","destination_id":"kc-b"}
dispositionTEXT enum
required
enum: kc_case_migration_disposition
Accepted or rejected.rejected
rejection_reasonTEXT enum
nullable
enum: kc_case_migration_rejection_reason
Typed reason; required exactly when disposition=rejected.cycle_scc
target_refsJSONB
required
Canonical ids or deterministic rejection evidence such as SCC membership.{"prerequisite_kc_id":"49371a30-9796-4bbd-a523-bd38a876e415"}
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
authoritative

alpha.kc_prerequisite

Canonical directed acyclic prerequisite graph.

curriculum_kc_registry_standard_separation

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
updated_atTIMESTAMPTZ
required
Server-assigned last mutable-row update time.
Constraint: Changes only through a successful If-Match write.
2026-07-16T12:00:00Z
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; null means active.
Constraint: Ordinary reads and reference validation require retired_at IS NULL.
null
prerequisite_idUUID
required
Edge identity.
Constraint: Primary key with tenant_id.
76d63c09-4090-4f90-939f-fe46d83d7d18
prerequisite_kc_idUUID
required
KC that must come first.
Relationship: FK active alpha.knowledge_component.
92b2eaf0-8ff1-42ea-a352-3ac08b9d5951
dependent_kc_idUUID
required
KC enabled by the prerequisite.
Constraint: Must differ from prerequisite_kc_id; insert/update must preserve DAG acyclicity.
Relationship: FK active alpha.knowledge_component.
49371a30-9796-4bbd-a523-bd38a876e415
provenanceJSONB
required
Why/source for the edge.{"reviewDecisionId":"review-71"}
confidenceNUMERIC(4,3)
required
Edge confidence in [0,1].0.9
authoritative

alpha.kc_relation

Governed non-prerequisite semantic and lineage edges.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
updated_atTIMESTAMPTZ
required
Server-assigned last mutable-row update time.
Constraint: Changes only through a successful If-Match write.
2026-07-16T12:00:00Z
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; null means active.
Constraint: Ordinary reads and reference validation require retired_at IS NULL.
null
relation_idUUID
required
Edge identity.
Constraint: Primary key with tenant_id.
d02a4eb7-44ce-407c-a42d-0b4b5e96b0e2
origin_kc_idUUID
required
Typed relation origin.
Relationship: FK active alpha.knowledge_component.
49371a30-9796-4bbd-a523-bd38a876e415
destination_kc_idUUID
required
Typed relation destination.
Constraint: Must differ from origin_kc_id.
Relationship: FK active alpha.knowledge_component.
b2cbd2ff-42ce-421b-a18c-07c488931ceb
relation_typeTEXT enum
required
enum: kc_relation_type
Semantic or lineage relation.
Constraint: Prerequisite is forbidden here; split/merge/supersede endpoints share lineage_id after transition.
applies_to
provenanceJSONB
required
Source/review evidence.{"reviewDecisionId":"review-72"}
confidenceNUMERIC(4,3)
required
Relation confidence in [0,1].0.86
authoritative

alpha.kc_review_decision

Immutable structured review audit for one KC version.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
review_decision_idUUID
required
Immutable audit identity.
Constraint: Primary key with tenant_id.
217b17b7-8f32-4901-a534-660593822932
version_idUUID
required
Reviewed KC version.
Relationship: FK alpha.knowledge_component_version.
7c3f9e18-d244-41b2-9727-79fb4c97f11a
decisionTEXT enum
required
enum: kc_review_decision
Structured review outcome.edit
edit_diffJSONB
nullable
Captured before/after or JSON Patch when decision edits/splits/merges.
Invalid when: Null for edit, split, or merge without an explicit no-change explanation in rationale.
[{"op":"replace","path":"/definition","value":"Represent multiplication facts using equal-size arrays."}]
rationaleTEXT
required
Review reasoning.Separates array representation from later division interpretation.
provenanceJSONB
required
Inputs/evidence considered.{"sourceRefs":["TEKS:3.4A"]}
actor_refTEXT
required
Authenticated reviewer.person:math-owner
decided_atTIMESTAMPTZ
required
Immutable decision time.2026-07-16T13:15:00Z
authoritative_target_pending_verified_cutover

alpha.course

Stable reusable course identity, role, scope, lifecycle, and approval reference.

curriculum_reusable_component_member_model

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
updated_atTIMESTAMPTZ
required
Server-assigned last mutable-row update time.
Constraint: Changes only through a successful If-Match write.
2026-07-16T12:00:00Z
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; null means active.
Constraint: Ordinary reads and reference validation require retired_at IS NULL.
null
course_idUUID
required
Stable course identity.
Constraint: Primary key with tenant_id.
d7eb3c5c-5051-4860-bc8d-25919be371dd
subject_idTEXT
required
Live authoring subject-registry value.math
grade_scopeJSONB
required
Exact grade/band scope.{"min":3,"max":3}
course_roleTEXT enum
required
enum: course_role
Progress-denominator role.
Constraint: Only main contributes to grade-level expected-XP denominator.
main
nameTEXT
required
Customer-visible course name.Grade 3 Core Math
descriptionTEXT
nullable
Course purpose, not student state.Core grade-three mathematics sequence.
lineage_idUUID
required
Course-family lineage identity.a20513bf-6a4c-424f-b714-32eb3d68f2ed
publication_statusTEXT enum
required
enum: publication_status
Course authoring lifecycle and learner-routing release boundary.
Constraint: Create as draft. Publish only after the complete intended member/component tree is read back, every descendant is published bottom-up, and this course root is published last.
draft
current_approval_idUUID
nullable
Latest approved immutable course snapshot.
Relationship: FK alpha.course_approval.
null
entry_policy_refTEXT
nullable
Versioned alpha.policy placement reference; no copied numbers.alpha.policy.placement_weights:v3
exit_policy_refTEXT
nullable
Versioned alpha.policy exit reference; no copied numbers.alpha.policy.mastery_cutoff:v5
authoritative_target_pending_verified_cutover

alpha.course_component

Reusable student-independent structure/KC scope decision and expected-effort leaf/fallback.

curriculum_reusable_component_member_model

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
updated_atTIMESTAMPTZ
required
Server-assigned last mutable-row update time.
Constraint: Changes only through a successful If-Match write.
2026-07-16T12:00:00Z
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; null means active.
Constraint: Ordinary reads and reference validation require retired_at IS NULL.
null
component_idUUID
required
Stable reusable component identity.
Constraint: Primary key with tenant_id.
5579cb54-e751-4eed-bc8e-95ab00b2f47e
kindTEXT enum
required
enum: component_kind
Governed component kind.lesson
scope_kc_idsUUID[]
required
Active KC identities in scope, or the explicit empty factual scope paired with authoritative_no_kc.
Constraint: Non-empty for ordinary lesson/acquisition components. An empty array is valid only for a kind=lesson exact Content target whose kc_scope_decision is authoritative_no_kc. Producer or learning-scientist clustering grain is preserved, so a scoped lesson may carry many KCs. Never use semantic source ids, standards, similarity, course roots, or lesson ids as KC UUIDs.
Relationship: Each non-empty element FK active alpha.knowledge_component; an authoritative_no_kc empty array creates no KC relationship.
["49371a30-9796-4bbd-a523-bd38a876e415","92b2eaf0-8ff1-42ea-a352-3ac08b9d5951"]
kc_scope_decisionTEXT enum
nullable
enum: kc_scope_decision
Producer-authoritative decision that this exact content-bearing lesson intentionally has no KC attribution.
Constraint: Only authoritative_no_kc is accepted; valid only for kind=lesson with scope_kc_ids=[], an exact immutable Content target, and a compatible renderer. Null means the ordinary non-empty scope rule applies. The decision is factual absence, not permission to infer or synthesize a KC.
authoritative_no_kc
nameTEXT
required
Reusable component name.Arrays for multiplication
descriptionTEXT
nullable
Reusable course-independent description.Initial acquisition of equal-group array representation.
expected_xpNUMERIC
nullable
Student-independent expected effort as a leaf value or container fallback.
Constraint: >=0; units xp_eq_expected_minutes; ignored at aggregate time whenever an active descendant carries expected_xp.
18
lineage_idUUID
required
Reusable component-family lineage.62b5cc63-8cd8-4ce8-af53-07a74b08ef63
publication_statusTEXT enum
required
enum: publication_status
Reusable component lifecycle and learner-routing eligibility state.
Constraint: Course assembly creates draft rows and publishes bottom-up. A draft, retired, or missing status on the selected frontier or any containment ancestor makes the course non-routable.
published
authoritative_target_pending_verified_cutover

alpha.course_component_member

One ordered course-local use of a reusable component or exact Content version.

curriculum_reusable_component_member_model · curriculum_member_content_reference_gate_placement

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
updated_atTIMESTAMPTZ
required
Server-assigned last mutable-row update time.
Constraint: Changes only through a successful If-Match write.
2026-07-16T12:00:00Z
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; null means active.
Constraint: Ordinary reads and reference validation require retired_at IS NULL.
null
member_idUUID
required
Stable ordered-use identity.
Constraint: Primary key with tenant_id.
c11c31b8-aef5-499d-874d-cd8dc435e920
course_idUUID
required
Owning course.
Relationship: FK active alpha.course.
d7eb3c5c-5051-4860-bc8d-25919be371dd
parent_component_idUUID
nullable
Reusable parent container; null means course root.
Relationship: Nullable FK active alpha.course_component.
null
positionINTEGER
required
Stable sibling order.
Constraint: >=0; unique with tenant_id + course_id + parent_component_id among active rows.
4
target_kindTEXT enum
required
enum: target_kind
Discriminator for exactly one target shape.course_component
target_component_idUUID
nullable
Reusable component target when target_kind=course_component.
Relationship: FK active alpha.course_component.
Invalid when: Null for course_component or non-null for content_version.
5579cb54-e751-4eed-bc8e-95ab00b2f47e
content_idUUID
nullable
Content identity when target_kind=content_version.
Relationship: Opaque Content-owned id resolved through the Content API.
Invalid when: Present without content_version_id or for course_component.
null
content_version_idUUID
nullable
Exact immutable Content version; never a floating latest pointer.
Relationship: Content exact-version contract.
Invalid when: Null when target_kind=content_version, or not owned by content_id.
null
content_kindTEXT
nullable
Typed Content kind echoed only for reference validation/routing.
Constraint: Must equal Content exact-version readback; enum remains Content-owned and is not copied into Curriculum's enumSets.
null
renderer_refTEXT
nullable
Active renderer capability accepting content_kind.
Relationship: Platform renderer capability registry.
Invalid when: Missing/unknown/inactive/incompatible for a Content target.
null
gatesBOOLEAN
required
Whether this course-local member enforces a gate.false
passing_criteria_refTEXT
nullable
Versioned alpha.policy reference for a gating member.
Invalid when: Missing when gates=true or present when gates=false; never stores a threshold number.
null
on_failJSONB
nullable
Typed remediation protocol for a gating member.
Constraint: Entries use never_learned→lesson, forgot→review, careless→practice. alpha.policy.pp100_lesson_mastery.v2 requires retry=resume_same_score_walk; every other current gate policy requires retry=same_or_equivalent_form.
null
authoritative_target_pending_verified_cutover

alpha.component_target_repoint_audit

Immutable audit row for every accepted producer self-service repoint/attach of a component's exact Content pin.

curriculum_member_content_reference_gate_placement

FieldType / nullMeaning and constraintsExample
tenant_idTEXT
required
Verified JWT tenant scope of the repointed component.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
demo
repoint_audit_idUUID
required
Immutable audit identity, one per accepted repoint or attach.
Constraint: Primary key.
0d1f5f6a-64f7-4a5e-8f8f-3a2b1c0d9e8f
component_idTEXT
required
Repointed CASE-backed component identity.
Relationship: Deployed compatibility FK to the active case.cf_item component; the accepted target model records the analogous member repoint.
3d5e5eb4-fefe-59e0-ba33-0397203b51f1
operationTEXT enum
required
enum: component_target_repoint_operation
Whether an existing exact pin moved within one Content identity (repoint) or a first pin was attached to an unpinned component (attach).repoint
previous_target_refJSONB
nullable
Exact pin the compare-and-set premise matched; null exactly for attach.
Constraint: repoint requires the full previous exact tuple; attach requires null.
{"module":"content","content_kind":"article","content_id":"41639afc-c1d1-952a-f579-ecb870428386","content_version_id":"6641dfed-3323-4cde-a8b3-19058caf5f77"}
new_target_refJSONB
required
Exact eligible tuple now pinned by the component.
Constraint: Must have passed Content-owned exact-version release eligibility with practice_eligible usage scope inside the accepting request.
{"module":"content","content_kind":"article","content_id":"41639afc-c1d1-952a-f579-ecb870428386","content_version_id":"fa0933dc-d3b2-4956-957d-e2fd0b9892f5"}
actor_refTEXT
required
Authenticated producer/integrator subject that performed the operation.integrator:ap-one
request_idTEXT
nullable
Originating request id for support correlation.req_4f518591-06b4-44e1-bfbe-aab60c598bdd
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
authoritative_target_pending_verified_cutover

alpha.course_approval

Immutable approval boundary pinning one reviewed course graph.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
approval_idUUID
required
Immutable approval identity.
Constraint: Primary key with tenant_id.
3810e51f-e4e3-45c9-9169-593cf6c1a5f7
course_idUUID
required
Approved course.
Relationship: FK alpha.course.
d7eb3c5c-5051-4860-bc8d-25919be371dd
course_graph_sha256TEXT
required
Hash of active course/component/member graph at approval.cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
approved_byTEXT
required
Authenticated approver.person:math-owner
approved_atTIMESTAMPTZ
required
Immutable approval time.2026-07-16T14:00:00Z
provenanceJSONB
required
Approval evidence/review references.{"reviewId":"course-review-12"}
authoritative_target_pending_verified_cutover

alpha.course_approval_kc_snapshot

Immutable exact KC-version set referenced by an approved course.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
approval_idUUID
required
Owning immutable approval.
Relationship: FK alpha.course_approval.
3810e51f-e4e3-45c9-9169-593cf6c1a5f7
kc_idUUID
required
Referenced KC identity.
Relationship: FK alpha.knowledge_component.
49371a30-9796-4bbd-a523-bd38a876e415
version_idUUID
required
Exact version approved, whether already firm or firmed in this transaction.
Relationship: FK alpha.knowledge_component_version for kc_id.
7c3f9e18-d244-41b2-9727-79fb4c97f11a
snapshot_sourceTEXT
required
Where the course graph referenced this KC.course_component:5579cb54-e751-4eed-bc8e-95ab00b2f47e
created_atTIMESTAMPTZ
required
Immutable insert time.2026-07-16T14:00:00Z
authoritative

alpha.kc_firming_event

Immutable proof of the only two legal draft-to-firm transitions.

curriculum_kc_storage_governance

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
firming_event_idUUID
required
Immutable event identity.
Constraint: Primary key with tenant_id.
62c6f3c7-d9ca-4d04-aa1b-a345332694b4
version_idUUID
required
KC version made firm.
Constraint: At most one effective firming event per version.
Relationship: FK alpha.knowledge_component_version.
7c3f9e18-d244-41b2-9727-79fb4c97f11a
firming_reasonTEXT enum
required
enum: firming_reason
Course approval or live-learning safety backstop.course_approval
source_refTEXT
required
approval_id, or governed live_learning first-genuine-response evidence ref.
Constraint: course_approval must resolve to a snapshot containing version_id; first_genuine_response requires an explicit live_learning signal and first response evidence.
approval:3810e51f-e4e3-45c9-9169-593cf6c1a5f7
actor_refTEXT
required
Authenticated/system actor.app:curriculum
firmed_atTIMESTAMPTZ
required
Immutable firming time.2026-07-16T14:00:00Z
authoritative

alpha.standard_kc_map

Release-pinned first-class standard-to-KC mapping with relationship, provenance, and confidence.

curriculum_kc_registry_standard_separation

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
created_atTIMESTAMPTZ
required
Server-assigned creation time.
Constraint: Immutable after insert.
2026-07-16T12:00:00Z
updated_atTIMESTAMPTZ
required
Server-assigned last mutable-row update time.
Constraint: Changes only through a successful If-Match write.
2026-07-16T12:00:00Z
retired_atTIMESTAMPTZ
nullable
Soft-retire timestamp; null means active.
Constraint: Ordinary reads and reference validation require retired_at IS NULL.
null
map_idUUID
required
Crosswalk identity.
Constraint: Unique with tenant_id; the table primary key is tenant_id + standard_id + kc_id.
db37d14f-af1d-4df2-9047-3d18df32ee0d
source_release_idUUID
required
Immutable standards release pin.
Relationship: FK alpha.standards_source_release.
8a17f30a-2a39-4a4c-b44e-643c3342756d
standard_binding_idUUID
required
Exact bound standard row in that release.
Relationship: FK alpha.standards_source_row_binding where case_resource_type=cf_item.
f1ad17e0-2508-4e7a-95df-93ffb5aa3399
standard_idUUID
required
Release-specific frozen CASE standard identity pinned by standard_binding_id.
Relationship: FK release-scoped case.cf_item; must equal the bound row's case_resource_id.
0f0eb990-7692-4bac-a0d0-aecb1cdd53ae
kc_idUUID
required
Mapped active KC identity.
Relationship: FK alpha.knowledge_component.
49371a30-9796-4bbd-a523-bd38a876e415
relationshipTEXT enum
required
enum: standard_kc_relationship
Direction/grain of the mapping.supports
provenanceJSONB
required
Mapping source/reviewer/method.
Constraint: Must be a non-empty JSON object. A guarded forward migration converts an existing TEXT column only when information_schema reports TEXT, wrapping every exact legacy string as {legacy: <exact stored string>}; arrays, null, empty objects, and new scalar writes are rejected.
{"reviewer":"person:math-owner","method":"manual"}
confidenceNUMERIC(4,3)
required
Mapping confidence in [0,1].0.92
derived_view

alpha.named_kc_set_v

Read-only named projection without a stored KC containment tree.

curriculum_kc_registry_standard_separation

FieldType / nullMeaning and constraintsExample
tenant_idUUID
required
Verified JWT tenant scope; part of every key and join.
Constraint: Must equal the authenticated tenant; never accepted from a URL or request body.
11111111-1111-4111-8111-111111111111
set_idTEXT
required
Deterministic projection id.course-approval:3810e51f-e4e3-45c9-9169-593cf6c1a5f7
set_kindTEXT
required
Projection source: course_approval, standards_release, or lineage_family.course_approval
nameTEXT
required
Deterministic human-readable set name.Grade 3 Core Math approved KC set
kc_idUUID
required
KC member.
Relationship: FK active alpha.knowledge_component.
49371a30-9796-4bbd-a523-bd38a876e415
version_idUUID
nullable
Exact version when projection source pins one.7c3f9e18-d244-41b2-9727-79fb4c97f11a
ordinalINTEGER
nullable
Deterministic display order; not a prerequisite or containment edge.1
source_refTEXT
required
Approval/release/lineage row that mechanically produces membership.approval:3810e51f-e4e3-45c9-9169-593cf6c1a5f7
deployed_compatibility_non_authoritative

curriculum.component_sample

Non-authoritative sample contract retained only for the deployed CASE-backed conformance probe.

Do not generate target migrations, KC storage, course storage, or authoring guidance from this table.

curriculum_shared_supabase_migration_gate

FieldType / nullMeaning and constraintsExample
component_idTEXT
required
Legacy CASE-backed component identifier.component-correction-live-58a7658fb35e
kindTEXT enum
required
enum: component_kind
Legacy served kind.lesson
subject_idTEXT
nullable
Legacy served subject.math
grade_levelINTEGER
nullable
Legacy served grade.3
course_grade_modeTEXT
nullable
Legacy compatibility value.single_grade
expected_xpNUMERIC
nullable
Legacy served expected effort.18
kc_scope_decisionTEXT enum
nullable
enum: kc_scope_decision
Explicit producer-authoritative no-KC lesson decision echoed by deployed compatibility reads.authoritative_no_kc
created_atTIMESTAMPTZ
required
Legacy created time.2026-07-09T13:23:39Z
updated_atTIMESTAMPTZ
required
Legacy updated time.2026-07-09T13:23:39Z

Expected XP leaf/fallback/aggregate

unit: xp_eq_expected_minutes

leaf Rule: An active expected-XP-bearing component with no active expected-XP-bearing descendant contributes its own expected_xp.

fallback Rule: A container expected_xp contributes only when its active subtree has no descendant with expected_xp.

aggregate Rule: Course/grade/track totals sum contributing leaves/fallbacks once per active member path. Reuse in two courses contributes once to each course, not once globally.

role Rule: Only course_role=main contributes to the Results grade-level denominator; hole_filling, remediation, and catalog remain separately queryable.

forbidden: Never average student actuals, add a container to descendant values, or read awarded/remaining XP from Curriculum.

curriculum_reusable_component_member_model

Exact-version Content contract

No copied producer decision. Curriculum stores only content_id, content_version_id, and the typed content_kind needed for reference validation/routing. It stores no producer verdict, quality-bar id/version, release status, evidence ref, policy field, Content body, answer, or eligibility cache.

Reference: {"content_id":"UUID","content_version_id":"UUID","content_kind":"Content-owned TEXT"}

Eligibility read: GET /tenants/{tenantId}/alpha/content/items/{contentId}/versions/{contentVersionId}/release-eligibility

At member write and again at launch, Curriculum reads the exact Content version and Content-owned release-eligibility response. The version must belong to content_id, be serveable, and be eligible. An authoritative missing, blocked, revoked, malformed, or otherwise non-green response fails closed as curriculum:content_not_eligible, while a target tuple mismatch becomes curriculum:content_version_mismatch. Network failure, request/operation timeout, or an upstream HTTP 5xx is HTTP 503 curriculum:content_unavailable with bounded Retry-After and is never mistaken for semantic ineligibility. Only component create, component update/publish, and TimeBack ingest prove that this check precedes their mutation; those exact boundaries may finalize an evidence-free failed_transient receipt and reclaim the identical Idempotency-Key after Content recovers. Reads and any unmarked or post-mutation 503 remain non-retry-authoritative. Content's content.invalid_test_bank_member_contract and content.bank_kc_attribution_incomplete responses are never flattened into eligibility, transport, skip, or fallback behavior: both map losslessly to HTTP 409 curriculum:invalid_content_bank_member_contract with the safe upstream code exposed in the public RFC 7807 cause field.

Accepted Content target; callers must treat 404/unsupported eligibility read as target-not-deployed, not as permission. The Curriculum target route cannot claim ready until exact-version Content eligibility is live.

Copy-paste raw SQL / API twins

Raw/API twin

Import, bind, verify, and admit a partial standards release

API: POST /alpha/curriculum/v1/standards-frameworks/import with Idempotency-Key; GET /alpha/curriculum/v1/standards-source-releases/{sourceReleaseId}/readback

-- The API resolves platform.idempotency_key before this transaction: an exact active request hash/protocol/server lease token owns mutation and finalization; terminal rows replay exact status/body/headers; live same-hash contenders return curriculum:idempotency_in_progress; different hashes return curriculum:idempotency_conflict.
BEGIN ISOLATION LEVEL SERIALIZABLE;
INSERT INTO alpha.standards_source_release (tenant_id,source_release_id,source_authority,official_source_url,declared_scope,partial_display_name,license_url,release_rights_status,required_attribution,retrieved_at,raw_artifact_sha256,raw_checksum_grain,parser_name,parser_version,normalized_artifact_ref,normalized_artifact_sha256,normalization_profile,source_policy_ref,imported_by,imported_at)
VALUES (:tenant_id,:release_id,:source_authority,:official_source_url,:declared_scope,:partial_display_name,:license_url,:release_rights_status,:required_attribution,:retrieved_at,:raw_sha256,:raw_checksum_grain,:parser_name,:parser_version,:normalized_artifact_ref,:normalized_sha256,:normalization_profile,:source_policy_ref,:imported_by,now())
;
INSERT INTO case.cf_document (tenant_id,id,identifier,body,retired_at) VALUES (:tenant_id,:document_id,:document_identifier,:document_body,NULL);
INSERT INTO case.cf_item (tenant_id,id,identifier,cf_document_id,body,retired_at) VALUES (:tenant_id,:item_id,:item_identifier,:document_id,:item_body,NULL);
INSERT INTO alpha.standards_source_row_binding (tenant_id,binding_id,source_release_id,source_row_key,material_grain,source_locator,source_identifier,case_resource_type,case_resource_id,raw_statement,normalized_statement,ordinal_path,binding_sha256,created_at) VALUES
(:tenant_id,:document_binding_id,:release_id,:document_source_row_key,'framework',:document_source_locator,:document_identifier,'cf_document',:document_id,:document_raw_title,:document_normalized_title,:document_ordinal_path,:document_binding_sha256,now()),
(:tenant_id,:item_binding_id,:release_id,:item_source_row_key,'statement',:item_source_locator,:item_identifier,'cf_item',:item_id,:item_raw_statement,:item_normalized_statement,:item_ordinal_path,:item_binding_sha256,now());
INSERT INTO alpha.standards_rights_record (tenant_id,rights_record_id,source_release_id,material_grain,material_ref,rights_status,admission_outcome,permission_basis,allowed_uses,required_attribution,effective_from,effective_to,source_policy_ref,evidence_ref,decided_by_role,counsel_decision_ref,created_at) VALUES
(:tenant_id,:release_rights_record_id,:release_id,'release',:release_id::text,:release_rights_status,:release_admission_outcome,:release_permission_basis,:release_allowed_uses,:release_required_attribution,:rights_effective_from,:rights_effective_to,:source_policy_ref,:release_evidence_ref,:release_decided_by_role,:release_counsel_decision_ref,now()),
(:tenant_id,:item_rights_record_id,:release_id,'statement',:item_source_row_key,:item_rights_status,:item_admission_outcome,:item_permission_basis,:item_allowed_uses,:item_required_attribution,:rights_effective_from,:rights_effective_to,:source_policy_ref,:item_evidence_ref,:item_decided_by_role,:item_counsel_decision_ref,now());
INSERT INTO alpha.standards_readback_evidence (tenant_id,evidence_id,source_release_id,binding_id,raw_sha256,normalized_sha256,readback_sha256,mismatch_code,mismatch_detail,checked_at,checker_version) VALUES
(:tenant_id,:document_evidence_id,:release_id,:document_binding_id,:raw_sha256,:normalized_sha256,:document_readback_sha256,:document_mismatch_code,:document_mismatch_detail,now(),:checker_version),
(:tenant_id,:item_evidence_id,:release_id,:item_binding_id,:raw_sha256,:normalized_sha256,:item_readback_sha256,:item_mismatch_code,:item_mismatch_detail,now(),:checker_version);
WITH scope_check AS (
  SELECT release.source_release_id,count(binding.binding_id) AS binding_count,
    jsonb_typeof(release.declared_scope)='object'
    AND NULLIF(release.declared_scope->>'jurisdiction','') IS NOT NULL
    AND NULLIF(release.declared_scope->>'subject','') IS NOT NULL
    AND NULLIF(release.declared_scope->>'edition','') IS NOT NULL
    AND jsonb_typeof(release.declared_scope->'grades')='array' AND jsonb_array_length(release.declared_scope->'grades')>0
    AND jsonb_typeof(release.declared_scope->'rowFilter')='string' AND NULLIF(release.declared_scope->>'rowFilter','') IS NOT NULL
    AND (release.declared_scope->>'expectedRowCount') ~ '^[1-9][0-9]*$'
    AND (release.declared_scope->>'sourceRowKeysSha256') ~ '^[0-9a-f]{64}$'
    AND (release.declared_scope->>'isPartial') IN ('true','false')
    AND NULLIF(release.declared_scope->>'officialFrameworkName','') IS NOT NULL
    AND CASE WHEN (release.declared_scope->>'expectedRowCount') ~ '^[1-9][0-9]*$' THEN (release.declared_scope->>'expectedRowCount')::bigint=count(binding.binding_id) ELSE false END
    AND release.declared_scope->>'sourceRowKeysSha256'=encode(digest(COALESCE(string_agg(binding.source_row_key,E'
' ORDER BY binding.source_row_key),''),'sha256'),'hex')
    AND ((release.declared_scope->>'isPartial')='false' AND release.partial_display_name IS NULL OR (release.declared_scope->>'isPartial')='true' AND NULLIF(release.partial_display_name,'') IS NOT NULL AND release.partial_display_name<>release.declared_scope->>'officialFrameworkName') AS scope_exact
  FROM alpha.standards_source_release release LEFT JOIN alpha.standards_source_row_binding binding ON binding.tenant_id=release.tenant_id AND binding.source_release_id=release.source_release_id
  WHERE release.tenant_id=:tenant_id AND release.source_release_id=:release_id
  GROUP BY release.source_release_id,release.declared_scope,release.partial_display_name
), ranked_rights AS (
  SELECT binding.binding_id,rights.rights_status,rights.admission_outcome,(rights.rights_status='public_domain' OR COALESCE(rights.required_attribution,release.required_attribution) IS NOT NULL) AS attribution_satisfied,count(*) OVER (PARTITION BY binding.binding_id,CASE WHEN rights.material_grain='release' THEN 'release' ELSE 'specific' END) AS active_at_specificity,row_number() OVER (PARTITION BY binding.binding_id ORDER BY CASE WHEN rights.material_grain='release' THEN 1 ELSE 2 END DESC,rights.effective_from DESC,rights.created_at DESC) AS precedence
  FROM alpha.standards_source_row_binding binding JOIN alpha.standards_source_release release ON release.tenant_id=binding.tenant_id AND release.source_release_id=binding.source_release_id JOIN alpha.standards_rights_record rights ON rights.tenant_id=binding.tenant_id AND rights.source_release_id=binding.source_release_id AND ((rights.material_grain=binding.material_grain AND rights.material_ref=binding.source_row_key) OR (rights.material_grain='release' AND rights.material_ref=binding.source_release_id::text)) AND rights.effective_from<=:evaluation_time AND (rights.effective_to IS NULL OR rights.effective_to>:evaluation_time)
  WHERE binding.tenant_id=:tenant_id AND binding.source_release_id=:release_id
), evidence_status AS (
  SELECT binding_id,count(*) AS evidence_count,bool_and(mismatch_code='none') AS all_pass,bool_or(mismatch_code<>'none') AS has_mismatch
  FROM alpha.standards_readback_evidence WHERE tenant_id=:tenant_id AND source_release_id=:release_id AND binding_id IS NOT NULL GROUP BY binding_id
), release_check AS (
  SELECT scope.binding_count,scope.scope_exact,count(DISTINCT evidence.binding_id) FILTER (WHERE evidence.evidence_count>0 AND evidence.all_pass) AS passing_readbacks,count(DISTINCT rights.binding_id) FILTER (WHERE rights.precedence=1 AND rights.active_at_specificity=1 AND rights.admission_outcome='green' AND rights.rights_status IN ('permitted','public_domain') AND rights.attribution_satisfied) AS green_rights,COALESCE(bool_or(rights.precedence=1 AND rights.active_at_specificity>1),false) AS has_rights_conflict,COALESCE(bool_or(rights.precedence=1 AND (rights.admission_outcome='red' OR rights.rights_status='prohibited')),false) AS has_red,COALESCE(bool_or(evidence.has_mismatch),false) AS has_any_mismatch
  FROM scope_check scope LEFT JOIN alpha.standards_source_row_binding binding ON binding.tenant_id=:tenant_id AND binding.source_release_id=scope.source_release_id LEFT JOIN evidence_status evidence ON evidence.binding_id=binding.binding_id LEFT JOIN ranked_rights rights ON rights.binding_id=binding.binding_id
  GROUP BY scope.binding_count,scope.scope_exact
)
SELECT CASE WHEN binding_count=0 OR NOT scope_exact OR has_any_mismatch OR has_red THEN 'red' WHEN has_rights_conflict THEN 'yellow' WHEN binding_count=passing_readbacks AND binding_count=green_rights THEN 'green' ELSE 'yellow' END AS admission_outcome,binding_count,scope_exact,passing_readbacks,green_rights,has_any_mismatch FROM release_check;
COMMIT;

Guardrails: Tenant predicate on every read/writeBounded serializationIdempotency replay happens before the transaction; every release-id collision abortsCanonical declared scope count + sorted row-key hash must equal every bindingEvery CASE row has one bindingEvery evidence row for every binding must passMost-specific effective rights winsOnly green becomes adoptable

Problems: curriculum:standards_scope_invalidcurriculum:rights_unresolvedcurriculum:standards_readback_mismatchcurriculum:idempotency_conflict

curriculum_immutable_standards_source_release

Raw/API twin

Author, version, review, relate, and crosswalk a KC

API: POST /alpha/curriculum/v1/kcs; POST /alpha/curriculum/v1/kcs/{kcId}/versions; POST /alpha/curriculum/v1/kc-prerequisites; POST /alpha/curriculum/v1/kc-relations; POST /alpha/curriculum/v1/kc-review-decisions; POST /alpha/curriculum/v1/standard-kc-maps

BEGIN ISOLATION LEVEL SERIALIZABLE;
SET CONSTRAINTS ALL DEFERRED;
-- Every prerequisite create, correction, and retirement acquires this same tenant-scoped transaction lock; serialization failures are retried with the same Idempotency-Key.
SELECT pg_advisory_xact_lock(hashtextextended('curriculum:kc-prerequisite:'||:tenant_id::text,0));
INSERT INTO alpha.knowledge_component (tenant_id,kc_id,source_kc_id,lineage_id,kc_kind,subject_id,current_version_id,publication_status,created_at,updated_at) VALUES (:tenant_id,:kc_id,:source_kc_id,:lineage_id,:kc_kind,:subject_id,:version_id,'draft',now(),now());
INSERT INTO alpha.knowledge_component_version (tenant_id,version_id,kc_id,version_number,status,definition,positive_examples,negative_examples,boundary_statement,misconceptions,instructional_classification,provenance,confidence,author_ref,created_at) VALUES (:tenant_id,:version_id,:kc_id,1,'draft',:definition,:positive_examples,:negative_examples,:boundary_statement,:misconceptions,:instructional_classification,:provenance,:confidence,:author_ref,now());
INSERT INTO alpha.kc_prerequisite (tenant_id,from_kc_id,to_kc_id,version,prerequisite_id,prerequisite_kc_id,dependent_kc_id,provenance,confidence,created_at,updated_at) SELECT :tenant_id,:prerequisite_kc_id,:kc_id,1,:edge_id,:prerequisite_kc_id,:kc_id,:edge_provenance,:edge_confidence,now(),now() WHERE NOT EXISTS (WITH RECURSIVE reach(kc_id) AS (SELECT dependent_kc_id FROM alpha.kc_prerequisite WHERE tenant_id=:tenant_id AND prerequisite_kc_id=:kc_id AND retired_at IS NULL UNION SELECT p.dependent_kc_id FROM alpha.kc_prerequisite p JOIN reach r ON p.prerequisite_kc_id=r.kc_id WHERE p.tenant_id=:tenant_id AND p.retired_at IS NULL) SELECT 1 FROM reach WHERE kc_id=:prerequisite_kc_id);
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM alpha.kc_prerequisite WHERE tenant_id=:tenant_id AND prerequisite_id=:edge_id) THEN RAISE EXCEPTION 'curriculum:prerequisite_cycle'; END IF; END $$;
INSERT INTO alpha.kc_relation (tenant_id,relation_id,origin_kc_id,destination_kc_id,relation_type,provenance,confidence,created_at,updated_at) VALUES (:tenant_id,:relation_id,:kc_id,:related_kc_id,'applies_to',:relation_provenance,:relation_confidence,now(),now());
INSERT INTO alpha.kc_review_decision (tenant_id,review_decision_id,version_id,decision,edit_diff,rationale,provenance,actor_ref,decided_at) VALUES (:tenant_id,:review_id,:version_id,:decision,:edit_diff,:rationale,:review_provenance,:actor_ref,now());
INSERT INTO alpha.standard_kc_map (tenant_id,map_id,source_release_id,standard_binding_id,standard_id,kc_id,relationship,provenance,confidence,created_at,updated_at) VALUES (:tenant_id,:map_id,:source_release_id,:standard_binding_id,:standard_id,:kc_id,:relationship,:map_provenance,:map_confidence,now(),now());
COMMIT;

Guardrails: No CASE KC rowPOST /kcs is owner-only and accepts stored_as=alpha.knowledge_component only with definition, optional provenance-only source_kc_id, and no document_id, name, or containment field; create replay, GET, version, and retire accept the exact canonical kc_id onlysource_kc_id is never a create-or-resolve key: reusing it for a different or server-minted canonical kc_id returns a typed conflict without partial rows even when semantics match, and it never enables a later lookup/reference fallback; HTTP retries use the original Idempotency-KeySemantic source ids and the #707 crosswalk remain immutable evidence only and are never written into UUID reference fieldsEvery scope/remediation/graph reference is an active tenant registry UUIDSemantic edits appendEvery prerequisite mutation takes one tenant-scoped graph lock inside SERIALIZABLESerialization failures replay through Idempotency-Keyalpha.kc_prerequisite alone is acyclicalpha.kc_relation holds other typed non-DAG semanticsalpha.standard_kc_map pins release + bindingKCs have no containment treeSteward queues/monitoring are absent

Problems: curriculum:invalid_kc_kindcurriculum:kc_definition_qualitycurriculum:kc_semantics_conflictcurriculum:prerequisite_cyclecurriculum:invalid_kc_relationcurriculum:precondition_failed

curriculum_kc_authoring_contract

Raw/API twin

Approve a course, snapshot every KC version, and firm drafts atomically

API: POST /alpha/curriculum/v1/courses/{courseId}/approvals with Idempotency-Key

Integrator policy: An app integrator may invoke this workflow directly with its Platform-issued tenant-scoped credential. An ordinary course approval needs no per-course owner ruling.

Authorization: Bearer JWT with curriculum:write

Request body: {"approved_by":"integrator:ap-one","provenance":{"source":"ap-one:course-approval","course_ref":"{integratorCourseRef}"}}

Turnaround: The authorized approval transaction is synchronous. If the integrator lacks curriculum:write, request that narrow tenant credential through Platform tenant onboarding; this contract defines no credential-provisioning SLA and there is no per-course approval queue.

Preconditions:

  • The course root is published and not retired.
  • The current graph is complete and contains at least one governed KC reference.
  • Every referenced KC is active, published, and resolves to its exact current immutable version.
  • Each exact Content gate target has independently passed Content-owned trust and release eligibility before gate publication.

Readback:

  • Require POST success to return the immutable approval with its complete exact KC-version snapshot.
  • GET /alpha/curriculum/v1/courses/{courseId}/approvals and match the approval id and snapshot.
  • GET /alpha/curriculum/v1/courses/{courseId}/next-lesson and require it to echo the current approval and firm snapshot; changed or incomplete graphs fail closed.
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT course_id FROM alpha.course WHERE tenant_id=:tenant_id AND course_id=:course_id AND retired_at IS NULL FOR UPDATE;
CREATE TEMP TABLE _approval_scope (kc_id uuid PRIMARY KEY, version_id uuid NOT NULL, snapshot_source text NOT NULL) ON COMMIT DROP;
INSERT INTO _approval_scope (kc_id,version_id,snapshot_source)
SELECT kc.kc_id,kc.current_version_id,'course_component:'||min(component.component_id::text)
FROM alpha.course_component_member member
JOIN alpha.course_component component ON component.tenant_id=member.tenant_id AND component.component_id=member.target_component_id AND component.retired_at IS NULL
CROSS JOIN LATERAL unnest(component.scope_kc_ids) scoped(kc_id)
JOIN alpha.knowledge_component kc ON kc.tenant_id=component.tenant_id AND kc.kc_id=scoped.kc_id AND kc.retired_at IS NULL
WHERE member.tenant_id=:tenant_id AND member.course_id=:course_id AND member.target_kind='course_component' AND member.retired_at IS NULL
GROUP BY kc.kc_id,kc.current_version_id;
DO $$ BEGIN
  IF NOT EXISTS (SELECT 1 FROM _approval_scope) OR (SELECT count(*) FROM _approval_scope) <> (SELECT count(DISTINCT scoped.kc_id) FROM alpha.course_component_member member JOIN alpha.course_component component ON component.tenant_id=member.tenant_id AND component.component_id=member.target_component_id AND component.retired_at IS NULL CROSS JOIN LATERAL unnest(component.scope_kc_ids) scoped(kc_id) WHERE member.tenant_id=:tenant_id AND member.course_id=:course_id AND member.target_kind='course_component' AND member.retired_at IS NULL) THEN RAISE EXCEPTION 'curriculum:approval_snapshot_incomplete'; END IF;
END $$;
INSERT INTO alpha.course_approval (tenant_id,approval_id,course_id,course_graph_sha256,approved_by,approved_at,provenance) VALUES (:tenant_id,:approval_id,:course_id,:course_graph_sha256,:approved_by,now(),:provenance);
INSERT INTO alpha.course_approval_kc_snapshot (tenant_id,approval_id,kc_id,version_id,snapshot_source,created_at) SELECT :tenant_id,:approval_id,kc_id,version_id,snapshot_source,now() FROM _approval_scope;
DO $$ BEGIN IF (SELECT count(*) FROM _approval_scope) <> (SELECT count(*) FROM alpha.course_approval_kc_snapshot WHERE tenant_id=:tenant_id AND approval_id=:approval_id) THEN RAISE EXCEPTION 'curriculum:approval_snapshot_incomplete'; END IF; END $$;
INSERT INTO alpha.kc_firming_event (tenant_id,firming_event_id,version_id,firming_reason,source_ref,actor_ref,firmed_at) SELECT :tenant_id,gen_random_uuid(),snapshot.version_id,'course_approval','approval:'||:approval_id,:approved_by,now() FROM alpha.course_approval_kc_snapshot snapshot JOIN alpha.knowledge_component_version version ON version.tenant_id=snapshot.tenant_id AND version.version_id=snapshot.version_id WHERE snapshot.tenant_id=:tenant_id AND snapshot.approval_id=:approval_id AND version.status='draft' ON CONFLICT (tenant_id,version_id) DO NOTHING;
UPDATE alpha.knowledge_component_version version SET status='firm' FROM alpha.course_approval_kc_snapshot snapshot WHERE snapshot.tenant_id=:tenant_id AND snapshot.approval_id=:approval_id AND version.tenant_id=snapshot.tenant_id AND version.version_id=snapshot.version_id AND version.status='draft';
UPDATE alpha.course SET current_approval_id=:approval_id,updated_at=now() WHERE tenant_id=:tenant_id AND course_id=:course_id;
COMMIT;

Guardrails: Complete immutable snapshotFirm only exact snapshotted versionsNo publication/assignment firmingApproval and firming atomic

Problems: curriculum:approval_snapshot_incompletecurriculum:invalid_kc_lifecyclecurriculum:idempotency_conflict

curriculum_kc_storage_governance

Raw/API twin

Firm a draft KC version on the first genuine live response

API: POST /alpha/curriculum/v1/kc-firming-events with Idempotency-Key and body {version_id, firming_reason:'first_genuine_response', source_ref}

-- First obtain :validated_live_learning_evidence_ref from the Results/Events-owned contract; Curriculum rows cannot prove genuine traffic.
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT status FROM alpha.knowledge_component_version WHERE tenant_id=:tenant_id AND version_id=:version_id FOR UPDATE;
INSERT INTO alpha.kc_firming_event (tenant_id,firming_event_id,version_id,firming_reason,source_ref,actor_ref,firmed_at) VALUES (:tenant_id,:event_id,:version_id,'first_genuine_response',:validated_live_learning_evidence_ref,:actor_ref,now()) ON CONFLICT (tenant_id,version_id) DO NOTHING;
UPDATE alpha.knowledge_component_version SET status='firm' WHERE tenant_id=:tenant_id AND version_id=:version_id AND status='draft' AND EXISTS (SELECT 1 FROM alpha.kc_firming_event WHERE tenant_id=:tenant_id AND version_id=:version_id AND firming_reason='first_genuine_response' AND source_ref=:validated_live_learning_evidence_ref);
COMMIT;

Guardrails: Explicit live_learning=trueFirst genuine response evidencePreview/developer traffic rejectedAt most one effective firming eventIdempotent replay

Problems: curriculum:invalid_kc_lifecyclecurriculum:live_learning_evidence_requiredcurriculum:idempotency_conflict

curriculum_kc_storage_governance

Raw/API twin

Reuse one lesson in two courses with independent order and behavior

API: POST /alpha/curriculum/v1/components; POST /alpha/curriculum/v1/courses/{courseId}/members (once per course)

BEGIN;
INSERT INTO alpha.course_component (tenant_id,component_id,kind,scope_kc_ids,name,description,expected_xp,lineage_id,publication_status,created_at,updated_at) VALUES (:tenant_id,:component_id,'lesson',ARRAY[:kc_id]::uuid[],:component_name,:component_description,:expected_xp,:lineage_id,'published',now(),now());
INSERT INTO alpha.course_component_member (tenant_id,member_id,course_id,parent_component_id,position,target_kind,target_component_id,content_id,content_version_id,content_kind,renderer_ref,gates,passing_criteria_ref,on_fail,created_at,updated_at) VALUES
(:tenant_id,:course_a_member_id,:course_a_id,:course_a_parent_id,:course_a_position,'course_component',:component_id,NULL,NULL,NULL,NULL,false,NULL,NULL,now(),now()),
(:tenant_id,:course_b_member_id,:course_b_id,:course_b_parent_id,:course_b_position,'course_component',:component_id,NULL,NULL,NULL,NULL,false,NULL,NULL,now(),now());
COMMIT;

Guardrails: One component identityTwo member identitiesOrder/renderer/gate are member-localLesson preserves its non-empty active KC cluster

Problems: curriculum:unknown_kccurriculum:invalid_component_kindcurriculum:member_position_conflict

curriculum_reusable_component_member_model

Raw/API twin

Create a member that targets one eligible exact Content version

API: GET Content exact-version release-eligibility; POST /alpha/curriculum/v1/courses/{courseId}/members

-- First require a 200 eligible response from GET /tenants/{tenantId}/alpha/content/items/{contentId}/versions/{contentVersionId}/release-eligibility and verify renderer_ref against the live capability registry. Raw Curriculum rows cannot infer either verdict.
INSERT INTO alpha.course_component_member (tenant_id,member_id,course_id,parent_component_id,position,target_kind,target_component_id,content_id,content_version_id,content_kind,renderer_ref,gates,passing_criteria_ref,on_fail,created_at,updated_at)
VALUES (:tenant_id,:member_id,:course_id,:parent_component_id,:position,'content_version',NULL,:validated_content_id,:validated_content_version_id,:validated_content_kind,:validated_renderer_ref,:gates,:passing_criteria_ref,:on_fail,now(),now());

Guardrails: Exact version, not latestContent owns eligibilityNo copied eligibility fieldsRenderer registry must accept content_kind404/unsupported fails closed

Problems: curriculum:content_not_eligiblecurriculum:content_unavailablecurriculum:content_version_mismatchcurriculum:invalid_content_bank_member_contractcurriculum:renderer_incompatible

curriculum_member_content_reference_gate_placement

Raw/API twin

Producer self-service repoint/attach of one exact Content pin

API: POST /alpha/curriculum/v1/components/{componentId}/repoint with Idempotency-Key

Integrator policy: A producer whose course already pins a Content identity repoints that pin to its amended exact version directly with its Platform-issued tenant-scoped curriculum:write credential; a platform re-pin ticket is not the steady-state path. operation=attach pins newly published content onto a component without a current pin. This endpoint exposes the same governed exact-version target update the platform's own repin corridor executes; it is not a new storage primitive.

Request body: {"operation":"repoint","expected_target_ref":{"module":"content","content_kind":"article","content_id":"{contentId}","content_version_id":"{currentlyPinnedVersionId}"},"target_ref":{"module":"content","content_kind":"article","content_id":"{contentId}","content_version_id":"{amendedVersionId}"}}

Preconditions:

  • expected_target_ref is a compare-and-set premise: it must equal the component's exact current pin (attach requires null and an unpinned component); any drift is 409 curriculum:repoint_pin_stale with zero writes.
  • A repoint keeps the Content identity: content_id and content_kind never change; a revision published under a new content_id is a NEW content identity and must be attached, never silently substituted (409 curriculum:repoint_identity_mismatch).
  • The exact new version must pass Content-owned release eligibility with practice_eligible usage scope inside the accepting request; an authoritative refusal is 409 curriculum:repoint_target_not_serveable carrying the safe upstream cause, and an unavailable Content answer stays 503 curriculum:content_unavailable.
  • A published component pinning an assessment chain (mastery_gate/quiz/test kinds, or test_bank/test/test_spec content kinds) is frozen: producer self-service returns 409 curriculum:approved_chain_repoint_frozen and the change goes through the reviewed-replacement manifest path — a committed reviewed manifest applied through the platform's dry-run-first ETag/CAS corridor.
  • The component resolves only inside the verified JWT tenant; a cross-tenant id is an ordinary 404.

Readback:

  • The 200 response returns the updated component plus its immutable alpha.component_target_repoint_audit row; the same Idempotency-Key replays the identical response with no second audit row.
  • The repoint changes the course graph, so next-lesson fails closed (409 curriculum:course_not_routable) until the producer appends a new immutable approval through POST /alpha/curriculum/v1/courses/{courseId}/approvals; ordinary next-lesson must then echo the new approval and serve the exact repointed version.
BEGIN;
-- First require a 200 exact-version release-eligibility response with usage_scope='practice_eligible'
-- for :new_content_version_id from the owning Content API; raw Curriculum rows cannot infer that verdict.
SELECT member_id, content_id, content_version_id, content_kind, gates
  FROM alpha.course_component_member
 WHERE tenant_id=:tenant_id AND member_id=:member_id AND retired_at IS NULL
 FOR UPDATE;
DO $$ BEGIN
  IF NOT FOUND THEN RAISE EXCEPTION 'curriculum:not_found'; END IF;
END $$;
UPDATE alpha.course_component_member member
   SET content_version_id=:new_content_version_id, updated_at=now()
 WHERE member.tenant_id=:tenant_id AND member.member_id=:member_id AND member.retired_at IS NULL
   AND member.target_kind='content_version'
   AND member.gates=false
   AND member.content_kind NOT IN ('test_bank','test','test_spec')
   AND member.content_id=:expected_content_id
   AND member.content_kind=:expected_content_kind
   AND member.content_version_id IS NOT DISTINCT FROM :expected_content_version_id
   AND member.content_id=:new_content_id;
DO $$ BEGIN
  IF NOT EXISTS (SELECT 1 FROM alpha.course_component_member WHERE tenant_id=:tenant_id AND member_id=:member_id AND content_version_id=:new_content_version_id) THEN
    RAISE EXCEPTION 'curriculum:repoint_pin_stale';
  END IF;
END $$;
INSERT INTO alpha.component_target_repoint_audit (tenant_id, repoint_audit_id, component_id, operation, previous_target_ref, new_target_ref, actor_ref, request_id, created_at)
VALUES (:tenant_id, :repoint_audit_id, :member_id, :operation, :previous_target_ref, :new_target_ref, :actor_ref, :request_id, now());
COMMIT;
-- The deployed CASE compatibility adapter performs the identical operation on the component's
-- alpha.target_ref through the same normalized patch and CAS-guarded persistence as component PATCH.

Guardrails: Compare-and-set on the exact current pin, never last-writer-winsOne Content identity per repoint; attach only onto an unpinned componentContent owns exact-version eligibility and the check happens server-side inside the accepting requestApproved assessment chains freeze to the reviewed-replacement manifest pathOne immutable audit row per accepted operation in the same transactionIdempotency-Key replay returns the stored response without a second audit rowRouting stays fail-closed until a new immutable course approval covers the changed graph

Problems: curriculum:not_foundcurriculum:repoint_pin_stalecurriculum:repoint_identity_mismatchcurriculum:repoint_target_not_serveablecurriculum:approved_chain_repoint_frozencurriculum:content_unavailablecurriculum:content_version_mismatchcurriculum:renderer_incompatiblecurriculum:idempotency_conflict

curriculum_member_content_reference_gate_placement

Raw/API twin

Compute any course's expected XP without double-counting fallbacks

API: GET /alpha/curriculum/v1/courses/{courseId}/expected-xp

WITH RECURSIVE active_tree AS (
  SELECT member.tenant_id,member.course_id,member.member_id,member.target_component_id,component.expected_xp,ARRAY[member.member_id]::uuid[] AS member_path
  FROM alpha.course_component_member member JOIN alpha.course_component component ON component.tenant_id=member.tenant_id AND component.component_id=member.target_component_id AND component.retired_at IS NULL
  WHERE member.tenant_id=:tenant_id AND member.course_id=:course_id AND member.parent_component_id IS NULL AND member.target_kind='course_component' AND member.retired_at IS NULL
  UNION ALL
  SELECT child.tenant_id,child.course_id,child.member_id,child.target_component_id,component.expected_xp,parent.member_path||child.member_id
  FROM active_tree parent JOIN alpha.course_component_member child ON child.tenant_id=parent.tenant_id AND child.course_id=parent.course_id AND child.parent_component_id=parent.target_component_id AND child.target_kind='course_component' AND child.retired_at IS NULL
  JOIN alpha.course_component component ON component.tenant_id=child.tenant_id AND component.component_id=child.target_component_id AND component.retired_at IS NULL
  WHERE NOT child.member_id=ANY(parent.member_path)
), contributing AS (
  SELECT node.* FROM active_tree node WHERE node.expected_xp IS NOT NULL AND NOT EXISTS (SELECT 1 FROM active_tree descendant WHERE cardinality(descendant.member_path)>cardinality(node.member_path) AND descendant.member_path[1:cardinality(node.member_path)]=node.member_path AND descendant.expected_xp IS NOT NULL)
), cycle_check AS (
  SELECT EXISTS (SELECT 1 FROM active_tree parent JOIN alpha.course_component_member child ON child.tenant_id=parent.tenant_id AND child.course_id=parent.course_id AND child.parent_component_id=parent.target_component_id AND child.retired_at IS NULL WHERE child.member_id=ANY(parent.member_path)) AS cycle_detected
)
SELECT CASE WHEN cycle_check.cycle_detected THEN NULL ELSE COALESCE(sum(contributing.expected_xp),0) END AS expected_xp,course.course_role,(course.course_role='main') AS included_in_results_grade_denominator,cycle_check.cycle_detected
FROM cycle_check JOIN alpha.course course ON course.tenant_id=:tenant_id AND course.course_id=:course_id AND course.retired_at IS NULL LEFT JOIN contributing ON true
GROUP BY course.course_role,cycle_check.cycle_detected;

Guardrails: Every active course role returns its own totalContainer ignored when descendant has expected_xpFallback contributes only without valued descendantActive member paths onlyMain role only for the separate Results grade denominatorNo student actuals

Problems: curriculum:lesson_structure_missingcurriculum:expected_xp_ambiguous

curriculum_reusable_component_member_model

Raw/API twin

Import source-shaped TimeBack course structure without fabricating pedagogy

API: POST /alpha/curriculum/v1/ingest/timeback-course-refs with Idempotency-Key

BEGIN;
CREATE TEMP TABLE _source_components (source_component_id uuid PRIMARY KEY,source_kind text NOT NULL,scope_kc_ids uuid[] NOT NULL,source_name text NOT NULL,expected_xp numeric,lineage_id uuid NOT NULL) ON COMMIT DROP;
INSERT INTO _source_components SELECT * FROM jsonb_to_recordset(:validated_source_components::jsonb) AS source(source_component_id uuid,source_kind text,scope_kc_ids uuid[],source_name text,expected_xp numeric,lineage_id uuid);
CREATE TEMP TABLE _ordered_members (member_id uuid PRIMARY KEY,parent_component_id uuid,source_position integer NOT NULL,target_kind text NOT NULL,target_component_id uuid,content_id uuid,content_version_id uuid,content_kind text,renderer_ref text,gates boolean NOT NULL,passing_criteria_ref text,on_fail jsonb) ON COMMIT DROP;
INSERT INTO _ordered_members SELECT * FROM jsonb_to_recordset(:validated_ordered_members::jsonb) AS member(member_id uuid,parent_component_id uuid,source_position integer,target_kind text,target_component_id uuid,content_id uuid,content_version_id uuid,content_kind text,renderer_ref text,gates boolean,passing_criteria_ref text,on_fail jsonb);
DO $$ BEGIN
  IF EXISTS (SELECT 1 FROM _source_components WHERE source_kind='lesson' AND cardinality(scope_kc_ids)=0) OR EXISTS (SELECT 1 FROM _source_components source CROSS JOIN LATERAL unnest(source.scope_kc_ids) scoped(kc_id) LEFT JOIN alpha.knowledge_component kc ON kc.tenant_id=:tenant_id AND kc.kc_id=scoped.kc_id AND kc.retired_at IS NULL WHERE kc.kc_id IS NULL) THEN RAISE EXCEPTION 'curriculum:adapter_rejected'; END IF;
  IF EXISTS (SELECT 1 FROM _ordered_members WHERE source_position<0 OR target_kind NOT IN ('course_component','content_version') OR (target_kind='course_component' AND (target_component_id IS NULL OR content_id IS NOT NULL OR content_version_id IS NOT NULL OR content_kind IS NOT NULL)) OR (target_kind='content_version' AND (target_component_id IS NOT NULL OR content_id IS NULL OR content_version_id IS NULL OR content_kind IS NULL OR renderer_ref IS NULL)) OR gates<>(passing_criteria_ref IS NOT NULL)) THEN RAISE EXCEPTION 'curriculum:adapter_rejected'; END IF;
END $$;
INSERT INTO alpha.course (tenant_id,course_id,subject_id,grade_scope,course_role,name,lineage_id,publication_status,created_at,updated_at) VALUES (:tenant_id,:course_id,:subject_id,:grade_scope,:course_role,:course_name,:course_lineage_id,'draft',now(),now()) ON CONFLICT (tenant_id,course_id) DO UPDATE SET subject_id=excluded.subject_id,grade_scope=excluded.grade_scope,course_role=excluded.course_role,name=excluded.name,lineage_id=excluded.lineage_id,publication_status='draft',updated_at=now();
INSERT INTO alpha.course_component (tenant_id,component_id,kind,scope_kc_ids,name,expected_xp,lineage_id,publication_status,created_at,updated_at) SELECT :tenant_id,source_component_id,source_kind,scope_kc_ids,source_name,expected_xp,lineage_id,'draft',now(),now() FROM _source_components ON CONFLICT (tenant_id,component_id) DO UPDATE SET kind=excluded.kind,scope_kc_ids=excluded.scope_kc_ids,name=excluded.name,expected_xp=excluded.expected_xp,lineage_id=excluded.lineage_id,publication_status='draft',updated_at=now();
INSERT INTO alpha.course_component_member (tenant_id,member_id,course_id,parent_component_id,position,target_kind,target_component_id,content_id,content_version_id,content_kind,renderer_ref,gates,passing_criteria_ref,on_fail,created_at,updated_at) SELECT :tenant_id,member_id,:course_id,parent_component_id,source_position,target_kind,target_component_id,content_id,content_version_id,content_kind,renderer_ref,gates,passing_criteria_ref,on_fail,now(),now() FROM _ordered_members ON CONFLICT (tenant_id,member_id) DO UPDATE SET course_id=excluded.course_id,parent_component_id=excluded.parent_component_id,position=excluded.position,target_kind=excluded.target_kind,target_component_id=excluded.target_component_id,content_id=excluded.content_id,content_version_id=excluded.content_version_id,content_kind=excluded.content_kind,renderer_ref=excluded.renderer_ref,gates=excluded.gates,passing_criteria_ref=excluded.passing_criteria_ref,on_fail=excluded.on_fail,updated_at=now();
DO $$ BEGIN
  IF (SELECT count(*) FROM _source_components) <> (SELECT count(*) FROM alpha.course_component component JOIN _source_components source ON source.source_component_id=component.component_id WHERE component.tenant_id=:tenant_id AND component.publication_status='draft') OR (SELECT count(*) FROM _ordered_members) <> (SELECT count(*) FROM alpha.course_component_member member JOIN _ordered_members source ON source.member_id=member.member_id WHERE member.tenant_id=:tenant_id AND member.course_id=:course_id AND member.retired_at IS NULL) THEN RAISE EXCEPTION 'curriculum:publication_incomplete'; END IF;
END $$;
CREATE TEMP TABLE _intended_tree (component_id uuid PRIMARY KEY,depth integer NOT NULL) ON COMMIT DROP;
WITH RECURSIVE intended_tree AS (
  SELECT component.component_id,0 AS depth FROM alpha.course_component component JOIN alpha.course_component_member member ON member.tenant_id=component.tenant_id AND member.target_component_id=component.component_id WHERE component.tenant_id=:tenant_id AND member.course_id=:course_id AND member.parent_component_id IS NULL AND member.target_kind='course_component' AND member.retired_at IS NULL
  UNION ALL
  SELECT child.component_id,parent.depth+1 FROM intended_tree parent JOIN alpha.course_component_member member ON member.tenant_id=:tenant_id AND member.course_id=:course_id AND member.parent_component_id=parent.component_id AND member.target_kind='course_component' AND member.retired_at IS NULL JOIN alpha.course_component child ON child.tenant_id=member.tenant_id AND child.component_id=member.target_component_id
)
INSERT INTO _intended_tree SELECT component_id,max(depth) FROM intended_tree GROUP BY component_id;
DO $$ DECLARE publish_depth integer; BEGIN
  FOR publish_depth IN SELECT DISTINCT depth FROM _intended_tree ORDER BY depth DESC LOOP
    UPDATE alpha.course_component component SET publication_status='published',updated_at=now() FROM _intended_tree tree WHERE component.tenant_id=:tenant_id AND component.component_id=tree.component_id AND tree.depth=publish_depth;
  END LOOP;
END $$;
DO $$ BEGIN
  IF NOT EXISTS (SELECT 1 FROM alpha.course_component_member member JOIN alpha.course_component component ON component.tenant_id=member.tenant_id AND component.component_id=member.target_component_id WHERE member.tenant_id=:tenant_id AND member.course_id=:course_id AND member.retired_at IS NULL AND component.kind IN ('lesson','review','practice','mastery_gate')) OR EXISTS (SELECT 1 FROM alpha.course_component_member member JOIN alpha.course_component component ON component.tenant_id=member.tenant_id AND component.component_id=member.target_component_id WHERE member.tenant_id=:tenant_id AND member.course_id=:course_id AND (member.retired_at IS NOT NULL OR component.retired_at IS NOT NULL OR component.publication_status<>'published')) THEN RAISE EXCEPTION 'curriculum:publication_incomplete'; END IF;
END $$;
UPDATE alpha.course SET publication_status='published',updated_at=now() WHERE tenant_id=:tenant_id AND course_id=:course_id;
COMMIT;

Guardrails: Dry-run firstDeterministic same-run upsert identitiesEvery course/component is draft before assembly writesExact intended-tree readback precedes publicationPublish descendants bottom-up and the course root lastAny write/readback/publish failure rolls back or leaves the root draftPreserve source parentage and orderContent resolves resource bodies first and returns the exact versionNever infer latest or copy Content eligibility evidenceExact Content release eligibility is required at authoring and launchEvery lesson has a non-empty active KC cluster and multi-KC scope is preservedStandards evidence returns curriculum:adapter_rejected and never writes CASE rowsQuiz label alone does not create a gateSource-empty anchors stay non-routable

Problems: curriculum:validation_failedcurriculum:adapter_rejectedcurriculum:lesson_structure_missingcurriculum:publication_incompletecurriculum:content_not_eligiblecurriculum:content_unavailablecurriculum:content_version_mismatchcurriculum:invalid_content_bank_member_contractcurriculum:idempotency_conflict

curriculum_source_shaped_ingest_adapter

Raw/API twin

Resolve placement candidates from a versioned Policy reference

API: GET /alpha/curriculum/v1/tracks/{trackId}/placement-candidates?studentId=:student_id&subjectId=:subject_id

SELECT m.member_id,m.position,m.target_component_id,c.entry_policy_ref FROM alpha.course c JOIN alpha.course_component_member m ON m.tenant_id=c.tenant_id AND m.course_id=c.course_id AND m.retired_at IS NULL WHERE c.tenant_id=:tenant_id AND c.course_id=:track_id AND c.retired_at IS NULL ORDER BY m.position,m.member_id; -- Apply the returned versioned entry_policy_ref through Policy/Results; MAP/RIT may seed the start only.

Guardrails: Tenant scopeActive ordered membersPolicy owns the algorithm/numbersMAP/RIT is not final placementStudent state stays in Results

Problems: curriculum:not_foundcurriculum:reference_unresolvedcurriculum:unsupported_query_parameter

curriculum_five_use_cases

Raw/API twin

Route to the next active instructional member

API: GET /alpha/curriculum/v1/courses/{courseId}/next-lesson?studentId=:student_id&subjectId=:subject_id

WITH RECURSIVE course_root AS (
  SELECT course_id,publication_status,retired_at FROM alpha.course WHERE tenant_id=:tenant_id AND course_id=:course_id
), tree AS (
  SELECT m.tenant_id,m.course_id,m.member_id,m.parent_component_id,m.position,m.target_kind,m.target_component_id,ARRAY[m.position]::integer[] AS position_path,ARRAY[m.member_id]::uuid[] AS member_path,(m.retired_at IS NULL AND component.publication_status='published' AND component.retired_at IS NULL) AS ancestor_publication_safe
  FROM alpha.course_component_member m JOIN alpha.course_component component ON component.tenant_id=m.tenant_id AND component.component_id=m.target_component_id
  WHERE m.tenant_id=:tenant_id AND m.course_id=:course_id AND m.parent_component_id IS NULL AND m.target_kind='course_component'
  UNION ALL
  SELECT child.tenant_id,child.course_id,child.member_id,child.parent_component_id,child.position,child.target_kind,child.target_component_id,parent.position_path||child.position,parent.member_path||child.member_id,(parent.ancestor_publication_safe AND child.retired_at IS NULL AND component.publication_status='published' AND component.retired_at IS NULL)
  FROM alpha.course_component_member child JOIN tree parent ON child.tenant_id=parent.tenant_id AND child.course_id=parent.course_id AND child.parent_component_id=parent.target_component_id JOIN alpha.course_component component ON component.tenant_id=child.tenant_id AND component.component_id=child.target_component_id
  WHERE child.target_kind='course_component' AND NOT child.member_id=ANY(parent.member_path)
), first_frontier AS (
  SELECT tree.member_id,tree.position,tree.position_path,tree.target_component_id,component.kind,component.scope_kc_ids,component.kc_scope_decision,tree.ancestor_publication_safe
  FROM tree JOIN alpha.course_component component ON component.tenant_id=tree.tenant_id AND component.component_id=tree.target_component_id
  WHERE component.kind IN ('lesson','review','practice','mastery_gate') ORDER BY tree.position_path,tree.member_id LIMIT 1
)
SELECT frontier.member_id,frontier.position,frontier.position_path,frontier.target_component_id,frontier.kind,frontier.scope_kc_ids,frontier.kc_scope_decision
FROM course_root root CROSS JOIN first_frontier frontier
WHERE root.publication_status='published' AND root.retired_at IS NULL AND frontier.ancestor_publication_safe;
-- Results supplies completed/current state; choose the first eligible row there, never in Curriculum storage.

Guardrails: Tenant + active-row predicatesChoose the first frontier before publication filtering so an unpublished node is never skippedCourse root, selected frontier, and every containment ancestor must be published and not retiredCurrent graph must match an immutable approval with a complete exact firm KC-version snapshot for every scoped KCThe exact Content target is revalidated through Content-owned release eligibility at launch and latest is never substitutedDraft, retired, changed, unapproved, incomplete, unavailable, malformed, mismatched, or ineligible state fails closedLexicographic ancestor position_path preserves sibling-local hierarchy orderCycle paths are rejectedAn ordinary lesson has a non-empty active KC cluster and preserves multi-KC scope; an exact content-bearing lesson may instead echo scope_kc_ids=[] plus kc_scope_decision=authoritative_no_kc and contributes no KC snapshot or mastery evidenceResults supplies student stateMissing authoritative members fails closed

Problems: curriculum:not_foundcurriculum:lesson_structure_missingcurriculum:course_not_routablecurriculum:content_not_eligiblecurriculum:content_unavailablecurriculum:content_version_mismatchcurriculum:invalid_content_bank_member_contractcurriculum:unsupported_query_parameter

curriculum_five_use_cases

Raw/API twin

Read a gate contract and typed remediation without client classification

API: GET /alpha/curriculum/v1/gates/{gateId}/status?studentId=:student_id; GET /alpha/curriculum/v1/gates/{gateId}/remediation?studentId=:student_id&resultRecordId=:result_id

SELECT m.member_id,m.passing_criteria_ref,m.on_fail,m.content_id,m.content_version_id,m.content_kind FROM alpha.course_component_member m WHERE m.tenant_id=:tenant_id AND m.member_id=:gate_id AND m.gates=true AND m.retired_at IS NULL; -- Resolve passing_criteria_ref through Policy and result_id through Results. Return on_fail entries exactly; never persist the student's inserted remediation path in Curriculum.

Guardrails: Gate behavior is member-localPolicy numbers are referencedResults outcomes are read, not copiednever_learned→lessonforgot→reviewcareless→practice

Problems: curriculum:not_foundcurriculum:invalid_gate_contractcurriculum:reference_unresolved

curriculum_five_use_cases

Raw/API twin

Select the lowest-index unseen eligible form for a fixed-form gate

API: GET /alpha/curriculum/v1/gates/{gateId}/next-form?studentId=:student_id

WITH base_gate AS (
  SELECT content_id,content_version_id FROM alpha.course_component_member WHERE tenant_id=:tenant_id AND member_id=:gate_id AND gates=true AND content_kind='test_bank' AND retired_at IS NULL
), content_eligible_versions AS (
  -- Populate from 200 ordinary exact-version release-eligibility responses, or for one Content-authorized
  -- demo test identity from current test-serve eligibility responses. Test mode returns only after
  -- Content appends the exact bank/form authorization receipt.
  SELECT content_id,content_version_id FROM jsonb_to_recordset(:validated_content_eligible_versions::jsonb) AS eligible(content_id text,content_version_id text)
), eligible_gate AS (
  SELECT gate.* FROM base_gate gate JOIN content_eligible_versions eligible ON eligible.content_id=gate.content_id::text AND eligible.content_version_id=gate.content_version_id::text
), raw_bank_members AS (
  -- Read every active row for the exact bank version before applying scope or eligibility filters.
  -- LEFT JOINs keep missing targets visible so malformed legacy rows cannot disappear.
  SELECT r.content_reference_id,r.referenced_content_artifact_id AS referenced_content_id,
         r.referenced_content_version_id AS referenced_version_id,r.form_index,r.usage_scope,
         referenced_ext.usage_scope AS referenced_usage_scope,
         CASE referenced_artifact.artifact_kind WHEN 'test' THEN 'test' WHEN 'item' THEN 'question' ELSE NULL END AS content_kind,
         referenced_test.assessment_role
  FROM alpha.content_reference r
  JOIN platform.tenant tenant ON tenant.tenant_id=r.tenant_id
  JOIN eligible_gate gate ON r.parent_content_artifact_id=gate.content_id AND r.parent_content_version_id=gate.content_version_id
  LEFT JOIN qti.artifact referenced_artifact ON referenced_artifact.tenant_id=r.tenant_id AND referenced_artifact.artifact_id=r.referenced_content_artifact_id
  LEFT JOIN alpha.content_item_extension referenced_ext ON referenced_ext.tenant_id=r.tenant_id AND referenced_ext.content_artifact_id=r.referenced_content_artifact_id
  LEFT JOIN alpha.assessment_test_extension referenced_test ON referenced_test.tenant_id=r.tenant_id AND referenced_test.content_artifact_id=r.referenced_content_artifact_id
  WHERE tenant.tenant_key=:tenant_key AND r.relationship_kind='bank_member' AND r.retired_at IS NULL
  ORDER BY r.form_index ASC NULLS LAST,r.content_reference_id
  LIMIT 51
), bank_member_contract AS (
  SELECT count(*) AS member_count,
         count(*) FILTER (WHERE content_kind IS DISTINCT FROM 'test' OR assessment_role IS DISTINCT FROM 'form' OR referenced_content_id IS NULL OR referenced_version_id IS NULL OR form_index IS NULL OR form_index<=0) AS malformed_count,
         count(form_index)-count(DISTINCT form_index) AS duplicate_form_index_count
  FROM raw_bank_members
), candidate_forms AS (
  SELECT member.referenced_content_id,member.referenced_version_id,member.form_index
  FROM raw_bank_members member CROSS JOIN bank_member_contract contract
  WHERE contract.malformed_count=0 AND contract.duplicate_form_index_count=0
    AND member.usage_scope='practice_eligible' AND member.referenced_usage_scope='practice_eligible'
), eligible_forms AS (
  SELECT candidate.* FROM candidate_forms candidate JOIN content_eligible_versions eligible ON eligible.content_id=candidate.referenced_content_id AND eligible.content_version_id=candidate.referenced_version_id
), seen_forms AS (
  SELECT data->>'content_id' AS content_id,data->>'content_version_id' AS content_version_id
  FROM alpha.result_content_evidence
  WHERE tenant_id=:tenant_key AND data->>'student_id'=:student_id
), unseen_forms AS (
  SELECT form.* FROM eligible_forms form LEFT JOIN seen_forms seen ON seen.content_id=form.referenced_content_id AND seen.content_version_id=form.referenced_version_id WHERE seen.content_id IS NULL
), next_form AS (
  SELECT * FROM unseen_forms ORDER BY form_index,referenced_content_id LIMIT 1
)
SELECT (SELECT count(*) FROM base_gate) AS gate_count,(SELECT count(*) FROM eligible_gate) AS eligible_gate_count,contract.member_count,contract.malformed_count,contract.duplicate_form_index_count,(SELECT count(*) FROM candidate_forms) AS candidate_count,(SELECT count(*) FROM eligible_forms) AS eligible_count,(SELECT count(*) FROM unseen_forms) AS unseen_count,next_form.referenced_content_id,next_form.referenced_version_id,next_form.form_index
FROM bank_member_contract contract LEFT JOIN next_form ON true;
-- Before scope filtering or form eligibility, map malformed_count>0 or duplicate_form_index_count>0 to curriculum:invalid_content_bank_member_contract. Map gate_count=0 to curriculum:not_found, eligible_gate_count=0 to curriculum:content_not_eligible, member_count=0 or >50 to curriculum:invalid_gate_contract, eligible_count=0 to curriculum:content_not_eligible, and eligible_count>0 plus unseen_count=0 to curriculum:bank_exhausted. Never synthesize form_index from display_order or row position, and never return an empty 200.

Guardrails: PP100 v2 gates reject this fixed-form route because Results owns adaptive question selection from the exact pp100_adaptive_shards bankTargetless drafts fail before Content and membership readsA complete exact-bank draft is limited to Content's demo grant plus live People & Orgs test/synthetic realityPublished gates try ordinary release eligibility first; only authoritative ineligibility can enter test-serveContent invalid-bank contract answers map to the typed Curriculum 409 and never enter test-serve, appear empty, or skipTransport, timeout, malformed, and target-mismatch outcomes fail closedContent owns exact-version bank membershipRead and validate every active exact-bank row before applying usage-scope or eligibility filtersEach member must be an exact assessment_role=form test with a unique positive integer form_indexNever coalesce or synthesize form_index from display_order or row positionPin parent_content_id + parent_version_idThe bank version and every candidate form version must each pass the selected Content eligibility laneTest mode commits an immutable exact bank/form Content receipt before returnBoth Content usage scopes must be practice_eligibleResults owns seen-form evidenceStable ascending form_indexNever client-chosen or header-enabledMissing, ineligible, malformed, invalid-bank, and exhausted are distinct

Problems: curriculum:not_foundcurriculum:invalid_gate_contractcurriculum:content_not_eligiblecurriculum:content_unavailablecurriculum:invalid_content_bank_member_contractcurriculum:bank_exhausted

curriculum_mastery_gate_test_bank_reference

Raw/API twin

Preview or import a Common Cartridge without creating a second store

API: POST /alpha/curriculum/v1/transports/common-cartridge/preview; POST /alpha/curriculum/v1/transports/common-cartridge/import with Idempotency-Key

SELECT c.course_id,m.member_id,m.position,m.target_kind,m.target_component_id,m.content_id,m.content_version_id FROM alpha.course c JOIN alpha.course_component_member m ON m.tenant_id=c.tenant_id AND m.course_id=c.course_id AND m.retired_at IS NULL WHERE c.tenant_id=:tenant_id AND c.course_id=:course_id AND c.retired_at IS NULL ORDER BY m.position,m.member_id; -- Join CASE standards and Content package bytes through their owning dictionaries to build/validate the transport; do not INSERT a cartridge authority row.

Guardrails: Transport onlyNo stored cartridge authorityStable identifiersCASE/Curriculum/Content owners remain separateImport is idempotent

Problems: curriculum:transport_invalidcurriculum:reference_unresolvedcurriculum:idempotency_conflict

curriculum_common_cartridge_transport_only

Raw/API twin

Discover the adaptive-eligible KC corpus from the owning registry

API: GET /alpha/curriculum/v1/kcs; GET /alpha/curriculum/v1/kcs/{kcId}

SELECT kc.kc_id,kc.lineage_id,kc.kc_kind,kc.subject_id,kc.current_version_id,kc.publication_status,version.version_number,version.status,version.definition FROM alpha.knowledge_component kc JOIN alpha.knowledge_component_version version ON version.tenant_id=kc.tenant_id AND version.version_id=kc.current_version_id WHERE kc.tenant_id=:tenant_id AND kc.retired_at IS NULL AND kc.current_version_id IS NOT NULL ORDER BY kc.updated_at DESC,kc.kc_id LIMIT :limit;

Guardrails: The registry is the single KC owner: adaptive-diagnostics consumers (for example AlphaTest adaptive_diagnostic blueprints) discover Knowledge Components here, never from case.cf_item or a CASE package, and no KC row is ever copied or relabeled into the standards catalogAdaptive-eligible means an active owner-typed registry KC: retired rows and the retained unclassified #707 legacy adoption row (kc_kind NULL, no current version) are never discoverable and never adaptive-eligibleEvery returned identity is the canonical kc_id UUID and reads back through GET /alpha/curriculum/v1/kcs/{kcId}; superseded legacy CASE KC rows remain immutable migration evidence onlyCASE package/framework identity for standards remains the contained CFDocument.identifier per official CASE 1.1; KC discovery never mints a CASE package identityStable updated_at desc, kc_id asc order with an opaque server-issued cursor; unsupported query parameters and malformed cursors fail closed

Problems: curriculum:unsupported_query_parametercurriculum:validation_failed

curriculum_kc_registry_standard_separation

Endpoint contract

MethodPathStateWrite behavior
POST/alpha/curriculum/v1/standards-frameworks/importauthoritative target pending cutoverIdempotency-Key. bounded serializable import. Immutable.
GET/alpha/curriculum/v1/standards-frameworkstarget contract; same path currently serves CASE-backed compatibilitystable list read with typed filters/cursor or inherited CASE offset. Immutable.
GET/alpha/curriculum/v1/standards-frameworks/{frameworkId}target contract; same path currently serves CASE-backed compatibilitydetail read with immutable source-release links. Immutable.
GET/alpha/curriculum/v1/standards-source-releasesauthoritative target pending cutoverimmutable created-at cursor list. Immutable.
GET/alpha/curriculum/v1/standards-source-releases/{sourceReleaseId}authoritative target pending cutoverread. Immutable.
GET/alpha/curriculum/v1/standards-source-releases/{sourceReleaseId}/readbackauthoritative target pending cutoverread. Immutable.
POST/alpha/curriculum/v1/kcsauthoritative governed implementationIdempotency-Key. identity ETag after create; optional source_kc_id is tenant-unique provenance, never an active alias.
GET/alpha/curriculum/v1/kcsauthoritative governed implementationtenant-isolated stable updated_at/kc_id cursor list of active registry KCs with their current immutable versions; typed subject_id/kc_kind/publication_status/updated_since filters; rows carry the same current-version readback as GET /kcs/{kcId}; retired rows and the retained unclassified #707 legacy row (no current version) are never listed.
GET/alpha/curriculum/v1/kcs/{kcId}authoritative governed implementationexact registry UUID read of current immutable version with ETag.
POST/alpha/curriculum/v1/kcs/{kcId}/versionsauthoritative governed implementationIdempotency-Key. append only. Immutable.
GET/alpha/curriculum/v1/kcs/{kcId}/versionsauthoritative governed implementationimmutable version-number cursor list. Immutable.
GET/alpha/curriculum/v1/kcs/{kcId}/versions/{versionId}authoritative governed implementationread. Immutable.
PATCH/alpha/curriculum/v1/kcs/{kcId}authoritative governed implementationIdempotency-Key. If-Match required.
DELETE/alpha/curriculum/v1/kcs/{kcId}authoritative governed implementationIdempotency-Key. If-Match; soft retire only; friendly active-reference precheck plus native content_kc_tag_active_kc_fk commit invariant; active Curriculum references and active Content tags return curriculum:resource_referenced, while serialization conflicts retry.
GET/alpha/curriculum/v1/kc-prerequisitesauthoritative governed implementationstable cursor list; registry UUID endpoint filters.
POST/alpha/curriculum/v1/kc-prerequisitesauthoritative governed implementationIdempotency-Key. tenant graph lock inside SERIALIZABLE; registry UUID endpoints only.
DELETE/alpha/curriculum/v1/kc-prerequisites/{prerequisiteId}authoritative target pending cutoverIdempotency-Key. If-Match; soft retire.
GET/alpha/curriculum/v1/kc-relationsauthoritative target pending cutoverstable cursor list; kc/relation_type filters.
POST/alpha/curriculum/v1/kc-relationsauthoritative target pending cutoverIdempotency-Key. If-Match on edge corrections.
DELETE/alpha/curriculum/v1/kc-relations/{relationId}authoritative target pending cutoverIdempotency-Key. If-Match; soft retire.
GET/alpha/curriculum/v1/kc-review-decisionsauthoritative governed implementationimmutable decided-at cursor list. Immutable.
POST/alpha/curriculum/v1/kc-review-decisionsauthoritative governed implementationIdempotency-Key. append only. Immutable.
GET/alpha/curriculum/v1/kc-firming-eventsauthoritative governed implementationimmutable firmed-at cursor list. Immutable.
POST/alpha/curriculum/v1/kc-firming-eventsauthoritative governed implementationIdempotency-Key. append only after governed first-genuine-response evidence; course approval creates its own events atomically. Immutable.
GET/alpha/curriculum/v1/standard-kc-mapsauthoritative governed implementationtenant-isolated stable updated_at/map_id cursor list; typed source_release_id/standard_id/kc_id/relationship/updated_since filters; exact ten-field rows.
POST/alpha/curriculum/v1/standard-kc-mapsauthoritative target pending cutoverIdempotency-Key. ETag after create.
PATCH/alpha/curriculum/v1/standard-kc-maps/{mapId}authoritative target pending cutoverIdempotency-Key. If-Match required.
DELETE/alpha/curriculum/v1/standard-kc-maps/{mapId}authoritative target pending cutoverIdempotency-Key. If-Match; soft retire.
GET/alpha/curriculum/v1/coursesauthoritative target pending cutoverstable cursor list; role/subject/status/updated_since filters.
POST/alpha/curriculum/v1/courses/{courseId}/approvalsauthoritative governed implementation over the current CASE-backed course graphIdempotency-Key. serializable complete graph snapshot and firming transaction. Immutable.
GET/alpha/curriculum/v1/courses/{courseId}/approvalsauthoritative governed implementationimmutable approved-at cursor list. Immutable.
GET/alpha/curriculum/v1/course-approvals/{approvalId}/kc-snapshotauthoritative target pending cutoverimmutable list. Immutable.
POST/alpha/curriculum/v1/coursesauthoritative target pending cutoverIdempotency-Key. ETag after create.
GET/alpha/curriculum/v1/courses/{courseId}authoritative target pending cutoverread with ETag.
PATCH/alpha/curriculum/v1/courses/{courseId}authoritative target pending cutoverIdempotency-Key. If-Match required.
DELETE/alpha/curriculum/v1/courses/{courseId}authoritative target pending cutoverIdempotency-Key. If-Match; soft retire only.
GET/alpha/curriculum/v1/componentstarget contract; same path currently serves CASE-backed compatibilitystable cursor list; kind/subject/status/updated_since filters.
POST/alpha/curriculum/v1/componentsdeployed CASE compatibility; authoritative target pending cutover; kind=lesson permits scope_kc_refs=[] only with exact eligible Content target + compatible renderer + kc_scope_decision=authoritative_no_kcIdempotency-Key. transactional insert-or-reactivate; same retired identity preserves created_at, advances CASE version, clears retired_at, returns active ETag.
GET/alpha/curriculum/v1/components/{componentId}authoritative target pending cutoverread with ETag.
PATCH/alpha/curriculum/v1/components/{componentId}deployed CASE compatibility plus authoritative target pending cutoverIdempotency-Key. If-Match required; isolated parent_component_id + position reparent is one locked transaction and requires exactly one active parent.
POST/alpha/curriculum/v1/components/{componentId}/repointauthoritative governed implementation over the current CASE-backed component graphIdempotency-Key. expected_target_ref is a mandatory compare-and-set premise over the exact current pin (optional If-Match additionally honored); the pin move and its immutable audit row commit in one CAS-guarded transaction.
DELETE/alpha/curriculum/v1/components/{componentId}authoritative target pending cutoverIdempotency-Key. If-Match; active references block or atomically repoint.
GET/alpha/curriculum/v1/courses/{courseId}/membersauthoritative target pending cutoverstable ordered cursor list.
POST/alpha/curriculum/v1/courses/{courseId}/membersauthoritative target pending cutoverIdempotency-Key. If-Match course graph.
PATCH/alpha/curriculum/v1/courses/{courseId}/members/{memberId}authoritative target pending cutoverIdempotency-Key. If-Match course graph.
DELETE/alpha/curriculum/v1/courses/{courseId}/members/{memberId}authoritative target pending cutoverIdempotency-Key. If-Match; soft retire.
GET/alpha/curriculum/v1/courses/{courseId}/treetarget contract; same path currently serves CASE-backed compatibilityresolved identity-preserving read.
GET/alpha/curriculum/v1/courses/{courseId}/expected-xpauthoritative target pending cutoverread.
GET/alpha/curriculum/v1/courses/{courseId}/next-lessontarget contract; same path currently serves CASE-backed compatibilityResults-assisted read.
GET/alpha/curriculum/v1/tracks/{trackId}/placement-candidatestarget contract; same path currently serves CASE-backed compatibilityPolicy/Results-assisted read.
GET/alpha/curriculum/v1/gatestarget contract pending cutoverstable cursor list over resolved active gate members; course/policy/content-kind filters.
GET/alpha/curriculum/v1/gates/{gateId}target contract pending cutoverresolved gate contract detail without student state.
GET/alpha/curriculum/v1/gates/{gateId}/statustarget contract; same path currently serves CASE-backed compatibilityPolicy/Results-assisted read.
GET/alpha/curriculum/v1/gates/{gateId}/next-formfixed-form gates only; PP100 v2 rejects because Results owns adaptive selectionContent/Results-assisted deterministic read; test mode commits one Content authorization receipt.
GET/alpha/curriculum/v1/gates/{gateId}/remediationtarget contract; same path currently serves CASE-backed compatibilityResults-assisted read.
GET/alpha/curriculum/v1/kc-setsauthoritative target pending cutoverstable projection list.
GET/alpha/curriculum/v1/kc-sets/{setId}authoritative target pending cutoverread-only projection.
POST/alpha/curriculum/v1/ingest/timeback-course-refstarget contract; same path currently writes CASE-backed compatibilityIdempotency-Key. bounded source-shaped import; shared containment lock rejects parent replacement or second-parent creation.
POST/alpha/curriculum/v1/transports/common-cartridge/previewauthoritative target pending cutoverIdempotency-Key. side-effect-free deterministic preview. Immutable.
POST/alpha/curriculum/v1/transports/common-cartridge/importauthoritative target pending cutoverIdempotency-Key. bounded transport import.
GET/alpha/curriculum/v1/import-jobsauthoritative target pending cutoverstable created-at cursor list. Immutable.
GET/alpha/curriculum/v1/import-jobs/{importJobId}authoritative target pending cutoverread. Immutable.

API axes

write-granularity

Per-resource target writes plus named bulk commands for standards release, Common Cartridge, and TimeBack ingest; no second KC representation or permanent dual write.

curriculum_api_write_granularity

read-shape

List, detail, and narrow sub-collection reads preserve identity/member separation and expose stored governed outputs without Incept computations.

curriculum_api_read_shape

query

Typed filters, stable sort, cursor paging, CASE offset only where inherited, updated_since polling, and 400 curriculum:unsupported_query_parameter for every unsupported parameter.

curriculum_api_query_model

concurrency

Mutable rows require ETag/If-Match (428 missing, 412 stale); immutable evidence/version/snapshot/event/audit rows have no PATCH route.

curriculum_api_concurrency

idempotency

Every POST/PATCH/DELETE/import/remediation insertion uses Idempotency-Key in platform.idempotency_key with module=curriculum and surface=alpha. Exactly one server lease-token owner may mutate and finalize a canonical request hash; a live same-hash contender receives bounded Retry-After, a different hash conflicts, and only an explicitly rollback-safe failed_transient classification may be reclaimed. Curriculum Content eligibility may use failed_transient only when the typed 503 is marked at a proven pre-mutation create/update/TimeBack-ingest boundary and the owner finalizes no response, resource, header, or lock evidence; generic or post-mutation 5xx remains commit_uncertain.

curriculum_api_idempotency

auth

Platform HS256 bearer JWT with tenantId, role(s), and curriculum:read/write/import/admin scopes; no school/class/student-scoped claims on shared Curriculum rows.

curriculum_api_auth

eventing

Poll list endpoints with updated_since. Webhooks remain deferred until three approved integrations in 90 days prove polling blocks a Curriculum workflow.

curriculum_api_eventing

errors

RFC 7807 with stable type URI, curriculum: code, requestId, traceId, fieldErrors, and rowErrors for bulk commands; Problems never use HTTP 200.

curriculum_api_error_envelope

conformance

Local and deployed evidence covers ownership, KC lifecycle/graphs, standards provenance/readback, reuse, source ingest, transport round-trip, auth/errors/idempotency/concurrency, and leak-free downstream consumers; no official certification claim.

curriculum_api_conformance_evidence

privacy

Tenant scope, audit provenance, soft-retire semantics, immutable source history, redacted errors/logs/evidence, and no learner PII beyond typed personalization refs. An active exact isChildOf child/parent pair is the current containment projection and supersedes retired same-pair history without erasing its tombstone. Retention/erasure waits for Platform policy.

curriculum_api_privacy_retention

lists

List every canonical collection named by the architecture. Derived per-kind/tree reads aid discovery but never become storage authority; no Incept queue/novelty/monitor/plan endpoints.

curriculum_api_list_endpoints

Typed RFC 7807 Problems

HTTP 400

curriculum:validation_failed

Field/type/enum/reference validation fails.

Extra fields: type, title, status, detail, code, requestId, traceId, fieldErrors

HTTP 413

curriculum:payload_too_large

An authenticated JSON request exceeds the bounded request-body limit; reject before repository or idempotency work and never echo request bytes.

HTTP 400

curriculum:unsupported_query_parameter

A query parameter is not in the endpoint contract.

Extra fields: fieldErrors

HTTP 401

curriculum:authentication_required

Bearer JWT is absent, invalid, expired, or not trusted.

HTTP 403

curriculum:forbidden

JWT tenant/scope/role does not authorize the operation.

HTTP 404

curriculum:not_found

Tenant-scoped active identity is absent.

HTTP 412

curriculum:precondition_failed

If-Match validator is stale.

HTTP 428

curriculum:precondition_required

A mutable write omits If-Match.

HTTP 409

curriculum:idempotency_in_progress

A live same-hash Curriculum lease already owns this retryable write; return Retry-After between 1 and 30 seconds and never enter the mutation callback.

HTTP 409

curriculum:idempotency_conflict

An Idempotency-Key is replayed with a different canonical request hash.

HTTP 409

curriculum:idempotency_key_expired

The stored key is expired and cannot prove a safe single-owner retry; fail closed without mutation.

HTTP 500

curriculum:idempotency_commit_uncertain

A prior owner may have committed a side effect without a trustworthy terminal receipt, or the ledger is legacy/tokenless/corrupt; fail closed without reclaim or mutation.

HTTP 409

curriculum:kc_semantics_conflict

KC creation reuses a canonical kc_id with different governed semantics, or reuses provenance-only source_kc_id for a different or server-minted canonical kc_id even when semantics match.

Extra fields: fieldErrors

HTTP 422

curriculum:standards_scope_invalid

Declared/partial scope is empty, dishonest, or inconsistent with bound rows.

Extra fields: fieldErrors, rowErrors

HTTP 422

curriculum:rights_unresolved

Effective rights/admission is yellow, red, unknown, expired, missing, or conflicting.

Extra fields: rowErrors

HTTP 422

curriculum:standards_readback_mismatch

Any source row/readback checksum or typed comparison fails.

Extra fields: rowErrors

HTTP 422

curriculum:invalid_kc_kind

kc_kind is not substantive, disciplinary, application, or integrative.

Extra fields: fieldErrors

HTTP 422

curriculum:kc_definition_quality

A KC version lacks the governed definition, examples, boundary, or provenance required for its lifecycle step.

Extra fields: fieldErrors

HTTP 422

curriculum:invalid_kc_relation

A relation type/endpoint is invalid, prerequisite is sent to the non-DAG relation store, or a lineage transition violates lineage_id rules.

Extra fields: fieldErrors

HTTP 422

curriculum:unknown_kc

A referenced KC is missing, retired, cross-tenant, or not an active registry identity.

Extra fields: fieldErrors

HTTP 409

curriculum:prerequisite_cycle

A prerequisite write would create a directed cycle.

Extra fields: fieldErrors

HTTP 409

curriculum:invalid_kc_lifecycle

A KC version is edited in place or firmed outside the two allowed paths.

Extra fields: fieldErrors

HTTP 422

curriculum:live_learning_evidence_required

A first_genuine_response firming write lacks validated first-response evidence with explicit live_learning=true.

Extra fields: fieldErrors

HTTP 409

curriculum:approval_snapshot_incomplete

An approval cannot pin every distinct active KC to one exact version.

Extra fields: rowErrors

HTTP 409

curriculum:course_not_routable

The current course graph is draft, retired, unapproved, changed since approval, or has a missing/non-firm KC snapshot.

Extra fields: fieldErrors

HTTP 422

curriculum:invalid_component_kind

kind is outside component_kind or violates its per-kind schema.

Extra fields: fieldErrors

HTTP 422

curriculum:reference_unresolved

A KC, component, Content version, renderer, policy, standard binding, or parent reference is absent, retired, cross-tenant, cyclic, or incompatible.

Extra fields: fieldErrors

HTTP 422

curriculum:content_not_eligible

Content authoritatively reports the exact version missing, blocked, revoked, otherwise non-green, malformed, or unresolved.

Extra fields: fieldErrors

HTTP 503

curriculum:content_unavailable

The exact-version eligibility read fails by network error, bounded timeout, or upstream HTTP 5xx. Return bounded Retry-After; only a proven pre-mutation component create/update or TimeBack ingest may leave an evidence-free failed_transient receipt for identical-key recovery.

Extra fields: cause

HTTP 422

curriculum:content_version_mismatch

content_version_id does not belong to content_id or content_kind does not match Content readback.

Extra fields: fieldErrors

HTTP 409

curriculum:invalid_content_bank_member_contract

Content returns content.invalid_test_bank_member_contract or content.bank_kc_attribution_incomplete for the exact test-bank graph; expose the safe upstream code as the public RFC 7807 cause field and never convert this into generic ineligibility, transport failure, empty membership, fallback, or skip behavior.

Extra fields: fieldErrors, cause

HTTP 422

curriculum:renderer_incompatible

renderer_ref is missing, inactive, unknown, or rejects content_kind.

Extra fields: fieldErrors

HTTP 409

curriculum:repoint_pin_stale

A repoint/attach expected_target_ref compare-and-set premise does not equal the component's exact current pin — including attach against an already-pinned component and repoint against a superseded or legacy versionless pin. Nothing is written.

Extra fields: fieldErrors

HTTP 409

curriculum:repoint_identity_mismatch

A repoint attempts to change content_id or content_kind. A revision published under a new Content identity is a NEW identity: attach it to an unpinned placement instead of silently replacing an existing pin.

Extra fields: fieldErrors

HTTP 409

curriculum:repoint_target_not_serveable

Content authoritatively refuses the requested exact version for a repoint/attach — missing, not serveable, not release-eligible, or outside practice_eligible usage scope. The safe upstream cause is exposed as the RFC 7807 cause field; transient Content unavailability stays 503 curriculum:content_unavailable.

Extra fields: cause

HTTP 409

curriculum:approved_chain_repoint_frozen

Producer self-service repoint/attach targets a published component pinning an approved immutable assessment chain. The pin moves only through the reviewed-replacement manifest path: a committed reviewed manifest applied through the platform's dry-run-first ETag/CAS corridor.

Extra fields: fieldErrors

HTTP 409

curriculum:member_position_conflict

Two active siblings claim one position.

Extra fields: fieldErrors

HTTP 409

curriculum:relationship_cycle

A component reparent or governed prerequisite mutation would make a node its own ancestor.

Extra fields: fieldErrors

HTTP 409

curriculum:lesson_structure_missing

A course anchor exists but has no active ordered, KC-scoped routable member structure.

Extra fields: rowErrors

HTTP 409

curriculum:publication_incomplete

A course/component publish is attempted before exact tree readback or before every descendant is published bottom-up; course roots must publish last.

Extra fields: fieldErrors, rowErrors

HTTP 409

curriculum:expected_xp_ambiguous

A graph/cycle/target error prevents deterministic leaf/fallback selection.

Extra fields: rowErrors

HTTP 422

curriculum:invalid_gate_contract

A gating member lacks valid Content target, passing criteria, remediation map, or test-bank/spec compatibility.

Extra fields: fieldErrors

HTTP 409

curriculum:bank_exhausted

Every eligible form in the gate's Content test_bank has already been seen by the student.

HTTP 422

curriculum:adapter_rejected

A source-shaped TimeBack row cannot materialize without inventing order, KC scope, Content identity, or gate semantics.

Extra fields: rowErrors

HTTP 422

curriculum:transport_invalid

A Common Cartridge/CFPackage preview or import fails structure, reference, or round-trip validation.

Extra fields: rowErrors

Auth, tenancy, idempotency, concurrency

authentication: Platform HS256 bearer JWT; tenantId plus curriculum:read/write/import/admin scopes. tenant_id is never caller-selected in path/body.

idempotency: Every POST/PATCH/DELETE/import uses Idempotency-Key stored in platform.idempotency_key with module=curriculum,surface=alpha. Claim, completion, and failure are conditional on the exact scope, canonical hash, curriculum-v2 protocol, and a server lease token; requestId is audit identity only. Exactly one token owner mutates. Terminal same-hash rows replay exact stored status/body/headers, live same-hash rows return 409 curriculum:idempotency_in_progress with bounded Retry-After, different hashes return 409 curriculum:idempotency_conflict, and reclaim requires an unexpired failed_transient row explicitly classified rollback-safe with no response, resource, header, or lock evidence. A typed curriculum:content_unavailable error qualifies only at the proven pre-mutation component create/update and TimeBack-ingest boundaries; any unmarked or post-mutation 5xx remains commit_uncertain. Expired, legacy/tokenless stale, corrupt, ordinary stale, and commit-uncertain rows fail closed.

concurrency: Mutable identities/edges use ETag and If-Match; missing is 428, stale is 412. Immutable releases, bindings, rights decisions, readback evidence, KC versions, review decisions, approvals, snapshots, and firming events have no PATCH route.

tenancy: Every PK/FK/unique query includes tenant_id from the verified JWT. Raw SQL examples use :tenant_id and must execute inside the same tenant-scoped transaction/RLS context.

polling: List endpoints use stable sort, cursor paging, and updated_since only where rows are mutable. Immutable evidence uses created-at cursors.

privacy: No learner PII, Content bodies/answers, producer credentials, source secrets, or unrestricted copyrighted source dumps in Problems/logs/evidence.

Confusing alternate guidance is forbidden

  • Never treat “KCs are CASE CFItems” as current guidance.
  • Never treat “KC isChildOf is the canonical KC tree” as current guidance.
  • Never treat “KC-to-standard maps are CASE isPartOf associations” as current guidance.
  • Never treat “Course/component/member is one CASE CFItem row” as current guidance.
  • Never treat “target_ref or gate behavior belongs on reusable component identity” as current guidance.
  • Never treat “Container expected_xp is added to descendant expected_xp” as current guidance.
  • Never treat “CASE transport grants content-use rights” as current guidance.
  • Never treat “Acquired means admitted” as current guidance.
  • Never treat “Yellow may proceed with warning” as current guidance.
  • Never treat “Curriculum stores Content producer verdict, quality bar, release status, or evidence” as current guidance.
  • Never treat “Publication or first assignment firms a KC” as current guidance.
  • Never treat “Incept steward monitoring/queues/plans are Curriculum schema” as current guidance.
  • Never treat “Target tables/routes are already deployed” as current guidance.