MOD-036 — Prudential return builder (RBNZ / APRA)
Purpose
Assembles RBNZ BS-series and APRA ARS-series prudential returns from
upstream MOD-032 (LCR/NSFR) and MOD-033 (capital/RWA) Snowflake-native
published views via dbt Dynamic Tables. Validates every cell against
the regulator-published rules (FR-218), maintains a per-return
submission calendar (FR-219), and submits to RBNZ / APRA endpoints —
but only after a Finance officer approves the assembled return in
MOD-170's Streamlit form, recorded as a row in
REGULATORY.RETURN_APPROVALS (REP-005 GATE / FR-807).
REP-005's GATE policy is the central contract — the submission
orchestrator Lambda runs four sequential checks (validation status,
approval exists, approver ≠ assembler, content hash matches snapshot)
and refuses any regulator POST that fails any check.
Functional requirements
| Code |
Requirement |
| FR-217 |
Compile and submit all RBNZ + APRA prudential returns by their respective statutory due dates, with no return submitted past the deadline. |
| FR-218 |
Validate every return against regulator-published validation rules before submission, blocking submission of any return that fails validation and alerting the Finance team. |
| FR-219 |
Maintain a submission calendar showing all return codes, due dates, and submission status, updated within 1 hour of any submission or acknowledgement event. |
| FR-220 |
Record submission reference, transmission timestamp, regulator acknowledgement, and return content hash for every submitted prudential return, retaining records for 7 years. |
| FR-807 |
Require a valid REGULATORY.RETURN_APPROVALS record for the current (run_id, return_code) pair before the submission orchestrator may post to a regulator endpoint. |
| NFR-010 |
Regulatory submissions produced without manual intervention ≥ 90% — the orchestrator + dbt DTs satisfy the auto-assembly path; only the dual-control approval click requires a human (by design). |
| NFR-011 |
Tier 1 availability. |
| NFR-019 |
RTO ≤ 4h / RPO ≤ 1h. |
| NFR-024 |
Audit log record mutability = 0 — RETURN_RUNS, RETURN_SUBMISSIONS, RETURN_APPROVALS have no UPDATE/DELETE/TRUNCATE grants. |
Policies satisfied
| Policy |
Mode |
How |
| REP-001 |
AUTO |
Four return DTs (DT_RBNZ_BS2A, DT_RBNZ_BS13, DT_APRA_ARS_110_0, DT_APRA_ARS_210_0) materialised as Snowflake Dynamic Tables with target_lag='1 hour' + refresh_mode='FULL'. No manual assembly path (verified by tests/policy/REP-001-auto.test.ts + tests/integration/snowflake-objects.test.ts asserting SCHEDULING_STATE=ACTIVE). |
| REP-002 |
LOG |
Every cell row carries _source_lineage VARIANT (source_module + source_view + jurisdiction + position_date + fetched_at). The lineage is constructed in the staging models (stg_capital_ratios, stg_liquidity_ratios) and propagated through every UNION ALL in the return DTs (verified by tests/policy/REP-002-log.test.ts + dbt not_null test). |
| REP-005 |
GATE |
The submission orchestrator Lambda evaluates four sequential checks via src/orchestrator/gate.ts::evaluateGate(...). Any refusal aborts the submission, publishes return_gate_refused to MOD-076, and updates RETURN_RUNS.STATUS to APPROVAL_REQUIRED or FAILED. NO code path reaches the regulator HTTP client unless decision.allow === true — structurally enforced (one call site for regClient.submit(...), gated by the if (!decision.allow) return; early-out). Tests in tests/unit/gate.test.ts cover every refusal reason; tests/policy/REP-005-gate.test.ts verifies the orchestrator-level "regulator client never called on refusal" invariant. |
| NFR-024 |
LOG |
RETURN_RUNS, RETURN_SUBMISSIONS, RETURN_APPROVALS have no UPDATE/DELETE/TRUNCATE grants. INSERT on RETURN_RUNS + RETURN_SUBMISSIONS goes to ingest_role (orchestrator); INSERT on RETURN_APPROVALS goes ONLY to REGULATORY_SUBMISSIONS_PORTAL_ROLE (MOD-170 Streamlit form) — orchestrator must NOT write its own approval, separation-of-duties (verified by tests/policy/NFR-024-log.test.ts + tests/integration/return-approvals-immutability.test.ts). |
Architecture
Hybrid module:
- dbt + DCM for transformations and base tables (REGULATORY schema,
3 DCM-managed tables, 4 return DTs, 2 published views, 1 calendar DT,
2 Snowflake Alerts).
- Lambda for the submission orchestrator (the REP-005 GATE owner;
thin runner per ADR-046 "When Lambda IS appropriate" — orchestrator
reads Snowflake state, evaluates the gate, and POSTs to external
regulator endpoints. Does NOT execute SQL transforms or mediate
intra-SD06 control flow).
- Streamlit-in-Snowflake for the cell viewer + Finance approval
form (legacy infra/snowflake/streamlits/ path per ADR-054 §"DCM v2
limitations — no Streamlit"). MOD-170 embeds this Streamlit.
Snowflake objects owned by MOD-036
| Object |
Type |
Schema |
Notes |
REGULATORY |
SCHEMA (DCM) |
— |
Authoritative declaration. MOD-037/057/060/170 reference under this schema but do NOT redeclare it (DCM v2 error 001531). |
RETURN_RUNS |
TABLE (DCM) |
REGULATORY |
Append-only audit; FR-220 + NFR-024. One row per assembly attempt. |
RETURN_SUBMISSIONS |
TABLE (DCM) |
REGULATORY |
FR-220 — submission ref + ack + content hash; 7y retention. |
RETURN_APPROVALS |
TABLE (DCM) |
REGULATORY |
REP-005 GATE / FR-807 / bank-wiki #35 Action B. Append-only. Written by MOD-170; read by submission orchestrators in MOD-036/037/057/060. |
DT_RBNZ_BS2A |
DT (dbt) |
REGULATORY |
refresh_mode=FULL, target_lag='1 hour'. NZ jurisdiction. 7 cells per period. |
DT_RBNZ_BS13 |
DT (dbt) |
REGULATORY |
NZ liquidity. 6 cells. |
DT_APRA_ARS_110_0 |
DT (dbt) |
REGULATORY |
AU capital. 7 cells. |
DT_APRA_ARS_210_0 |
DT (dbt) |
REGULATORY |
AU liquidity. 6 cells. |
SUBMISSION_CALENDAR |
DT (dbt) |
REGULATORY |
FR-219; target_lag='1 hour' satisfies the 1h freshness requirement directly. |
V_RETURN_PREVIEW |
VIEW (dbt) |
REGULATORY |
Unified cell + lineage view; the contract MOD-170's Streamlit consumes (ADR-046 §3). |
V_RETURN_VALIDATION |
VIEW (dbt) |
REGULATORY |
Per-run validation summary; ALERT_RETURN_VALIDATION_FAILED reads this. |
ALERT_RETURN_VALIDATION_FAILED |
ALERT (DCM) |
REGULATORY |
FR-218 — 15-min schedule; routes via BANK_SNS_INTEGRATION to MOD-076. |
ALERT_DUE_DATE_APPROACHING |
ALERT (DCM) |
REGULATORY |
FR-217 — 60-min schedule; fires when due_date within 5 days. |
MOD_036_STREAMLIT_STAGE |
STAGE |
REGULATORY |
Streamlit source stage (legacy infra/snowflake/ — ADR-054). |
STREAMLIT_RETURN_BUILDER |
STREAMLIT |
REGULATORY |
Cell viewer + approval form; embedded by MOD-170. |
SSM outputs
| Path |
Value |
Consumer |
/bank/{env}/risk-platform/prudential-return-builder/return-approvals-table |
REGULATORY.RETURN_APPROVALS |
MOD-037 / MOD-057 / MOD-060 (Action C — REP-005 gate clients), MOD-170 (Action D writes). |
/bank/{env}/risk-platform/prudential-return-builder/return-runs-table |
REGULATORY.RETURN_RUNS |
MOD-170 (SoD display), MOD-171/172 (hub status). |
/bank/{env}/risk-platform/prudential-return-builder/return-submissions-table |
REGULATORY.RETURN_SUBMISSIONS |
MOD-076 audit, MOD-171/172. |
/bank/{env}/risk-platform/prudential-return-builder/return-preview-view |
REGULATORY.V_RETURN_PREVIEW |
MOD-170 embedded Streamlit cell viewer. |
/bank/{env}/risk-platform/prudential-return-builder/submission-calendar |
REGULATORY.SUBMISSION_CALENDAR |
MOD-171 (executive risk view), MOD-172 (operations view). |
/bank/{env}/risk-platform/prudential-return-builder/submission-orchestrator-arn |
(Lambda ARN) |
MOD-170 — publishes mod-036.submit-return EB events targeting this Lambda. |
/bank/{env}/risk-platform/prudential-return-builder/event-source-name |
bank.risk-platform |
EB subscribers (MOD-076). |
/bank/{env}/risk-platform/prudential-return-builder/streamlit-url |
REGULATORY.STREAMLIT_RETURN_BUILDER |
MOD-170 (embed), MOD-171/172. |
Dependencies
| Module |
Surface consumed |
Why optional? |
| MOD-032 |
RISK_CAPITAL.V_LCR_CURRENT, V_NSFR_CURRENT |
No — required for BS13 + ARS 210.0. |
| MOD-033 |
RISK_CAPITAL.V_CAPITAL_CURRENT, V_CAPITAL_BY_PORTFOLIO |
No — required for BS2A + ARS 110.0. |
| MOD-038 |
GOVERNANCE_META.V_QUALITY_SCORES, V_OPEN_BREAKS |
No — Finance signs off knowing DQ state. |
| MOD-102 |
Snowflake account + roles; REGULATORY_SUBMISSIONS_PORTAL_ROLE (outbound issue #37) |
No — but the role is conditional; the conditional grant block no-ops cleanly until MOD-102 ships. |
| MOD-104 |
KMS key, EventBridge bus |
No. |
| MOD-170 |
Reverse — MOD-170 writes RETURN_APPROVALS, publishes mod-036.submit-return |
No (runtime). MOD-036 is buildable independently; approval gate is dead-air until MOD-170 lands (by design — return state stuck at VALIDATED/APPROVAL_REQUIRED). |
Event flow
- Published (external boundary, ADR-046 §5):
bank.risk-platform / return_submitted → MOD-076 audit + MOD-048 ledger
bank.risk-platform / return_gate_refused → MOD-076 alarm-intake
- Consumed (external trigger, ADR-046 §5):
bank.risk-platform / mod-036.submit-return ← MOD-170 (after Finance officer approves a return in the Streamlit form)
- NOT used: intra-SD06 EB signals. MOD-036 reads MOD-032/033/038 output via dbt
source() per ADR-046 §1/§2.
Build sequence references
- bank-wiki entity:
../bank-wiki/source/entities/modules/MOD-036.yaml, .md
- bank-wiki #35 Action B handoff:
docs/handoffs/SD06-streamlit-dashboard-layer.handoff.md
- Outbound issue #37 (MOD-102 role provisioning):
../bank-platform/docs/handoffs/MOD-102-sd06-streamlit-roles.handoff.md
- env_role CI gap (affects MOD-038 today; MOD-036 unaffected — HAS_DCM=true): bank-wiki #39