# EDU-010 — Tenant content publication and graph integrity - **Status:** in progress — catalog graph-integrity slice delivered; authoring/publication lifecycle remains - **Type:** implementation program - **Phase:** 3 - **Blockers:** EDU-004 ✓ (done), EDU-009 ✓ (done), provider-authority decision ✓ (resolved 2026-07-30, see decisions.md), PUBLIC graph-semantics decision ✓ (resolved 2026-07-30, see decisions.md) ## Tenant-admin outcome Authorized tenant administrators can author, classify, publish, archive, and retire education content without creating cross-tenant or invalid PUBLIC/tenant relationships, and student reads remain consistent with publication state. ## Scope - Question banks, questions, versions, classifications, catalogs, collections, blueprints, and bindings. - Draft/published/archived lifecycle. - System RBAC/DataPermission enforcement. - Tenant-consistent graph constraints or equivalent transactional enforcement. - Provider consistency between authoring source and student reads. - Safe projections preserved from EDU-001. ## Acceptance criteria - [ ] Admin permission and data-scope matrix is explicit. - [x] Cross-tenant graph relationships cannot be persisted in the current V4020 catalog graph. - [x] PUBLIC and tenant-owned reference rules are enforced across the current 19 reference edges, with exact trigger-mapping and focused behavior tests. - [ ] Unpublished/archived content is never student-visible. - [ ] Publication is transactional and auditable. - [x] Database graph-integrity changes use `flyway-postgresql` and forward migration V4070. ## Delivery progress — graph-integrity slice (2026-07-30) Delivered: - `V4070__enforce_catalog_reference_scope.sql` guards all 19 current foreign-key edges and makes ownership scope immutable on all 11 catalog tables: - PUBLIC children may reference only PUBLIC parents; - tenant-owned children may reference PUBLIC or same-tenant parents; - cross-tenant and PUBLIC-to-tenant references fail closed; - `tenant_id` and `scope` cannot change after insert, so parent mutations and concurrent ownership moves cannot invalidate existing children. - V4070 installs write guards before historical pre-validation, eliminating the migration-time validation/write window. The reference trigger function uses a fixed `search_path`, locks referenced rows during validation, and exposes no PUBLIC execute grant. - Real PostgreSQL/Flyway integration tests cover fresh and 4009-baselined migration histories, allowed and rejected references, parent ownership immutability, exact configuration of all 19 reference guards and 11 scope guards, function ACLs, and historical invalid-data failure. No non-Education module changed. Application rollback can disable native authoring when it is introduced; database recovery remains a higher forward migration and must preserve existing content. ## Remaining publication slice Do not add a local-only admin write path while `SCALAR_READ` remains the default authoritative provider: a successful PostgreSQL write would not be visible to students. The next bounded slice is a `JAVA_READ`-only tenant question `draft → publish → archive → student read` path that fails closed in unsupported provider modes. Before its Flyway/application implementation, resolve and document: 1. the exact `DRAFT/PUBLISHED/ARCHIVED/RETIRED` state machine and mapping of V4020 `HIDDEN/INACTIVE` rows; 2. immutable question-version and transactional publication-audit ownership; 3. whether ancestor entry/node/collection/archive state makes a question unavailable to fresh student reads; 4. the explicit tenant-admin versus platform-curator permission/data-scope matrix. ## Risk and rollback - **Risk:** High content-integrity and authorization risk. - **Rollback:** Disable authoring/publishing and roll back application; preserve data and correct forward.