T6A W03 Product Decision Gate¶
Historical T6A-B record (do not rewrite)¶
Issued: 2026-08-16
Status at issue: FAIL — ENTRY BLOCKED
Engineering label at issue: T6A ENTRY BLOCKED — NAMED PRODUCT DECISIONS REQUIRED
That label was correct when issued. No signed immutable W03 contract existed; implementing inferred code would have invented lifecycle, approval, privacy, recovery, or enrollment semantics. Phases 3–10 were correctly not entered. The tables below are the original T6A-B evidence and must remain as chronology.
Descendant approval (2026-08-16, later the same day)¶
| Field | Value |
|---|---|
| Approver | Requesting user via current conversation |
| Date | 2026-08-16 |
| Scope | SC-ADM / W03 only |
| Status | APPROVED_BY_REQUESTING_USER_CONVERSATION |
| Immutable contract | T6A-W03-approved-product-contract.md |
| RC1 impact | None |
| Production authority | None |
The 15 properties are now approved by that descendant record. Engineering may implement only that contract. Current code remains non-authoritative until replaced.
Current gate (after approval): PASS for product-decision entry. Implementation, tests, and runtime evidence are tracked in later T6A artefacts — not in this historical T6A-B section.
Proceed to Phase 3 only when every material item below is explicit, signed, immutable, and traceable. Current code behavior is not an approved lifecycle.
Gate result¶
| # | Property | Signed immutable approval? | Current evidence | Recommended decision (not implemented) |
|---|---|---|---|---|
| 1 | Canonical applicant/application identity and duplicate key | NO | scola.spmb.applicant email unique constraint is commented out (spmb_applicant.py _sql_constraints). Duplicate check is search-then-create. op.admission identity uses NISN/NIK fields without a signed duplicate key. |
Owner must name the duplicate key (email, NIK, NISN, KK, or composite + company) and uniqueness scope. |
| 2 | Approved lifecycle and exact state-transition table | NO | Applicant states draft/verified/active/completed/archived/blocked plus lifecycle_stage. Application (op.admission) uses OpenEduCat states (draft/submit/confirm/admission/reject/pending/done/cancel) plus announcement guard. Catalog says ordering is Inferred from code. RT-W03-STA-01 BLOCKED. |
Signed table: actor × from-state × to-state × side effects. Do not freeze the inferred table. |
| 3 | Actors allowed to verify, reject, approve, reopen, withdraw, enroll | NO | Staff APIs use model write / dashboard view capabilities. Portal applicant uses admissions.portal.view for almost all self actions. DEC-004 unsigned for Admissions. |
Named actor/action matrix per transition. |
| 4 | Final admission approver and maker-checker/self-approval | NO | DEC-004 open. /api/SPMB/admissions/<id>/transition accepts state/action with op.admission write. No SoD. |
Named final approver; self-approval allowed or denied. |
| 5 | Quota enforcement point and concurrent last-seat | NO | enroll_student takes pg_advisory_lock, counts state=done, then unlocks before student create (admission.py ~1008–1032). Public /api/SPMB/public/quota-monitor is public. |
Exact enforce point (decision vs enroll); last-seat fail-closed; public capacity disclosure yes/no. |
| 6 | Exact transition at which a Core student/onboarding record is created | NO | enroll_student creates op.student when student_id empty, then state=done, then applicant action_complete_lifecycle. Product contract: “moment of student creation require decision.” |
Name the exact from→to transition and the Core/W02 record created. |
| 7 | Student creation automatic vs explicitly invoked | NO | Code creates student inside enroll_student. Whether UI “Daftar Ulang” / staff transition / cron may invoke it is unsigned. |
Automatic on approved state or explicit enroll action only. |
| 8 | Idempotency key for application, decision, and W02 handoff | NO | No durable idempotency key on application/decision/handoff. Retry/concurrency unproven. RT-W03-IDM-01 BLOCKED. |
Keys and uniqueness constraints, not controller search-then-create. |
| 9 | Correction, reversal, withdrawal, failed-enrollment recovery | NO | Reject/cancel exist as OpenEduCat states. Recovery via archive cron / per-record ir.cron.sudo().create. No signed compensating W02 path. RT-W03-REC-01 BLOCKED. |
Who may reopen/withdraw; failed-handoff queue; never delete audit history. |
| 10 | Config ownership, effective dates, referenced-record deletion | NO | DEC-008 unsigned. Config CRUD uses view capabilities for save/delete (admission_config_api.py VIEW_CAPABILITIES). STAB-013 / BLK-A13 open. |
Owner, effective-date rule, referenced-delete fail-closed. |
| 11 | Applicant document type/size/signature/download/retention/deletion | NO | Upload/delete APIs exist (spmb_document_api.py, native /my/admission/<id>/upload). No signed DPO policy. |
Type/size/signature/scan, download actors, retention, deletion. |
| 12 | Public abuse/rate-limit and anti-enumeration | NO | Public allowlist includes register/status/pathways/quota. Status lookup by application number. Comment in registry: “Abuse/rate controls remain controller-owned.” No signed rate/enum policy. | Rate limit; identical shapes for missing/disabled/duplicate identities. |
| 13 | Notification recipients and whether failure blocks the authoritative transaction | NO | OTP email failure is logged and registration still succeeds (spmb_api.py register). Enrollment sends notification then completes lifecycle. |
Recipients; block vs continue on notify failure. |
| 14 | Fees, CBT, Foundation as separately entitled bridges | partial catalog, not certified | Catalog and manifests treat them as overlays. Fees bridge auto_install=True. Assessment hard-depends scola_cbt. Foundation depends analytics. Base still ships payment/formulir/complaint/test leftover surfaces. |
Confirm bridges separately entitled; base SC-ADM must run without them; auto_install policy. |
| 15 | Applicant credential lifecycle under DEC-014 | DEC-014 signed; Admissions mapping NOT signed | DEC-014 frozen 2026-08-10: server-generated temp or recovery token; no shared/default; no native Odoo customer lifecycle; SPMB OTP must stay separate from password-reset. Current code: dual /api/SPMB/account/register; caller-supplied password; email-link to existing parent skips verification (spmb_lifecycle_api.py). |
Apply DEC-014 with no Admissions exception. Name one canonical applicant credential path. Do not keep email-link-by-email. |
Signed decisions that do exist (do not invent around them)¶
| Decision | Status | Implication for T6A |
|---|---|---|
| DEC-001 bounded Core | Approved / RC1-A certified | SC-ADM is optional, not Core. Core smoke must not own W03 business tests. |
| DEC-003 atomic capabilities | Approved as Core control | Admissions must not collapse view/configure/decide/enroll. STAB-013 still open. |
| DEC-014 credential lifecycle | Frozen V1 | Admissions may not weaken it. Current public register contradicts it. Containment of the bypass is not a substitute for a signed applicant mapping (which of OTP / temp password / token is canonical). |
| DEC-004 maker-checker | Unsigned for Admissions | Blocks item 4. |
| DEC-008 master data / effective dates | Unsigned for Admissions | Blocks item 10. |
Questions requiring owner approval¶
Copy-ready for Product / Admissions SME / Security / DPO:
- What is the canonical applicant identity and duplicate key, including company scope?
- What is the exact state-transition table (applicant and application), including illegal transitions?
- Which acting roles may verify, reject, approve, reopen, withdraw, and enroll?
- Who is the final admission approver, and is self-approval denied?
- At which transition is quota enforced, and what happens on concurrent last seat? Is public quota visible?
- At which exact transition is a Core student / W02 onboarding record created?
- Is that creation automatic or an explicit enroll invocation?
- What are the idempotency keys for application, decision, and W02 handoff?
- What is the correction / reversal / withdrawal / failed-handoff recovery contract?
- Who owns config records, how do effective dates work, and how is referenced-record deletion rejected?
- What is the document type/size/signature/download/retention/deletion policy?
- What is the public abuse, rate-limit, and anti-enumeration contract?
- Who is notified, and does notification failure block the authoritative write?
- Confirm Fees / CBT / Foundation are separately entitled bridges, not base SC-ADM, including
auto_installof the fees bridge. - Confirm the single applicant credential lifecycle under DEC-014 (no caller-chosen initial password, no email-link hijack, no second register controller, no native
/websignup).
Until those answers are signed and immutable, engineering must not implement “the current code path” as the approved W03 lifecycle.
Containment note¶
Phase 2 permits containing an unsafe surface only when that containment follows an existing security/package contract.
DEC-014 already forbids email-link-by-email and caller-chosen initial passwords. Choosing which of the two /api/SPMB/account/register controllers remains would itself invent the canonical applicant credential path (item 15). Therefore this tranche did not change application code. The dual register and parent-email link are recorded as S1 findings, not silently “fixed” into a new lifecycle.