Phase 2 -- Specification Post-Implementation
Reflects the specification as actually implemented. The original ../specification.md remains as the contract that drove implementation; this document records deltas between the contract and reality. Where the implementation matches the spec, the spec stands; only differences are listed here.
Deltas vs the original specification
Section titled “Deltas vs the original specification”D-1: ardamails.com zone adopted via cdk import rather than created from scratch
Section titled “D-1: ardamails.com zone adopted via cdk import rather than created from scratch”Original spec (Task 4): “instantiate a new r53.PublicHostedZone(this, 'ArdamailsZone', { zoneName: 'ardamails.com' })” — implicit “create from scratch via standard cdk deploy”.
Implemented: same CDK code shape, but the zone was adopted via cdk import because it already existed in Route53 as Z0721066239FWCD47EJDX (auto-created by Route53 Domains when the ardamails.com domain was originally registered through the AWS registrar). The CDK code now also sets comment: "HostedZone created by Route53 Registrar" (mirroring the live AWS-default comment, so the import is read-only) and applyRemovalPolicy(cdk.RemovalPolicy.RETAIN).
The deploy ran in two CFN operations:
- IMPORT change-set with a hand-built
deployed-state + ArdamailsZone resource onlytemplate (no Outputs added, no other resource modifications). CFN reportedAction: Import,Replacement: null,Scope: []. - Normal
cdk deploywith the full synthesized template, adding theardamailsZoneOutput and reconcilingCDKMetadata.
Reason: see DQ-R1-008 and learnings.md L-1, L-2, L-3. The pre-existing zone was discovered when the operator challenged the cdk-diff result with “is the zone already there?”. Without the import, the deploy would have created a duplicate zone with a different NS set, orphaned at the DNS layer.
D-2: V-ROOT-001 extended with a strict-equality assertion locking the import target
Section titled “D-2: V-ROOT-001 extended with a strict-equality assertion locking the import target”Original spec (Task 4 verification): the spec assumed standard cdk synth checks would suffice for the new zone.
Implemented: root-dns-stack.test.ts now includes a strict-equality test:
template.hasResource("AWS::Route53::HostedZone", { Properties: { Name: "ardamails.com.", HostedZoneConfig: { Comment: "HostedZone created by Route53 Registrar" }, }, DeletionPolicy: "Retain", UpdateReplacePolicy: "Retain",});The assertion locks every property the import target depends on. Future CDK code edits that drift from the import target fail at unit-test time, before deploy.
Reason: see learnings.md L-3 and alternatives.md A-4. Strict-equality is the right discipline for an imported (read-only-on-import) resource because any property deviation — whether modification or accidental addition — is a defect.
D-3: V-ROOT-002 (dig assertion against arda.ardamails.com) moved to Phase 3
Section titled “D-3: V-ROOT-002 (dig assertion against arda.ardamails.com) moved to Phase 3”Original spec (verification + exit criterion implicitly): the original phases.md Phase 2 exit criterion included a dig +short NS arda.ardamails.com @8.8.8.8 assertion, deferred-but-coupled.
Implemented: per DQ-R1-006 and the phases.md patch authored as part of Task 6, Phase 2’s exit criterion is narrowed to forward declaration only. The dig assertion belongs to Phase 3’s verification regime and Phase 3’s exit criteria.
Reason: see DQ-R1-006. With the writer-stack-owns-its-NS-record pattern (chosen Option B), Phase 2 cannot make the dig assertion true on its own; Phase 3 owns both the child zone and the upstream delegation write.
D-4: New decisions DQ-R1-006 and DQ-R1-008 recorded under Round R1-Phase2
Section titled “D-4: New decisions DQ-R1-006 and DQ-R1-008 recorded under Round R1-Phase2”Original spec: anticipated that any open questions surfaced during implementation would be resolved into DQ-R1-NNN entries.
Implemented:
DQ-R1-006(locus of cross-zone NS-delegation writes) was the precondition that shaped Task 6’sphases.mdpatch and the Phase-2-vs-Phase-3 ownership boundaries.DQ-R1-008(adopt vs create the existingardamails.comzone) was a fresh decision taken during Phase 2 implementation, after the pre-existing zone was discovered.
Both entries appear in three places in decision-log.md: the upper Decision Table, a full section under Round R1-Phase2, and the Summary table at the bottom. The full section text records context, options considered, recommendation, decision, consequences, and applied-to references.
Reason: see decision-log.md for the entries. Decisions resolved during implementation get their own DQ entries even when they were not flagged as Open Questions in the original spec; the log is append-only and rounds-bucketed.
D-5: Two-phase deploy choreography documented in the runbook section
Section titled “D-5: Two-phase deploy choreography documented in the runbook section”Original spec: assumed standard cdk deploy <stack> is the entire deploy operation for Task 4.
Implemented: the actual deploy was a two-phase choreography:
# Phase 1: IMPORT change-set adopts the live zoneaws cloudformation get-template --stack-name RootConfiguration --template-stage Original \ > scratch/deployed.json# build importv2 = deployed + ArdamailsZone resource onlyjq '.Resources.ArdamailsZone1DCDDC15 = $newRes' \ --argjson newRes "$(jq '.Resources.ArdamailsZone1DCDDC15' scratch/synth.json)" \ scratch/deployed.json > scratch/importv2.jsonaws cloudformation create-change-set \ --stack-name RootConfiguration --change-set-name import-ardamails-v2 \ --change-set-type IMPORT \ --resources-to-import "ResourceType=AWS::Route53::HostedZone,LogicalResourceId=ArdamailsZone1DCDDC15,ResourceIdentifier={Id=Z0721066239FWCD47EJDX}" \ --template-body file://scratch/importv2.jsonaws cloudformation describe-change-set --stack-name RootConfiguration --change-set-name import-ardamails-v2# Confirm Action=Import, Replacement=null, Scope=[]aws cloudformation execute-change-set --stack-name RootConfiguration --change-set-name import-ardamails-v2
# Phase 2: normal cdk deploy adds the Output and reconciles CDKMetadatacd /Users/jmp/code/arda/projects/email-integration-worktrees/phase-2/infrastructureGITHUB_TOKEN=$(gh auth token) npm installnpx cdk deploy --app 'npx ts-node src/main/cdk/apps/Root/r53-zones.ts' RootConfiguration --profile Admin-Alpha1# Verify zero-diff after the deploynpx cdk diff --app 'npx ts-node src/main/cdk/apps/Root/r53-zones.ts' RootConfiguration --profile Admin-Alpha1Reason: see learnings.md L-2 (CFN IMPORT change-sets reject Output additions and other resource modifications). The CDK CLI’s cdk import command does not provide a flag to strip the Output; the operator-driven choreography is the working pattern.
D-6: No CDK code changes needed in apps/Root/r53-zones.ts beyond the ArdamailsZone wiring
Section titled “D-6: No CDK code changes needed in apps/Root/r53-zones.ts beyond the ArdamailsZone wiring”Original spec (Task 3): “Update apps/Root/r53-zones.ts to import from instances/Root/dns.ts instead of using inline literals (where the declarative pattern dictates — this may be minimal in Phase 2 if the existing app file doesn’t have many literals to extract; do not refactor beyond what’s needed to make instances/Root/dns.ts the source of truth for the new ardamails.com zone).”
Implemented: the update was indeed minimal — only the new ardamails.com zone name comes from instances/Root/dns.ts. The four arda.cards family zone literals continue to come from their pre-existing source (platform/ari-configuration.ts).
Reason: see alternatives.md A-8. Pulling the family zone literals up into instances/Root/dns.ts would have been a refactor with its own surface area, unrelated to Phase 2’s actual scope. The minimal pattern still establishes instances/Root/dns.ts as the rev1 declarative-config home; future phases follow the pattern in their own instance-group directories.
D-7: No infrastructure-side script changes needed for the import detour
Section titled “D-7: No infrastructure-side script changes needed for the import detour”Original spec: didn’t anticipate the import detour, so no script changes were planned beyond deploy-root.sh line-56 path update.
Implemented: no changes were needed to deploy-root.sh or any other script to accommodate the import. The two-phase deploy was a one-time operator-driven choreography against the live stack; subsequent deploys (Phase 3, Phase 4, future phases) use the standard cdk deploy flow because the zone is now CDK/CFN-managed.
Reason: the import is a one-time state transition. After the IMPORT change-set executed, the live stack’s resource graph included ArdamailsZone1DCDDC15; the second cdk deploy saw a normal additive-Output diff and applied it cleanly. No CI / script / runbook changes carry forward.
Acceptance state at end of Phase 2 implementation
Section titled “Acceptance state at end of Phase 2 implementation”| Criterion | State | Notes |
|---|---|---|
T-I1 — folder rename apps/rootConfiguration/ → apps/Root/ | Complete | git mv preserved history; no in-repo imports needed updating |
T-I2 — class rename RootConfigurationStack → RootDnsStack + CFN-name preservation comment | Complete | Inline comment // CFN stack name MUST remain "RootConfiguration"... above the constructor call; V-IAC-003 grep test passes |
T-I3 — instances/Root/dns.ts declarative configuration | Complete | Source of truth for ardamails.com zone name and expectedExports keys |
T-I4 — ardamails.com PublicHostedZone + export | Complete via import (D-1) | Zone Z0721066239FWCD47EJDX adopted; arda-ardamails-zone export resolves to the live ID |
T-I5 — deploy-root.sh line-56 path update | Complete | Path-only edit; no other script change |
T-I6 — phases.md patch + DQ-R1-006 | Complete | Phase 2 deliverables row updated; Phase 2 exit criterion narrowed; Phase 3 row extended; DQ-R1-006 recorded |
| T-I7 — CHANGELOG entries | Complete | Documentation [0.30.0] + Infrastructure [2.29.0] |
| V-ROOT-001 strict-match assertion | Added (D-2) | New unit test in root-dns-stack.test.ts |
V-ROOT-002 dig against arda.ardamails.com | Deferred to Phase 3 (D-3) | See skipped.md SK-8 |
Live deploy to Admin-Alpha1 | Complete | Two-phase: IMPORT change-set then cdk deploy; final cdk diff reports zero differences |
DQ-R1-008 recorded | Complete (D-1, D-4) | Round R1-Phase2; full section + summary table |
What this enables for downstream phases
Section titled “What this enables for downstream phases”| Capability | Phase 2 deliverable that enables it | Consumer |
|---|---|---|
Phase 3 / Phase 4 child stacks can write NS records into ardamails.com | arda-ardamails-zone CFN export + AllowCreatingNSRecordsRole IAM role (unchanged) | Phase 3 (Corporate WriteNSRecordsToUpstreamDns), Phase 4 (per-partition WriteNSRecordsToUpstreamDns) |
| Address Root-instance-group DNS configuration declaratively | instances/Root/dns.ts — pattern home for ROOT_ZONE_NAMES and expectedExports | Phase 3 / Phase 4 follow the convention in their own instance-group directories |
| Adopt-vs-create pattern for AWS resources that pre-date their CDK declaration | DQ-R1-008 formal entry + learnings.md L-1..L-5 + strict-equality test pattern in root-dns-stack.test.ts | Any future phase that adopts a pre-existing AWS resource (rare; documented case-by-case) |
RootDnsStack class name aligned with its concern | Renamed class + inline CFN-name preservation comment + V-IAC-003 grep test | Future Root-instance-group additions (no plans on the horizon) |
References
Section titled “References”changelog.md— what landed.learnings.md— what we learned.alternatives.md— options weighed.suggestions.md— forward-looking improvements.skipped.md— what was deliberately skipped.../specification.md— the original contract.../../decision-log.md— decisions includingDQ-R1-006andDQ-R1-008.../../phases.md— Phase 2 deliverables and exit criteria as patched.
Copyright: © Arda Systems 2025-2026, All rights reserved