Skip to content

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 main and monitors the CI run
  • Updates the wiki when build_status changes
  • 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:

  1. Implementation code — the Lambda function(s), handlers, and business logic
  2. Unit tests — one test per FR criterion; property-based tests for calculations
  3. Integration tests — one test per interface contract the module participates in
  4. Policy satisfaction tests — one automated test per row in the module's policies_satisfied list (see Acceptance criteria)
  5. 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 at source/pages/design/modules/MOD-xxx.md during handoff processing, where it becomes visible in the wiki NAV and in the AI context design aggregation page
  6. 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 runs snow dcm plandbt buildsnow 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 via dbt build --select tag:mod-NNN. dbt must complete before snow dcm deploy because 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.yml manifest 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}/ with materialized: dynamic_table, target_lag, and cluster_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_SCHEMA or SHOW OBJECTS to 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 declared TARGET_LAG window.
  • 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 plan after 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.