T6A W03 Approved Product Contract¶
| Field | Value |
|---|---|
| Approver | Requesting user via current conversation |
| Date | 2026-08-16 |
| Scope | SC-ADM / W03 only |
| Status | APPROVED_BY_REQUESTING_USER_CONVERSATION |
| RC1 impact | None. RC1-A bounded Core remains certified and unchanged. |
| Production authority | None |
| Ancestor | T6A-W03-product-decision-gate.md T6A-B (2026-08-16) — historically correct when issued |
This record is the immutable W03 product contract for engineering. It does not rewrite T6A-B. It does not certify SC-ADM. It does not authorize production or GBN change.
Related signed platform decisions that this contract binds rather than weakens: DEC-001, DEC-003, DEC-014. For the Admissions domain only, this record also supplies the missing DEC-004 (maker-checker) and DEC-008 (master-data / effective dates) answers listed below.
1. Identity¶
- Login identity: normalized email, unique within the database.
- Applicant person:
(company_id, NIK). - NISN is a secondary unique identity when present.
- Application: unique
(company_id, admission_register_id, applicant_person_id). - KK and parent email are not applicant-person identity keys.
- Pre-NIK draft uses an opaque expiring registration UUID.
- One authenticated parent account may own multiple applicant persons only through an explicit verified relationship.
2. Application state machine¶
Canonical states: draft, submitted, correction_required, verified, accepted, rejected, withdrawn, enrolled.
Allowed transitions:
| Actor | From | To |
|---|---|---|
| applicant | draft |
submitted |
| applicant | correction_required |
submitted |
| applicant | any non-enrolled applicant-owned state | withdrawn |
| verifier | submitted |
correction_required |
| verifier | submitted |
verified |
| approver | verified |
accepted |
| approver | verified |
rejected |
| manager with reason | rejected |
submitted |
| manager with reason, before enrollment | accepted |
verified |
| enrollment officer | accepted |
enrolled |
enrolled is terminal inside W03. Post-enrollment correction belongs to W02.
Applicant lifecycle fields (scola.spmb.applicant.state / lifecycle_stage) may remain only as derived projections during migration. They must not remain an independent authoritative state machine. Authoritative application state is w03_state on the application record.
Illegal, skipped, backward, repeated, late, and unauthorized transitions fail closed with zero mutation.
3–4. Roles and separation of duty¶
Acting roles:
- Applicant: own draft/edit/upload/submit/correct/withdraw/status.
- Admissions Viewer: read only.
- Admissions Verifier: document verification and
submitted → correction_required|verified. - Admissions Approver:
verified → accepted|rejectedonly. - Enrollment Officer: explicit
accepted → enrolledonly. - Admissions Manager: configuration and controlled reopen (
rejected → submitted,accepted → verifiedwith reason). - School Admin receives no approve/enroll authority merely from being school admin.
Rules:
- last verifier cannot final-approve the same application;
- final approver cannot enroll the same application;
- self-approval denied;
- no customer break-glass;
- acting-role/session authority, never assigned-role union.
V1 engineering binding to existing Scola acting roles¶
The conversation named W03 acting roles, not new login personas. V1 binds them as follows (capabilities, not wildcards):
| W03 acting role | V1 Scola acting role |
|---|---|
| Applicant | pendaftar |
| Viewer | principal, vice_principal_public_relations (view only) |
| Verifier | admin_staff, head_admin |
| Approver | vice_principal_student_affairs |
| Enrollment Officer | admin_staff, head_admin |
| Manager | school_admin (config + reopen; no verify/approve/enroll) |
SoD is enforced per application even when one login may hold verifier and enrollment-officer capabilities.
5. Quota¶
- Reserve quota atomically at
verified → accepted. - Count
accepted + enrolled. - Hold row/advisory transaction lock until commit.
- Concurrent last-seat: exactly one success.
- Default public projection is
open/full. - Exact capacity requires explicit
publish_capacity=True.
6–7. Student handoff¶
- Student creation happens only on explicit
accepted → enrolled. - Invoked only by Enrollment Officer.
- No cron, notification, applicant CTA, acceptance action, Fees, CBT, or Foundation callback may create a student.
- Student creation, parent link, W03/W02 source link, and application transition must be one authoritative transaction.
- Post-enrollment correction belongs to W02.
- Failed enrollment returns failure and leaves the application
accepted.
8. Idempotency¶
- Require
Idempotency-Keyon W03 mutations. - Persist company, actor, operation, request hash, response/result, and timestamps.
- Application uniqueness:
(company_id, admission_register_id, applicant_person_id). - Enrollment business key:
spmb:<company_id>:<application_id>:enroll. - Student/W02 record must have a unique SPMB source reference.
- Search-then-create alone is insufficient. Use constraints or conditional updates.
Same key + same payload: return the original authoritative result. Same key + different payload: reject.
9. Recovery¶
- Submitted applications are immutable except through
correction_required. - Failed enrollment returns failure and leaves application
accepted. - One durable queue/outbox with unique jobs for retry and recovery.
- Remove per-record dynamic cron creation.
- Retry is idempotent.
- Never delete audit history.
- W03 must not delete an enrolled Core student.
10. Configuration¶
Atomic capabilities, at minimum:
admissions.<resource>.view|create|edit|deleteadmissions.application.verifyadmissions.application.approveadmissions.application.reopenadmissions.application.enroll
No mutation may use a .view capability. All records and foreign keys are company-scoped. Referenced config cannot be hard-deleted; archive or version it. Material configuration changes use effective dates and before/after audit.
11. Documents¶
- Allow PDF/JPEG/PNG; maximum 10 MiB per file; maximum 20 files per application.
- Validate extension, MIME, and magic signature.
- Private storage and unguessable storage key.
- Malware scan/quarantine before verification/download.
- Applicant replacement/deletion only in
draftorcorrection_required. - Download only for owner or assigned tenant-scoped Admissions staff.
- Retention: abandoned draft 90 days; rejected/withdrawn 12 months; enrolled decision/audit 5 years; avoid duplicate W03/W02 document retention.
- Batched retention jobs with audit tombstones, not one cron per record.
12. Public protection¶
- Replace all public prefix matching with exact path + method declarations.
- Registration/OTP: 5 requests/hour per IP and normalized identity.
- Verification: 5 attempts per token.
- Status: 30 requests/minute and requires authenticated session or application number plus opaque secret.
- Generic response shape for existing, missing, duplicate, disabled, and blocked identities.
- CAPTCHA/challenge may activate after threshold.
- Public quota returns open/full by default.
13. Notifications¶
- Notifications are non-authoritative.
- Transactional outbox, dedupe key, bounded retry, operator-visible failures.
- Notification failure does not roll back acceptance/enrollment.
- Activation remains incomplete until OTP/token verification.
- No sensitive PII or secrets in subject, response, audit, or logs.
14. Bridges¶
- Base SC-ADM must work without SC-FEES, SC-ASSESS, or SC-FOUND.
- Fees/payment/invoice → Fees bridge; test/CBT → Assessment bridge; Foundation → Foundation bridge.
- Base APIs return
feature_disabledor are absent when the bridge entitlement is missing. - Internal bridge auto-install is allowed only when both parent modules exist.
- Runtime execution additionally requires both parent entitlements.
- Each bridge has a separate suite and is not certified by T6A.
- Complaints remain excluded and fail-closed.
15. Applicant credentials (DEC-014 mapping)¶
Canonical flow:
registration request → email OTP verification → purpose-bound activation token → applicant chooses first password → account activation
Rules:
- registration request does not accept a password;
- no active user before successful verification;
- activation token follows DEC-014: hash-at-rest, high entropy, TTL, single-use, purpose/user bound, supersession, no raw logging;
- applicant may choose their first password only after activation-token proof;
- remove dual
/api/SPMB/account/register; - remove public link-by-email;
- existing parent must authenticate and explicitly attach an applicant relationship;
- SPMB OTP remains separate from password-reset tokens;
- native Odoo signup/reset remains denied;
/my/admission*must not remain a second mutation lifecycle — safe GET may redirect to canonical/spmb, CUD must be absent/410;- forgot/change/admin reset remain canonical DEC-014 flows.
Non-goals (unchanged)¶
Fees/CBT/Foundation certification, complaints, messaging, W01/W02 redesign, Core navigation redesign, production promotion, GBN mutation.