PRD-SOQ-TMS-1.1 · Formal Requirement Oracle
| Field | Normative value |
|---|
| Doc-ID | PRD-SOQ-TMS-1.1 |
| Baseline | ATT-A-HK-2026-08 |
| Conformance | RFC 2119 |
| Identifier namespaces | FR-{domain}-{nn} · NFR-{nn} · DQ-{nn} |
| Status | DRAFT_ARCH_ALIGNED |
Reader profile: Staff engineers, QA automation, integration owners. Non-engineering interpretation discouraged — refer §23–§25 for commercial semantics.
0 · Identifier lattice
| Prefix | Semantics | Test binding |
|---|
FR-* | Functional SHALL/MUST | tests/acceptance/fr/{id}.test.ts |
NFR-* | Quality attribute | k6 · axe · chaos · DR drill |
DQ-* | Discovery delta (unresolved) | Block release if M priority |
SYS-* | Attachment-A invariant | Trace matrix §00 |
MoSCoW: M = release blocker · S = contract milestone · C = backlog · W = won't v1
1 · Problem invariant (compressed)
Legacy SOQ stack = {legacy_tms, privyr, xero} without unified student_id keyspace. Symptom classes:
| Symptom-ID | Root cause | Target subsystem |
|---|
S-RECON-01 | Dual SoR enrolment vs CRM | Enrolment + Crm handoff event |
S-FUND-02 | Retroactive SWDA evidence assembly | SwdaHub outbox from AttendanceFact |
S-FIN-03 | Manual Xero re-key | FinanceOrders → XeroAdapter mirror |
S-EXM-04 | Hard-coded exemption window | policy-engine DSL |
2 · Product boundary (normative)
```
APP = { public-web, admin-workspace, student-portal, trainer-portal }
SVC = { swda-hub, xero-adapter, notification-dispatcher, policy-engine }
|APP_user_facing| = 3 + 1 public
```
Course-type taxonomy: DIPLOMA | SHORT | WSQ — enum frozen in catalogue.course_funding_type.
SWDA nomenclature: canonical SWDA; adapter maintains legacy_alias ∈ {SSG, WSG} field map until vendor EOL.
3 · Success metrics (instrumentation IDs)
| Metric-ID | Definition | Collection | MVP target |
|---|
M-CONV-01 | enrolled / enquiries | crm.funnel | +10% (S) |
M-ADM-02 | median(handle_time) diploma_app | OTel span | −60% (M) |
M-SWDA-03 | reject_rate(submissions) | integration_txn_log | <2% (M) |
M-FIN-04 | manual_journal_count | Xero diff job | −50% (S) |
4 · Scope tensor
In-scope v1: FR domains §7–§15 · SwdaHub P2 · Xero mirror · RBAC §15 · Outbox async
Out-of-scope v1: GATCOMS agency portal · ai.HumanCapital · D-GENIO proctor parity · native mobile · multi-region funding
5 · Persona → RBAC role map
| Persona | Role slug | Data scope |
|---|
| SysAdmin | platform.admin | * |
| Admissions | admissions.officer | programme_id ∈ assigned |
| Finance | finance.officer | order.* read/write |
| Sales | sales.agent | lead.* pre-enrolment |
| Trainer | trainer | session_id ∈ roster |
| Student | student | self |
| Auditor | auditor.ro | read-only append-log |
6 · Container overview
See CRD §17. Event bus: Redis Streams or Horizon queue — domain events MUST NOT invoke HTTP synchronously to SWDA from request thread.
7 · FR — Student Portal (public layer)
| ID | Priority | Requirement |
|---|
| FR-STU-001 | M | GET /v1/catalogue paginated; filter funding_type, category, run_date |
| FR-STU-010 | M | Applicant auth: email_otp | singpass_oidc | overseas_credential |
| FR-STU-020 | M | WSQ reg wizard: sequential gate identity → eligibility → fee_estimate → payment |
| FR-STU-030 | S | Course compare max 3 SKUs client-side |
| FR-STU-040 | C | Pigeonhole embed post-MVP |
Illegal state transition → HTTP 409 + error_code=FSM_VIOLATION.
8 · FR — Admissions / registration / funding
| ID | Priority | Requirement |
|---|
| FR-ADM-001 | M | Diploma FSM: DRAFT→SUBMITTED→SCREENING→OFFERED→ACCEPTED→ENROLLED |
| FR-ADM-010 | M | Seat lock: SELECT FOR UPDATE on course_run.seats_available |
| FR-ADM-020 | M | WSQ: subsidy_estimate computed pre-payment via policy-engine |
| FR-ADM-030 | M | SFC hold/release integrated with payment intent |
| FR-ADM-040 | S | Waitlist priority queue FIFO with promoted_at |
9 · FR — Admin Workspace
| ID | Priority | Requirement |
|---|
| FR-ADM-100 | M | Programme version immutability post first enrolment on version |
| FR-ADM-110 | M | Scheduler conflict detection O(n²) slots acceptable MVP; optimize P2 |
| FR-ADM-120 | M | Exemption: POL-EXM-* output binds order_line adjustment |
| FR-ADM-130 | M | Student360 materialized view refresh lag ≤ 60s |
| FR-ADM-140 | M | Attendance QR nonce TTL 120s single-use |
10 · FR — CRM / commission
| ID | Priority | Requirement |
|---|
| FR-CRM-001 | M | Lead dedupe key hash(normalize(email), normalize(mobile)) |
| FR-CRM-010 | S | Privyr import idempotent on external_id |
| FR-CRM-020 | M | ApplicationCreated event freezes CRM opportunity writes |
| FR-CRM-030 | M | Commission accrual on PaymentCleared; clawback on RefundPosted |
11 · FR — Learning portal (enrolled)
| ID | Priority | Requirement |
|---|
| FR-LRN-001 | M | Timetable ICS export optional |
| FR-LRN-010 | M | Check-in POST /v1/sessions/{id}/checkin {nonce} |
| FR-LRN-020 | S | SCORM/LTI adapter interface stub only MVP |
12 · FR — Trainer portal
| ID | Priority | Requirement |
|---|
| FR-TRN-001 | M | Roster accept/decline within configurable TTL |
| FR-TRN-010 | M | Attendance batch submit triggers AttendanceFactRecorded × N |
| FR-TRN-020 | S | Payable export POST /v1/trainer/payables/export-xero |
13 · FR — SwdaHub
| ID | Priority | Requirement |
|---|
| FR-SWDA-001 | M | All gov IO via hub; credential vault |
| FR-SWDA-010 | M | Outbox + idempotency keys |
| FR-SWDA-020 | M | Ops dashboard: pending/rejected/DLQ |
| FR-SWDA-030 | S | Sandbox/prod env switch RBAC-gated |
API inventory: DQ-SWDA-001 — confirm against SOQ subscription manifest.
14 · FR — Finance / Xero
| ID | Priority | Requirement |
|---|
| FR-FIN-001 | M | Order aggregate root owns pricing snapshot |
| FR-FIN-010 | M | InvoiceMirror 1:1 order |
| FR-FIN-020 | M | Cert issuance blocked if financial_hold.active |
| FR-FIN-030 | S | Reconciliation diff report cron |
15 · FR — Platform (RBAC, audit, notify, docs)
| ID | Priority | Requirement |
|---|
| FR-PLT-001 | M | RBAC middleware on all /v1/* mutators |
| FR-PLT-010 | M | audit_log append-only; no UPDATE/DELETE |
| FR-PLT-020 | M | PDPA consent record on applicant PII create |
| FR-PLT-030 | S | Notification template versioning |
16 · NFR catalogue
| ID | Requirement |
|---|
| NFR-01 | 99.9% availability monthly |
| NFR-02 | API p95 < 300ms reads at 500 RPS burst registration |
| NFR-03 | SWDA submission exactly-once effect |
| NFR-04 | WCAG 2.2 AA public/student |
| NFR-05 | PDPA access logging |
| NFR-06 | RPO 24h / RTO 4h MVP |
| NFR-07 | PCI SAQ-A (gateway redirect) |
17 · Journey sequences (reference)
WSQ happy path:
```
catalogue.select → singpass.auth → eligibility.check → order.create → payment.capture → enrolment.commit → swda.register (async)
```
Sequence diagrams: external docs/sequences/ (not in this oracle).
18 · Conceptual ERD (abbreviated)
```
organisation ||--o{ programme : owns
programme ||--o{ course_run : schedules
course_run ||--o{ session : contains
student ||--o{ enrolment : has
enrolment ||--o{ order : generates
order ||--o{ order_line : contains
session ||--o{ attendance_fact : records
```
Full DDL: services/api/database/migrations/ (implementation phase).
19 · Dependencies / constraints
| Dep-ID | System | Blocking |
|---|
| DEP-01 | SWDA sandbox credentials | P2 start |
| DEP-02 | Singpass OIDC client | WSQ public reg |
| DEP-03 | Xero OAuth tenant | P3 |
| DEP-04 | Payment gateway MID | Paid reg |
20 · Risk register (technical)
| R-ID | Risk | Mitigation |
|---|
| R-01 | SWDA API drift | Adapter version pins + contract tests |
| R-02 | Rate limit 429 storm | Token bucket worker pool |
| R-03 | Xero sync drift | Reconciliation cron + alert |
21 · Release milestones
| Phase | Gate | FR subset |
|---|
| P1 | Non-SWDA E2E | FR-STU, FR-ADM-100–140 (subset) |
| P2 | SWDA parallel-run | FR-SWDA-* |
| P3 | Finance reconcile | FR-FIN-* |
| P4 | CRM conversion | FR-CRM-* |
22 · Open discovery (DQ-*)
| ID | Question | Owner |
|---|
| DQ-SWDA-001 | Complete API manifest | HK |
| DQ-XERO-001 | COA mapping | Finance |
| DQ-PRIV-001 | Privyr import mode | CPO |
23 · Glossary (symbol table)
| Term | Definition |
|---|
| SWDA | Skills and Workforce Development Agency — Singapore agency (from Jul 2026 merger of SSG + WSG) overseeing WSQ, subsidies, SkillsFuture Credit, and ATP compliance via systems such as TPGateway |
| WSQ | Workforce Skills Qualifications — national skills certification framework |
| ATP | Approved Training Provider |
| TPGateway | Training Partners Gateway — SWDA platform for ATP course/trainee/claim submissions |
| SFC | SkillsFuture Credit — individual learner funding wallet |
| SoR | System of Record |
| NYC | Not Yet Competent |
| Outbox | Transactional event relay pattern |
Author: Eddy (CPO) · AIEE.DEV · Cross-ref: CRD §02 · Implementation Doc v1