Platform3 · Canonical architecture

Case 1edtech Architecture

This page is rendered directly from the committed architecture decisions. Each stable section anchor is part of the public traceability contract.

Source-bound decision record

21 committed decisions · case/1edtech

#case_alignment_resource

case alignment resource

Unspecified

Decision

SHIP Alignment as a tenant-scoped TimeBack resource with fields alignmentId, externalSystem, externalId, cfItemUri, cfItemIdentifier, label, metadata, createdAt, updatedAt, and import_batch_id. Alignments are never emitted inside CFPackage export and never counted as CFAssociations.

Alternatives and tradeoffs

  • Store external IDs in CFItem extensions
  • Store external IDs as CFAssociation ext:* edges
  • Separate Alignment resource

Rationale and consequences

A separate Alignment resource lets integrators resolve their own external IDs without leaking tenant-specific identifiers into spec-pure CASE exports.

#case_association_validation

case association validation

Unspecified

Decision

SHIP validation that accepts the CASE 1.1 associationType values and ext:* extension values matching the OpenAPI pattern. originNodeURI and destinationNodeURI targetType default to CASE and accept ext:* for non-CASE targets. Cross-framework exactMatchOf, replacedBy, isRelatedTo, isPeerOf, precedes, hasSkillLevel, exemplar, and isTranslationOf remain canonical CFAssociation edges.

Alternatives and tradeoffs

  • Free-form associationType
  • Closed enum only
  • Known enum plus ext:* validation

Rationale and consequences

Known CASE values plus ext:* validation preserves 1EdTech extensibility while turning typos into deterministic validation errors.

#case_auth_shape

case auth shape

Unspecified

Decision

SHIP platform JWTs with tenantId, role or roles, and CASE scopes: case:read, case:write, case:import, and case:admin. DEFER school, class, and person-scoped claims unless a future CASE workflow introduces tenant-internal visibility boundaries.

Alternatives and tradeoffs

  • Role-only JWT
  • Role plus CASE scopes
  • School/class/domain claims

Rationale and consequences

CASE graph data needs tenant and scope separation but does not need school, class, or person relationship claims.

#case_case11_extensions_markdown

case case11 extensions markdown

Unspecified

Decision

SHIP lossless storage and API pass-through for CASE 1.1 fields and extensions. Statement, notes, and description fields remain source text in API responses; customer docs tell browser apps to render Markdown/LaTeX through a sanitizer. The implementation must not emit pre-rendered trusted HTML for these fields.

Alternatives and tradeoffs

  • Strip extensions and Markdown
  • Render source Markdown as trusted HTML in API responses
  • Preserve source text and extensions; sanitize at render boundaries

Rationale and consequences

Lossless source-text storage preserves CASE 1.1 round trips while sanitizing at the browser boundary keeps rendered standards safe.

#case_concurrency_model

case concurrency model

Unspecified

Decision

SHIP ETag on mutable resource reads and require If-Match for PUT, PATCH, and DELETE of CFDocument, CFItem, CFAssociation, and Alignment. Missing validators return 428; stale validators return 412; package import conflicts return a typed 409 with conflicting identifiers.

Alternatives and tradeoffs

  • No concurrency model
  • Bulk-only package version
  • Per-resource ETag and If-Match

Rationale and consequences

ETag and If-Match prevent silent overwrites of standards tree edits with standard HTTP semantics.

#case_conformance_evidence

case conformance evidence

Unspecified

Decision

SHIP local evidence: OpenAPI path tests, JSON schema validation, package import/export round trips, auth/error/idempotency/concurrency tests, live demo and reviewer smoke tests, surface QC, and an integration app. DEFER official 1EdTech certification claim until a certificate or TrustEd Apps Directory listing exists.

Alternatives and tradeoffs

  • No conformance evidence
  • Claim official certification from local tests
  • Local evidence with explicit non-certification

Rationale and consequences

Local executable evidence gives integrators reproducible proof without falsely claiming external 1EdTech certification.

#case_data_model_primitives

case data model primitives

Unspecified

Decision

Persist CFDocument, CFItem, and CFAssociation as first-class CASE resources, persist imported CFDefinitions and CFRubrics as read-through support tables, and add Alignment as a separate platform resource for external-system ID to CFItem links.

Alternatives and tradeoffs

  • CFDocument and CFItem only
  • Store raw CFPackage JSON only
  • Put external IDs into CFAssociation extensions

Rationale and consequences

First-class CASE tables give the visual browser relational integrity and graph queries while Alignment keeps external IDs outside canonical CASE data.

#case_error_envelope

case error envelope

Unspecified

Decision

SHIP typed RFC 7807 Problem Details with stable type URIs, code values prefixed case:, requestId, traceId, optional fieldErrors, and optional rowErrors for package import. Initial codes include case:validation_failed, case:not_found, case:forbidden, case:unsupported_query_parameter, case:association_cycle, case:missing_parent, case:idempotency_conflict, case:precondition_required, case:precondition_failed, and case:package_import_failed.

Alternatives and tradeoffs

  • No envelope
  • Plain RFC 7807 only
  • Typed RFC 7807 with case:* codes

Rationale and consequences

Typed Problems with case:* codes let clients branch on stable machine fields and attach request evidence to support tickets.

#case_eventing_model

case eventing model

Unspecified

Decision

SHIP polling through list filters and updatedSince. DEFER webhooks and push streams until three integrator decisions in 90 days show that polling prevents a visual browser or standards-sync app from completing its documented job.

Alternatives and tradeoffs

  • No change observation
  • Poll with updated filters
  • Webhooks now

Rationale and consequences

Pollable updated filters cover the first browser refresh job without adding webhook subscription and replay infrastructure.

#case_idempotency_model

case idempotency model

Unspecified

Decision

SHIP Idempotency-Key on retryable writes, persisted in platform.idempotency_key with method, route template, tenant, request hash, and response replay metadata. CFPackage imports also store a canonical package hash and import_batch_id for audit and duplicate-import detection.

Alternatives and tradeoffs

  • No idempotency
  • Natural unique constraints only
  • Idempotency-Key plus package hash

Rationale and consequences

The platform retry ledger plus package hashes makes imports and edits safe across network retries and duplicate submissions.

#case_item_tree_invariant

case item tree invariant

Unspecified

Decision

SHIP exactly one active isChildOf placement per CFItem within a tenant: the destination is either the owning CFDocument root or another CFItem in the same CFDocument. Additional cross-framework relationships use other CFAssociation types.

Alternatives and tradeoffs

  • Allow no parent
  • Allow multiple tree parents
  • Exactly one isChildOf parent plus other association edges

Rationale and consequences

Exactly one structural isChildOf parent gives visual browsers a deterministic tree while other associations remain graph edges.

#case_list_endpoints

case list endpoints

Unspecified

Decision

SHIP list endpoints for CFDocuments, CFItems, CFAssociations, and Alignments. Definition support resources imported from CFDefinitions and CFRubrics remain official detail reads unless a future app needs definition browsing.

Alternatives and tradeoffs

  • Write-and-remember
  • Only official CFDocuments list
  • One list per browser-owned collection

Rationale and consequences

Cold browser apps need discoverable CFItem, CFAssociation, and Alignment collections instead of remembered identifiers only.

#case_model_scope_definitions_rubrics

case model scope definitions rubrics

Unspecified

Decision

SHIP package import persistence and official detail reads for CFConcept, CFSubject, CFLicense, CFItemType, CFAssociationGrouping, and CFRubric. DEFER first-class list/create/update/delete authoring for those support resources until a standards-authoring or rubric-browser integration decision cites it as job-blocking.

Alternatives and tradeoffs

  • Ignore definitions and rubrics
  • Full CRUD for every definition and rubric class
  • Persist and read through definitions/rubrics from packages

Rationale and consequences

Read-through support preserves official CASE lookup routes and package round trips without expanding first-release authoring scope.

#case_package_import_export

case package import export

Unspecified

Decision

SHIP POST /CFPackages for transactional import and GET /CFPackages/{sourcedId} for spec-pure export. Import persists CFDocument, CFItems, CFAssociations, CFDefinitions, and CFRubrics with _platform.tenant_id and _platform.import_batch_id sidecar metadata. Export omits platform metadata and Alignment rows.

Alternatives and tradeoffs

  • No package import/export
  • Export platform metadata inside CFPackage extensions
  • Spec-pure package with sidecar audit provenance

Rationale and consequences

CFPackage remains the clean interchange envelope while platform audit metadata stays in sidecar persistence fields.

#case_privacy_retention

case privacy retention

Unspecified

Decision

SHIP tenant-scoped rows with audit provenance, redaction from Problems/logs/evidence, soft-retire semantics for CFDocument/CFItem/CFAssociation deletes, hard delete only for Alignment when no audit retention rule requires a tombstone, and spec-pure CFPackage exports that omit platform metadata. DEFER age-based retention and public erasure APIs until platform policy names a required retention period.

Alternatives and tradeoffs

  • No privacy or retention behavior
  • Hard DELETE everything
  • Tenant isolation, redaction, and soft-retire

Rationale and consequences

Tenant isolation, redaction, and soft retirement protect institutional standards data without inventing learner-data retention APIs.

#case_query_model

case query model

Unspecified

Decision

SHIP official CASE list parameters where the spec defines them, plus named filters on TimeBack list and sub-collection routes: CFDocumentURI, parentIdentifier, originIdentifier, destinationIdentifier, associationType, subject, educationLevel, updatedSince, limit, offset, sort, orderBy, and fields. DEFER replacing official offset paging with cursor paging until three integrator decisions in 90 days cite offset paging as job-blocking at framework scale.

Alternatives and tradeoffs

  • No query model
  • Arbitrary expression filters
  • Official CASE params plus named filters

Rationale and consequences

Official CASE query parameters plus named browser filters give predictable discovery without an undocumented query language.

#case_read_shape

case read shape

Unspecified

Decision

SHIP list + detail + package + narrow sub-collection reads: document lists/details, item lists/details, association lists/details, CFPackage export, item association sets, item children, item parents, item related edges, and Alignment lookup.

Alternatives and tradeoffs

  • List-only
  • List plus detail only
  • List plus detail plus narrow sub-collections

Rationale and consequences

List, detail, package, and narrow sub-collection reads let a frontend navigate large standards graphs without owning a local database.

#case_served_audiences

case served audiences

Unspecified

Decision

The served audience is a developer building a visual standards-browser app against this CASE 1EdTech surface alone. The browser must navigate CFDocument -> CFItem hierarchies, follow CFAssociation cross-framework relationships, and resolve external IDs through Alignment.

Alternatives and tradeoffs

  • Standards publisher first
  • One-time ETL pipeline
  • Spec validator

Rationale and consequences

The visual standards-browser persona makes persona-blocking concrete and keeps publisher-only or validator-only features from expanding the first surface.

#case_source_contract

case source contract

Unspecified

Decision

Official CASE Provider reads are preserved under /ims/case/v1p1. TimeBack adds write, list, package import, and Alignment routes under the same base path only where the visual-browser job needs persistence or discovery, and every such route is labeled as a TimeBack extension in downstream docs.

Alternatives and tradeoffs

  • Implement only official GET operations
  • Replace CASE paths with TimeBack CRUD paths
  • Use official reads plus labeled TimeBack write and browse extensions

Rationale and consequences

Preserving official CASE reads while labeling TimeBack extensions keeps the surface recognizable and prevents accidental certification overclaims.

#case_tenant_routing

case tenant routing

Unspecified

Decision

SHIP tenant routing by signed JWT tenantId on flat /ims/case/v1p1 paths. /dev/mint?tenantId=demo may mint demo tokens only for the demo tenant. No X-Timeback-Tenant header or /tenants/{tenantId} prefix is part of the CASE surface.

Alternatives and tradeoffs

  • Tenant in URL
  • Tenant in header
  • Tenant in JWT only

Rationale and consequences

JWT tenant routing preserves the official /ims/case/v1p1 path shape while enforcing platform tenant isolation.

#case_write_granularity

case write granularity

Unspecified

Decision

SHIP both CFPackage bulk import/export and per-resource POST, PUT, PATCH, and DELETE for CFDocument, CFItem, CFAssociation, and Alignment.

Alternatives and tradeoffs

  • Bulk-only CFPackage import
  • Per-resource writes only
  • Both bulk import and per-resource writes

Rationale and consequences

Bulk package import and per-resource CRUD solve different jobs: initial framework ingestion and ongoing browser/editor maintenance.