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:
contentskillssubjectsidentity
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.
4) Deep-Link + Drawer Contract (Agreed)
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.mddocs/ux/UX-FRONTEND-CONVENTIONS.mddocs/ux/UX-AUTHORING.md
5) Backend Contract (Current)
5.1 Schema Foundation
Implemented foundation tables:
taxonomy_facetstaxonomy_termstaxonomy_term_relationsquestion_termsevaluation_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.readtaxonomy.manage
5.3 API Foundation
Implemented taxonomy API surface:
GET /api/v1/taxonomy/facetsGET /api/v1/taxonomy/facets/\{id\}POST /api/v1/taxonomy/facetsPATCH /api/v1/taxonomy/facets/\{id\}GET /api/v1/taxonomy/termsGET /api/v1/taxonomy/terms/\{id\}GET /api/v1/taxonomy/terms/\{id\}/childrenGET /api/v1/taxonomy/terms/\{id\}/ancestorsGET /api/v1/taxonomy/terms/\{id\}/where-usedPOST /api/v1/taxonomy/termsPATCH /api/v1/taxonomy/terms/\{id\}POST /api/v1/taxonomy/terms/\{id\}/retirePOST /api/v1/taxonomy/terms/\{id\}/restoreGET /api/v1/taxonomy/questions/\{id\}/termsPUT /api/v1/taxonomy/questions/\{id\}/termsGET /api/v1/taxonomy/evaluations/\{id\}/termsPUT /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/excludeTagsas the architecture
Explicit compatibility shims (non-canonical):
- raw
tagsinputs still accepted on some question/passage authoring boundaries - raw
tagFilter/excludeTagsfields 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
- Terminology alignment:
- Library vs Packs naming split.
- Pack surfaces renamed to content packs.
- 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/viewsGET/PATCH/DELETE /api/v1/questions/views/{viewId}GET/POST /api/v1/evaluations/viewsGET/PATCH/DELETE /api/v1/evaluations/views/{viewId}
- Visibility model implemented:
personal(owner-scoped) andshared(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\}/archivePOST /api/v1/questions/\{id\}/restorePOST /api/v1/evaluations/\{id\}/archivePOST /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 returns409 question_in_useand 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.draftstatus.publishedstatus.archivedtaxonomy.focus(requires taxonomy terms; defaultsincludeDescendants=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/bulkPOST /api/v1/evaluations/bulk
- Supported actions:
archiverestoretaxonomyReplace
- Bulk contract includes:
dryRunpreview 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\}/usageGET /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)
- revision identity (
- Coverage added in integration + smoke:
backend/internal/http/handler/integration/content_packs_lineage_integration_test.gobackend/tests/content_packs.sh
Next
- Packs phase:
- Workflow expansion on top of lineage read surfaces.
- 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.mdfor hierarchy + cross-domain mapping strategy (content->skills). -
Inventory list APIs now support taxonomy-term semantics (
terms_any/terms_all, optionalinclude_descendants) for question/evaluation discovery. -
Submission
versionSnapshotnow 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 (
contentselectors ->skillstargets) - Publish gate per mapping set version
- Resolver endpoint returns matched skills with rule/term provenance
- Versioned mapping sets/rules (
-
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).