Delivery methodology¶
Delivery model¶
The platform is built by AI coding agents operating under human orchestration. The human defines the sequence, assembles context, reviews output, and approves merges. The agent writes code, tests, and design artefacts.
There are no sprints, no squads, no velocity metrics, no standups. The unit of delivery is the module. A module is done when all its acceptance criteria have passing automated tests in CI and build_status reaches Built.
Roles¶
Human orchestrator¶
- Selects the next module to build from the delivery sequence, respecting dependency order
- Assembles the context package (see below) and assigns it to an agent
- Reviews agent output against acceptance criteria
- Approves or returns with specific revision instructions
- Merges approved work to
mainand monitors the CI run - Updates the wiki when
build_statuschanges - Escalates blockers (missing specs, ambiguous FRs, conflicting contracts) before assigning
The orchestrator does not write code. The orchestrator's leverage is sequencing, context quality, and review precision.
AI coding agent¶
- Reads all assigned context before writing a single line of code
- Writes the implementation, tests, and module technical design document
- Follows the test strategy — one test per FR, one per policy satisfaction
- Produces a handoff file for any wiki updates required
- Does not make architectural decisions — surfaces ambiguities to the orchestrator before proceeding
Module lifecycle¶
WIKI: build_status = Not started
│
│ Orchestrator: confirms all dependencies are Built,
│ assembles context package, assigns module to agent
│
▼
WIKI: build_status = In progress ← only manual transition
│ (orchestrator sets this)
│ Agent builds: implementation + tests + module design doc
│ Orchestrator: reviews → approve or return
│
▼ (approved, merged to main)
CI pipeline runs:
typecheck → unit tests → policy tests → build artefacts
→ upload to S3 (integration-passed=false) → deploy to dev
→ integration tests (RUN_INTEGRATION=1, against live dev)
→ tag S3 artefacts integration-passed=true
→ CI writes handoff: {module_id}-ci-built-{sha}.handoff.md
│
▼ (bank-wiki processes handoff)
WIKI: build_status = Built ← advanced by bank-wiki after CI handoff
│
│ CI continues: smoke test (verify-deployment.mjs)
→ CI writes handoff: {module_id}-ci-deployed-{sha}.handoff.md
│
▼ (bank-wiki processes handoff)
WIKI: build_status = Deployed ← advanced by bank-wiki after CI handoff
Transition rules:
- Not started → In progress: orchestrator sets manually via update-wiki.py --status "In progress".
This is the only manual status transition permitted.
- In progress → Built: the CI pipeline writes a ci_status handoff document
({module_id}-ci-built-{sha}.handoff.md) to docs/handoffs/ in the code repo
once all gates pass; bank-wiki processes the handoff and calls
update-wiki.py --status Built. Prerequisites: typecheck ✓, unit tests ✓
(≥80% coverage), policy tests ✓, deploy ✓, integration tests ✓ with
RUN_INTEGRATION=1, S3 artefacts tagged integration-passed=true.
The orchestrator must not set Built manually — doing so bypasses the quality
gate and violates DT-007.
- Built → Deployed: the CI pipeline writes a ci_status handoff document
({module_id}-ci-deployed-{sha}.handoff.md) immediately after the smoke test
passes; bank-wiki processes the handoff and calls update-wiki.py --status Deployed.
The orchestrator must not set Deployed manually.
A module must not be started until every module in its dependencies list has
build_status = Built. The dependency graph in the module register is the
enforcement mechanism — it is not advisory.
All deployments go through CI. Running sst deploy, pulumi up, snow dcm deploy,
or dbt run directly from a developer terminal is not permitted after initial bootstrapping.
See MOD-156 CI/CD mandate for the narrow
bootstrapping exceptions.
Context package¶
The orchestrator assembles a context package for each agent assignment. A well-assembled context package is the single biggest determinant of output quality. Minimum required context:
| Context item | Source | Why |
|---|---|---|
| Module spec (YAML + MD) | docs/systems/SD0x-*/MOD-xxx-*.md |
What the module does, policies it satisfies, dependencies |
| FR register entries | docs/goals/fr-register.md |
The specific FRs this module must satisfy |
| Owning system domain data model | docs/design/system/data-models/SD0x-*.md |
Tables the module reads/writes |
| Interface contracts | docs/design/system/interface-contracts.md |
Synchronous APIs the module exposes or calls |
| Event catalogue entries | docs/design/system/event-catalogue.md |
Events the module publishes or consumes |
| Acceptance criteria | docs/delivery/acceptance-criteria.md |
Test requirements per policy satisfaction mode |
| Jurisdiction model | docs/architecture/jurisdiction-model.md |
How to handle NZ/AU branching |
| Observability standard | docs/delivery/methodology.md (this page) |
Logging and tracing requirements |
| Module design template | docs/design/module/template.md |
What the agent must produce |
For modules with cross-domain dependencies, also include the data model page for the dependency's system domain.
Use the AI context URLs from source/.wiki-state.yaml for efficient bulk context loading rather than individual page fetches.
What the agent produces¶
For each module assignment, the agent must deliver:
- Implementation code — the Lambda function(s), handlers, and business logic
- Unit tests — one test per FR criterion; property-based tests for calculations
- Integration tests — one test per interface contract the module participates in
- Policy satisfaction tests — one automated test per row in the module's
policies_satisfiedlist (see Acceptance criteria) - Module technical design doc — stored in the repo under
docs/design/MOD-xxx.md; covers data flow, key decisions, edge cases. Archived to the wiki atsource/pages/design/modules/MOD-xxx.mdduring handoff processing, where it becomes visible in the wiki NAV and in the AI context design aggregation page - Wiki handoff file — placed in
docs/handoffs/for any required wiki updates (build status, dependency updates, new events discovered)
The orchestrator reviews against items 2–4 before approving. Item 1 is reviewed for correctness relative to the spec; the orchestrator is not performing a code style review.
IaC and infrastructure modules¶
Some modules deliver infrastructure definitions rather than Lambda application handlers. There are two distinct IaC patterns, each with different tooling and quality gates.
Module classification¶
AWS / Neon IaC module — primary output is AWS or Neon infrastructure provisioned via Pulumi or SST. Examples: MOD-104 (AWS bootstrap), MOD-103 (Neon bootstrap), MOD-043 (EventBridge governance), MOD-045 (Secrets & key management), MOD-076 (Observability platform).
Snowflake module — primary output is Snowflake objects, using two complementary tracks:
- DCM Projects (
MOD-NNN-{slug}/dcm/) — Snowflake's native declarative state management (Database Change Management). New modules declare all non-dbt Snowflake objects (schemas, tables, DMFs, DMF attachments, tasks, alerts, grants) as desired state in a DCM project manifest. CI runssnow dcm plan→dbt build→snow dcm deploy. DCM diffs current environment state against declared target; no idempotency workarounds required. This is the standard for all new modules (ADR-054). - dbt transformations (
dbt/models/MOD-NNN-{slug}/) — Dynamic Tables, analytical views, incremental materializations. dbt is the standard tool for all Snowflake transformation objects. Run per-module viadbt build --select tag:mod-NNN. dbt must complete beforesnow dcm deploybecause DCM alert objects may reference dbt-built views (ADR-054 dependency ordering).
These two tracks are kept strictly separate. DCM does not define Dynamic Tables (dbt's responsibility), and dbt models do not define raw schemas or grants (DCM's responsibility). Account-level objects (warehouses, databases, RBAC roles, integrations) are owned by MOD-102 — domain modules do not touch them.
Legacy: MOD-038, MOD-085, and MOD-098 use the deprecated infra/snowflake/ SQL
script runner (apply-snowflake-ddl.ts). These are migrated to DCM on next deploy per
the ADR-054 migration path. The infra/snowflake/ pattern must not be used in new modules.
Examples: all SD06 bank-risk-platform modules.
Hybrid module — IaC provisioning (AWS or Snowflake) plus one or more Lambda handlers (e.g. a scheduled ingestion Lambda that also provisions its own EventBridge rule and SSM outputs). Apply both sets of rules.
AWS / Neon IaC modules¶
Adapted deliverables¶
| Standard deliverable | IaC adaptation |
|---|---|
| Implementation code | Pulumi/SST resource definitions in the module directory |
| Unit tests | Not required — no business logic to unit test; Pulumi state management handles idempotency |
| Integration tests | Infrastructure integration tests — verify the provisioned resources exist and are correctly configured (see below) |
| Policy satisfaction tests | Required, same rules — GATE requires negative test; LOG requires immutability test; ALERT requires latency assertion |
| Module technical design doc | Required — SSM outputs table is mandatory (see below) |
| Wiki handoff file | Required as normal |
Infrastructure integration tests¶
Replace unit tests with infrastructure integration tests that verify the provisioned state:
- Resource exists at the expected ARN or name
- Resource has the required configuration (e.g. KMS key rotation enabled, S3 bucket versioning on, Neon branch structure correct)
- Access controls are enforced — attempt a disallowed action and verify it is rejected
- Cross-resource wiring is correct (e.g. Firehose stream delivers to the correct S3 bucket)
Tests run against the deployed dev environment via AWS SDK assertions, not against a mock.
SSM outputs table — mandatory in the design doc¶
IaC modules write their outputs (ARNs, endpoints, names) to AWS SSM Parameter Store so downstream modules can consume them without hardcoding. The design doc must include a complete SSM outputs table:
| SSM path | Value | Consumed by |
|---|---|---|
| /bank/{env}/kms/pii/arn | KMS key ARN for PII data classification | bank-kyc, bank-app |
| /bank/{env}/eventbridge/bank-core/arn | EventBridge bus ARN for SD01 | bank-core |
Without this table, Phase 2+ modules cannot be built — they have no documented path to the infrastructure they depend on. The SSM outputs table is the primary hand-off artefact from an IaC module to its consumers.
Standards that do not apply to pure AWS/Neon IaC modules¶
- Observability standard (structured logs) — applies only to Lambda handlers within the module. Pure Pulumi resource definitions do not emit structured logs. If the module has no Lambda handlers, omit the observability compliance gate.
- Idempotency standard — not applicable. Pulumi state management makes IaC deploys inherently idempotent.
- Error handling standard — applies only to Lambda handlers within the module, not to Pulumi resource provisioning.
- Interface contracts — IaC modules are consumed via SSM/IAM, not HTTP. Only include interface contract tests if the module exposes a Lambda-backed API endpoint.
Quality gates for AWS/Neon IaC modules¶
| Gate | Requirement |
|---|---|
| Infrastructure integration tests | One test per FR; each test asserts against the deployed dev environment |
| Policy satisfaction tests | One test per row in policies_satisfied; GATE modes must include a negative test |
| SSM outputs table | Present in docs/design/MOD-xxx.md and verified accurate against deployed state |
| Observability compliance | Required only if the module contains Lambda handlers |
| compile --check | Wiki handoff validated before merge |
Snowflake modules (DCM Projects + dbt)¶
New SD06 modules use Snowflake DCM Projects for all non-dbt Snowflake objects. Snowflake itself is the source of truth for object state. No Pulumi state file, no Terraform provider, no imperative script runner.
DCM project conventions¶
- All Snowflake objects (schemas, tables, DMFs, DMF attachments, tasks, alerts, grants)
are declared as desired state in
MOD-NNN-{slug}/dcm/— the DCM project directory. - The
dcm/project.ymlmanifest declares: target environment variables (resolved from SSM at plan time), object dependency graph (alerts after dbt, DMF attachments after tables), warehouse and role context. - Account-level objects (warehouses, databases, RBAC roles, storage integrations, network policy) are owned by MOD-102. Domain modules own only objects within their assigned database and schemas.
- Dynamic Tables are dbt's responsibility — declare them in
dbt/models/MOD-NNN-{slug}/withmaterialized: dynamic_table,target_lag, andcluster_by. Do not declare Dynamic Tables in the DCM project. - Dynamic Table lag and cluster key must be documented in
docs/design/MOD-NNN.md.
CI deploy sequence¶
snow dcm plan --project ./dcm --target {stage} --output plan.json
→ upload plan.json to S3 artefacts bucket
dbt build --target {stage} --select tag:mod-NNN
→ dbt tests run inline
snow dcm deploy --plan plan.json
→ applies schema, tables, DMFs, alerts, grants in dependency order
snow dcm deploy is called by reusable-risk-platform.yml only. It is never run manually.
Adapted deliverables¶
| Standard deliverable | Snowflake / DCM adaptation |
|---|---|
| Implementation code | DCM project in dcm/ + dbt models in dbt/models/MOD-NNN-{slug}/ |
| Unit tests | Not required for pure DDL/dbt — no imperative logic to unit test |
| Integration tests | Snowflake object tests — verify objects exist and are active (see below) |
| Policy satisfaction tests | Required — GATE: verify access denied without correct role; LOG: verify no UPDATE/DELETE granted |
| Module technical design doc | Required — Snowflake objects table (object name, type, target lag, cluster key); SSM outputs table if Lambda/AWS resources also provisioned |
| Wiki handoff file | Required as normal |
Snowflake integration tests¶
- Object exists: query
INFORMATION_SCHEMAorSHOW OBJECTSto confirm each schema, table, and Dynamic Table was created with the correct definition. - Grants enforced: connect as the restricted role and attempt SELECT on a protected object — must be denied.
- Dynamic Table liveness:
SCHEDULING_STATE = 'RUNNING'and last refresh timestamp within the declaredTARGET_LAGwindow. - Task liveness:
STATE = 'STARTED'. - Alert liveness:
STATE = 'STARTED'. - Immutability (LOG modes): verify the owning role has no UPDATE or DELETE privilege on append-only tables; attempt DML and assert rejection.
- DCM idempotency: re-running
snow dcm planafter a completed deploy must produce a zero-change plan (no unexpected drift).
Standards that do not apply to pure Snowflake modules¶
- Observability standard — applies only to Lambda handlers co-deployed in the same module. DCM objects and dbt models do not emit structured logs.
- Idempotency standard — DCM declarative state management makes re-runs inherently safe. No idempotency table required.
- Error handling standard — applies only to co-deployed Lambda handlers.
Quality gates for Snowflake modules¶
| Gate | Requirement |
|---|---|
| Snowflake object tests | One per FR; Snowflake SDK or SQL assertions against deployed dev |
| Policy satisfaction tests | One per policies_satisfied row; GATE requires negative test; LOG requires immutability test |
| Dynamic Table correctness | CALC mode policies require numerical correctness assertion against known input/output pair |
| DCM idempotency | snow dcm plan re-run after deploy produces zero-change plan |
| DCM plan.json uploaded | Present in S3 artefacts at {repo}/{sha}/modules/{module_id}/dcm-plan.json |
| dbt manifest.json uploaded | Present in S3 artefacts at {repo}/{sha}/modules/{module_id}/dbt/manifest.json |
| Snowflake objects table | Every object in docs/design/MOD-NNN.md (schema, type, lag, cluster key) |
| compile --check | Wiki handoff validated before merge |
Legacy Snowflake modules (apply-snowflake-ddl.ts)¶
MOD-038, MOD-085, and MOD-098 use the deprecated infra/snowflake/ SQL script runner.
These modules are not to be rebuilt from scratch — they are migrated to DCM on next
deploy per the ADR-054 migration path. Until migration:
- Scripts must remain idempotent (
CREATE OR REPLACE,CREATE ... IF NOT EXISTS) - CI applies scripts via
pnpm apply-ddl --phase=pre-dbt→ dbt build →pnpm apply-ddl --phase=post-dbt - The two-phase pattern persists only for these legacy modules
Observability standard¶
Every Lambda must emit structured JSON logs to stdout. CloudWatch Logs captures them; MOD-076 ingests via subscription filter. Compliant logging is a build acceptance criterion — a module without it will not be accepted.
Full specification: Observability standard — mandatory log fields, trace propagation rules, EMF custom metrics, SLOs, dashboard requirements, and log retention policy.
Summary of mandatory log fields: trace_id, correlation_id, module_id, jurisdiction, event_type, party_id, duration_ms, level. Never log PII values — log the reference (party_id) only.
Idempotency standard¶
Every module that processes payments, writes to the ledger, or performs an irreversible action must implement idempotency on idempotency_key. This is an acceptance criterion for all SD01, SD04, and SD05 modules.
Implementation pattern:
1. On receipt of a request with idempotency_key, check the idempotency store (a dedicated Postgres table {schema}.idempotency_keys)
2. If a record exists with the same key and module, return the stored result immediately — do not re-execute
3. If no record exists, execute the operation, store the result, then return it
4. Idempotency records expire after 24 hours
The idempotency store table for each domain:
CREATE TABLE {schema}.idempotency_keys (
key text NOT NULL,
module_id text NOT NULL,
result jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz NOT NULL,
PRIMARY KEY (key, module_id)
);
Error handling standard¶
Full specification: Error handling and resilience standard — error classification, sync/async patterns, retry policy, DLQ processing, poison pill handling, partial failure compensation, external provider resilience, circuit breaker, and bulkhead concurrency limits.
Summary:
- All errors are classified as TRANSIENT_INFRA, PROVIDER_ERROR, VALIDATION_FAILURE, or COMPLIANCE_BLOCK before handling
- Synchronous failures return the standard error envelope (HTTP 422 / 503 / 500 depending on class)
- Async failures allow EventBridge retry; after retry exhaustion the event routes to the domain DLQ
- Dead-lettered events must be replayable — no destructive side effects from partial processing
- Business rule and compliance failures must not consume retries — write to DLQ directly
Quality gates¶
A module PR is not approved unless all of the following pass in CI:
| Gate | Requirement |
|---|---|
| Unit tests | All pass; ≥ 80% line coverage |
| FR tests | One test per FR in the module's requirements list |
| Policy satisfaction tests | One test per row in policies_satisfied; GATE modes must include a negative test |
| Integration tests | All interface contracts the module participates in |
| Observability compliance | Structured log format verified by log schema test |
| Idempotency test | Where applicable — verified by submitting the same request twice |
| compile --check | Wiki handoff validated before merge |
A module that passes CI but has known spec gaps (missing FR test, undocumented edge case) is flagged in the handoff file with a TODO. The orchestrator decides whether to accept with the flag or return for completion.