{
  "module": "case",
  "surface": "1edtech",
  "deliverable": "data_dictionary",
  "generatedAt": "2026-07-23T18:26:57Z",
  "canonicalUrl": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/data_dictionary/",
  "architectureUrl": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/",
  "benchmarkUrl": "https://platform3-24kpiksyo-andymontgomery-9773s-projects.vercel.app/data_dictionary",
  "sourceClasses": [
    "1EdTech pass-through",
    "TimeBack gap fill",
    "Mixed pass-through plus gap fill",
    "Inherited platform contract"
  ],
  "sourcesRead": [
    "Supplied loop task, brainlift, DECISIONS, CASE doer protocol, and approved-upstream list.",
    "Data dictionary benchmark URL: https://platform3-24kpiksyo-andymontgomery-9773s-projects.vercel.app/data_dictionary and local loop/context/benchmarks/data_dictionary.html.",
    "Official CASE 1.1 specification: https://www.imsglobal.org/spec/case/v1p1/.",
    "CASE Service 1.1 information model HTML.",
    "CASE Service 1.1 OpenAPI 3 JSON: https://purl.imsglobal.org/spec/case/v1p1/schema/openapi/imscasev1p1_openapi3_v1p0.json.",
    "Approved CASE architecture site, traceability JSON, and commitments sidecar.",
    "Approved platform architecture commitments and platform data dictionary URL.",
    "Live CASE implementation migration/source: loop/case/artifacts/1edtech/implementation/impl/src/migrations/caseSchema.mjs and impl/migrations/001_case_core.sql.",
    "Existing OneRoster and Caliper data-dictionary generators and summaries for local loop conventions.",
    "Issue #1726 reconstruction: byte-for-byte live machine JSON captured 2026-07-23, then explicitly reconciled against tracked architecture commitments, both CASE schema sources, the official CASE 1.1 OpenAPI resource, and the #707/#1721 ownership ruling."
  ],
  "apiContract": {
    "basePath": "/ims/case/v1p1",
    "auth": {
      "token": "HS256 bearer JWT with tenantId, role/roles, and scopes.",
      "scopes": "case:read, case:write, case:import, case:admin",
      "demoMint": "POST /dev/mint?tenantId=demo is demo-only and outside the CASE base path.",
      "itds": [
        {
          "id": "citd-009-auth-shape",
          "title": "CITD-009 Auth shape",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-009-auth-shape"
        },
        {
          "id": "citd-012-tenant-routing",
          "title": "CITD-012 Tenant routing",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
        },
        {
          "id": "pitd-025-api-axis-auth-shape",
          "title": "PITD-025 Platform JWT auth",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-025-api-axis-auth-shape"
        }
      ]
    },
    "headers": [
      {
        "name": "Authorization",
        "required": "Yes",
        "meaning": "Bearer token carrying tenantId and CASE scopes.",
        "itds": [
          {
            "id": "citd-009-auth-shape",
            "title": "CITD-009 Auth shape",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-009-auth-shape"
          }
        ]
      },
      {
        "name": "Idempotency-Key",
        "required": "Retryable writes",
        "meaning": "Required for safe retry semantics on POST/PUT/PATCH/DELETE and CFPackage import.",
        "itds": [
          {
            "id": "citd-008-idempotency-model",
            "title": "CITD-008 Idempotency model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
          }
        ]
      },
      {
        "name": "If-Match",
        "required": "PUT/PATCH/DELETE",
        "meaning": "Required on mutable resource writes; missing returns case:precondition_required.",
        "itds": [
          {
            "id": "citd-007-concurrency-model",
            "title": "CITD-007 Concurrency model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
          }
        ]
      },
      {
        "name": "ETag",
        "required": "Read responses",
        "meaning": "Strong validator returned on mutable resource reads.",
        "itds": [
          {
            "id": "citd-007-concurrency-model",
            "title": "CITD-007 Concurrency model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
          }
        ]
      },
      {
        "name": "requestId / traceId",
        "required": "Every response",
        "meaning": "Support and audit correlation fields, also present in Problem responses.",
        "itds": [
          {
            "id": "citd-011-error-envelope",
            "title": "CITD-011 Error envelope",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-011-error-envelope"
          },
          {
            "id": "pitd-027-api-axis-error-envelope",
            "title": "PITD-027 Platform Problem envelope",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-027-api-axis-error-envelope"
          }
        ]
      }
    ],
    "queryParameters": [
      {
        "name": "limit",
        "routes": "List routes",
        "meaning": "Maximum records returned. Minimum 1. Offset paging is retained for CASE compatibility.",
        "itds": [
          {
            "id": "citd-006-query-model",
            "title": "CITD-006 Query model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
          }
        ]
      },
      {
        "name": "offset",
        "routes": "List routes",
        "meaning": "Zero-based starting offset for CASE-style paging.",
        "itds": [
          {
            "id": "citd-006-query-model",
            "title": "CITD-006 Query model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
          }
        ]
      },
      {
        "name": "sort / orderBy",
        "routes": "List routes",
        "meaning": "Named sort field plus asc or desc ordering.",
        "itds": [
          {
            "id": "citd-006-query-model",
            "title": "CITD-006 Query model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
          }
        ]
      },
      {
        "name": "fields",
        "routes": "List/detail routes",
        "meaning": "Optional field selection for projected response fields.",
        "itds": [
          {
            "id": "citd-006-query-model",
            "title": "CITD-006 Query model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
          }
        ]
      },
      {
        "name": "CFDocumentURI",
        "routes": "CFItems, CFAssociations",
        "meaning": "Filter graph rows to one framework document.",
        "itds": [
          {
            "id": "citd-006-query-model",
            "title": "CITD-006 Query model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
          }
        ]
      },
      {
        "name": "parentIdentifier",
        "routes": "CFItems",
        "meaning": "Filter children of one CFDocument or CFItem parent.",
        "itds": [
          {
            "id": "citd-005-read-shape",
            "title": "CITD-005 Read shape",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-005-read-shape"
          },
          {
            "id": "citd-016-item-tree-invariant",
            "title": "CITD-016 CFItem tree invariant",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
          }
        ]
      },
      {
        "name": "originIdentifier / destinationIdentifier",
        "routes": "CFAssociations",
        "meaning": "Filter graph edges by either endpoint.",
        "itds": [
          {
            "id": "citd-006-query-model",
            "title": "CITD-006 Query model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
          },
          {
            "id": "citd-017-association-validation",
            "title": "CITD-017 Association validation",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
          }
        ]
      },
      {
        "name": "associationType",
        "routes": "CFAssociations",
        "meaning": "Filter graph edges by a known CASE association type or ext:* value.",
        "itds": [
          {
            "id": "citd-017-association-validation",
            "title": "CITD-017 Association validation",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
          }
        ]
      },
      {
        "name": "subject / educationLevel",
        "routes": "CFDocuments, CFItems",
        "meaning": "Filter standards by publisher subject or education-level arrays.",
        "itds": [
          {
            "id": "citd-006-query-model",
            "title": "CITD-006 Query model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
          }
        ]
      },
      {
        "name": "updatedSince",
        "routes": "List routes",
        "meaning": "Poll for rows with platform updated_at later than the supplied timestamp.",
        "itds": [
          {
            "id": "citd-010-eventing-model",
            "title": "CITD-010 Eventing model",
            "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
          }
        ]
      }
    ],
    "routes": [
      [
        "GET",
        "/CFDocuments",
        "CASE Provider pass-through",
        "List CFDocuments."
      ],
      [
        "POST",
        "/CFDocuments",
        "TimeBack write extension",
        "Create a CFDocument."
      ],
      [
        "GET",
        "/CFDocuments/{sourcedId}",
        "CASE Provider pass-through",
        "Read one CFDocument."
      ],
      [
        "PUT/PATCH/DELETE",
        "/CFDocuments/{sourcedId}",
        "TimeBack write extension",
        "Replace, update, or retire a document with If-Match."
      ],
      [
        "GET/POST",
        "/CFItems",
        "TimeBack browse/write extension",
        "List or create CFItems; POST requires identifier, uri, fullStatement, CFDocumentURI, and parentIdentifier."
      ],
      [
        "GET/PUT/PATCH/DELETE",
        "/CFItems/{sourcedId}",
        "CASE Provider read plus TimeBack writes",
        "Read, update, or retire one CFItem."
      ],
      [
        "GET",
        "/CFItemAssociations/{sourcedId}",
        "CASE Provider pass-through",
        "Read a CFItem and all its associations."
      ],
      [
        "GET/POST",
        "/CFAssociations",
        "TimeBack browse/write extension",
        "List or create graph edges."
      ],
      [
        "GET/PUT/PATCH/DELETE",
        "/CFAssociations/{sourcedId}",
        "CASE Provider read plus TimeBack writes",
        "Read, update, or retire one association."
      ],
      [
        "GET",
        "/CFPackages/{sourcedId}",
        "CASE Provider pass-through",
        "Export a spec-pure package."
      ],
      [
        "POST",
        "/CFPackages",
        "TimeBack import extension",
        "Import a package transactionally."
      ],
      [
        "GET",
        "/CFConcepts/{sourcedId}",
        "CASE Provider pass-through",
        "Read imported concept definitions."
      ],
      [
        "GET",
        "/CFSubjects/{sourcedId}",
        "CASE Provider pass-through",
        "Read imported subject definitions."
      ],
      [
        "GET",
        "/CFLicenses/{sourcedId}",
        "CASE Provider pass-through",
        "Read imported license definitions."
      ],
      [
        "GET",
        "/CFItemTypes/{sourcedId}",
        "CASE Provider pass-through",
        "Read imported item-type definitions."
      ],
      [
        "GET",
        "/CFAssociationGroupings/{sourcedId}",
        "CASE Provider pass-through",
        "Read imported association-grouping definitions."
      ],
      [
        "GET",
        "/CFRubrics/{sourcedId}",
        "CASE Provider pass-through",
        "Read imported rubric definitions."
      ],
      [
        "GET/POST",
        "/Alignments",
        "TimeBack platform extension",
        "List/create external-ID to CFItem links."
      ],
      [
        "GET/PUT/PATCH/DELETE",
        "/Alignments/{alignmentId}",
        "TimeBack platform extension",
        "Read, update, or delete one Alignment."
      ]
    ]
  },
  "sqlSections": [
    {
      "id": "tenant-rls-contract",
      "title": "Tenant RLS contract",
      "purpose": "Direct Postgres roles should enforce the same text tenant_id boundary the API derives from the HS256 bearer JWT.",
      "sql": "create schema if not exists \"case\";\n\ncreate or replace function \"case\".current_tenant_id()\nreturns text\nlanguage sql\nstable\nas $$\n  select nullif(current_setting('app.tenant_id', true), '')::text\n$$;\n\n-- Run this block after the CREATE TABLE statements if a direct DB role will read/write CASE rows.\ndo $$\ndeclare\n  table_name text;\n  policy_name text;\nbegin\n  foreach table_name in array array[\n    'cf_document',\n    'cf_item',\n    'cf_association',\n    'alignment',\n    'cf_package_import',\n    'cf_concept',\n    'cf_subject',\n    'cf_license',\n    'cf_item_type',\n    'cf_association_grouping',\n    'cf_rubric',\n    'cf_rubric_criterion',\n    'cf_rubric_criterion_level'\n  ]\n  loop\n    policy_name := table_name || '_tenant_isolation';\n    execute format('alter table \"case\".%I enable row level security', table_name);\n    execute format('drop policy if exists %I on \"case\".%I', policy_name, table_name);\n    execute format(\n      'create policy %I on \"case\".%I using (tenant_id = \"case\".current_tenant_id()) with check (tenant_id = \"case\".current_tenant_id())',\n      policy_name,\n      table_name\n    );\n  end loop;\nend $$;",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "cf-document-ddl",
      "title": "CREATE TABLE case.cf_document",
      "purpose": "Live physical storage for CFDocument. CASE fields live in body JSONB; tenant/id/resource_type/import/version/etag/timestamps/retired_at are sidecar columns.",
      "sql": "create table if not exists \"case\".cf_document (\n  tenant_id text not null,\n  id text not null,\n  resource_type text not null default 'cf_document',\n  import_batch_id text not null,\n  body jsonb not null,\n  version integer not null default 1,\n  etag text not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  retired_at timestamptz,\n  primary key (tenant_id, id)\n);\n\ncreate index if not exists cf_document_uri_idx\n  on \"case\".cf_document (tenant_id, (body->>'uri'))\n  where retired_at is null;\ncreate index if not exists cf_document_updated_idx\n  on \"case\".cf_document (tenant_id, updated_at desc)\n  where retired_at is null;\n\n-- Required CASE fields are validated by the API before insert:\n-- body->>'identifier', body->>'uri', body->>'creator', body->>'title', body->>'lastChangeDateTime'.",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "cf-item-ddl",
      "title": "CREATE TABLE case.cf_item",
      "purpose": "Live physical storage for CFItem. The required CFDocumentURI and parentIdentifier write contract is in body JSONB, not flat columns.",
      "sql": "create table if not exists \"case\".cf_item (\n  tenant_id text not null,\n  id text not null,\n  resource_type text not null default 'cf_item',\n  import_batch_id text not null,\n  body jsonb not null,\n  version integer not null default 1,\n  etag text not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  retired_at timestamptz,\n  primary key (tenant_id, id)\n);\n\ncreate index if not exists cf_item_document_idx\n  on \"case\".cf_item (tenant_id, (body->>'CFDocumentURI'), (body->>'parentIdentifier'))\n  where retired_at is null;\ncreate index if not exists cf_item_document_identifier_idx\n  on \"case\".cf_item (tenant_id, (body->>'cfDocumentIdentifier'))\n  where retired_at is null;\ncreate index if not exists cf_item_uri_idx\n  on \"case\".cf_item (tenant_id, (body->>'uri'))\n  where retired_at is null;\ncreate index if not exists cf_item_updated_idx\n  on \"case\".cf_item (tenant_id, updated_at desc)\n  where retired_at is null;\n\n-- Required CFItem write fields are validated by the API before insert:\n-- body->>'identifier', body->>'uri', body->>'fullStatement',\n-- body->'CFDocumentURI' with identifier/uri, and body->>'parentIdentifier'.",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "cf-association-ddl",
      "title": "CREATE TABLE case.cf_association",
      "purpose": "Live physical storage for typed CASE graph edges. Endpoint projections are JSONB paths in body.",
      "sql": "create table if not exists \"case\".cf_association (\n  tenant_id text not null,\n  id text not null,\n  resource_type text not null default 'cf_association',\n  import_batch_id text not null,\n  body jsonb not null,\n  version integer not null default 1,\n  etag text not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  retired_at timestamptz,\n  primary key (tenant_id, id)\n);\n\ncreate index if not exists cf_association_origin_idx\n  on \"case\".cf_association (tenant_id, (body->'originNodeURI'->>'identifier'))\n  where retired_at is null;\ncreate index if not exists cf_association_destination_idx\n  on \"case\".cf_association (tenant_id, (body->'destinationNodeURI'->>'identifier'))\n  where retired_at is null;\ncreate index if not exists cf_association_origin_identifier_idx\n  on \"case\".cf_association (tenant_id, (body->>'originIdentifier'))\n  where retired_at is null;\ncreate index if not exists cf_association_destination_identifier_idx\n  on \"case\".cf_association (tenant_id, (body->>'destinationIdentifier'))\n  where retired_at is null;\ncreate index if not exists cf_association_type_idx\n  on \"case\".cf_association (tenant_id, (body->>'associationType'))\n  where retired_at is null;",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "alignment-ddl",
      "title": "CREATE TABLE case.alignment",
      "purpose": "Live physical storage for external-ID mappings. There is no deleted_at column; active rows use retired_at is null and public DELETE hard-deletes.",
      "sql": "create table if not exists \"case\".alignment (\n  tenant_id text not null,\n  id text not null,\n  resource_type text not null default 'alignment',\n  import_batch_id text not null,\n  body jsonb not null,\n  version integer not null default 1,\n  etag text not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  retired_at timestamptz,\n  primary key (tenant_id, id)\n);\n\ncreate unique index if not exists alignment_active_external_id_idx\n  on \"case\".alignment (tenant_id, (body->>'externalSystem'), (body->>'externalId'))\n  where retired_at is null;\n\n-- body fields validated by the API: alignmentId, externalSystem, externalId,\n-- and at least one resolvable CFItem target in cfItemIdentifier or cfItemUri.",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "retire-reference-trigger",
      "title": "Retire reference guard trigger",
      "purpose": "Live guardrail for CFDocument/CFItem soft-retire. The API surfaces this as HTTP 409 case:resource_referenced; raw DB migrations must install the same trigger so direct retired_at updates cannot strand active references.",
      "sql": "-- The exact superseded trigger bytes remain reviewable in loop/case/artifacts/1edtech/data_dictionary/source/reconstruction/deployed-case-data-dictionary-20260723.json (sqlSections#retire-reference-trigger).\n-- Do not execute reconstructed snapshot SQL. Use loop/case/artifacts/1edtech/implementation/impl/migrations/001_case_core.sql, loop/case/artifacts/1edtech/implementation/impl/src/migrations/caseSchema.mjs, and the reviewed post-#707 release operator.",
      "snapshotStatus": "superseded-by-issues-707-and-1721",
      "executionRule": "Do not execute this reconstructed trigger excerpt. The tracked CASE DDL and the post-#707 release operator own the exact retirement guard."
    },
    {
      "id": "package-import-ddl",
      "title": "CREATE TABLE case.cf_package_import",
      "purpose": "Live physical storage for CFPackage import evidence. This control table is flat because it is platform audit state, not a CASE resource body.",
      "sql": "create table if not exists \"case\".cf_package_import (\n  tenant_id text not null,\n  id text not null,\n  import_batch_id text not null,\n  document_identifier text not null,\n  package_hash text not null,\n  source_uri text,\n  status text not null check (status in ('pending', 'succeeded', 'failed')),\n  row_counts jsonb not null default '{}'::jsonb,\n  row_errors jsonb,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  committed_at timestamptz,\n  primary key (tenant_id, id)\n);\n\ncreate index if not exists cf_package_import_hash_idx\n  on \"case\".cf_package_import (tenant_id, document_identifier, package_hash);\n",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "definition-resource-ddl",
      "title": "CREATE TABLE imported CFDefinitions resources",
      "purpose": "CFConcept, CFSubject, CFLicense, CFItemType, CFAssociationGrouping, and CFRubric use the same JSONB-body physical shape.",
      "sql": "create table if not exists \"case\".cf_concept (\n  tenant_id text not null,\n  id text not null,\n  resource_type text not null default 'cf_concept',\n  import_batch_id text,\n  body jsonb not null,\n  version integer not null default 1,\n  etag text not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  retired_at timestamptz,\n  primary key (tenant_id, id)\n);\n\n-- Repeat this shape for cf_subject, cf_license, cf_item_type,\n-- cf_association_grouping, and cf_rubric, changing only resource_type default\n-- and table name. Reads are tenant-scoped; package export scopes definitions\n-- by import_batch_id rather than by retired_at.",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "rubric-child-ddl",
      "title": "CREATE TABLE rubric child rows",
      "purpose": "Rubric criteria and criterion levels use parent-key columns plus body JSONB because CASE packages nest them under a CFRubric.",
      "sql": "create table if not exists \"case\".cf_rubric_criterion (\n  tenant_id text not null,\n  rubric_id text not null,\n  id text not null,\n  body jsonb not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  primary key (tenant_id, rubric_id, id)\n);\n\ncreate table if not exists \"case\".cf_rubric_criterion_level (\n  tenant_id text not null,\n  rubric_id text not null,\n  criterion_id text not null,\n  id text not null,\n  body jsonb not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now(),\n  primary key (tenant_id, rubric_id, criterion_id, id)\n);",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "tree-query",
      "title": "Tenant-scoped CFItem hierarchy query",
      "purpose": "Walk the active CFItem tree for one CFDocument using body->>'parentIdentifier' and body->>'cfDocumentIdentifier', without reading source code.",
      "sql": "with recursive item_tree as (\n  select\n    i.tenant_id,\n    i.id as identifier,\n    i.body->>'parentIdentifier' as parent_identifier,\n    coalesce(i.body->>'cfDocumentIdentifier', i.body->'CFDocumentURI'->>'identifier') as cf_document_identifier,\n    i.body->>'humanCodingScheme' as human_coding_scheme,\n    i.body->>'fullStatement' as full_statement,\n    i.body->>'listEnumeration' as list_enumeration,\n    0 as depth,\n    array[i.id] as path\n  from \"case\".cf_item i\n  where i.tenant_id = $1::text\n    and coalesce(i.body->>'cfDocumentIdentifier', i.body->'CFDocumentURI'->>'identifier') = $2::text\n    and i.body->>'parentIdentifier' = $2::text\n    and i.retired_at is null\n\n  union all\n\n  select\n    child.tenant_id,\n    child.id,\n    child.body->>'parentIdentifier',\n    coalesce(child.body->>'cfDocumentIdentifier', child.body->'CFDocumentURI'->>'identifier'),\n    child.body->>'humanCodingScheme',\n    child.body->>'fullStatement',\n    child.body->>'listEnumeration',\n    item_tree.depth + 1,\n    item_tree.path || child.id\n  from \"case\".cf_item child\n  join item_tree\n    on item_tree.tenant_id = child.tenant_id\n   and item_tree.identifier = child.body->>'parentIdentifier'\n  where child.retired_at is null\n    and child.id <> all(item_tree.path)\n)\nselect depth, human_coding_scheme, identifier, parent_identifier, full_statement\nfrom item_tree\norder by path;",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    },
    {
      "id": "alignment-query",
      "title": "Resolve a publisher external ID to a CFItem",
      "purpose": "Use Alignment as the only tenant-specific external-ID link, then join to the active CASE item row.",
      "sql": "select\n  a.body->>'externalSystem' as external_system,\n  a.body->>'externalId' as external_id,\n  i.id as cf_item_identifier,\n  i.body->>'uri' as cf_item_uri,\n  i.body->>'humanCodingScheme' as human_coding_scheme,\n  i.body->>'fullStatement' as full_statement,\n  coalesce(i.body->>'cfDocumentIdentifier', i.body->'CFDocumentURI'->>'identifier') as cf_document_identifier\nfrom \"case\".alignment a\njoin \"case\".cf_item i\n  on i.tenant_id = a.tenant_id\n and (\n   i.id = a.body->>'cfItemIdentifier'\n   or i.body->>'uri' = a.body->>'cfItemUri'\n )\nwhere a.tenant_id = $1::text\n  and a.body->>'externalSystem' = $2\n  and a.body->>'externalId' = $3\n  and a.retired_at is null\n  and i.retired_at is null;",
      "snapshotStatus": "reconstructed-deployed-snapshot-2026-06-11",
      "executionRule": "Use the tracked, hash-bound CASE DDL for execution; this excerpt is retained so reconstruction differences remain reviewable."
    }
  ],
  "enumSets": [
    {
      "name": "case_association_type",
      "title": "CFAssociation associationType",
      "field": "case.cf_association.association_type",
      "constraint": "Known CASE 1.1 values or ext:[A-Za-z0-9._-]+",
      "itds": [
        {
          "id": "citd-017-association-validation",
          "title": "CITD-017 Association validation",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
        },
        {
          "id": "citd-016-item-tree-invariant",
          "title": "CITD-016 CFItem tree invariant",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
        }
      ],
      "values": [
        {
          "value": "isChildOf",
          "meaning": "Structural tree edge from a child CFItem to its parent CFItem or CFDocument root.",
          "useWhen": "Use exactly once per active CFItem within a tenant.",
          "invalidWhen": "Missing for a CFItem, duplicated for one CFItem, cyclic, or points outside the same CFDocument for the structural tree.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "isPeerOf",
          "meaning": "Non-hierarchical equivalence or peer relationship between items at comparable scope.",
          "useWhen": "Use when two standards should be browsed as peers without implying exact semantic identity.",
          "invalidWhen": "Used to create the primary tree or to replace exactMatchOf when the publisher asserts exact equivalence.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "isPartOf",
          "meaning": "Part-whole relationship that is not the primary isChildOf tree placement.",
          "useWhen": "Use for non-tree component membership or package-level grouping.",
          "invalidWhen": "Used when the browser needs a single structural parent; use isChildOf for that.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "exactMatchOf",
          "meaning": "Cross-framework edge saying two items carry the same intended competency.",
          "useWhen": "Use for strict standards alignment across frameworks.",
          "invalidWhen": "Used for loose topical similarity; use isRelatedTo instead.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "precedes",
          "meaning": "Ordering edge saying the origin should be learned or displayed before the destination.",
          "useWhen": "Use for prerequisite or sequence relationships outside the primary tree.",
          "invalidWhen": "Used as a parent-child edge or with negative sequenceNumber values.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "isRelatedTo",
          "meaning": "Loose relationship when no stronger CASE association type applies.",
          "useWhen": "Use for general topical or instructional links.",
          "invalidWhen": "Used when the relationship is actually structural, replacement, exact match, or translation.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "replacedBy",
          "meaning": "Lifecycle edge saying the origin item has been superseded by the destination item.",
          "useWhen": "Use when a publisher retires one standard in favor of another.",
          "invalidWhen": "Used while both records are current equivalents; exactMatchOf is the better value.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "exemplar",
          "meaning": "Edge from an item to an example or exemplar item/resource represented in CASE form.",
          "useWhen": "Use for CASE 1.1 exemplar relationships preserved from imported packages.",
          "invalidWhen": "Used for tenant-specific lesson IDs; use Alignment instead.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "hasSkillLevel",
          "meaning": "Edge that links a competency to a skill-level item.",
          "useWhen": "Use when a standards publisher expresses proficiency levels as CASE items.",
          "invalidWhen": "Used to model grade or educationLevel fields directly.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "isTranslationOf",
          "meaning": "CASE 1.1 edge saying the origin item is a translation of the destination item.",
          "useWhen": "Use for language translations of the same standard.",
          "invalidWhen": "Used for loosely adapted or simplified statements that are not translations.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "ext:*",
          "meaning": "CASE extension association type matching ext:[A-Za-z0-9._-]+.",
          "useWhen": "Use only for a publisher-defined association type that cannot be represented by known CASE values.",
          "invalidWhen": "Missing the ext: prefix, contains spaces, duplicates an existing known type, or stores an external-system ID link.",
          "source": "Mixed pass-through plus gap fill"
        }
      ]
    },
    {
      "name": "case_target_type",
      "title": "LinkGenURI targetType",
      "field": "case.cf_association.origin_target_type / destination_target_type",
      "constraint": "CASE or ext:[A-Za-z0-9._-]+; missing targetType defaults to CASE.",
      "itds": [
        {
          "id": "citd-017-association-validation",
          "title": "CITD-017 Association validation",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
        }
      ],
      "values": [
        {
          "value": "CASE",
          "meaning": "The linked identifier and URI refer to another CASE CFDocument or CFItem.",
          "useWhen": "Use for ordinary CASE graph edges and default missing values to CASE.",
          "invalidWhen": "Used for external-system lesson IDs or non-CASE resource IDs; those belong in Alignment.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "ext:*",
          "meaning": "An extension target type for a non-CASE framework reference.",
          "useWhen": "Use only when the source package legitimately links to a non-CASE target and names it with ext: prefix.",
          "invalidWhen": "No ext: prefix, spaces, or a value that should be represented as CASE.",
          "source": "Mixed pass-through plus gap fill"
        }
      ]
    },
    {
      "name": "case_version",
      "title": "CASE document version",
      "field": "case.cf_document.case_version",
      "constraint": "If present in API JSON, it MUST equal 1.1.",
      "itds": [
        {
          "id": "citd-002-source-contract",
          "title": "CITD-002 Source contract",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
        },
        {
          "id": "citd-019-case11-extensions-markdown",
          "title": "CITD-019 CASE 1.1 extensions and Markdown",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
        }
      ],
      "values": [
        {
          "value": "1.1",
          "meaning": "The CFDocument follows the CASE 1.1 information model, including the 1.1 fields and data-type changes.",
          "useWhen": "Use for all new platform3 CASE 1EdTech documents that include a caseVersion.",
          "invalidWhen": "Any other version string, including 1.0 or v1p1, on this surface.",
          "source": "1EdTech pass-through"
        }
      ]
    },
    {
      "name": "case_import_status",
      "title": "CFPackage import status",
      "field": "case.cf_package_import.status",
      "constraint": "Internal platform enum: pending, succeeded, failed.",
      "itds": [
        {
          "id": "citd-020-package-import-export",
          "title": "CITD-020 CFPackage import/export",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
        },
        {
          "id": "citd-011-error-envelope",
          "title": "CITD-011 Error envelope",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-011-error-envelope"
        },
        {
          "id": "citd-008-idempotency-model",
          "title": "CITD-008 Idempotency model",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
        }
      ],
      "values": [
        {
          "value": "pending",
          "meaning": "The package has been accepted for validation but has not committed any CASE resource rows yet.",
          "useWhen": "Use only within a transaction or asynchronous future import path before final validation outcome.",
          "invalidWhen": "Left pending after a request has returned success or failure.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "succeeded",
          "meaning": "The package validated and all document, item, association, definition, and rubric rows committed atomically.",
          "useWhen": "Use after a successful POST /CFPackages import.",
          "invalidWhen": "Set when any rowErrors exist or row counts do not match committed rows.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "failed",
          "meaning": "The package failed validation or conflicted with current state and no partial import committed.",
          "useWhen": "Use for typed case:package_import_failed or conflict outcomes.",
          "invalidWhen": "Any CASE resource row was partially committed for the same import_batch_id.",
          "source": "TimeBack gap fill"
        }
      ]
    },
    {
      "name": "case_problem_code",
      "title": "Typed Problem code",
      "field": "Problem.code",
      "constraint": "Stable case:* code values in RFC 7807 responses.",
      "itds": [
        {
          "id": "citd-011-error-envelope",
          "title": "CITD-011 Error envelope",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-011-error-envelope"
        },
        {
          "id": "pitd-027-api-axis-error-envelope",
          "title": "PITD-027 Platform Problem envelope",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-027-api-axis-error-envelope"
        }
      ],
      "values": [
        {
          "value": "case:validation_failed",
          "meaning": "A request body, query parameter, or CASE field violates the dictionary constraints.",
          "useWhen": "Use when the route and parameter names are supported but a submitted value breaks a field type, CHECK, required-field, allowed-value, or JSON-shape rule. Use a sibling code for unsupported query names, missing parents, cycles, or stale ETags.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:not_found",
          "meaning": "The tenant has no active resource for the supplied identifier.",
          "useWhen": "Use after authentication succeeds and the supplied identifier is well-formed, but no active same-tenant row exists. Do not use to hide missing scopes; that is case:forbidden.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:forbidden",
          "meaning": "The JWT is valid but lacks the CASE scope or tenant access required for the route.",
          "useWhen": "Use when the token verifies but its tenantId or scopes do not authorize the attempted read, write, import, or admin action. Do not use for malformed values or absent rows.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:unsupported_query_parameter",
          "meaning": "A list route received a query parameter outside the published query model.",
          "useWhen": "Use when the query string names a parameter this route does not publish at all. If the parameter is supported but the value is malformed, use case:validation_failed.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:association_cycle",
          "meaning": "An isChildOf edge would create a cycle in the active CFItem tree.",
          "useWhen": "Use only for structural isChildOf mutations where walking destination parents reaches the origin item. Missing or cross-document parents use case:missing_parent instead.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:missing_parent",
          "meaning": "A CFItem has no active structural isChildOf parent or references a missing parent.",
          "useWhen": "Use when a CFItem placement omits its one active parent, references no active same-document CFDocument/CFItem, or has duplicate active parents. Use case:association_cycle when the parent exists but creates a loop.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:idempotency_conflict",
          "meaning": "The same Idempotency-Key was reused with different method, route, or request body hash.",
          "useWhen": "Use when the key is syntactically valid and already recorded for this tenant but the replay does not match the stored request fingerprint. Use case:package_import_failed for package row validation errors.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:precondition_required",
          "meaning": "A mutable route required If-Match and the request omitted it.",
          "useWhen": "Use for PUT, PATCH, or DELETE on ETag-protected resources when no If-Match header is present. Use case:precondition_failed when the header is present but stale.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:precondition_failed",
          "meaning": "The supplied If-Match validator no longer matches the current row.",
          "useWhen": "Use when the client supplied If-Match but it differs from the current strong ETag. Use case:precondition_required only for missing validators.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:package_import_failed",
          "meaning": "A CFPackage failed validation or atomic import.",
          "useWhen": "Use when package-level validation, graph validation, or atomic commit fails and rowErrors describe the failing package paths. Use case:validation_failed for ordinary per-resource payload errors.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:resource_referenced",
          "meaning": "HTTP 409: a requested soft-retire/delete would leave an active CASE relationship or CASE-local component reference pointing at the retired resource.",
          "useWhen": "Use when DELETE or a raw retired_at update targets an active CFDocument or ordinary CFItem still referenced by an active CASE item, association, Alignment, or CASE-local component reference. Under CITD-019, opaque registry KC extension fields are not CASE retirement blockers.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        },
        {
          "value": "case:legacy_kc_read_only",
          "meaning": "HTTP 409: a post-#707 request attempted to mutate a retained legacy CASE Knowledge Component or one of its four governed legacy relationship types.",
          "useWhen": "Use only for retained legacy Knowledge Component CFItem writes and precedes, ext:alpha.needs_first, replacedBy, or exactMatchOf relationship writes. Reads remain available; other stock CASE association types keep normal behavior.",
          "invalidWhen": "Used for active registry-native KC authoring (owned by Curriculum), for an ordinary CASE CFItem, or for a stock CASE relationship outside the exact four governed legacy types.",
          "source": "TimeBack gap fill — Issues #707, #1721, and #1726 ownership cutover."
        },
        {
          "value": "case:server_error",
          "meaning": "HTTP 500: the CASE API could not complete the documented operation.",
          "useWhen": "Use only for unexpected server failures after request validation and known conflict handling. The response must include requestId and traceId for support correlation and must not expose raw database errors.",
          "invalidWhen": "Do not use for another failure category or without requestId and traceId.",
          "source": "TimeBack gap fill"
        }
      ]
    },
    {
      "name": "case_scope",
      "title": "CASE JWT scopes",
      "field": "JWT.scopes[]",
      "constraint": "case:read, case:write, case:import, case:admin.",
      "itds": [
        {
          "id": "citd-009-auth-shape",
          "title": "CITD-009 Auth shape",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-009-auth-shape"
        },
        {
          "id": "pitd-025-api-axis-auth-shape",
          "title": "PITD-025 Platform JWT auth",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-025-api-axis-auth-shape"
        }
      ],
      "values": [
        {
          "value": "case:read",
          "meaning": "Read CFDocuments, CFItems, CFAssociations, CFPackage exports, support definitions, and Alignments.",
          "useWhen": "Grant to clients that browse standards, resolve external IDs, follow graph edges, or export packages without mutating CASE rows.",
          "invalidWhen": "Missing from a route that requires it, misspelled, or used as a substitute for tenantId enforcement.",
          "source": "Mixed pass-through plus gap fill"
        },
        {
          "value": "case:write",
          "meaning": "Create and edit per-resource CFDocument, CFItem, CFAssociation, and Alignment rows.",
          "useWhen": "Grant to authoring clients that use per-resource POST, PUT, PATCH, or DELETE. Do not rely on it for CFPackage import without case:import.",
          "invalidWhen": "Missing from a route that requires it, misspelled, or used as a substitute for tenantId enforcement.",
          "source": "Mixed pass-through plus gap fill"
        },
        {
          "value": "case:import",
          "meaning": "Import CFPackages and run package-level writes.",
          "useWhen": "Grant to package loader jobs that call POST /CFPackages and need atomic package persistence. Pair with case:read for post-import verification.",
          "invalidWhen": "Missing from a route that requires it, misspelled, or used as a substitute for tenantId enforcement.",
          "source": "Mixed pass-through plus gap fill"
        },
        {
          "value": "case:admin",
          "meaning": "Administrative override scope for destructive or audit-sensitive CASE operations.",
          "useWhen": "Reserve for audit views, retained tombstones, repair jobs, or policy exceptions that ordinary read/write/import clients must not reach.",
          "invalidWhen": "Missing from a route that requires it, misspelled, or used as a substitute for tenantId enforcement.",
          "source": "Mixed pass-through plus gap fill"
        }
      ]
    },
    {
      "name": "case_framework_type",
      "title": "CFDocument frameworkType",
      "field": "case.cf_document.framework_type",
      "constraint": "Optional normalized string; CourseCodes is the only predefined CASE 1.1 value.",
      "itds": [
        {
          "id": "citd-002-source-contract",
          "title": "CITD-002 Source contract",
          "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
        }
      ],
      "values": [
        {
          "value": "CourseCodes",
          "meaning": "Predefined CASE 1.1 framework type for a course-code framework.",
          "useWhen": "Use when the CFDocument represents course codes rather than academic standards or competencies.",
          "invalidWhen": "Used as a generic default for every standards framework. Null is better when the publisher does not name a framework type.",
          "source": "1EdTech pass-through"
        },
        {
          "value": "publisher-defined normalized string",
          "meaning": "A standards publisher's type label for a framework when it is not the predefined CourseCodes value.",
          "useWhen": "Use only when preserving a publisher's CASE 1.1 payload or authoritative metadata.",
          "invalidWhen": "Contains control characters, leading/trailing whitespace, or is invented by platform3 instead of the publisher.",
          "source": "1EdTech pass-through"
        }
      ]
    }
  ],
  "tables": [
    {
      "name": "case.cf_document",
      "title": "CFDocument",
      "source": "Mixed pass-through plus gap fill",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFDocumentDType / CFPckgDocumentDType",
      "purpose": "Stores one standards-framework container, such as Common Core State Standards Math 2010. It is the root object a browser lists before loading CFItems.",
      "lifecycle": "Created by POST /CFDocuments or CFPackage import, updated with If-Match, soft-retired on DELETE unless active CFItems/CFAssociations/Alpha references block retirement with case:resource_referenced, and exported inside CFPackage without platform sidecar fields.",
      "authority": "CASE 1.1 CFDocument fields are pass-through in body JSONB; platform3 adds tenant, id, resource_type, import, ETag, timestamps, and retirement sidecars.",
      "example": {
        "tenant_id": "00000000-0000-4000-8000-00000000ca11",
        "identifier": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
        "uri": "https://standards.example.org/case/cfdocuments/3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
        "title": "Common Core State Standards for Mathematics",
        "creator": "National Governors Association Center for Best Practices",
        "subject": [
          "Mathematics"
        ],
        "case_version": "1.1"
      },
      "validations": [
        "identifier and uri are both required and must stay one-to-one within a tenant.",
        "case_version, when present, must be 1.1.",
        "description and notes are stored as source text; browser renderers sanitize Markdown/LaTeX at display time.",
        "retired rows are excluded from default CFPackage export."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.tenant_id scopes CFDocument rows before any CASE identifier is compared; a migration must enable RLS on case.cf_document and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.import_batch_id is audit evidence for the write that produced this CFDocument; package imports can stamp many case.cf_document rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.created_at is platform storage time for CFDocument, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.updated_at drives updatedSince polling for CFDocument; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "retired_at",
          "apiField": "_platform.retired_at",
          "type": "TIMESTAMPTZ",
          "nullable": "Yes",
          "meaning": "Soft-retirement timestamp for CASE resources that should disappear from default reads and spec-pure package export while remaining auditable.",
          "constraints": "Null means active. Non-null rows are excluded from default list/detail/export responses. Setting non-null is blocked while active CFItems or CFAssociations point at the document, including the declared standards_framework_refs document link.",
          "example": "null",
          "invalidWhen": "Set while active same-tenant rows still reference the document; the API must return case:resource_referenced and the trigger raises pg 23503 with the blocker path.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CASE does not define platform retention; platform3 pins soft-retire semantics for CFDocument, CFItem, and CFAssociation.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.retired_at soft-retires CFDocument without erasing audit evidence. The guard preserves active CASE document relationships; registry KC references are not part of this document contract.",
          "allowedValues": null,
          "storagePath": "retired_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-document-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.etag protects read-edit-write cycles for CFDocument; recalculate it after any mutable column in case.cf_document changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for the CFDocument.",
          "constraints": "Must match the CASE UUID pattern and be unique per tenant.",
          "example": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
          "invalidWhen": "Not a UUID, duplicate in same tenant, or changed after dependent CFItems exist.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.required.identifier.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.identifier is the stable framework identity; name and status changes live in title, creator, publisher, version, adoption_status, status_start_date, or status_end_date, so changing identifier after CFItems exist should create a new CFDocument lineage instead of updating the primary key.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI that identifies the CFDocument for CASE exchange.",
          "constraints": "Absolute URI. Must be unique per tenant and should remain stable across export/import.",
          "example": "https://standards.example.org/case/cfdocuments/3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
          "invalidWhen": "Relative URL, malformed URI, or points at a different identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.required.uri.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.uri is the publisher exchange URI for CFDocument, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "framework_type",
          "apiField": "frameworkType",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Publisher's type label for this framework, such as the CASE predefined value CourseCodes.",
          "constraints": "Normalized string. Only CourseCodes has predefined CASE 1.1 semantics; other values are publisher-defined and preserved.",
          "example": "CourseCodes",
          "invalidWhen": "Invented by platform3 as a default, contains control characters, or used as a closed enum beyond CourseCodes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.frameworkType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.framework_type has one predefined CASE value, CourseCodes, plus publisher-defined strings; do not close the database enum around CourseCodes or a standards-browser import will reject valid non-course-code frameworks.",
          "allowedValues": "CourseCodes, publisher-defined normalized string",
          "storagePath": "body->>'frameworkType'"
        },
        {
          "column": "case_version",
          "apiField": "caseVersion",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "CASE model version for this document.",
          "constraints": "If present, the only valid value on this surface is 1.1.",
          "example": "1.1",
          "invalidWhen": "Any value other than 1.1.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.caseVersion enum.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.case_version may be null only for a legacy CFDocument payload that omitted caseVersion; any non-null value except 1.1 must fail this 1EdTech surface instead of being coerced or silently downgraded.",
          "allowedValues": "1.1",
          "storagePath": "body->>'caseVersion'"
        },
        {
          "column": "creator",
          "apiField": "creator",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Authority that promulgated or created the framework.",
          "constraints": "Required normalized string. Preserve publisher spelling because it is citation data.",
          "example": "National Governors Association Center for Best Practices",
          "invalidWhen": "Blank, replaced by importing tenant name, or rewritten to platform3.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.required.creator.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.creator carries citation authority, not tenant ownership; package import must preserve exact publisher spelling and search should index it separately from publisher so creator/publisher equality is not normalized away.",
          "allowedValues": null,
          "storagePath": "body->>'creator'"
        },
        {
          "column": "title",
          "apiField": "title",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Human-readable title of the framework shown in document lists.",
          "constraints": "Required normalized string, not unique by itself.",
          "example": "Common Core State Standards for Mathematics",
          "invalidWhen": "Blank or used as the primary key instead of identifier/uri.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.required.title.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.title is display text, not identity; allow the same title in multiple frameworks or tenants and keep identifier plus uri as the only uniqueness guards.",
          "allowedValues": null,
          "storagePath": "body->>'title'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system timestamp for the most recent change to the CASE document record.",
          "constraints": "Required ISO 8601 date-time from the CASE payload; distinct from platform updated_at.",
          "example": "2026-01-15T09:30:00Z",
          "invalidWhen": "Missing, local-time string without offset, or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.last_change_date_time is the publisher/system change time for CFDocument; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "official_source_url",
          "apiField": "officialSourceURL",
          "type": "TEXT URI",
          "nullable": "Yes",
          "meaning": "URL to the formal standards citation intended for humans.",
          "constraints": "Absolute URI when present.",
          "example": "https://www.corestandards.org/Math/",
          "invalidWhen": "Relative URL or a platform mirror substituted for the official source.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.officialSourceURL.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.official_source_url is human citation evidence; leave it null when the publisher omits it and do not substitute a platform mirror because spec-pure export should round-trip the source citation.",
          "allowedValues": null,
          "storagePath": "body->>'officialSourceURL'"
        },
        {
          "column": "publisher",
          "apiField": "publisher",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Entity responsible for making the framework available.",
          "constraints": "Normalized string; may differ from creator.",
          "example": "Common Core State Standards Initiative",
          "invalidWhen": "Copied from creator without evidence when source payload provides a different publisher.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.publisher.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.publisher may equal creator or differ from it; preserve both fields as publisher metadata because public standards citations often rely on the distinction.",
          "allowedValues": null,
          "storagePath": "body->>'publisher'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable framework description, stored as source text.",
          "constraints": "String may contain Markdown/LaTeX; do not render or store trusted HTML in API responses.",
          "example": "Grade-band mathematics standards covering counting, operations, geometry, and statistics.",
          "invalidWhen": "Pre-rendered trusted HTML, stripped Markdown, or sanitized text that cannot round-trip.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.description changed to String in CASE 1.1.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.description is long-form framework prose for browser detail pages; store source text losslessly, sanitize only at render time, and keep it separate from title/subject fields so search ranking can weight concise metadata first.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "subject",
          "apiField": "subject",
          "type": "TEXT[]",
          "nullable": "Yes",
          "meaning": "Topic or academic subject labels for the document.",
          "constraints": "Array of normalized strings. Preserve publisher vocabulary; empty array means no subject provided.",
          "example": [
            "Mathematics"
          ],
          "invalidWhen": "Single comma-delimited string when API JSON uses an array, or unapproved translation of publisher labels.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.subject.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.subject is the array-valued subject field for CFDocument: topic or academic subject labels for the document; null means omitted, [] means explicitly empty, and each string must remain a separate element rather than a comma-delimited scalar.",
          "allowedValues": null,
          "storagePath": "body->'subject'"
        },
        {
          "column": "subject_uri",
          "apiField": "subjectURI",
          "type": "JSONB LinkURIDType[]",
          "nullable": "Yes",
          "meaning": "CASE link collection pointing to authoritative CFSubject resources referenced by this row.",
          "constraints": "When present, store a JSON array of LinkURI objects; each object must include title, identifier, and uri, with identifier as a CASE UUID and uri as a valid absolute URI.",
          "example": "[{\"title\":\"Example CFSubject\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFSubject/11111111-1111-4111-8111-111111111111\"}]",
          "invalidWhen": "Scalar JSON, object instead of array, missing title/identifier/uri on any member, invalid URI, or an identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable zero-or-more link collection to CFSubject; each member resolves locally only when the target is persisted by this dictionary.",
          "edgeCases": "case.cf_document.subject_uri preserves the publisher's subject LinkURI array separately from subject text labels; every member must carry title, identifier, and uri, locally persisted CFSubject identifiers must resolve in the same tenant, and import must not collapse multiple subject links into one text value.",
          "allowedValues": null,
          "storagePath": "body->'subjectURI'"
        },
        {
          "column": "language",
          "apiField": "language",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Default language of text in the framework.",
          "constraints": "Use ISO 639-2 language code when supplied by the publisher.",
          "example": "eng",
          "invalidWhen": "Browser locale such as en-US when publisher supplied ISO 639-2, or guessed values.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.language.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.language is publisher language metadata; preserve the supplied ISO 639-2 code, reject browser locale strings at API validation, and never infer it from Accept-Language.",
          "allowedValues": null,
          "storagePath": "body->>'language'"
        },
        {
          "column": "version",
          "apiField": "version",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Publisher revision label for the framework.",
          "constraints": "Normalized string; this is publisher versioning, not API or CASE version.",
          "example": "2010",
          "invalidWhen": "Confused with caseVersion or platform deployment version.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.version.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.version is the publisher revision label; keep it separate from case_version and deployment version so a 2010 standards release can coexist with CASE 1.1 storage.",
          "allowedValues": null,
          "storagePath": "body->>'version'"
        },
        {
          "column": "adoption_status",
          "apiField": "adoptionStatus",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Publication/adoption status label from the standards publisher.",
          "constraints": "Normalized string preserved from source.",
          "example": "adopted",
          "invalidWhen": "Invented lifecycle values that contradict publisher status; platform retirement uses retired_at instead.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.adoptionStatus.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.adoption_status is publisher lifecycle vocabulary; queries for platform-active rows must use retired_at because a publisher can mark standards archived while platform3 still retains them for audit.",
          "allowedValues": null,
          "storagePath": "body->>'adoptionStatus'"
        },
        {
          "column": "status_start_date",
          "apiField": "statusStartDate",
          "type": "DATE",
          "nullable": "Yes",
          "meaning": "Date the publisher status started.",
          "constraints": "ISO 8601 date without time.",
          "example": "2010-06-02",
          "invalidWhen": "Timestamp, text date, or later than status_end_date.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.statusStartDate.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.status_start_date pairs with status_end_date; the migration CHECK should compare dates only when both are present and must not coerce historical publisher dates into platform timestamps.",
          "allowedValues": null,
          "storagePath": "body->>'statusStartDate'"
        },
        {
          "column": "status_end_date",
          "apiField": "statusEndDate",
          "type": "DATE",
          "nullable": "Yes",
          "meaning": "Date the publisher status ended or changed.",
          "constraints": "ISO 8601 date without time; null means no publisher end date supplied.",
          "example": null,
          "invalidWhen": "Earlier than status_start_date or used as platform delete timestamp.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.statusEndDate.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.status_end_date is the publisher status close date; leave it null for open-ended status and never use it as soft delete because retired_at controls platform visibility.",
          "allowedValues": null,
          "storagePath": "body->>'statusEndDate'"
        },
        {
          "column": "license_uri",
          "apiField": "licenseURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFLicense referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFLicense\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFLicense/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFLicense; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_document.license_uri points to the legal CFLicense for the whole framework; when CFDefinitions contains that CFLicense its identifier must resolve in the same tenant, and absence of a local license row preserves the link object without copying license text into notes or extensions.",
          "allowedValues": null,
          "storagePath": "body->'licenseURI'"
        },
        {
          "column": "notes",
          "apiField": "notes",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Publisher notes about the framework.",
          "constraints": "String source text. Preserve Markdown/LaTeX and sanitize only at render boundaries.",
          "example": "Imported from the public standards package released by the publisher.",
          "invalidWhen": "Trusted HTML or internal platform notes mixed into publisher notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentDType.notes.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.notes is publisher-supplied framework annotation; keep importer diagnostics and platform audit notes out of this field so CFPackage export does not leak operational data.",
          "allowedValues": null,
          "storagePath": "body->>'notes'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for CFDocument data that is not in the core model. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "Must be a JSON object when present. Preserve unknown keys exactly; do not put platform audit metadata here.",
          "example": "{\"publisherExtension\":{\"reviewCycle\":\"annual\"}}",
          "invalidWhen": "Array/scalar JSON, platform tenant metadata, or Alignment external IDs.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFDocumentExtensionDType additionalProperties=true.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_document.extensions is the publisher extension object for CFDocument; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        },
        {
          "column": "cf_package_uri",
          "apiField": "CFPackageURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFPackage referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFPackage\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFPackage/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFPackage; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_document.cf_package_uri identifies the CFPackage envelope that supplied the framework; package imports must align it with the import_batch_id and canonical package hash, while per-resource CFDocument writes leave it null instead of inventing a package link.",
          "allowedValues": null,
          "storagePath": "body->'CFPackageURI'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_item",
      "title": "CFItem",
      "source": "Mixed pass-through plus gap fill",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFItemDType / CFPckgItemDType",
      "purpose": "Stores each standard, competency, skill, or grouping node inside a CFDocument tree.",
      "lifecycle": "Created by POST /CFItems or package import, placed in the tree through one active isChildOf CFAssociation, edited with If-Match, and soft-retired on DELETE unless active child CFItems/CFAssociations/Alignments/Alpha references block retirement with case:resource_referenced.",
      "authority": "CASE 1.1 item fields are pass-through in body JSONB. cfDocumentIdentifier and parentIdentifier are validated body fields, not flat physical columns.",
      "example": {
        "identifier": "59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
        "human_coding_scheme": "CCSS.MATH.CONTENT.3.OA.A.1",
        "full_statement": "Interpret products of whole numbers, e.g., interpret 5 x 7 as the total number of objects in 5 groups of 7 objects each.",
        "education_level": [
          "03"
        ],
        "cf_document_identifier": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48"
      },
      "validations": [
        "Every active CFItem must have exactly one active isChildOf parent in the same tenant.",
        "The parent may be the owning CFDocument root or another CFItem in the same CFDocument.",
        "full_statement remains source text and may contain markup; API responses do not emit trusted HTML.",
        "education_level is an array, not a comma-delimited string."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.tenant_id scopes CFItem rows before any CASE identifier is compared; a migration must enable RLS on case.cf_item and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.import_batch_id is audit evidence for the write that produced this CFItem; package imports can stamp many case.cf_item rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.created_at is platform storage time for CFItem, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.updated_at drives updatedSince polling for CFItem; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "retired_at",
          "apiField": "_platform.retired_at",
          "type": "TIMESTAMPTZ",
          "nullable": "Yes",
          "meaning": "Soft-retirement timestamp for CASE resources that should disappear from default reads and spec-pure package export while remaining auditable.",
          "constraints": "Null means active. Non-null rows are excluded from default list/detail/export responses. Setting non-null is blocked by active child CFItems, CFAssociations, Alignments, or CASE-local component references; opaque registry KC extension refs do not block CASE retirement.",
          "example": "null",
          "invalidWhen": "Set while active same-tenant rows still reference the item; the API must return case:resource_referenced and the trigger raises pg 23503 with the blocker path.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CASE does not define platform retention; platform3 pins soft-retire semantics for CFDocument, CFItem, and CFAssociation.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.retired_at soft-retires an ordinary CFItem without erasing audit evidence. Retained legacy Knowledge Component rows instead reject mutation with case:legacy_kc_read_only; Curriculum owns registry KC retirement validation.",
          "allowedValues": null,
          "storagePath": "retired_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-item-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.etag protects read-edit-write cycles for CFItem; recalculate it after any mutable column in case.cf_item changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this item.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
          "invalidWhen": "Duplicate in tenant, missing, not UUID, or changed after associations point to it.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.required.identifier.",
            "itds": [
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.identifier is the stable item identity; publisher recoding belongs in human_coding_scheme or list_enumeration and statement edits belong in full_statement, abbreviated_statement, or notes, so identity replacement should use a new CFItem plus a replacedBy or exactMatchOf CFAssociation.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "full_statement",
          "apiField": "fullStatement",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Complete statement text for the standard, competency, or grouping node.",
          "constraints": "Required string. Preserve source markup/LaTeX; sanitize only when rendered by a browser app.",
          "example": "Interpret products of whole numbers, e.g., interpret 5 x 7 as the total number of objects in 5 groups of 7 objects each.",
          "invalidWhen": "Blank, stripped of markup needed for meaning, or stored as trusted HTML.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.required.fullStatement; String in CASE 1.1.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.full_statement is the primary standard text a visual browser renders; require nonblank source text, sanitize at display time, and never replace it with abbreviated_statement during package import.",
          "allowedValues": null,
          "storagePath": "body->>'fullStatement'"
        },
        {
          "column": "alternative_label",
          "apiField": "alternativeLabel",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Publisher's alternate term for competency, such as outcome or objective.",
          "constraints": "Normalized string when present.",
          "example": "Learning Objective",
          "invalidWhen": "Used to rename platform fields or override CFItemType.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.alternativeLabel.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.alternative_label is publisher vocabulary for what this item is called; preserving it lets a browser display terms like outcome or objective without renaming database or API columns.",
          "allowedValues": null,
          "storagePath": "body->>'alternativeLabel'"
        },
        {
          "column": "cf_item_type",
          "apiField": "CFItemType",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Text label for the class of statement as named by the publisher.",
          "constraints": "Normalized string. Prefer CFItemTypeURI when the package defines typed item-type records.",
          "example": "Content Standard",
          "invalidWhen": "Invented by platform3 when the publisher did not supply a type.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.CFItemType.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.cf_item_type is the publisher's text item-type label; when cf_item_type_uri is also present, keep both because the text field preserves source display while the URI gives controlled-vocabulary lookup.",
          "allowedValues": null,
          "storagePath": "body->>'CFItemType'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI identifying this CFItem in CASE exchange.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/cfitems/59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
          "invalidWhen": "Relative URL, malformed URI, or points to a different identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.required.uri.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.uri is the publisher exchange URI for CFItem, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "human_coding_scheme",
          "apiField": "humanCodingScheme",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-referenceable publisher code shown in standards browsers.",
          "constraints": "Normalized string; not globally unique unless the publisher makes it so. Query with CFDocument scope.",
          "example": "CCSS.MATH.CONTENT.3.OA.A.1",
          "invalidWhen": "Used as the primary key, stripped of punctuation, or assumed unique across frameworks.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.humanCodingScheme.",
            "itds": [
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.human_coding_scheme is a display and search code; index it with cf_document_identifier because codes repeat across frameworks and must not become tenant-wide unique.",
          "allowedValues": null,
          "storagePath": "body->>'humanCodingScheme'"
        },
        {
          "column": "list_enumeration",
          "apiField": "listEnumeration",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Publisher list-position text for display ordering.",
          "constraints": "Normalized string. sequence_number on isChildOf associations is preferred for tree ordering when both exist.",
          "example": "A.1",
          "invalidWhen": "Used as the only ordering mechanism when a structural CFAssociation sequenceNumber is present.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.listEnumeration.",
            "itds": [
              {
                "id": "citd-016-item-tree-invariant",
                "title": "CITD-016 CFItem tree invariant",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.list_enumeration is publisher display-order text; if sequence_number exists on the isChildOf edge, use sequence_number for tree sorting and keep list_enumeration for round-trip display.",
          "allowedValues": null,
          "storagePath": "body->>'listEnumeration'"
        },
        {
          "column": "abbreviated_statement",
          "apiField": "abbreviatedStatement",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Short display form of fullStatement.",
          "constraints": "Normalized string; may be null even when fullStatement is long.",
          "example": "Interpret products of whole numbers.",
          "invalidWhen": "Used as a replacement for fullStatement in detail/export responses.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.abbreviatedStatement.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.abbreviated_statement is an optional display summary; null should make clients fall back to full_statement, not overwrite or truncate full_statement during import.",
          "allowedValues": null,
          "storagePath": "body->>'abbreviatedStatement'"
        },
        {
          "column": "concept_keywords",
          "apiField": "conceptKeywords",
          "type": "TEXT[]",
          "nullable": "Yes",
          "meaning": "Free-text keywords and phrases describing the item's concepts.",
          "constraints": "Array of strings. Preserve publisher terms and order.",
          "example": [
            "multiplication",
            "equal groups"
          ],
          "invalidWhen": "Stored as one pipe-delimited string in API JSON or lowercased when publisher case matters.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.conceptKeywords.",
            "itds": [
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.concept_keywords is the array-valued conceptKeywords field for CFItem: free-text keywords and phrases describing the item's concepts; null means omitted, [] means explicitly empty, and each string must remain a separate element rather than a comma-delimited scalar.",
          "allowedValues": null,
          "storagePath": "body->'conceptKeywords'"
        },
        {
          "column": "concept_keywords_uri",
          "apiField": "conceptKeywordsURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFConcept referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFConcept\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFConcept/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFConcept; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_item.concept_keywords_uri is the optional controlled-vocabulary link for conceptKeywords, not the free-text concept_keywords array; when CFConcept rows were imported the identifier must resolve to case.cf_concept in the same tenant, and a publisher payload with more than one controlled concept must be rejected or represented by an explicit future schema decision rather than silently squashed.",
          "allowedValues": null,
          "storagePath": "body->'conceptKeywordsURI'"
        },
        {
          "column": "notes",
          "apiField": "notes",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Information about derivation or editorial context for the item.",
          "constraints": "Source text. Preserve markup; sanitize only at render boundaries.",
          "example": "Clarification examples are copied from the publisher release notes.",
          "invalidWhen": "Internal import notes mixed with publisher notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.notes.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.notes is publisher editorial context for the item; keep internal import warnings in row_errors or audit logs so exported CFItems do not gain platform-only commentary.",
          "allowedValues": null,
          "storagePath": "body->>'notes'"
        },
        {
          "column": "subject",
          "apiField": "subject",
          "type": "TEXT[]",
          "nullable": "Yes",
          "meaning": "Subject labels for this item.",
          "constraints": "Array of normalized strings. May be narrower than the document subject.",
          "example": [
            "Mathematics",
            "Operations and Algebraic Thinking"
          ],
          "invalidWhen": "Comma-delimited string in API JSON or guessed subject not present in source.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.subject.",
            "itds": [
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.subject is the array-valued subject field for CFItem: subject labels for this item; null means omitted, [] means explicitly empty, and each string must remain a separate element rather than a comma-delimited scalar.",
          "allowedValues": null,
          "storagePath": "body->'subject'"
        },
        {
          "column": "subject_uri",
          "apiField": "subjectURI",
          "type": "JSONB LinkURIDType[]",
          "nullable": "Yes",
          "meaning": "CASE link collection pointing to authoritative CFSubject resources referenced by this row.",
          "constraints": "When present, store a JSON array of LinkURI objects; each object must include title, identifier, and uri, with identifier as a CASE UUID and uri as a valid absolute URI.",
          "example": "[{\"title\":\"Example CFSubject\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFSubject/11111111-1111-4111-8111-111111111111\"}]",
          "invalidWhen": "Scalar JSON, object instead of array, missing title/identifier/uri on any member, invalid URI, or an identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable zero-or-more link collection to CFSubject; each member resolves locally only when the target is persisted by this dictionary.",
          "edgeCases": "case.cf_item.subject_uri preserves the item-level subject LinkURI array that CASE 1.1 added separately from the document subject; each supplied subject link must stay a distinct array member, resolve to same-tenant CFSubject when definitions exist, and never overwrite the subject text array.",
          "allowedValues": null,
          "storagePath": "body->'subjectURI'"
        },
        {
          "column": "language",
          "apiField": "language",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Default language of the item text.",
          "constraints": "Use ISO 639-2 language code when supplied.",
          "example": "eng",
          "invalidWhen": "Browser locale or guessed language when source is silent.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.language.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.language is item-level publisher language metadata; preserve the supplied ISO 639-2 code separately from the document default and do not infer it from UI locale.",
          "allowedValues": null,
          "storagePath": "body->>'language'"
        },
        {
          "column": "education_level",
          "apiField": "educationLevel",
          "type": "TEXT[]",
          "nullable": "Yes",
          "meaning": "Grade, level, or instructional level at which the item is intended.",
          "constraints": "Array of normalized strings from the publisher vocabulary; use filters exactly as stored.",
          "example": [
            "03"
          ],
          "invalidWhen": "Stored as integer, comma string, or translated to local grade names during import.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.educationLevel.",
            "itds": [
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.education_level is the array-valued educationLevel field for CFItem: grade, level, or instructional level at which the item is intended; null means omitted, [] means explicitly empty, and each string must remain a separate element rather than a comma-delimited scalar.",
          "allowedValues": null,
          "storagePath": "body->'educationLevel'"
        },
        {
          "column": "cf_item_type_uri",
          "apiField": "CFItemTypeURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFItemType referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFItemType\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFItemType/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFItemType; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_item.cf_item_type_uri links the item to a CFItemType definition; when present its identifier must resolve to an active same-tenant case.cf_item_type row if definitions were imported, and it must not replace the publisher's CFItemType text label.",
          "allowedValues": null,
          "storagePath": "body->'CFItemTypeURI'"
        },
        {
          "column": "license_uri",
          "apiField": "licenseURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFLicense referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFLicense\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFLicense/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFLicense; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_item.license_uri overrides or supplements document-level license evidence for this item only; the link must resolve to same-tenant CFLicense when definitions exist, and an omitted item license must not cause the importer to persist a copied document license object.",
          "allowedValues": null,
          "storagePath": "body->'licenseURI'"
        },
        {
          "column": "status_start_date",
          "apiField": "statusStartDate",
          "type": "DATE",
          "nullable": "Yes",
          "meaning": "Date the publisher status for this item started.",
          "constraints": "ISO 8601 date without time.",
          "example": "2010-06-02",
          "invalidWhen": "Timestamp or later than status_end_date.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.statusStartDate.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.status_start_date is item-level publisher status evidence; compare it to status_end_date only when both are present and keep platform visibility decisions on retired_at.",
          "allowedValues": null,
          "storagePath": "body->>'statusStartDate'"
        },
        {
          "column": "status_end_date",
          "apiField": "statusEndDate",
          "type": "DATE",
          "nullable": "Yes",
          "meaning": "Date the publisher status for this item ended or changed.",
          "constraints": "ISO 8601 date without time; null means no end date supplied.",
          "example": null,
          "invalidWhen": "Used as platform retirement timestamp instead of retired_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.statusEndDate.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.status_end_date is item-level publisher status close evidence; null means no publisher end date and should not hide the item from tree traversal.",
          "allowedValues": null,
          "storagePath": "body->>'statusEndDate'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system timestamp for the most recent change to the CASE item record.",
          "constraints": "Required ISO 8601 date-time; distinct from platform updated_at.",
          "example": "2026-01-15T09:31:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.last_change_date_time is the publisher/system change time for CFItem; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for item data outside the core model. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present. Preserve unknown keys exactly; do not store tenant-private Alignment data here.",
          "example": "{\"publisherExtension\":{\"depth\":3}}",
          "invalidWhen": "Array/scalar, platform metadata, or externalSystem/externalId links.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemExtensionDType additionalProperties=true.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              },
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item.extensions is the publisher extension object for CFItem; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        },
        {
          "column": "cf_document_uri",
          "apiField": "CFDocumentURI",
          "type": "JSONB LinkURIDType",
          "nullable": "No",
          "meaning": "CASE link object pointing to the authoritative CFDocument referenced by this row.",
          "constraints": "Required on POST/PUT. The object must include title, identifier, and uri; identifier must resolve to an active same-tenant CFDocument.",
          "example": "{\"title\":\"Example CFDocument\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFDocument/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing from a CFItem write body, lacks title/identifier/uri, has invalid URI, mismatches body->>'cfDocumentIdentifier', or does not resolve to the same tenant.",
          "source": {
            "label": "Mixed pass-through plus gap fill",
            "basis": "CASE LinkURIDType pass-through plus TimeBack write requiredness from the implementation contract.",
            "itds": [
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              },
              {
                "id": "citd-004-write-granularity",
                "title": "CITD-004 Write granularity",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-004-write-granularity"
              },
              {
                "id": "citd-016-item-tree-invariant",
                "title": "CITD-016 CFItem tree invariant",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
              }
            ]
          },
          "relation": "Required belongs-to-one link to an active same-tenant case.cf_document row by (tenant_id, id).",
          "edgeCases": "case.cf_item.cf_document_uri is required for CFItem writes; raw queries read it at body->'CFDocumentURI', the API backfills body->>'cfDocumentIdentifier' from its identifier, and omitting it returns case:validation_failed instead of deriving document ownership from another field.",
          "allowedValues": null,
          "storagePath": "body->'CFDocumentURI'"
        },
        {
          "column": "cf_document_identifier",
          "apiField": "cfDocumentIdentifier",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Body-level document join key derived from CFDocumentURI.identifier for efficient tenant-scoped raw queries.",
          "constraints": "Must equal CFDocumentURI.identifier and reference an active or retained cf_document row in the same tenant.",
          "example": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
          "invalidWhen": "Differs from CFDocumentURI.identifier or points across tenant.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Derived body field persisted in body->>'cfDocumentIdentifier'.",
            "itds": [
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              },
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              }
            ]
          },
          "relation": "Belongs to one case.cf_document by (tenant_id, id).",
          "edgeCases": "case.cf_item.cf_document_identifier is the document-scope join key for CFItem queries; it must match CFDocumentURI.identifier, stay same-tenant, and be included in hierarchy indexes so tree walks never infer document ownership from URI text.",
          "allowedValues": null,
          "storagePath": "body->>'cfDocumentIdentifier'"
        },
        {
          "column": "parent_identifier",
          "apiField": "parentIdentifier",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Required body-level structural parent for tree navigation; points to either the owning CFDocument root or another CFItem.",
          "constraints": "Required on POST/PUT. Null is never valid for an active CFItem; the value is checked against the same-document parent invariant.",
          "example": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
          "invalidWhen": "Missing parentIdentifier, multiple active parents, cycle, or parent from another CFDocument.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "TimeBack invariant over CASE associations and implementation write contract.",
            "itds": [
              {
                "id": "citd-016-item-tree-invariant",
                "title": "CITD-016 CFItem tree invariant",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
              }
            ]
          },
          "relation": "References case.cf_document.id or case.cf_item.id in same tenant.",
          "edgeCases": "case.cf_item.parent_identifier is the denormalized browser tree parent for CFItem; it must come from exactly one active isChildOf association, point to the owning document root or same-document item, and fail on missing, duplicate, cross-document, or cyclic placement.",
          "allowedValues": null,
          "storagePath": "body->>'parentIdentifier'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_association",
      "title": "CFAssociation",
      "source": "Mixed pass-through plus gap fill",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFAssociationDType / CFPckgAssociationDType",
      "purpose": "Stores typed graph edges between CFItems or CFDocuments, including the isChildOf edges that create the navigable tree.",
      "lifecycle": "Created by POST /CFAssociations, CFItem create placement, or package import; edited with If-Match; soft-retired on DELETE.",
      "authority": "CASE 1.1 association fields are pass-through in body JSONB, with TimeBack validation that makes tree and extension behavior deterministic.",
      "example": {
        "identifier": "0a77d011-0f33-4e2e-8aac-5b59860e1fc1",
        "association_type": "isChildOf",
        "origin_identifier": "59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
        "destination_identifier": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
        "sequence_number": 10
      },
      "validations": [
        "association_type accepts the known CASE 1.1 values or ext:* values only.",
        "isChildOf is structural and must satisfy the one-parent, same-document, acyclic tree invariant.",
        "origin and destination targetType default to CASE; ext:* targets cannot create structural tree parents.",
        "External-system lesson IDs use Alignment, not CFAssociation."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.tenant_id scopes CFAssociation rows before any CASE identifier is compared; a migration must enable RLS on case.cf_association and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.import_batch_id is audit evidence for the write that produced this CFAssociation; package imports can stamp many case.cf_association rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.created_at is platform storage time for CFAssociation, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.updated_at drives updatedSince polling for CFAssociation; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "retired_at",
          "apiField": "_platform.retired_at",
          "type": "TIMESTAMPTZ",
          "nullable": "Yes",
          "meaning": "Soft-retirement timestamp for CASE resources that should disappear from default reads and spec-pure package export while remaining auditable.",
          "constraints": "Null means active. Non-null rows are excluded from default list/detail/export responses unless an administrative audit route asks for retired rows.",
          "example": "null",
          "invalidWhen": "Set on an Alignment hard-delete row without an audit retention rule, or set while the resource is still returned in normal CFPackage export.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CASE does not define platform retention; platform3 pins soft-retire semantics for CFDocument, CFItem, and CFAssociation.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.retired_at soft-retires CFAssociation without erasing audit evidence; default reads, tree traversal, relationship validation, and spec-pure CFPackage export must exclude non-null rows unless an admin/audit view explicitly opts in.",
          "allowedValues": null,
          "storagePath": "retired_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-association-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.etag protects read-edit-write cycles for CFAssociation; recalculate it after any mutable column in case.cf_association changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this association.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "0a77d011-0f33-4e2e-8aac-5b59860e1fc1",
          "invalidWhen": "Duplicate, missing, not UUID, or changed after dependent tree projections use it.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.required.identifier.",
            "itds": [
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.identifier is the stable edge identity; relationship edits belong in association_type, origin_node_uri, destination_node_uri, sequence_number, notes, or extensions, so an imported edge that changes identity should retire or replace the association row rather than mutate the identifier.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "association_type",
          "apiField": "associationType",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Type of graph relationship between origin and destination.",
          "constraints": "Required. Known CASE values or ext:* extension value only.",
          "example": "isChildOf",
          "invalidWhen": "Unknown value without ext:, typo such as isChildrenOf, or external-ID relationship better modeled as Alignment.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.associationType anyOf enum/pattern.",
            "itds": [
              {
                "id": "citd-017-association-validation",
                "title": "CITD-017 Association validation",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
              },
              {
                "id": "citd-016-item-tree-invariant",
                "title": "CITD-016 CFItem tree invariant",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.association_type selects graph semantics for CFAssociation; known CASE values drive tree/alignment behavior, ext:* is only publisher graph vocabulary, and tenant-specific lesson IDs must use Alignment instead.",
          "allowedValues": "isChildOf, isPeerOf, isPartOf, exactMatchOf, precedes, isRelatedTo, replacedBy, exemplar, hasSkillLevel, isTranslationOf, ext:*",
          "storagePath": "body->>'associationType'"
        },
        {
          "column": "sequence_number",
          "apiField": "sequenceNumber",
          "type": "INTEGER",
          "nullable": "Yes",
          "meaning": "Ordering hint for associated objects from the perspective of this relationship.",
          "constraints": "32-bit integer. For tree children, ascending sequence_number is the default sibling order when present.",
          "example": 10,
          "invalidWhen": "Negative when publisher disallows it, non-integer, or used to replace isChildOf parentage.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.sequenceNumber.",
            "itds": [
              {
                "id": "citd-016-item-tree-invariant",
                "title": "CITD-016 CFItem tree invariant",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.sequence_number is an ordering hint for CFAssociation; null preserves unspecified publisher order, duplicate values require deterministic secondary sorting, and the value must never be treated as stable identity.",
          "allowedValues": null,
          "storagePath": "body->>'sequenceNumber'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this association.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/cfassociations/0a77d011-0f33-4e2e-8aac-5b59860e1fc1",
          "invalidWhen": "Relative URL, malformed URI, or points at a different association identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.required.uri.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.uri is the publisher exchange URI for CFAssociation, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "origin_node_uri",
          "apiField": "originNodeURI",
          "type": "JSONB LinkGenURIDType",
          "nullable": "No",
          "meaning": "Origin side of the association, usually the child item for isChildOf.",
          "constraints": "Required object with title, identifier, uri, and optional targetType. Missing targetType defaults to CASE.",
          "example": "{\"title\":\"CCSS.MATH.CONTENT.3.OA.A.1\",\"identifier\":\"59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f\",\"uri\":\"https://standards.example.org/case/cfitems/59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f\",\"targetType\":\"CASE\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid targetType, or external system IDs stored here.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.required.originNodeURI.",
            "itds": [
              {
                "id": "citd-017-association-validation",
                "title": "CITD-017 Association validation",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
              }
            ]
          },
          "relation": "Belongs to one origin CFDocument or CFItem when targetType is CASE.",
          "edgeCases": "case.cf_association.origin_node_uri is the source endpoint object for the graph edge; originNodeURI.identifier must equal origin_identifier, originNodeURI.targetType must equal origin_target_type after defaulting to CASE, and isChildOf edges require a same-document CASE CFItem origin.",
          "allowedValues": null,
          "storagePath": "body->'originNodeURI'"
        },
        {
          "column": "origin_identifier",
          "apiField": "originNodeURI.identifier",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Extracted origin identifier for indexed graph queries.",
          "constraints": "Must equal originNodeURI.identifier. For targetType CASE and known local objects, references CFDocument or CFItem in the same tenant.",
          "example": "59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
          "invalidWhen": "Differs from originNodeURI.identifier or references another tenant.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Relational projection of CASE LinkGenURIDType.",
            "itds": [
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              },
              {
                "id": "citd-017-association-validation",
                "title": "CITD-017 Association validation",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
              }
            ]
          },
          "relation": "References case.cf_item.identifier or case.cf_document.identifier when origin_target_type=CASE.",
          "edgeCases": "case.cf_association.origin_identifier is the indexed origin endpoint projection for CFAssociation; when the origin targetType is CASE it must resolve same-tenant, and for isChildOf specifically it must stay within the owning document tree without cycles.",
          "allowedValues": null,
          "storagePath": "body->>'originIdentifier' (fallback body->'originNodeURI'->>'identifier')"
        },
        {
          "column": "origin_target_type",
          "apiField": "originNodeURI.targetType",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Target type of the origin link.",
          "constraints": "Defaults to CASE when absent. Otherwise must match ext:*.",
          "example": "CASE",
          "invalidWhen": "Blank, invalid ext pattern, or ext:* used for isChildOf structural tree edges.",
          "source": {
            "label": "Mixed pass-through plus gap fill",
            "basis": "LinkGenURIDType targetType.",
            "itds": [
              {
                "id": "citd-017-association-validation",
                "title": "CITD-017 Association validation",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
              },
              {
                "id": "citd-016-item-tree-invariant",
                "title": "CITD-016 CFItem tree invariant",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.origin_target_type defaults a missing origin targetType to CASE; only ext:* values may point outside CASE, and ext:* origin endpoints are invalid for structural isChildOf placement because tree traversal needs resolvable CASE identifiers.",
          "allowedValues": "CASE, ext:*",
          "storagePath": "body->>'originTargetType' (fallback body->'originNodeURI'->>'targetType')"
        },
        {
          "column": "destination_node_uri",
          "apiField": "destinationNodeURI",
          "type": "JSONB LinkGenURIDType",
          "nullable": "No",
          "meaning": "Destination side of the association, usually the parent item/document for isChildOf.",
          "constraints": "Required object with title, identifier, uri, and optional targetType. Missing targetType defaults to CASE.",
          "example": "{\"title\":\"Operations and Algebraic Thinking\",\"identifier\":\"3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48\",\"uri\":\"https://standards.example.org/case/cfdocuments/3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48\",\"targetType\":\"CASE\"}",
          "invalidWhen": "Missing title/identifier/uri or creates a cycle in the isChildOf tree.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.required.destinationNodeURI.",
            "itds": [
              {
                "id": "citd-017-association-validation",
                "title": "CITD-017 Association validation",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
              },
              {
                "id": "citd-016-item-tree-invariant",
                "title": "CITD-016 CFItem tree invariant",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-016-item-tree-invariant"
              }
            ]
          },
          "relation": "Belongs to one destination CFDocument or CFItem when targetType is CASE.",
          "edgeCases": "case.cf_association.destination_node_uri is the target endpoint object for the graph edge; destinationNodeURI.identifier must equal destination_identifier, destinationNodeURI.targetType must equal destination_target_type after defaulting, and isChildOf destinations must be the owning CFDocument root or a same-document CFItem parent.",
          "allowedValues": null,
          "storagePath": "body->'destinationNodeURI'"
        },
        {
          "column": "destination_identifier",
          "apiField": "destinationNodeURI.identifier",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Extracted destination identifier for indexed graph queries.",
          "constraints": "Must equal destinationNodeURI.identifier and reference same-tenant CASE rows when targetType is CASE.",
          "example": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
          "invalidWhen": "Cross-tenant reference or differs from destinationNodeURI.identifier.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Relational projection of CASE LinkGenURIDType.",
            "itds": [
              {
                "id": "citd-006-query-model",
                "title": "CITD-006 Query model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-006-query-model"
              },
              {
                "id": "citd-017-association-validation",
                "title": "CITD-017 Association validation",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
              }
            ]
          },
          "relation": "References case.cf_item.identifier or case.cf_document.identifier when destination_target_type=CASE.",
          "edgeCases": "case.cf_association.destination_identifier is the indexed destination endpoint projection for CFAssociation; when the destination targetType is CASE it must resolve same-tenant, and for isChildOf specifically it must stay within the owning document tree without cycles.",
          "allowedValues": null,
          "storagePath": "body->>'destinationIdentifier' (fallback body->'destinationNodeURI'->>'identifier')"
        },
        {
          "column": "destination_target_type",
          "apiField": "destinationNodeURI.targetType",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Target type of the destination link.",
          "constraints": "Defaults to CASE when absent. Otherwise must match ext:*.",
          "example": "CASE",
          "invalidWhen": "Invalid ext pattern or used to represent a tenant-specific lesson/resource ID.",
          "source": {
            "label": "Mixed pass-through plus gap fill",
            "basis": "LinkGenURIDType targetType.",
            "itds": [
              {
                "id": "citd-017-association-validation",
                "title": "CITD-017 Association validation",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-017-association-validation"
              },
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.destination_target_type defaults a missing destination targetType to CASE; only ext:* values may point outside CASE, and ext:* destination endpoints are invalid for structural isChildOf placement because tree traversal needs resolvable CASE identifiers.",
          "allowedValues": "CASE, ext:*",
          "storagePath": "body->>'destinationTargetType' (fallback body->'destinationNodeURI'->>'targetType')"
        },
        {
          "column": "cf_association_grouping_uri",
          "apiField": "CFAssociationGroupingURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFAssociationGrouping referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFAssociationGrouping\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFAssociationGrouping/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFAssociationGrouping; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_association.cf_association_grouping_uri optionally classifies the association through a CFAssociationGrouping definition; local group identifiers resolve only when CFDefinitions supplied the grouping, and grouping never changes association_type or creates a structural parent.",
          "allowedValues": null,
          "storagePath": "body->'CFAssociationGroupingURI'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system timestamp for the most recent change to this association.",
          "constraints": "Required ISO 8601 date-time; distinct from platform updated_at.",
          "example": "2026-01-15T09:32:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.last_change_date_time is the publisher/system change time for CFAssociation; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "notes",
          "apiField": "notes",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable publisher notes describing the association.",
          "constraints": "String source text. Preserve markup and sanitize at render boundaries.",
          "example": "Primary grade 3 operations-and-algebraic-thinking placement.",
          "invalidWhen": "Used for internal import errors or rendered trusted HTML.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationDType.notes added in CASE 1.1.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.notes is publisher context for an edge; keep it out of relationship validation logic so changing prose cannot change tree placement or alignment semantics.",
          "allowedValues": null,
          "storagePath": "body->>'notes'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for association data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present. Preserve unknown keys exactly.",
          "example": "{\"publisherExtension\":{\"alignmentStrength\":\"high\"}}",
          "invalidWhen": "Platform audit metadata or Alignment external IDs.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationExtensionDType additionalProperties=true.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              },
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association.extensions is the publisher extension object for CFAssociation; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        },
        {
          "column": "cf_document_uri",
          "apiField": "CFDocumentURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFDocument referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFDocument\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFDocument/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFDocument; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_association.cf_document_uri names the document context for listing/exporting this association; for same-document CASE endpoints it must match the owning CFDocument, and cross-framework or ext:* endpoints must still keep this field as context rather than endpoint identity.",
          "allowedValues": null,
          "storagePath": "body->'CFDocumentURI'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_package_import",
      "title": "CFPackage Import",
      "source": "TimeBack gap fill",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "POST /ims/case/v1p1/CFPackages control record",
      "purpose": "Records each transactional CFPackage import, its canonical package hash, row counts, and validation result.",
      "lifecycle": "Created at import start, committed as succeeded only when all document, item, association, definition, and rubric rows commit atomically, or failed with rowErrors and no partial writes.",
      "authority": "CASE defines the CFPackage envelope. This table is TimeBack audit/idempotency evidence and is never exported inside a CASE package.",
      "example": {
        "import_batch_id": "22222222-2222-4222-8222-222222222222",
        "document_identifier": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
        "package_hash": "sha256:2e1a0f8e7a4d5e1a7b0c9c4a8b3c2d1f0e9a7b6c5d4e3f2a1b0c9d8e7f6a5b4c",
        "status": "succeeded",
        "row_counts": {
          "cf_documents": 1,
          "cf_items": 344,
          "cf_associations": 343
        }
      },
      "validations": [
        "package_hash is computed over canonicalized package JSON before persistence.",
        "A repeated package_hash for the same document can be detected without treating it as a new standards version.",
        "failed imports keep diagnostics but no partial CASE graph rows.",
        "raw CFPackage JSON is not the source of truth after import; normalized rows are."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.tenant_id scopes CFPackage Import rows before any CASE identifier is compared; a migration must enable RLS on case.cf_package_import and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.import_batch_id is audit evidence for the write that produced this CFPackage Import; package imports can stamp many case.cf_package_import rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.created_at is platform storage time for CFPackage Import, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.updated_at drives updatedSince polling for CFPackage Import; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "document_identifier",
          "apiField": "CFPackage.CFDocument.identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "CFDocument identifier targeted by the package.",
          "constraints": "Must match the package CFDocument.identifier and any rows committed under this import.",
          "example": "3f0d4d8c-2d1c-4fd6-98d5-47ad5dfc3d48",
          "invalidWhen": "Differs from the package document identifier or references a document not touched by the import.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Import audit projection.",
            "itds": [
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "References case.cf_document.identifier after successful import.",
          "edgeCases": "case.cf_package_import.document_identifier ties import evidence to the package CFDocument; make the foreign key deferrable so the package control row and normalized document row can commit atomically.",
          "allowedValues": null,
          "storagePath": "document_identifier"
        },
        {
          "column": "package_hash",
          "apiField": "Idempotency package hash",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Canonical SHA-256 digest of the submitted CFPackage JSON.",
          "constraints": "Format sha256:<64 lowercase hex>. Same tenant + same route + same hash can be recognized as duplicate package content.",
          "example": "sha256:2e1a0f8e7a4d5e1a7b0c9c4a8b3c2d1f0e9a7b6c5d4e3f2a1b0c9d8e7f6a5b4c",
          "invalidWhen": "Uppercase, missing sha256: prefix, not computed from canonical payload, or reused with different body.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-008 package hash.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.package_hash must be computed over canonical CFPackage JSON before normalization; recomputing from persisted rows can hide dropped extensions, changed ordering, or duplicate-import conflicts.",
          "allowedValues": null,
          "storagePath": "package_hash"
        },
        {
          "column": "source_uri",
          "apiField": "sourceUri",
          "type": "TEXT URI",
          "nullable": "Yes",
          "meaning": "Optional source location from which the package was obtained.",
          "constraints": "Absolute URI when supplied. Used for evidence only, not as identity.",
          "example": "https://publisher.example.org/case/packages/common-core-math-2010.json",
          "invalidWhen": "Relative URL or used as the package primary key.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Conformance and import evidence field.",
            "itds": [
              {
                "id": "citd-013-conformance-evidence",
                "title": "CITD-013 Conformance evidence",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-013-conformance-evidence"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.source_uri is import evidence for where a package was obtained, not a LinkURI object or primary key; failed imports retain it for audit, and retry/idempotency comparison must use package_hash rather than fetching or trusting this URL.",
          "allowedValues": null,
          "storagePath": "source_uri"
        },
        {
          "column": "status",
          "apiField": "status",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Outcome of the import transaction.",
          "constraints": "One of pending, succeeded, failed.",
          "example": "succeeded",
          "invalidWhen": "Unknown status, succeeded while errors are present, or failed with committed rows.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "TimeBack import control enum.",
            "itds": [
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              },
              {
                "id": "citd-011-error-envelope",
                "title": "CITD-011 Error envelope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-011-error-envelope"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.status is the CFPackage import transaction state; pending cannot survive request completion, succeeded requires committed row_counts, and failed must retain row_errors without partial CASE graph rows.",
          "allowedValues": "pending, succeeded, failed",
          "storagePath": "status"
        },
        {
          "column": "row_counts",
          "apiField": "rowCounts",
          "type": "JSONB",
          "nullable": "No",
          "meaning": "Counts of rows validated and committed by table.",
          "constraints": "JSON object with integer counts per CASE table. Missing tables default to 0.",
          "example": "{\"cf_documents\":1,\"cf_items\":344,\"cf_associations\":343,\"cf_rubrics\":0}",
          "invalidWhen": "Negative counts, strings, or counts that do not match committed rows.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Import evidence projection.",
            "itds": [
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              },
              {
                "id": "citd-013-conformance-evidence",
                "title": "CITD-013 Conformance evidence",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-013-conformance-evidence"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.row_counts is import evidence for CFPackage; for succeeded imports it must match committed case.cf_package_import companion rows by table, and for failed imports it must not imply partial persistence.",
          "allowedValues": null,
          "storagePath": "row_counts"
        },
        {
          "column": "row_errors",
          "apiField": "Problem.rowErrors",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Validation failures tied to package rows or graph invariants.",
          "constraints": "Null for succeeded imports. Failed imports store row number/path, code, message, and field when available.",
          "example": "[{\"path\":\"CFAssociations[12].associationType\",\"code\":\"case:validation_failed\",\"message\":\"Unknown associationType\"}]",
          "invalidWhen": "Present on succeeded imports, lacks stable code, or contains raw sensitive payload fragments.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Typed Problem rowErrors for package import.",
            "itds": [
              {
                "id": "citd-011-error-envelope",
                "title": "CITD-011 Error envelope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-011-error-envelope"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.row_errors is diagnostic evidence for failed package imports; cite package paths and stable case:* codes, but redact sensitive payload fragments so validation logs do not become a data leak.",
          "allowedValues": null,
          "storagePath": "row_errors"
        },
        {
          "column": "committed_at",
          "apiField": "committedAt",
          "type": "TIMESTAMPTZ",
          "nullable": "Yes",
          "meaning": "Server time when normalized CASE rows committed.",
          "constraints": "Required when status=succeeded; null for pending or failed.",
          "example": "2026-05-27T12:06:00Z",
          "invalidWhen": "Set for failed imports or before row counts match committed rows.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Import lifecycle evidence.",
            "itds": [
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_package_import.committed_at marks the moment normalized CFPackage rows committed; leave it null for pending/failed imports, and set it only after row_counts and graph rows are durably written.",
          "allowedValues": null,
          "storagePath": "committed_at"
        }
      ],
      "physicalStorage": "Flat platform audit/control row. Columns shown here are physical columns."
    },
    {
      "name": "case.alignment",
      "title": "Alignment",
      "source": "TimeBack gap fill",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "TimeBack Alignment extension",
      "purpose": "Links an integrator's external system identifier, such as a curriculum lesson ID, to a CASE CFItem without contaminating the canonical CASE graph.",
      "lifecycle": "Created by POST /Alignments or direct app writes, edited with If-Match, deleted as a hard delete in the public API, and never exported in CFPackage. The physical table has retired_at for retained tombstones and active uniqueness.",
      "authority": "Alignment is platform3-specific and intentionally separate from CFAssociation.",
      "example": {
        "alignment_id": "aln_01jz8t6m4v8q3f0x5e2n9p7r6s",
        "external_system": "acme-curriculum",
        "external_id": "lesson-3oa-a1-intro",
        "cf_item_identifier": "59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
        "cf_item_uri": "https://standards.example.org/case/cfitems/59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
        "label": "Lesson introduction aligns to 3.OA.A.1"
      },
      "validations": [
        "external_system + external_id must be unique per tenant.",
        "At least one of cf_item_identifier or cf_item_uri must resolve to an active same-tenant CFItem; the API backfills the other target representation when it can.",
        "metadata is tenant-confidential and excluded from CFPackage export.",
        "Alignment rows are not CFAssociations and do not count as CASE conformance evidence."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.tenant_id scopes Alignment rows before any CASE identifier is compared; a migration must enable RLS on case.alignment and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.import_batch_id is audit evidence for the write that produced this Alignment; package imports can stamp many case.alignment rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.created_at is platform storage time for Alignment, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.updated_at drives updatedSince polling for Alignment; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "retired_at",
          "apiField": "_platform.retired_at",
          "type": "TIMESTAMPTZ",
          "nullable": "Yes",
          "meaning": "Physical liveness/tombstone column for Alignment active uniqueness; public DELETE normally hard-deletes the row.",
          "constraints": "Null means active. Active external-ID uniqueness is scoped by where retired_at is null; there is no deleted_at column.",
          "example": "null",
          "invalidWhen": "A raw lookup filters deleted_at instead of retired_at, includes a non-null retained tombstone as active, or expects Alignment rows in CFPackage export.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CASE does not define platform retention; platform3 pins soft-retire semantics for CFDocument, CFItem, and CFAssociation.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.retired_at is the only liveness column on the physical Alignment table; the public API hard-deletes Alignment rows, but any retained tombstone or direct raw lookup must use retired_at is null and must never reference a deleted_at column.",
          "allowedValues": null,
          "storagePath": "retired_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-alignment-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.etag protects read-edit-write cycles for Alignment; recalculate it after any mutable column in case.alignment changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "alignment_id",
          "apiField": "alignmentId",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Stable TimeBack identifier for this external-ID link; stored as the physical id and in body.alignmentId.",
          "constraints": "Generated by platform3 or accepted as an idempotent client ID. Unique per tenant.",
          "example": "aln_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Blank, reused for a different external ID, or shaped like a CASE identifier to imply 1EdTech authority.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-018 Alignment resource.",
            "itds": [
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.alignment_id is TimeBack identity for an external-ID mapping, not a CASE UUID; idempotent clients may reuse it only for the same external_system/external_id/resolved-CFItem target tuple.",
          "allowedValues": null,
          "storagePath": "id and body->>'alignmentId'"
        },
        {
          "column": "external_system",
          "apiField": "externalSystem",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Integrator-owned system namespace for the external identifier.",
          "constraints": "Required normalized string, max practical length 200, unique with external_id per tenant.",
          "example": "acme-curriculum",
          "invalidWhen": "Contains credentials, tenant secrets, or ambiguous labels such as default.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-018 Alignment fields.",
            "itds": [
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.external_system namespaces customer-owned IDs; generic labels such as default or production make future imports ambiguous, and credentials or tenant secrets must never be stored as the namespace.",
          "allowedValues": null,
          "storagePath": "body->>'externalSystem'"
        },
        {
          "column": "external_id",
          "apiField": "externalId",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Identifier from the external system being aligned to a CFItem.",
          "constraints": "Required string, max practical length 500, unique with external_system per tenant. Preserve case and punctuation.",
          "example": "lesson-3oa-a1-intro",
          "invalidWhen": "Normalized in a way that breaks lookup, stored inside CFItem.extensions, or used as a CFAssociation target.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-018 Alignment fields.",
            "itds": [
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.external_id is case-sensitive external truth; trim only invalid transport whitespace, preserve punctuation and case, and enforce uniqueness with external_system so reverse lookup never guesses.",
          "allowedValues": null,
          "storagePath": "body->>'externalId'"
        },
        {
          "column": "cf_item_uri",
          "apiField": "cfItemUri",
          "type": "TEXT URI",
          "nullable": "Conditional",
          "meaning": "CASE URI for the CFItem target.",
          "constraints": "Individually nullable. Required only when cfItemIdentifier is blank; when supplied, it must be an absolute URI matching the referenced CFItem uri.",
          "example": "https://standards.example.org/case/cfitems/59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
          "invalidWhen": "Both cf_item_uri and cf_item_identifier are blank, points to a CFDocument/non-CASE resource, or resolves to a different CFItem than cf_item_identifier.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-018 Alignment fields.",
            "itds": [
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              }
            ]
          },
          "relation": "Conditionally belongs to one case.cf_item by URI; at least one of cf_item_uri/cf_item_identifier must resolve.",
          "edgeCases": "case.alignment.cf_item_uri is one of the two accepted CFItem target keys for Alignment; it may be null when cf_item_identifier is supplied, must resolve to the same active same-tenant CFItem when both are supplied, and create/update backfills the missing target representation when resolution succeeds.",
          "allowedValues": null,
          "storagePath": "body->>'cfItemUri'"
        },
        {
          "column": "cf_item_identifier",
          "apiField": "cfItemIdentifier",
          "type": "UUID",
          "nullable": "Conditional",
          "meaning": "Local CASE identifier for the CFItem target, indexed for joins.",
          "constraints": "Individually nullable. Required only when cfItemUri is blank; when supplied, it must reference an active same-tenant CFItem at create/update time.",
          "example": "59e8f77c-f1aa-4326-87d8-4b86fa6f7e6f",
          "invalidWhen": "Both cf_item_identifier and cf_item_uri are blank, points at retired CFItem during create, or resolves to a different CFItem than cf_item_uri.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-018 Alignment fields.",
            "itds": [
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "Conditionally belongs to one case.cf_item; at least one of cf_item_identifier/cf_item_uri must resolve.",
          "edgeCases": "case.alignment.cf_item_identifier is one of the two accepted CFItem target keys for Alignment; it may be null when cf_item_uri is supplied, must resolve to the same active same-tenant CFItem when both are supplied, and create/update backfills the missing target representation when resolution succeeds.",
          "allowedValues": null,
          "storagePath": "body->>'cfItemIdentifier'"
        },
        {
          "column": "label",
          "apiField": "label",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable description of why the external ID maps to the CFItem.",
          "constraints": "Optional display text. It is tenant-confidential and not exported in CFPackage.",
          "example": "Lesson introduction aligns to 3.OA.A.1",
          "invalidWhen": "Used as alignment authority when both CFItem target fields are missing.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-018 Alignment fields.",
            "itds": [
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.label is tenant-private display help; allow null and do not include it in uniqueness or authority checks because external_system, external_id, and cf_item_identifier carry identity.",
          "allowedValues": null,
          "storagePath": "body->>'label'"
        },
        {
          "column": "metadata",
          "apiField": "metadata",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Integrator-owned metadata about the alignment.",
          "constraints": "JSON object only. Keep tenant-private details here, not in CASE extensions.",
          "example": "{\"confidence\":\"manual-review\",\"reviewedBy\":\"curriculum-team\"}",
          "invalidWhen": "Scalar/array JSON, credentials, learner data, or any value exported into CFPackage.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "CITD-018 Alignment metadata.",
            "itds": [
              {
                "id": "citd-018-alignment-resource",
                "title": "CITD-018 Alignment resource",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-018-alignment-resource"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.alignment.metadata is tenant-confidential JSON for Alignment only; keep it out of CFPackage, CFItem.extensions, public Problem responses, and any cross-tenant analytics unless explicitly redacted.",
          "allowedValues": null,
          "storagePath": "body->'metadata'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_concept",
      "title": "CFConcept",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFConceptDType",
      "purpose": "Stores concept definitions imported through CFDefinitions and linked from CFItem concept keyword URI fields.",
      "lifecycle": "Imported with CFPackage, read through GET /CFConcepts/{sourcedId}, and updated only by future package imports.",
      "authority": "Imported from CFDefinitions or CFRubrics and served through official detail reads. First-class list/create/update/delete authoring is deferred.",
      "example": {
        "identifier": "0bb9e0f4-5108-4cab-8f8e-b9c1a57f7531",
        "uri": "https://standards.example.org/case/concepts/multiplication",
        "title": "Multiplication",
        "hierarchy_code": "M.3.OA",
        "keywords": "multiply|equal groups"
      },
      "validations": [
        "identifier, uri, required title/description fields, and last_change_date_time preserve the CASE payload.",
        "Rows are scoped by tenant and import_batch_id but platform metadata is never exported inside CFPackage.",
        "Direct authoring endpoints are not part of the shipped 1EdTech surface."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.tenant_id scopes CFConcept rows before any CASE identifier is compared; a migration must enable RLS on case.cf_concept and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.import_batch_id is audit evidence for the write that produced this CFConcept; package imports can stamp many case.cf_concept rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.created_at is platform storage time for CFConcept, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.updated_at drives updatedSince polling for CFConcept; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-concept-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.etag protects read-edit-write cycles for CFConcept; recalculate it after any mutable column in case.cf_concept changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this CFConcept.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "0bb9e0f4-5108-4cab-8f8e-b9c1a57f7531",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptDType.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.identifier is the stable concept identity linked from conceptKeywordsURI; publisher concept changes belong in title, keywords, hierarchy_code, or description, so imports must not infer a new identifier from changed keyword text.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this CFConcept.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/concepts/multiplication",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptDType.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.uri is the publisher exchange URI for CFConcept, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "title",
          "apiField": "title",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Human-readable concept title.",
          "constraints": "Required normalized string.",
          "example": "Multiplication",
          "invalidWhen": "Blank or replaced with an internal taxonomy label.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptDType.required.title.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.title is controlled-concept display text; duplicate titles can exist under different hierarchy_code values, so migrations should not make title tenant-unique.",
          "allowedValues": null,
          "storagePath": "body->>'title'"
        },
        {
          "column": "keywords",
          "apiField": "keywords",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Pipe-delimited concept keywords from the publisher.",
          "constraints": "Preserve delimiter and order when supplied.",
          "example": "multiply|equal groups",
          "invalidWhen": "Split/lowercased when exact publisher text is required for round trip.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptDType.keywords.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.keywords is pipe-delimited publisher text; store it as text instead of text[] so export does not change delimiter or ordering semantics.",
          "allowedValues": null,
          "storagePath": "body->>'keywords'"
        },
        {
          "column": "hierarchy_code",
          "apiField": "hierarchyCode",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Publisher code locating the concept in its concept hierarchy.",
          "constraints": "Required normalized string.",
          "example": "M.3.OA",
          "invalidWhen": "Null, changed during import, or used as the row primary key.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptDType.required.hierarchyCode.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.hierarchy_code is controlled-vocabulary placement; index it for browsing if needed but keep identifier as the primary key because hierarchy codes can change across publisher releases.",
          "allowedValues": null,
          "storagePath": "body->>'hierarchyCode'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable concept description.",
          "constraints": "String source text. Preserve markup.",
          "example": "Concepts related to multiplication and equal groups.",
          "invalidWhen": "Trusted HTML or internal import notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptDType.description.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.description is explanatory concept text; keep it source-authored and do not merge it into keywords because keywords preserve a delimiter-sensitive publisher field.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.last_change_date_time is the publisher/system change time for CFConcept; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for concept data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present.",
          "example": "{\"publisherExtension\":{\"domain\":\"math\"}}",
          "invalidWhen": "Platform metadata or non-object JSON.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFConceptExtensionDType.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_concept.extensions is the publisher extension object for CFConcept; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_subject",
      "title": "CFSubject",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFSubjectDType",
      "purpose": "Stores subject definitions imported through CFDefinitions and linked from document/item subjectURI fields.",
      "lifecycle": "Imported with CFPackage, read through GET /CFSubjects/{sourcedId}, and updated only by future package imports.",
      "authority": "Imported from CFDefinitions or CFRubrics and served through official detail reads. First-class list/create/update/delete authoring is deferred.",
      "example": {
        "identifier": "8c8f5d1e-9d94-4fcb-bfa3-f2e0a2fa9a10",
        "uri": "https://standards.example.org/case/subjects/math",
        "title": "Mathematics",
        "hierarchy_code": "MATH"
      },
      "validations": [
        "identifier, uri, required title/description fields, and last_change_date_time preserve the CASE payload.",
        "Rows are scoped by tenant and import_batch_id but platform metadata is never exported inside CFPackage.",
        "Direct authoring endpoints are not part of the shipped 1EdTech surface."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.tenant_id scopes CFSubject rows before any CASE identifier is compared; a migration must enable RLS on case.cf_subject and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.import_batch_id is audit evidence for the write that produced this CFSubject; package imports can stamp many case.cf_subject rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.created_at is platform storage time for CFSubject, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.updated_at drives updatedSince polling for CFSubject; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-subject-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.etag protects read-edit-write cycles for CFSubject; recalculate it after any mutable column in case.cf_subject changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this CFSubject.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "8c8f5d1e-9d94-4fcb-bfa3-f2e0a2fa9a10",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFSubjectDType.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.identifier is the stable subject identity linked from subjectURI arrays; publisher subject changes belong in title, hierarchy_code, or description, so subject link validation must not match by title alone.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this CFSubject.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/subjects/math",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFSubjectDType.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.uri is the publisher exchange URI for CFSubject, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "title",
          "apiField": "title",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Human-readable subject title.",
          "constraints": "Required normalized string.",
          "example": "Mathematics",
          "invalidWhen": "Blank or translated away from publisher text.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFSubjectDType.required.title.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.title is subject display text; duplicate titles across imported packages should not collide because subjectURI references use identifier, not title.",
          "allowedValues": null,
          "storagePath": "body->>'title'"
        },
        {
          "column": "hierarchy_code",
          "apiField": "hierarchyCode",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Publisher code locating the subject in its hierarchy.",
          "constraints": "Required normalized string.",
          "example": "MATH",
          "invalidWhen": "Null or used as primary key.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFSubjectDType.required.hierarchyCode.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.hierarchy_code is publisher subject-tree placement; keep it queryable but do not use it as identity because publishers can reorganize subject hierarchies.",
          "allowedValues": null,
          "storagePath": "body->>'hierarchyCode'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable subject description.",
          "constraints": "String source text. Preserve markup.",
          "example": "Mathematics standards and competencies.",
          "invalidWhen": "Trusted HTML or internal notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFSubjectDType.description.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.description is subject explanatory text; store it as optional source prose and do not use it to infer subject hierarchy because hierarchy_code carries that placement.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFSubjectDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.last_change_date_time is the publisher/system change time for CFSubject; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for subject data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present.",
          "example": "{\"publisherExtension\":{\"cedsCode\":\"02\"}}",
          "invalidWhen": "Platform metadata or non-object JSON.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFSubjectExtensionDType.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_subject.extensions is the publisher extension object for CFSubject; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_license",
      "title": "CFLicense",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFLicenseDType",
      "purpose": "Stores license records imported through CFDefinitions and linked from CFDocument/CFItem licenseURI fields.",
      "lifecycle": "Imported with CFPackage, read through GET /CFLicenses/{sourcedId}, and updated only by future package imports.",
      "authority": "Imported from CFDefinitions or CFRubrics and served through official detail reads. First-class list/create/update/delete authoring is deferred.",
      "example": {
        "identifier": "3f6ae243-4c10-4568-a817-0892d3e3e5e0",
        "uri": "https://standards.example.org/case/licenses/cc-by",
        "title": "CC BY 4.0",
        "license_text": "Creative Commons Attribution 4.0 International"
      },
      "validations": [
        "identifier, uri, required title/description fields, and last_change_date_time preserve the CASE payload.",
        "Rows are scoped by tenant and import_batch_id but platform metadata is never exported inside CFPackage.",
        "Direct authoring endpoints are not part of the shipped 1EdTech surface."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.tenant_id scopes CFLicense rows before any CASE identifier is compared; a migration must enable RLS on case.cf_license and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.import_batch_id is audit evidence for the write that produced this CFLicense; package imports can stamp many case.cf_license rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.created_at is platform storage time for CFLicense, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.updated_at drives updatedSince polling for CFLicense; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-license-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.etag protects read-edit-write cycles for CFLicense; recalculate it after any mutable column in case.cf_license changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this CFLicense.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "3f6ae243-4c10-4568-a817-0892d3e3e5e0",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFLicenseDType.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.identifier is the stable license identity linked from licenseURI; title, description, and license_text can change without changing the link target, so migrations should not dedupe licenses by title.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this CFLicense.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/licenses/cc-by",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFLicenseDType.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.uri is the publisher exchange URI for CFLicense, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "title",
          "apiField": "title",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Human-readable license title.",
          "constraints": "Required normalized string.",
          "example": "CC BY 4.0",
          "invalidWhen": "Blank or replaced with a platform shortcut.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFLicenseDType.required.title.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.title is the license display label; it is not the legal text and should not be unique because multiple publishers can reuse common labels like CC BY.",
          "allowedValues": null,
          "storagePath": "body->>'title'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable license description.",
          "constraints": "String source text.",
          "example": "Allows sharing and adaptation with attribution.",
          "invalidWhen": "Internal legal notes mixed into publisher license description.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFLicenseDType.description.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.description is publisher license explanation; keep legal terms in license_text and use description only for supplemental human context.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "license_text",
          "apiField": "licenseText",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Legal license text or link text conveyed by the organization.",
          "constraints": "Required string. Preserve text exactly enough for round trip.",
          "example": "Creative Commons Attribution 4.0 International",
          "invalidWhen": "Blank, truncated, or moved only to notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFLicenseDType.required.licenseText.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.license_text is the legal license content carried by CASE; preserve the full string for round trip and do not move it into description or notes.",
          "allowedValues": null,
          "storagePath": "body->>'licenseText'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFLicenseDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.last_change_date_time is the publisher/system change time for CFLicense; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for license data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present.",
          "example": "{\"publisherExtension\":{\"spdx\":\"CC-BY-4.0\"}}",
          "invalidWhen": "Platform metadata or non-object JSON.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFLicenseExtensionDType.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_license.extensions is the publisher extension object for CFLicense; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_item_type",
      "title": "CFItemType",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFItemTypeDType",
      "purpose": "Stores publisher-defined item type definitions, such as cluster, domain, standard, competency, or benchmark.",
      "lifecycle": "Imported with CFPackage, read through GET /CFItemTypes/{sourcedId}, and updated only by future package imports.",
      "authority": "Imported from CFDefinitions or CFRubrics and served through official detail reads. First-class list/create/update/delete authoring is deferred.",
      "example": {
        "identifier": "7d7647f5-4d6f-4e67-a968-9915078aa1e1",
        "uri": "https://standards.example.org/case/item-types/standard",
        "title": "Standard",
        "hierarchy_code": "3",
        "description": "A leaf or near-leaf academic standard."
      },
      "validations": [
        "identifier, uri, required title/description fields, and last_change_date_time preserve the CASE payload.",
        "Rows are scoped by tenant and import_batch_id but platform metadata is never exported inside CFPackage.",
        "Direct authoring endpoints are not part of the shipped 1EdTech surface."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.tenant_id scopes CFItemType rows before any CASE identifier is compared; a migration must enable RLS on case.cf_item_type and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.import_batch_id is audit evidence for the write that produced this CFItemType; package imports can stamp many case.cf_item_type rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.created_at is platform storage time for CFItemType, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.updated_at drives updatedSince polling for CFItemType; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-item-type-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.etag protects read-edit-write cycles for CFItemType; recalculate it after any mutable column in case.cf_item_type changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this CFItemType.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "7d7647f5-4d6f-4e67-a968-9915078aa1e1",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeDType.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.identifier is the stable item-type identity linked from CFItemTypeURI; publisher edits belong in title, description, hierarchy_code, or type_code, and the text CFItemType field on CFItem remains separate display vocabulary.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this CFItemType.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/item-types/standard",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeDType.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.uri is the publisher exchange URI for CFItemType, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "title",
          "apiField": "title",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Human-readable item type title.",
          "constraints": "Required normalized string.",
          "example": "Standard",
          "invalidWhen": "Blank or mapped to local product label.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeDType.required.title.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.title is the publisher's item-type label; preserve it even when type_code exists because browser filters often show the title.",
          "allowedValues": null,
          "storagePath": "body->>'title'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Description of what the item type means in this framework.",
          "constraints": "Required string source text.",
          "example": "A leaf or near-leaf academic standard.",
          "invalidWhen": "Blank or replaced with generic 'item type'.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeDType.required.description.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.description defines what the item type means in the publisher framework; require nonblank source text and do not replace it with a generic local label like standard.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "hierarchy_code",
          "apiField": "hierarchyCode",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Publisher code locating the type in the item-type hierarchy.",
          "constraints": "Required normalized string.",
          "example": "3",
          "invalidWhen": "Null or used as primary key.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeDType.required.hierarchyCode.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.hierarchy_code is item-type hierarchy placement; it can drive ordered displays but must not replace identifier as the FK target from CFItemTypeURI.",
          "allowedValues": null,
          "storagePath": "body->>'hierarchyCode'"
        },
        {
          "column": "type_code",
          "apiField": "typeCode",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Text code used by the publisher for type identification.",
          "constraints": "Normalized string when present.",
          "example": "STD",
          "invalidWhen": "Invented by platform3 or confused with hierarchy_code.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeDType.typeCode.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.type_code is an optional publisher code; keep it separate from hierarchy_code and title, and do not synthesize it when the publisher omits it.",
          "allowedValues": null,
          "storagePath": "body->>'typeCode'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.last_change_date_time is the publisher/system change time for CFItemType; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for item type data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present.",
          "example": "{\"publisherExtension\":{\"displayRank\":3}}",
          "invalidWhen": "Platform metadata or non-object JSON.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFItemTypeExtensionDType.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_item_type.extensions is the publisher extension object for CFItemType; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_association_grouping",
      "title": "CFAssociationGrouping",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFAssociationGroupingDType",
      "purpose": "Stores publisher grouping definitions for sets of associations.",
      "lifecycle": "Imported with CFPackage, read through GET /CFAssociationGroupings/{sourcedId}, and updated only by future package imports.",
      "authority": "Imported from CFDefinitions or CFRubrics and served through official detail reads. First-class list/create/update/delete authoring is deferred.",
      "example": {
        "identifier": "19ce0a3d-3703-4877-8c51-fda04bb1c676",
        "uri": "https://standards.example.org/case/association-groups/math-progressions",
        "title": "Math progressions"
      },
      "validations": [
        "identifier, uri, required title/description fields, and last_change_date_time preserve the CASE payload.",
        "Rows are scoped by tenant and import_batch_id but platform metadata is never exported inside CFPackage.",
        "Direct authoring endpoints are not part of the shipped 1EdTech surface."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.tenant_id scopes CFAssociationGrouping rows before any CASE identifier is compared; a migration must enable RLS on case.cf_association_grouping and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.import_batch_id is audit evidence for the write that produced this CFAssociationGrouping; package imports can stamp many case.cf_association_grouping rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.created_at is platform storage time for CFAssociationGrouping, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.updated_at drives updatedSince polling for CFAssociationGrouping; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-association-grouping-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.etag protects read-edit-write cycles for CFAssociationGrouping; recalculate it after any mutable column in case.cf_association_grouping changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this CFAssociationGrouping.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "19ce0a3d-3703-4877-8c51-fda04bb1c676",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationGroupingDType.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.identifier is the stable grouping identity linked from CFAssociationGroupingURI; title and description changes do not alter the association_type semantics of member CFAssociations.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this CFAssociationGrouping.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/association-groups/math-progressions",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationGroupingDType.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.uri is the publisher exchange URI for CFAssociationGrouping, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "title",
          "apiField": "title",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Human-readable association grouping title.",
          "constraints": "Required normalized string.",
          "example": "Math progressions",
          "invalidWhen": "Blank or generated from internal grouping ID.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationGroupingDType.required.title.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.title is grouping display text; it does not define association_type and should not change how member edges are validated.",
          "allowedValues": null,
          "storagePath": "body->>'title'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Description of what this association group means.",
          "constraints": "String source text.",
          "example": "Progression relationships across grade-level math standards.",
          "invalidWhen": "Trusted HTML or internal import notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationGroupingDType.description.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.description explains why a publisher grouped associations; preserve it for detail reads but keep association membership and type validation on CFAssociation rows.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationGroupingDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.last_change_date_time is the publisher/system change time for CFAssociationGrouping; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for association grouping data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present.",
          "example": "{\"publisherExtension\":{\"groupType\":\"progression\"}}",
          "invalidWhen": "Platform metadata or non-object JSON.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFAssociationGroupingExtensionDType.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_association_grouping.extensions is the publisher extension object for CFAssociationGrouping; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_rubric",
      "title": "CFRubric",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, id)",
      "apiObject": "CFRubricDType",
      "purpose": "Stores rubric definitions imported with CFPackage, including criteria and levels that complete the rubric.",
      "lifecycle": "Imported with CFPackage, read through GET /CFRubrics/{sourcedId}, and updated only by future package imports.",
      "authority": "Imported from CFDefinitions or CFRubrics and served through official detail reads. First-class list/create/update/delete authoring is deferred.",
      "example": {
        "identifier": "e9af0e37-fc6d-4680-9889-7d4b3460b8ee",
        "uri": "https://standards.example.org/case/rubrics/math-practice",
        "title": "Mathematical practice rubric"
      },
      "validations": [
        "identifier, uri, required title/description fields, and last_change_date_time preserve the CASE payload.",
        "Rows are scoped by tenant and import_batch_id but platform metadata is never exported inside CFPackage.",
        "Direct authoring endpoints are not part of the shipped 1EdTech surface."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.tenant_id scopes CFRubric rows before any CASE identifier is compared; a migration must enable RLS on case.cf_rubric and every (tenant_id, id) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.import_batch_id is audit evidence for the write that produced this CFRubric; package imports can stamp many case.cf_rubric rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.created_at is platform storage time for CFRubric, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.updated_at drives updatedSince polling for CFRubric; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-rubric-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.etag protects read-edit-write cycles for CFRubric; recalculate it after any mutable column in case.cf_rubric changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for this CFRubric.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "e9af0e37-fc6d-4680-9889-7d4b3460b8ee",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricDType.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.identifier is the stable rubric identity; title and description are optional display metadata, while criteria and levels keep their own identifiers for joins and package round trips.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this CFRubric.",
          "constraints": "Required absolute URI. Unique per tenant.",
          "example": "https://standards.example.org/case/rubrics/math-practice",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricDType.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.uri is the publisher exchange URI for CFRubric, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "title",
          "apiField": "title",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable rubric title.",
          "constraints": "Optional in CASE schema but should be preserved when supplied.",
          "example": "Mathematical practice rubric",
          "invalidWhen": "Invented title on import when source omitted it.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricDType.title.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.title is optional rubric display metadata; null is valid, and clients should fall back to uri or identifier instead of inventing a title on import.",
          "allowedValues": null,
          "storagePath": "body->>'title'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Human-readable rubric description.",
          "constraints": "String source text. Preserve markup.",
          "example": "Rubric for explaining reasoning and modeling with mathematics.",
          "invalidWhen": "Trusted HTML or internal notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricDType.description.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.description is rubric-level source prose; preserve it on the rubric row and do not copy criterion or level feedback into it during normalization.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.last_change_date_time is the publisher/system change time for CFRubric; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for rubric data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present.",
          "example": "{\"publisherExtension\":{\"audience\":\"teachers\"}}",
          "invalidWhen": "Platform metadata or non-object JSON.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricExtensionDType.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric.extensions is the publisher extension object for CFRubric; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "CASE resource row: tenant_id/id/resource_type/import_batch_id/body/version/etag/timestamps/retired_at are physical columns; CASE payload fields are stored in body JSONB."
    },
    {
      "name": "case.cf_rubric_criterion",
      "title": "CFRubricCriterion",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, identifier)",
      "apiObject": "CFRubricCriterionDType",
      "purpose": "Stores one criterion row inside an imported CFRubric.",
      "lifecycle": "Imported with its parent CFRubric, read as part of CFRubric detail, and not directly authored in the first release.",
      "authority": "CASE 1.1 rubric criterion fields are pass-through; rubric_identifier is the relational parent key.",
      "example": {
        "identifier": "d3b07384-d9a6-4d4f-9f29-2141f2513fd2",
        "rubric_identifier": "e9af0e37-fc6d-4680-9889-7d4b3460b8ee",
        "category": "Reasoning",
        "position": 1
      },
      "validations": [
        "rubric_identifier must reference a same-tenant CFRubric.",
        "position is an integer ordering hint and may be null if the publisher omits it.",
        "CFItemURI, when present, references the standard this criterion evaluates."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.tenant_id scopes CFRubricCriterion rows before any CASE identifier is compared; a migration must enable RLS on case.cf_rubric_criterion and every (tenant_id, identifier) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.import_batch_id is audit evidence for the write that produced this CFRubricCriterion; package imports can stamp many case.cf_rubric_criterion rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.created_at is platform storage time for CFRubricCriterion, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.updated_at drives updatedSince polling for CFRubricCriterion; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-rubric-criterion-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.etag protects read-edit-write cycles for CFRubricCriterion; recalculate it after any mutable column in case.cf_rubric_criterion changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for the criterion.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "d3b07384-d9a6-4d4f-9f29-2141f2513fd2",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.required.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.identifier is the stable criterion identity; category, description, cf_item_uri, weight, and position can change independently, and rubric_identifier remains the parent join key.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "rubric_identifier",
          "apiField": "rubricId",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Parent CFRubric identifier.",
          "constraints": "Must reference case.cf_rubric in same tenant.",
          "example": "e9af0e37-fc6d-4680-9889-7d4b3460b8ee",
          "invalidWhen": "Missing or points to another tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.rubricId.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "Belongs to one case.cf_rubric.",
          "edgeCases": "case.cf_rubric_criterion.rubric_identifier is the parent CFRubric join; every FK and index must include tenant_id, and hard-deleting the parent should be restricted to preserve package round-trip evidence.",
          "allowedValues": null,
          "storagePath": "rubric_id"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this criterion.",
          "constraints": "Required absolute URI.",
          "example": "https://standards.example.org/case/rubric-criteria/reasoning",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.required.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.uri is the publisher exchange URI for CFRubricCriterion, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "category",
          "apiField": "category",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Text label by which criteria may be grouped.",
          "constraints": "Normalized string when present.",
          "example": "Reasoning",
          "invalidWhen": "Used as the primary key or invented during import.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.category.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.category is a grouping label inside a rubric; it may repeat across criteria and must not become part of the primary key.",
          "allowedValues": null,
          "storagePath": "body->>'category'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Criterion description shown to rubric users.",
          "constraints": "String source text. Preserve markup.",
          "example": "Explains mathematical reasoning clearly.",
          "invalidWhen": "Trusted HTML or internal scoring notes.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.description.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.description is criterion-level scoring guidance; preserve it separately from category so a category filter does not discard the human scoring instructions.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "cf_item_uri",
          "apiField": "CFItemURI",
          "type": "JSONB LinkURIDType",
          "nullable": "Yes",
          "meaning": "CASE link object pointing to the authoritative CFItem referenced by this row.",
          "constraints": "When present, the object must include title, identifier, and uri; identifier is a CASE UUID and uri is a valid absolute URI.",
          "example": "{\"title\":\"Example CFItem\",\"identifier\":\"11111111-1111-4111-8111-111111111111\",\"uri\":\"https://standards.example.org/CFItem/11111111-1111-4111-8111-111111111111\"}",
          "invalidWhen": "Missing title/identifier/uri, invalid URI, or identifier not scoped to the same tenant when the target is persisted locally.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CASE LinkURIDType.",
            "itds": [
              {
                "id": "citd-002-source-contract",
                "title": "CITD-002 Source contract",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-002-source-contract"
              },
              {
                "id": "citd-003-data-model-primitives",
                "title": "CITD-003 Data model primitives",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-003-data-model-primitives"
              }
            ]
          },
          "relation": "Nullable belongs-to-one link to CFItem; not a separate table unless the target is persisted by this dictionary.",
          "edgeCases": "case.cf_rubric_criterion.cf_item_uri optionally ties a rubric criterion to a CASE item; when supplied its identifier/uri must resolve to a same-tenant CFItem, but a criterion can remain valid without it and tenant-specific lesson IDs still belong in Alignment.",
          "allowedValues": null,
          "storagePath": "body->'CFItemURI'"
        },
        {
          "column": "weight",
          "apiField": "weight",
          "type": "DOUBLE PRECISION",
          "nullable": "Yes",
          "meaning": "Numeric weight assigned to this criterion.",
          "constraints": "Float. Null means publisher did not weight the criterion.",
          "example": 0.25,
          "invalidWhen": "String percentage, negative value without publisher semantics, or normalized away from source.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.weight.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.weight is nullable numeric source data; store it as double precision and do not normalize percentages or negative publisher values without a separate validation decision.",
          "allowedValues": null,
          "storagePath": "body->>'weight'"
        },
        {
          "column": "position",
          "apiField": "position",
          "type": "INTEGER",
          "nullable": "Yes",
          "meaning": "Ordering position of this criterion within the rubric.",
          "constraints": "32-bit integer when present.",
          "example": 1,
          "invalidWhen": "Non-integer or used as stable identity.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.position.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.position is an ordering hint for CFRubricCriterion; null preserves unspecified publisher order, duplicate values require deterministic secondary sorting, and the value must never be treated as stable identity.",
          "allowedValues": null,
          "storagePath": "body->>'position'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.last_change_date_time is the publisher/system change time for CFRubricCriterion; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension object for criterion data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "JSON object when present.",
          "example": "{\"publisherExtension\":{\"scale\":\"4-point\"}}",
          "invalidWhen": "Platform metadata or non-object JSON.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionExtensionDType.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion.extensions is the publisher extension object for CFRubricCriterion; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "Rubric child parent keys are physical columns; CASE criterion/level payload fields remain in body JSONB."
    },
    {
      "name": "case.cf_rubric_criterion_level",
      "title": "CFRubricCriterionLevel",
      "source": "1EdTech pass-through",
      "primaryKey": "(tenant_id, identifier)",
      "apiObject": "CFRubricCriterionLevelDType",
      "purpose": "Stores one achievement level inside an imported rubric criterion.",
      "lifecycle": "Imported with the parent criterion, read as part of CFRubric detail, and not directly authored in the first release.",
      "authority": "CASE 1.1 level fields are pass-through; rubric_criterion_identifier is the relational parent key.",
      "example": {
        "identifier": "a61af36a-4e73-4b51-809f-980111bb4d8d",
        "rubric_criterion_identifier": "d3b07384-d9a6-4d4f-9f29-2141f2513fd2",
        "quality": "Meets",
        "score": 3
      },
      "validations": [
        "rubric_criterion_identifier must reference a same-tenant criterion.",
        "score is a float and may be null if the publisher supplies only qualitative levels.",
        "feedback remains source text and must not leak learner-specific comments."
      ],
      "fields": [
        {
          "column": "tenant_id",
          "apiField": "_platform.tenant_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Tenant boundary for this row; every query must scope by this value and it must match the signed JWT tenantId.",
          "constraints": "Required on every persisted CASE row. Stored as text to match the JWT tenantId claim; clients never send it inside CASE JSON.",
          "example": "reviewer-case",
          "invalidWhen": "Missing, blank, or different from the token tenantId. A cross-tenant join is invalid even when identifiers match.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Inherits platform_audit_provenance and platform_single_deploy_tenant_scoped.",
            "itds": [
              {
                "id": "citd-012-tenant-routing",
                "title": "CITD-012 Tenant routing",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-012-tenant-routing"
              },
              {
                "id": "pitd-028-api-axis-tenant-routing",
                "title": "PITD-028 Platform tenant enforcement",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/architecture/#pitd-028-api-axis-tenant-routing"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.tenant_id scopes CFRubricCriterionLevel rows before any CASE identifier is compared; a migration must enable RLS on case.cf_rubric_criterion_level and every (tenant_id, identifier) join must include tenant_id or two tenants can expose identical publisher identifiers to each other.",
          "allowedValues": null,
          "storagePath": "tenant_id"
        },
        {
          "column": "import_batch_id",
          "apiField": "_platform.import_batch_id",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Audit handle for the write or CFPackage import that produced the row.",
          "constraints": "Set by the API for per-resource writes and by CFPackage import for package rows. Same batch id may touch many CASE tables.",
          "example": "imp_01jz8t6m4v8q3f0x5e2n9p7r6s",
          "invalidWhen": "Null, blank, or reused for two package imports with different canonical package hashes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Architecture requires _platform.import_batch_id sidecar metadata on every persisted row.",
            "itds": [
              {
                "id": "citd-008-idempotency-model",
                "title": "CITD-008 Idempotency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-008-idempotency-model"
              },
              {
                "id": "citd-020-package-import-export",
                "title": "CITD-020 CFPackage import/export",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-020-package-import-export"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.import_batch_id is audit evidence for the write that produced this CFRubricCriterionLevel; package imports can stamp many case.cf_rubric_criterion_level rows with one batch id, but retrying a different body under the same idempotency key must surface case:idempotency_conflict instead of overwriting provenance.",
          "allowedValues": null,
          "storagePath": "import_batch_id"
        },
        {
          "column": "created_at",
          "apiField": "_platform.created_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp when platform3 first stored this row for the tenant.",
          "constraints": "Generated by the service, UTC ISO 8601 when serialized, never accepted from client payloads.",
          "example": "2026-05-27T12:00:00Z",
          "invalidWhen": "Client-supplied timestamps, local-time strings without offset, or values earlier than the import batch start.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Platform audit provenance.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.created_at is platform storage time for CFRubricCriterionLevel, not publisher time; use last_change_date_time or status date fields for CASE payload chronology, and reject client-supplied created_at values during imports and per-resource writes.",
          "allowedValues": null,
          "storagePath": "created_at"
        },
        {
          "column": "updated_at",
          "apiField": "_platform.updated_at",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Server timestamp for the most recent accepted mutation of this row.",
          "constraints": "Generated by the service. Used by updatedSince polling and ETag recalculation.",
          "example": "2026-05-27T12:05:00Z",
          "invalidWhen": "Older than created_at, supplied by the client, or not advanced after a mutable field changes.",
          "source": {
            "label": "Inherited platform contract",
            "basis": "Pollable update filters and HTTP validators.",
            "itds": [
              {
                "id": "citd-010-eventing-model",
                "title": "CITD-010 Eventing model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-010-eventing-model"
              },
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.updated_at drives updatedSince polling for CFRubricCriterionLevel; an accepted mutation must advance it and recalculate ETag, while an idempotent replay or rejected validation must leave it unchanged.",
          "allowedValues": null,
          "storagePath": "updated_at"
        },
        {
          "column": "etag",
          "apiField": "ETag header",
          "type": "TEXT",
          "nullable": "No",
          "meaning": "Strong HTTP validator for safe read-edit-write cycles.",
          "constraints": "Generated from tenant, table, identifier, row version, and updated_at. PUT, PATCH, and DELETE require If-Match with this value.",
          "example": "\"case-cf-rubric-criterion-level-7a4f-v3\"",
          "invalidWhen": "Missing on mutable resource reads, reused after a mutation, weak validator, or accepted with a stale If-Match.",
          "source": {
            "label": "TimeBack gap fill",
            "basis": "Per-resource ETag/If-Match is a TimeBack persistence gap fill over CASE reads.",
            "itds": [
              {
                "id": "citd-007-concurrency-model",
                "title": "CITD-007 Concurrency model",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-007-concurrency-model"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.etag protects read-edit-write cycles for CFRubricCriterionLevel; recalculate it after any mutable column in case.cf_rubric_criterion_level changes, return case:precondition_required when If-Match is absent, and case:precondition_failed when stale.",
          "allowedValues": null,
          "storagePath": "etag"
        },
        {
          "column": "identifier",
          "apiField": "identifier",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Synthetic globally unique CASE identifier for the criterion level.",
          "constraints": "Must match CASE UUID pattern and be unique per tenant.",
          "example": "a61af36a-4e73-4b51-809f-980111bb4d8d",
          "invalidWhen": "Missing, not UUID, or duplicate in tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.required.identifier.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.identifier is the stable criterion-level identity; quality, score, feedback, description, and position carry the level semantics while rubric_criterion_identifier carries the parent join.",
          "allowedValues": null,
          "storagePath": "id and body->>'identifier'"
        },
        {
          "column": "rubric_criterion_identifier",
          "apiField": "rubricCriterionId",
          "type": "UUID",
          "nullable": "No",
          "meaning": "Parent CFRubricCriterion identifier.",
          "constraints": "Must reference case.cf_rubric_criterion in same tenant.",
          "example": "d3b07384-d9a6-4d4f-9f29-2141f2513fd2",
          "invalidWhen": "Missing or points to another tenant.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.rubricCriterionId.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "Belongs to one case.cf_rubric_criterion.",
          "edgeCases": "case.cf_rubric_criterion_level.rubric_criterion_identifier is the parent criterion join; include tenant_id in the FK and index so levels from two tenants cannot attach to the same visible criterion.",
          "allowedValues": null,
          "storagePath": "criterion_id"
        },
        {
          "column": "uri",
          "apiField": "uri",
          "type": "TEXT URI",
          "nullable": "No",
          "meaning": "Network-resolvable URI for this level.",
          "constraints": "Required absolute URI.",
          "example": "https://standards.example.org/case/rubric-levels/meets",
          "invalidWhen": "Relative URL or mismatched identifier.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.required.uri.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.uri is the publisher exchange URI for CFRubricCriterionLevel, not the platform route; preserve redirected or externally hosted URIs as supplied and use API paths only for local retrieval.",
          "allowedValues": null,
          "storagePath": "body->>'uri'"
        },
        {
          "column": "description",
          "apiField": "description",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Level description.",
          "constraints": "String source text. Preserve markup.",
          "example": "Reasoning is complete and uses appropriate examples.",
          "invalidWhen": "Trusted HTML or learner-specific feedback.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.description.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.description describes the performance level; keep it distinct from feedback so UI can show the level definition separately from response text.",
          "allowedValues": null,
          "storagePath": "body->>'description'"
        },
        {
          "column": "quality",
          "apiField": "quality",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Qualitative label for this level.",
          "constraints": "Normalized string when present.",
          "example": "Meets",
          "invalidWhen": "Invented by platform3 or confused with numeric score.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.quality.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.quality is the qualitative rubric label; it is publisher text, not a closed enum, and should stay separate from numeric score.",
          "allowedValues": null,
          "storagePath": "body->>'quality'"
        },
        {
          "column": "score",
          "apiField": "score",
          "type": "DOUBLE PRECISION",
          "nullable": "Yes",
          "meaning": "Points awarded for achieving this level.",
          "constraints": "Float when present. Null is valid for qualitative-only rubrics.",
          "example": 3,
          "invalidWhen": "String such as '3 pts' or normalized to percentage without source evidence.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.score.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.score is nullable numeric source data; preserve absent qualitative-only scores as null and reject formatted strings before storage.",
          "allowedValues": null,
          "storagePath": "body->>'score'"
        },
        {
          "column": "feedback",
          "apiField": "feedback",
          "type": "TEXT",
          "nullable": "Yes",
          "meaning": "Pre-defined feedback text associated with the level.",
          "constraints": "String source text. This is rubric template feedback, not learner-specific feedback.",
          "example": "Explain how each example supports your conclusion.",
          "invalidWhen": "Stores actual student feedback or includes learner PII.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.feedback.",
            "itds": [
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              },
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.feedback is reusable rubric template feedback; reject learner-specific comments or PII here because those belong in assessment-result systems, not CASE package metadata.",
          "allowedValues": null,
          "storagePath": "body->>'feedback'"
        },
        {
          "column": "position",
          "apiField": "position",
          "type": "INTEGER",
          "nullable": "Yes",
          "meaning": "Ordering position of this level within the criterion.",
          "constraints": "32-bit integer when present.",
          "example": 3,
          "invalidWhen": "Non-integer or used as stable identity.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.position.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.position is an ordering hint for CFRubricCriterionLevel; null preserves unspecified publisher order, duplicate values require deterministic secondary sorting, and the value must never be treated as stable identity.",
          "allowedValues": null,
          "storagePath": "body->>'position'"
        },
        {
          "column": "last_change_date_time",
          "apiField": "lastChangeDateTime",
          "type": "TIMESTAMPTZ",
          "nullable": "No",
          "meaning": "Publisher/system change timestamp.",
          "constraints": "Required ISO 8601 date-time.",
          "example": "2026-01-15T09:00:00Z",
          "invalidWhen": "Missing or overwritten with platform updated_at.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelDType.required.lastChangeDateTime.",
            "itds": [
              {
                "id": "citd-021-model-scope-definitions-rubrics",
                "title": "CITD-021 Definitions and rubrics scope",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-021-model-scope-definitions-rubrics"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.last_change_date_time is the publisher/system change time for CFRubricCriterionLevel; it can move independently of platform updated_at, so local sync polling must use updated_at and CASE round-trip/export must preserve this source timestamp.",
          "allowedValues": null,
          "storagePath": "body->>'lastChangeDateTime'"
        },
        {
          "column": "extensions",
          "apiField": "extensions",
          "type": "JSONB[]",
          "nullable": "Yes",
          "meaning": "Proprietary CASE extension objects for criterion level data. Under CITD-019 these values are opaque pass-through bytes; CASE preserves them without assigning Curriculum registry semantics.",
          "constraints": "Array of JSON objects when present.",
          "example": "[{\"publisherExtension\":{\"levelColor\":\"green\"}}]",
          "invalidWhen": "Scalar JSON, platform metadata, or learner-specific values.",
          "source": {
            "label": "1EdTech pass-through",
            "basis": "CFRubricCriterionLevelExtensionDType array.",
            "itds": [
              {
                "id": "citd-019-case11-extensions-markdown",
                "title": "CITD-019 CASE 1.1 extensions and Markdown",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-019-case11-extensions-markdown"
              },
              {
                "id": "citd-014-privacy-retention",
                "title": "CITD-014 Privacy and retention",
                "href": "https://platform3-andymontgomery-9773s-projects.vercel.app/case/1edtech/architecture/#citd-014-privacy-retention"
              }
            ]
          },
          "relation": "",
          "edgeCases": "case.cf_rubric_criterion_level.extensions is the publisher extension object for CFRubricCriterionLevel; preserve unknown keys exactly, require the documented JSON shape, and keep tenant-private Alignment metadata plus _platform audit fields out of the CASE extension payload.",
          "allowedValues": null,
          "storagePath": "body->'extensions'"
        }
      ],
      "physicalStorage": "Rubric child parent keys are physical columns; CASE criterion/level payload fields remain in body JSONB."
    }
  ],
  "schemaVersion": "1.0.0",
  "legacyKnowledgeComponentCutover": {
    "effectiveAfterIssue": 707,
    "caseReadContract": "Retained legacy CASE Knowledge Component CFItems remain readable, listable, and exportable as immutable historical evidence. CASE rejects their create, update, retire, delete, and governed relationship mutation with case:legacy_kc_read_only.",
    "curriculumOwner": "Curriculum is the sole owner of active Knowledge Component authoring, retirement validation, and governed KC graph semantics.",
    "governedLegacyAssociationTypes": [
      "precedes",
      "ext:alpha.needs_first",
      "replacedBy",
      "exactMatchOf"
    ],
    "extensionBoundary": "Under CITD-019, CASE extensions are opaque pass-through bytes. CASE does not interpret registry KC references such as scope_kc_refs or remediation kc_ref/kc_id; Curriculum validates those references in the registry-native model.",
    "nonGovernedAssociationRule": "Other stock CASE association types retain normal CASE behavior; this cutover must not broaden the four governed legacy relationship types."
  },
  "sqlAuthority": {
    "semanticOwner": "This approved CASE data dictionary owns field meaning, lifecycle meaning, public errors, and #707 ownership boundaries.",
    "executableMechanics": [
      "loop/case/artifacts/1edtech/implementation/impl/migrations/001_case_core.sql",
      "loop/case/artifacts/1edtech/implementation/impl/src/migrations/caseSchema.mjs"
    ],
    "snapshotRule": "sqlSections preserve the previously deployed June 11 documentation for reconciliation. They are not an executable migration source; each generated artifact binds the exact tracked DDL hashes, and stale excerpts are labeled instead of silently presented as current."
  },
  "generatedFrom": {
    "canonicalSource": "loop/case/artifacts/1edtech/data_dictionary/source/case-data-dictionary-source.json",
    "deployedSnapshot": "loop/case/artifacts/1edtech/data_dictionary/source/reconstruction/deployed-case-data-dictionary-20260723.json",
    "reconciliation": "loop/case/artifacts/1edtech/data_dictionary/source/reconciliation.json",
    "officialOpenApi": "loop/case/artifacts/1edtech/data_dictionary/source/upstream/imscasev1p1-openapi3-v1p0.json",
    "architecture": "loop/case/artifacts/1edtech/architecture/commitments.json",
    "migration": "loop/case/artifacts/1edtech/implementation/impl/migrations/001_case_core.sql",
    "runtimeSchema": "loop/case/artifacts/1edtech/implementation/impl/src/migrations/caseSchema.mjs",
    "deploymentMirror": "loop/case/artifacts/1edtech/implementation/impl/data_dictionary"
  },
  "sourceSha256": "2388c34481a3cb57d8734aa390775b7ff74887c295c9fd83fcd1dfb8b2b9aff8",
  "reconciliationSha256": "a1d8c356e3ceb26138fd562ef2c84c5b2c388f641863cab342813677332579d4",
  "authoritySha256": {
    "architecture": "e3e4a4e9d2e127c10e191249849b3683715b33dcb9e616f9d9fc958b8b45f869",
    "migration": "a9b81cb6e001ae3ae261a78987e5dafdcf14052f27ede753e04ce24d01e35c56",
    "runtimeSchema": "7eb36416212515f01c1a82df2d48f8c2819b5373eebb38a7bf0fd892bdb3c98b",
    "officialOpenApi": "40f183b6ca3ce4979e4908d4414b9d099dd22493bea45694b60e7a72a19a22bf",
    "provenance": "a718f67352809a8e275bf4e03aeeec25d8a36d20f9b7dd8b00d61e39d39b452f",
    "implementationVercel": "9203ad73e5fdb4ccf3a8639cbd6dd5feec126318ed5404d291bde1d9c0f8437a",
    "deployedSnapshot": "838632479b5ff201b7791193dc78b89112847c8e35aad81a47b7d75070f5baba"
  },
  "sourceSummary": {
    "tables": 13,
    "fields": 204,
    "enumSets": 7,
    "enumValues": 36,
    "sqlSections": 11,
    "routes": 19
  }
}
