Skip to content

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.

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:

  1. IMPORT change-set with a hand-built deployed-state + ArdamailsZone resource only template (no Outputs added, no other resource modifications). CFN reported Action: Import, Replacement: null, Scope: [].
  2. Normal cdk deploy with the full synthesized template, adding the ardamailsZone Output and reconciling CDKMetadata.

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’s phases.md patch and the Phase-2-vs-Phase-3 ownership boundaries.
  • DQ-R1-008 (adopt vs create the existing ardamails.com zone) 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:

Terminal window
# Phase 1: IMPORT change-set adopts the live zone
aws cloudformation get-template --stack-name RootConfiguration --template-stage Original \
> scratch/deployed.json
# build importv2 = deployed + ArdamailsZone resource only
jq '.Resources.ArdamailsZone1DCDDC15 = $newRes' \
--argjson newRes "$(jq '.Resources.ArdamailsZone1DCDDC15' scratch/synth.json)" \
scratch/deployed.json > scratch/importv2.json
aws 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.json
aws 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 CDKMetadata
cd /Users/jmp/code/arda/projects/email-integration-worktrees/phase-2/infrastructure
GITHUB_TOKEN=$(gh auth token) npm install
npx cdk deploy --app 'npx ts-node src/main/cdk/apps/Root/r53-zones.ts' RootConfiguration --profile Admin-Alpha1
# Verify zero-diff after the deploy
npx cdk diff --app 'npx ts-node src/main/cdk/apps/Root/r53-zones.ts' RootConfiguration --profile Admin-Alpha1

Reason: 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”
CriterionStateNotes
T-I1 — folder rename apps/rootConfiguration/apps/Root/Completegit mv preserved history; no in-repo imports needed updating
T-I2 — class rename RootConfigurationStackRootDnsStack + CFN-name preservation commentCompleteInline comment // CFN stack name MUST remain "RootConfiguration"... above the constructor call; V-IAC-003 grep test passes
T-I3 — instances/Root/dns.ts declarative configurationCompleteSource of truth for ardamails.com zone name and expectedExports keys
T-I4 — ardamails.com PublicHostedZone + exportComplete via import (D-1)Zone Z0721066239FWCD47EJDX adopted; arda-ardamails-zone export resolves to the live ID
T-I5 — deploy-root.sh line-56 path updateCompletePath-only edit; no other script change
T-I6 — phases.md patch + DQ-R1-006CompletePhase 2 deliverables row updated; Phase 2 exit criterion narrowed; Phase 3 row extended; DQ-R1-006 recorded
T-I7 — CHANGELOG entriesCompleteDocumentation [0.30.0] + Infrastructure [2.29.0]
V-ROOT-001 strict-match assertionAdded (D-2)New unit test in root-dns-stack.test.ts
V-ROOT-002 dig against arda.ardamails.comDeferred to Phase 3 (D-3)See skipped.md SK-8
Live deploy to Admin-Alpha1CompleteTwo-phase: IMPORT change-set then cdk deploy; final cdk diff reports zero differences
DQ-R1-008 recordedComplete (D-1, D-4)Round R1-Phase2; full section + summary table
CapabilityPhase 2 deliverable that enables itConsumer
Phase 3 / Phase 4 child stacks can write NS records into ardamails.comarda-ardamails-zone CFN export + AllowCreatingNSRecordsRole IAM role (unchanged)Phase 3 (Corporate WriteNSRecordsToUpstreamDns), Phase 4 (per-partition WriteNSRecordsToUpstreamDns)
Address Root-instance-group DNS configuration declarativelyinstances/Root/dns.ts — pattern home for ROOT_ZONE_NAMES and expectedExportsPhase 3 / Phase 4 follow the convention in their own instance-group directories
Adopt-vs-create pattern for AWS resources that pre-date their CDK declarationDQ-R1-008 formal entry + learnings.md L-1..L-5 + strict-equality test pattern in root-dns-stack.test.tsAny future phase that adopts a pre-existing AWS resource (rare; documented case-by-case)
RootDnsStack class name aligned with its concernRenamed class + inline CFN-name preservation comment + V-IAC-003 grep testFuture Root-instance-group additions (no plans on the horizon)