Lewati ke isi

DEC-W16 — Bounded Product Freezes (Canonical News V1)

Mode: Server /home/scola/odoo
Approval: APPROVED_BY_REQUESTING_USER_CONVERSATION (2026-08-29)
Scope: Minimum product decisions to unblock §5.3 BLOCKED rows without new feature scope
Format: Same bounded-decision pattern as T4-DEC-W01-W02-bounded-freezes.md


RT-W16-NEG-01 — Capability / approval matrix

Blockers: DEC-003 (RBAC scope), DEC-004 (maker-checker / approval roles)

Aspect V1 bounded decision
Create / edit draft news.author capability — assigned author or school_admin
Submit for review Author only on own draft
Approve / reject news.approve — distinct from author (no self-approval on publish path)
Publish news.publish — may coincide with approver role in small schools; audit records actor
Archive / unpublish news.publish or school_admin; reversal reason required (see STA)
Read-only leadership / reader Denied create/approve/publish/archive; direct API denied; record/outbox unchanged
Harness proof W16-NEG-* denied cases; capability bypass RPC denied

RT-W16-STA-01 — Publication state machine / reversal

Blocker: DEC-007 (state transition / reversal policy)

Aspect V1 bounded decision
States draftsubmittedapprovedpublishedarchived; rejected returns to editable draft
Invalid transition Denied atomically; history immutable (append-only audit)
Reversal after publish Unpublish → archived only (no silent delete); correction = new revision or edit-before-republish per Product
Late transition Publish window enforced server-side; late publish denied with audit reason
Self-approval Denied on publish path when author = approver (configurable override = out of V1)
Harness proof W16-STA-* transition matrix; repeated/invalid transition denied

RT-W16-REC-01 — Notification / delivery recovery

Blocker: DEC-010 (notification semantics, retry, dedupe)

Aspect V1 bounded decision
Authoritative record Canonical scola.news row remains source of truth after publish
Delivery failure Outbox/notification failure visible to publisher (status field or job log)
Retry Manual or cron retry deduped — one notification effect per reader per publication event
Unpublish / archive Stops future delivery; does not retract already-delivered push (bounded V1 — in-app read state updated)
Harness proof Fault injection after publish; canonical record unchanged; retry creates no duplicate

RT-W16-XRP-01 — Reader propagation

Blocker: DEC-010 (notification behavior to target/non-target readers)

Aspect V1 bounded decision
Target readers Receive publication once per audience projection; correction/archive updates in-app feed
Non-target Never receive notification or sensitive audience metadata
Guessed news ID Denied read outside audience scope
Dedupe Same reader + same publication event = one in-app entry + one push (if enabled)
Harness proof Target/non-target student/parent/teacher fixtures; propagation once; non-target zero leak

RT-W16-XMD-01 — Legacy migration / cross-module integration

Blockers: DEC-011 (legacy data policy), STAB-033 (duplicate canonical/legacy publication)

Aspect V1 bounded decision
Writable source scola.news only — legacy announcement write API/menu absent or 403
Legacy read Historical announcement rows read-only in portal; deep links redirect or show migrated banner
Migration One-time migration maps legacy ID → canonical ID; no duplicate notification on migration run
Calendar / notification shell Declared link preserves canonical ID; no second write path
Harness proof Legacy direct API denied; migrated history readable; canonical publish creates single effect

Blockers: STAB-033, DEC-011, IA-010

Aspect V1 bounded decision
Create / publish Exactly one canonical menu/route/CTA (communication app)
Legacy write Menu leaf and route absent or fail-closed redirect
Reader UI Valid empty/loading/error states; responsive mobile/desktop
Deep links Legacy URLs redirect to canonical read view or safe unsupported state
Harness proof check:core-nav-ux communication routes + spot crawl; legacy write menu absent

RT-W16-VAL-01 — Invalid payload / window

Aspect V1 bounded decision
Invalid window / missing target Rejected atomically; zero partial row
Harness proof W16-VAL-INVALID-PAYLOAD

RT-W16-VAL-02 — Optional summary (no body snippet)

Patch: BE 7ffb0adscola.news.summary optional on create/write.

Aspect V1 bounded decision
Omitted / empty summary Stays empty — no auto-snippet from body_html
Explicit summary Preserved verbatim
Portal/list payload Empty summary omitted or false; body unchanged
Harness proof W16-VAL-OPTIONAL-SUMMARY on Golden :8090

Supporting rows (currently NOT RUN — engineering defaults)

These rows unblock at W16-2/W16-3 without additional Product gate beyond charter approval:

RT-W16-POS-01 — Positive publish path

Aspect V1 bounded decision
Flow Author creates → submit → approver approves → publisher publishes → intended reader sees once
Audit Actors and state transitions recorded
Harness proof W16-POS-* end-to-end on GT-TXN-W16-NEWS fixtures

RT-W16-IDM-01 — Publish idempotency

Aspect V1 bounded decision
Concurrent / repeated publish One publication/circular/notification effect; stable audit
Cron / scheduler Idempotent outbox dispatch
Harness proof GT-TXN-RETRY-IDEMPOTENT duplicate publish/cron cases

Explicit NOT RUN (non-claim)

Item Status Reason
UAT PROMOTE_UAT Out of W16 track Gelombang terpisah — charter §1
Production promotion Not authorized Immutable CERTIFIED manifest required
Full DEC-010 enterprise notification bus Out of V1 bounded In-app + bounded outbox only unless Product expands

Certification denominator summary

Workstream Harness Target Matrix rows
W16 w16_runtime_certify.py (TBD) 12/12 PASS §5.3 all 12 rows
E1.2 regression e12_security_matrix.py 29/29 PASS No SC-CORE regression

Sign-off record

Date Approver Decision Scope
2026-08-29 Requesting user (Post-SCORE Build/Lanjutkan) APPROVED — all bounded options in this document as written W16-1 unblocks W16-2 engineering; does not authorize production promotion or UAT wave

Product bounded options signed 2026-08-29 via conversational approval in Post-SCORE program execution (APPROVED_BY_REQUESTING_USER_CONVERSATION).