Skip to main content

Content Library Approach (Working Agreements)

This document records the current product and backend agreements for content inventory, taxonomy, and content packs.

Canonical authored-content contract: docs/architecture/CONTENT-DOCUMENT-V1-CONTRACT.md freezes the localized content schema, capability model, runtime API semantics, and publish/submission lifecycle rules. This document MUST NOT redefine that contract.

1) Terminology Boundaries (Agreed)

  • Question Library / Evaluation Library = inventory and authoring management surfaces.
  • Content Packs = publish/install packaging workflow.
  • We do not use “library” for pack endpoints anymore to avoid conceptual overlap.

2) Taxonomy Model (Agreed)

We are implementing a shared taxonomy engine with domain scopes, not a single undifferentiated tag pool.

2.1 Domain Scopes

Supported taxonomy domains:

  • content
  • skills
  • subjects
  • identity

2.2 Why Domain Scopes

This avoids cross-domain confusion and keeps authoring pickers/contextual filters relevant:

  • Question/Evaluation authoring uses content-domain terms.
  • Skills flows use skills-domain terms.
  • Subject/identity surfaces can use their own domains later.

2.3 Stable IDs

Taxonomy terms are stored and referenced by stable IDs. Labels/slugs are human-facing metadata, not identity.

3) User Experience Contract (Agreed)

  • Users select predefined terms rather than free-typing arbitrary taxonomy values.
  • Users with taxonomy-manage permission can create/edit/retire terms.
  • Inline “add/edit” from authoring context is allowed and expected (read-only peek drawer for browse/review; full page for edit/deeper workflow).
  • Retired terms are not selectable for new assignment.

Taxonomy terms are first-class deep-linkable entities:

  • Drawer pattern: ?drawer=<type>:<id> (per UX docs).
  • Term entities must be navigable as canonical pages in frontend routes.
  • Backend must provide permission-aware read behavior for drawer peek and write behavior on canonical full-page workflows.

See UX contracts:

  • docs/ux/COMPONENT-INTERACTION-MAP.md
  • docs/ux/UX-FRONTEND-CONVENTIONS.md
  • docs/ux/UX-AUTHORING.md

5) Backend Contract (Current)

5.1 Schema Foundation

Implemented foundation tables:

  • taxonomy_facets
  • taxonomy_terms
  • taxonomy_term_relations
  • question_terms
  • evaluation_terms

With constraints:

  • Domain-scoped facets (content/skills/subjects/identity).
  • RLS enforced with tenant + org scope.
  • Content inventory assignment is content-domain only.
  • Relation-first hierarchy with strict-tree default and cycle rejection.

5.2 Capabilities

Implemented capabilities:

  • taxonomy.read
  • taxonomy.manage

5.3 API Foundation

Implemented taxonomy API surface:

  • GET /api/v1/taxonomy/facets
  • GET /api/v1/taxonomy/facets/\{id\}
  • POST /api/v1/taxonomy/facets
  • PATCH /api/v1/taxonomy/facets/\{id\}
  • GET /api/v1/taxonomy/terms
  • GET /api/v1/taxonomy/terms/\{id\}
  • GET /api/v1/taxonomy/terms/\{id\}/children
  • GET /api/v1/taxonomy/terms/\{id\}/ancestors
  • GET /api/v1/taxonomy/terms/\{id\}/where-used
  • POST /api/v1/taxonomy/terms
  • PATCH /api/v1/taxonomy/terms/\{id\}
  • POST /api/v1/taxonomy/terms/\{id\}/retire
  • POST /api/v1/taxonomy/terms/\{id\}/restore
  • GET /api/v1/taxonomy/questions/\{id\}/terms
  • PUT /api/v1/taxonomy/questions/\{id\}/terms
  • GET /api/v1/taxonomy/evaluations/\{id\}/terms
  • PUT /api/v1/taxonomy/evaluations/\{id\}/terms

OpenAPI coverage for the taxonomy surface is now published under openapi/.

5.4 Taxonomy-only authoring rule (current canonical boundary)

Canonical rule for authoring/content-definition work:

  • taxonomy is the only source of truth for tag-like requirements
  • target-state write contracts use taxonomy term IDs or taxonomy replacement endpoints
  • evaluation bucket OpenAPI/generated SDK shape is the current canonical example (taxonomyTermIds / excludeTaxonomyTermIds)
  • passage question-source bucket criteria must converge to the same taxonomy-native rule shape rather than preserving raw tagFilter / excludeTags as the architecture

Explicit compatibility shims (non-canonical):

  • raw tags inputs still accepted on some question/passage authoring boundaries
  • raw tagFilter / excludeTags fields still present in some local/frontend or passage-question-source seams during migration
  • these shims exist only to preserve staged migration safety and must not be copied into new APIs

When OpenAPI/generated contracts and local convenience types disagree, treat OpenAPI/generated taxonomy-native shapes as canonical and the local raw-tag seam as migration debt.

5.4 Cross-Inventory Parity Rule

For inventory hardening capabilities, parity is the default:

  • If a capability applies to both Questions and Evaluations, backend contracts should be implemented for both in the same workstream.
  • Any intentional asymmetry must be documented explicitly with rationale and acceptance criteria.

Parity-first capability targets:

  • Folders/collections and saved-filter behavior.
  • Soft-delete + restore lifecycle behavior.
  • Bulk metadata operations.
  • Dependency/where-used graph visibility.

6) Current Implementation Status (Milestones)

Completed

  1. Terminology alignment:
  • Library vs Packs naming split.
  • Pack surfaces renamed to content packs.
  1. Inventory hardening phase 1:
  • Richer inventory filters/sort in question/evaluation list APIs.

2b. Taxonomy foundation:

  • Domain-scoped taxonomy schema + capabilities + API + content assignment endpoints.

2c. Inventory views parity slice:

  • Shared backend inventory-views engine is live for saved filters / virtual folders.
  • Paired API contracts are live for Questions and Evaluations:
    • GET/POST /api/v1/questions/views
    • GET/PATCH/DELETE /api/v1/questions/views/{viewId}
    • GET/POST /api/v1/evaluations/views
    • GET/PATCH/DELETE /api/v1/evaluations/views/{viewId}
  • Visibility model implemented: personal (owner-scoped) and shared (org-scoped), with RLS enforcement.

2d. Lifecycle parity slice (archive/restore):

  • Explicit lifecycle endpoints are now paired for Questions and Evaluations:
    • POST /api/v1/questions/\{id\}/archive
    • POST /api/v1/questions/\{id\}/restore
    • POST /api/v1/evaluations/\{id\}/archive
    • POST /api/v1/evaluations/\{id\}/restore
  • This closes the restore contract gap while preserving existing delete semantics.

2e. Soft-delete harmonization increment:

  • Question delete now follows the same in-use safety contract as evaluations:
    • DELETE /api/v1/questions/\{id\} hard-deletes only when unreferenced.
    • If referenced by authored structures or execution (sectionItems, passageLinks, submissions, activeSessions), it returns 409 question_in_use and archives instead.
  • This removes inconsistent FK-error behavior and makes lifecycle semantics predictable across inventory entities.

2f. Collection presets + taxonomy filter conventions increment:

  • Saved inventory views now enforce a canonical filter schema (question/evaluation parity) instead of accepting arbitrary JSON.
  • Filter conventions include normalized taxonomy semantics:
    • termsAny / termsAll (UUID arrays)
    • includeDescendants (boolean)
  • Preset convention keys are supported in saved filters:
    • status.draft
    • status.published
    • status.archived
    • taxonomy.focus (requires taxonomy terms; defaults includeDescendants=true)
  • Legacy snake_case filter keys are accepted on write and normalized to canonical camelCase in persisted filters.

2g. Bulk metadata actions parity slice:

  • Paired bulk endpoints are live for Questions and Evaluations:
    • POST /api/v1/questions/bulk
    • POST /api/v1/evaluations/bulk
  • Supported actions:
    • archive
    • restore
    • taxonomyReplace
  • Bulk contract includes:
    • dryRun preview mode
    • per-item outcomes (success/failure/no-change)
    • aggregate summary counts (total, succeeded, failed, changed)

2h. Dependency/where-used graph parity slice:

  • Usage/where-used graph endpoints are now paired for Questions and Evaluations:
    • GET /api/v1/questions/\{id\}/usage
    • GET /api/v1/evaluations/\{id\}/usage
  • Usage contract now exposes:
    • top-level in-use counts (existing delete-guard semantics preserved)
    • dependency graph detail blocks to power authoring where-used UX:
      • Questions: evaluation section-item references + passage links
      • Evaluations: assignments + programme requirement references + content pack item references

2i. Packs lineage read slice:

  • Content pack revision lineage read endpoint is now live:
    • GET /api/v1/content-packs/\{id\}/revisions/\{revisionId\}/lineage
  • Lineage response exposes:
    • revision identity (id, revisionNumber, manifestHash)
    • revision item chain (pos, sourceEvaluationVersionId, metadata)
    • install chain (installedEvaluationId, installedEvaluationVersionId, install timestamps)
    • submission linkage metrics per install (submissionCount, snapshotLinkedSubmissionCount)
    • aggregate lineage summary counts (installCount, submissionCount, snapshotLinkedSubmissionCount)
  • Coverage added in integration + smoke:
    • backend/internal/http/handler/integration/content_packs_lineage_integration_test.go
    • backend/tests/content_packs.sh

Next

  1. Packs phase:
  • Workflow expansion on top of lineage read surfaces.
  1. Derived readiness/status lens phase (cross-inventory + KOE projection):
  • Implement dual-time proof readiness (defensibleAtExecution, readyNow) with canonical reason codes.
  • Add explainable koeStatus/readiness blocks (reasons, asOf, policy/snapshot refs) and capability-based redaction for glass-box surfaces.
  • Add exceptions queue projection with triage state (open|acknowledged|resolved, owner, first/last seen, suppression).
  • See contract: docs/architecture/STATUS-AND-READINESS-CONTRACT.md.

7) Explicit Non-Goals for This Phase

  • No frontend implementation in this phase.
  • No backward-compat requirement for legacy free-tag behavior.
  • DB reset/re-init is acceptable while this model is being established.

8) Known Follow-Ups

  • See also: docs/product/taxonomy-engine-evolution-proposal.md for hierarchy + cross-domain mapping strategy (content -> skills).

  • Inventory list APIs now support taxonomy-term semantics (terms_any / terms_all, optional include_descendants) for question/evaluation discovery.

  • Submission versionSnapshot now freezes taxonomy refs for execution-time reconstruction:

    • evaluationTaxonomyTermIds (evaluation-level assignments)
    • questions.{questionVersionId}.taxonomyTermIds (question-level assignments)
  • Cross-domain mapping foundation is now live under taxonomy:

    • Versioned mapping sets/rules (content selectors -> skills targets)
    • Publish gate per mapping set version
    • Resolver endpoint returns matched skills with rule/term provenance
  • Skills explainability endpoints stay gated until deterministic per-submission provenance facts are persisted (mapping_set_version_id, mapping_rule_id, source term IDs).

  • Add folders/collections behavior on top of taxonomy (not as a replacement for taxonomy).