Skip to content

Phase 3 -- Corporate Updates -- Analysis

Current-state assessment of the Corporate instance group (none yet exists), the gap to the Phase 3 target, and the prioritized recommendations for closing it.

The Phase 3 target is described in specification.md (the task contract); the requirements that Phase 3 must satisfy are listed in requirements.md. The downstream contracts Phase 3 produces are catalogued in exports.md. The execution plan is in plan/task-plan.md.

Phase 3 stands up the Corporate instance group with its first asset (the Free Kanban Tool) using the Revision-1 declarative pattern established in Phases 1 and 2. The work is a modification project: it adds new IaC artifacts on top of the foundations Phases 1 and 2 already deployed — the typed external references in platform/, the renamed Root app on RootDnsStack, the live ardamails.com hosted zone, and the cross-account NS-delegation primitives.

The gaps fall into five categories:

  1. Corporate IaC tree absent. No apps/Corporate/, no instances/Corporate/, no stacks/corporate/ directories yet. Phase 3 creates the full subtree.
  2. Postmark thin-wrapper constructs absent. No platform/constructs/ directory exists yet; the Postmark server / sending-domain wrappers (the IaC-side analogue of L1 protocol proxies) are introduced in this phase.
  3. Generic DNS xgress constructs absent. route-53-hosted-zone.ts is arda.cards-shaped and needs generalizing into dns-zone.ts. A second xgress construct, dns-email-records.ts, is added to publish a generic sending sub-domain’s DKIM TXT and Return-Path CNAME records (inputs supplied by the PostmarkSendingDomain thin-wrapper).
  4. Operator orchestration absent. No tools/corporate-cli.ts — the two-phase Phase A (Postmark) / Phase B (cdk deploy) orchestrator must be authored. The Phase 1 retirement of the HUMAN-STEPS.md parser gate (per DQ-R1-004) means this orchestrator does not consume any human-steps document; its preconditions are checked at Phase A entry instead (per DQ-R1-016’s conflict-check).
  5. Drift-detection coverage absent. Phase 1 added external-resources-drift.yml; Phase 3 adds a Corporate-scoped drift workflow that exercises the live Free Kanban Tool surfaces (Postmark server reachability, DNS records present, 1Password item resolves).

Phase 3 is deploy-order-dependent on Phase 2 (Phase 2’s arda-ardamails-zone export is the parent zone Phase 3 writes its NS-delegation record into via WriteNSRecordsToUpstreamDns). It is independent of Phase 4; Phases 3 and 4 may proceed in either order or in parallel after Phase 2 lands.

The Phase 3 deploy lands real AWS resources to the production Root account (Admin-Alpha1 profile, same platformRoot AWS account that hosts RootConfiguration). All resource-creating tasks below carry the AWS-impact: Resource-touching classification per the AWS-impact discipline in the project-level CLAUDE.md. Each gap is tagged with its expected AWS impact in the comparison tables.

Note on what becomes “known to Postmark”

Section titled “Note on what becomes “known to Postmark””

Phase 3’s domain registration with Postmark happens through two distinct Postmark-side entities, both created via the Postmark Account API at CLI Phase A (no UI clicks for registration):

  • Sender Signature (sending-domain entry) for the parent Corporate zone arda.ardamails.com. This is the entity Postmark signs DKIM under and the entity the operator (or the Account API’s verifyDkim / verifyReturnPath calls) marks as verified. Sub-domains like freekanban.arda.ardamails.com inherit DKIM from this parent (per DQ-R1-009). One Sender Signature for the entire Corporate group.
  • Server for each Corporate consumer that needs an isolated sending workspace and per-consumer credentials. The Free Kanban Tool gets one server in PostmarkProd; future Corporate consumers (HubSpot, marketing) get their own.

The root ardamails.com zone is not registered with Postmark because nothing sends from ardamails.com apex. Apex-level SPF / DMARC on ardamails.com is out of scope of this project per the project-level CLAUDE.md (“revisit if org-wide DMARC reporting becomes a requirement”). This was not a Phase 2 gap — Phase 2 only owned the zone, not its content; the Corporate-zone records belong to Phase 3, and the apex defense-in-depth records belong to a future hardening project.

The Free Kanban Tool’s leaf sub-domain (freekanban.arda.ardamails.com) does not get its own Postmark Sender Signature — it inherits DKIM from arda.ardamails.com (per DQ-R1-009).

Current-state vs. Phase 3 target — Comparison

Section titled “Current-state vs. Phase 3 target — Comparison”

Category 1: Significant Gaps (Resources Not in Place)

Section titled “Category 1: Significant Gaps (Resources Not in Place)”

New IaC artifacts and AWS / Postmark / 1Password resources Phase 3 introduces.

#GapCurrent statePhase 3 targetAWS impact
G-1Postmark thin-wrapper constructsNo platform/constructs/ directory.New platform/constructs/postmark/server.ts (PostmarkServer) and sending-domain.ts (PostmarkSendingDomain). Construct-line shape; external surface stable across the interim Phase-A and the future Custom-Resource Lambda mechanism.1Synth-only
G-2Generic dns-zone.ts xgress constructconstructs/xgress/route-53-hosted-zone.ts is arda.cards-defaulted (its overrideDomainName defaults to arda.cards).constructs/xgress/dns-zone.ts — generalized hosted-zone construct with no domain default; existing Route53HostedZone callers continue to work or are migrated.Synth-only
G-3Generic dns-email-records.ts xgress constructNo email-records construct on the active branches.New constructs/xgress/dns-email-records.ts — emits one DKIM TXT and one Return-Path CNAME for any sending sub-domain. Props-only inputs sourced from PostmarkSendingDomain.Built.2Synth-only
G-4arda.ardamails.com hosted zoneNo CDK-managed zone. The parent ardamails.com zone exists (Phase 2, imported); no arda. sub-zone records exist yet.New r53.PublicHostedZone(zoneName: "arda.ardamails.com") in CorporateMailDns stack via DnsZone. Exported as arda-corporate-mail-zone. RemovalPolicy.RETAIN.Resource-touching
G-5NS-delegation record for arda in ardamails.comNo arda. NS record set in the parent zone.CorporateMailDns stack instantiates WriteNSRecordsToUpstreamDns with subdomain: "arda" and nameServers from the new zone’s hostedZoneNameServers. The CR Lambda assumes AllowCreatingNSRecordsRole (Phase 2 export) and writes the record into ardamails.com. See DQ-R1-006.Resource-touching
G-6SPF and DMARC at arda.ardamails.comNo SPF / DMARC records in any Corporate zone.TXT records: SPF v=spf1 include:spf.mtasv.net ~all at apex; DMARC p=quarantine; sp=quarantine; rua=mailto:dmarc-reports@arda.cards at _dmarc.arda.ardamails.com (relaxed alignment, per DQ-R1-015). Apex SPF / DMARC on the root ardamails.com is out of project scope.3Resource-touching
G-7Postmark Sender Signature for arda.ardamails.comNo Sender Signature exists in PostmarkProd for arda.ardamails.com.Phase A of the Corporate CLI creates the Sender Signature via the Postmark Account API using the PostmarkSendingDomain thin-wrapper (G-1). Postmark issues a DKIM selector + DKIM key (public) + Return-Path target; these are written to cdk.context.json for Phase B to read. Verification (DKIM, Return-Path) is API-driven (verifyDkim / verifyReturnPath); no Postmark Console click-through.Resource-touching*
G-8Free Kanban Tool sending-domain recordsNo freekanban.arda.ardamails.com records exist.FreeKanbanToolMailDns stack instantiates DnsEmailRecords against the Corporate zone with the Postmark-issued DKIM and Return-Path values from G-7. Two records: <selector>._domainkey.freekanban.arda.ardamails.com (DKIM TXT) and pm-bounces.freekanban.arda.ardamails.com (Return-Path CNAME). DKIM is inherited from the parent Sender Signature (arda.ardamails.com) per DQ-R1-009.Resource-touching
G-9Free Kanban Tool Postmark serverNo Postmark server exists for Free Kanban in any account.A new server provisioned in PostmarkProd by Phase A of the Corporate CLI (idempotent via list-then-create). Server-token captured in memory and written to 1Password (G-10); public values written to cdk.context.json. Token-write ordering follows DQ-R1-013 (in-memory buffer + retries; fail loud).Resource-touching*
G-10Free-Kanban-Generator-Postmark-Server 1P itemVault Arda-CorporateOAM exists (provisioned 2026-05-05); item does not.Phase A of the Corporate CLI creates the item with credential field set to the Postmark server token. Canonical reference: op://Arda-CorporateOAM/Free-Kanban-Generator-Postmark-Server/credential (per DQ-R1-007).Resource-touching*
G-11CorporateMailDns and FreeKanbanToolMailDns stacksNo stacks/corporate/ directory.stacks/corporate/corporate-mail-dns.ts and stacks/corporate/free-kanban-tool-mail-dns.ts. Composition: CorporateMailDns owns the zone + SPF + DMARC + NS-write; FreeKanbanToolMailDns composes DnsEmailRecords + PostmarkServer. The stacks pass Built values between them; no inter-construct dependency.Synth-only
G-12apps/Corporate/index.ts + tools/cdk-corporate.tsNo apps/Corporate/ directory.apps/Corporate/index.ts exports the reusable CorporateApp class (no side effects at module load). tools/cdk-corporate.ts is the entry script: calls new cdk.App() and instantiates CorporateMailDns and FreeKanbanToolMailDns with names + configuration sourced from instances/Corporate/corporate.ts.Synth-only
G-13instances/Corporate/free-kanban-tool.tsNo instances/Corporate/ directory.Declarative configuration: Postmark account ref (from platform/postmark-service.ts), sending sub-domain (freekanban.arda.ardamails.com), 1Password item ref for the server token (from platform/one-password.ts, reintroduced in Phase 3 — see G-14), plan attributes (per Free Kanban Tool’s chosen Postmark plan tier).Synth-only
G-14FREE_KANBAN_POSTMARK_ITEM typed referenceRemoved from platform/one-password.ts in Phase 1 per DQ-R1-007.Reintroduced under platform/one-password.ts with the new vault (Arda-CorporateOAM), title (Free-Kanban-Generator-Postmark-Server), and field (credential). Drift surface (tools/drift-check.ts) is extended to assert the item resolves.Synth-only
G-15tools/corporate-cli.tsNo corporate-cli.ts in tools/.New two-phase orchestrator: Phase A imperative (Postmark + 1P + cdk.context.json), Phase B cdk deploy apps/Corporate/. Phase A entry runs a conflict-check (per DQ-R1-016).4None
G-16Drift-detection workflow for CorporateOnly Phase 1’s external-resources-drift.yml exists.New corporate-drift.yml (per DQ-R1-012): monthly schedule + manual trigger; data-driven over instances/Corporate/; auto-issue on failure.5None
G-17arda reserved at ardamails.com levelplatform/ari-configuration.ts does not yet reserve arda at the ardamails.com level.Reserved-words list extended to prevent future tenant slugs from colliding with the Corporate zone name (per architecture-overview § 6.5).Synth-only
G-18Operator companion (domain verification)Stub at 3-corporate-updates/operator-domain-verification-checklist.md (Phase 1 byproduct).Expanded at implementation time (just-in-time, per user direction): full operator companion to the spec for any step that lacks an automated path. Verification (DKIM, Return-Path) is API-driven from the CLI by default; the operator companion covers only steps that have no API equivalent (sandbox-to-live approval if applicable, end-to-end smoke send). Authoring is part of implementation, not Pass 2 docs.None
G-19Phase 3 documentation pagesNo Corporate-related pages in current-system/.New pages distributed by content type per the documentation tree’s structure: OAM (Postmark service overview for Corporate, Corporate drift notes, Free Kanban Tool service page) → current-system/oam/; Runtime architecture (Corporate Resource Group structure, Free Kanban Tool component placement) → current-system/runtime/. No new functional or data-model pages (Phase 3 is all IaC). The current-system retrofit at project completion reconciles new pages with the broader trees.None
G-20dmarc-reports@arda.cards Google Workspace mailboxMailbox does not exist.Operator action (out of IaC): provision dmarc-reports@arda.cards in Arda’s Google Workspace before Phase B deploy (per DQ-R1-015). Required because the DMARC record G-6 references this address as the aggregate-report destination. Captured in the operator companion (G-18) at implementation time.None

* G-7, G-9, and G-10 are flagged Resource-touching because they create non-AWS state (a Postmark Sender Signature, a Postmark server, a 1Password vault item) that the cdk deploy workflow does not roll back. Recovery for these is documented under phases.md § Phase 3 — Recovery / partial-failure handling.

Category 2: Generalizations / Refactors of Existing Code

Section titled “Category 2: Generalizations / Refactors of Existing Code”
#RefactorCurrent statePhase 3 targetNotes
R-1Route53HostedZoneDnsZoneconstructs/xgress/route-53-hosted-zone.ts defaults overrideDomainName to arda.cards. Tightly bound to the arda.cards zone family.Generalized as constructs/xgress/dns-zone.ts with no domain default. Public shape adjusted for any registrable domain. Existing callers either continue to call the old construct (kept as a deprecation alias) or are migrated.Decision needed on coexistence vs. one-shot rename (open question OQ-3 below).
R-2Authoring of dns-email-records.ts (no existing equivalent)No email-records construct on the active branches; nothing under constructs/email/ or constructs/xgress/ publishes DKIM / Return-Path records today.Generic constructs/xgress/dns-email-records.ts that reads its inputs from props only (Built values from the PostmarkSendingDomain thin-wrapper, ultimately sourced from cdk.context.json). Public values stay in cdk.context.json (committed to the repo per DQ-R1-014); private values (the Postmark server token) stay in 1Password (op://Arda-CorporateOAM/...). Neither flows through environment variables, file artifacts in the deploy pipeline, or CI context.Tracked in this category because it is a sibling of R-1 (the construct authored alongside the rename); both land in the same xgress folder and are exercised by the Free Kanban Tool stack. The construct’s contract is end-to-end props-driven so its behavior is invariant under the eventual Custom-Resource migration.
R-3Reserved-words list extensionplatform/ari-configuration.ts includes partition-zone reserved slugs (mail, dmarc, postmaster, etc.). No ardamails.com-level reserved list.New constant MAIL_RESERVED_SLUGS (or similar, name TBD) holding arda plus future Corporate zone names. Per partition-zone validation imports the constant where relevant.Additive; no breakage to existing partition-zone validation.
R-4Drift-check surface extensiontools/drift-check.ts checks the three Phase 1 1Password items; does not check the Free Kanban Tool item (since Phase 1 removed the typed reference per DQ-R1-007).Add Free-Kanban-Generator-Postmark-Server (in Arda-CorporateOAM) to ALL_OP_ITEMS once Phase A creates it; tests adjust the expected count from 3 to 4. The Phase 3 drift workflow may also exercise the Postmark server’s reachability through the dedicated Free Kanban Tool drift workflow.Decision on whether the existing external-resources-drift.yml exercises the new item or whether the Corporate workflow does it (overlap question — see OQ-5).

Category 3: Out of Scope of Phase 3 (Scoped to Other Phases)

Section titled “Category 3: Out of Scope of Phase 3 (Scoped to Other Phases)”
#ConcernWhere it belongsReasoning
O-1Per-partition mail sub-zones (prod.ardamails.com, dev.ardamails.com, …)Phase 4 (Runtime Platform Updates).Each Application Runtime partition owns its mail sub-zone; uses the same WriteNSRecordsToUpstreamDns pattern Phase 3 establishes.
O-2Per-partition Postmark account-token + encryption-key Secrets Manager entriesPhase 4.These secrets gate the runtime ShopAccess/Email module’s per-tenant operations (Phase 5b).
O-3Backend ShopAccess/Email module in operationsPhase 5b.Per-tenant server provisioning, sending APIs, webhook handling. Depends on Phase 4 (per-partition secrets) and Phase 5a (common-module minor with new APIs).
O-4Tightening AllowCreatingNSRecordsRole.allowedParentHostedZoneIds from * to specific zone IDsOut of scope of this project entirely.Future security-hardening project. Phase 3 consumes the role as-is.
O-5Migration of the PostmarkServer thin-wrapper internals from “CLI Phase A” to a Custom-Resource LambdaOut of scope of this project; deferred.The construct’s external surface is designed to be identical between the two; the migration is a future change to the construct’s internals only. Migration trigger documented in architecture-overview § 5.3.
O-6Helm release equivalent of the IaC patternsOut of scope (deferred per runtime-design-review.md § 7 Q4).The principle is documented but not exercised in this phase.

Category 4: No Action Needed (Already in Place)

Section titled “Category 4: No Action Needed (Already in Place)”
#Already in placeSource
K-1POSTMARK_PROD_ACCOUNT typed reference (with credentialReference, plan, API base URL, surface freshness metadata)infrastructure/src/main/cdk/platform/postmark-service.ts (Phase 1).
K-2POSTMARK_PROD_ITEM and OAM_VAULT (Arda-SystemsOAM) typed referencesinfrastructure/src/main/cdk/platform/one-password.ts (Phase 1).
K-3Arda-CorporateOAM 1Password vaultProvisioned 2026-05-05 (operator action). Phase 3 creates the Free-Kanban-Generator-Postmark-Server item inside.
K-4ardamails.com hosted zone (deployed in Root account)RootDnsStack (Phase 2). Imported via cdk import per DQ-R1-008. Exported as arda-ardamails-zone. Apex DKIM / SPF / DMARC on the root ardamails.com are intentionally not present — nothing sends from the apex, and apex defense-in-depth records are out of scope of this project per project-level CLAUDE.md.
K-5AllowCreatingNSRecordsRole (Root-account role for cross-account NS writes)Renamed-and-preserved through Phase 2. Exported as arda-allow-create-ns-record-role.
K-6WriteNSRecordsToUpstreamDns construct (Lambda + CR pattern)infrastructure/src/main/cdk/constructs/xgress/write-ns-records-to-upstream-dns.ts. Phase 3 instantiates it from the CorporateMailDns stack.
K-7Drift-check shell + report shapeinfrastructure/tools/drift-check.ts (Phase 1). Phase 3 extends ALL_OP_ITEMS with the new 1P item; the report shape is unchanged.
K-8Drift-workflow shape (issue-on-failure, scheduled cadence, manual trigger)infrastructure/.github/workflows/external-resources-drift.yml (Phase 1). The Corporate workflow follows the same shape.

Standing rule. Every artifact recommended below ships with parallel unit tests (Jest + CDK Template matcher for constructs and stacks; injectable-dependency tests for CLI / drift-tool code) and a verification.md entry with one or more V-CORP-NNN test IDs. The Pass-2 verification.md is the single source for the test catalogue; this section names where tests are needed, not their content.

API-over-GUI rule. Wherever the Postmark Account API offers an endpoint (server create, server list, sending-domain create, sending-domain list, verifyDkim, verifyReturnPath, server-token retrieval), the operator path uses the API via corporate-cli.ts. The Postmark Console is reserved for steps that have no API equivalent (e.g., sandbox-to-live approval gating). The same rule applies to GitHub (gh CLI), 1Password (SDK), and AWS (CDK / aws CLI).

  1. Land the construct generalizations (R-1, R-2) first. They are the building blocks every downstream artifact composes. Tests come with the constructs (Jest + CDK Template matcher, mirroring the Phase-2 pattern in root-dns-stack.test.ts). AWS impact: Synth-only.
  2. Add the Postmark thin-wrappers (G-1) next. Their external surface (Configuration / Built) is the contract every later artifact depends on. Internals follow the interim pattern (read public values from CDK context). The IaC-side analogue of L1 protocol proxies in the application layer. Tests: thin-wrapper-level unit tests with the Postmark client mocked at the HTTP boundary, asserting idempotency (list-then-create) and Built shape.
  3. Add instances/Corporate/free-kanban-tool.ts (G-13) and the Corporate stacks (G-11). The stack composition is value-flowing only — Built from PostmarkServer flows into DnsEmailRecords. No inter-construct dependency. Tests: stack-level CDK Template assertions (zone declared, NS-write CR present, SPF + DMARC TXT records present with the agreed strings, Free Kanban DKIM + Return-Path records present).
  4. Add apps/Corporate/index.ts and tools/cdk-corporate.ts (G-12). Reusable App class plus dedicated entry script. No deploy at this point yet — the App synthesizes against fixture context. Tests: synth-only smoke test (cdk synth via tools/cdk-corporate.ts against a fixture context produces a valid template; CFN stack name is what the spec declares).
  5. Author tools/corporate-cli.ts (G-15). Two-phase shell: Phase A imperative (Postmark API + 1P write + cdk.context.json write); Phase B cdk deploy invocation. Phase A entry includes the conflict-check from DQ-R1-016. Tests: dependency-injectable mock for the Postmark client, the 1Password SDK, and the filesystem; assertions cover idempotent re-run, conflict-check failure path, in-memory token-buffer + retries (DQ-R1-013), and structured-output shape with redaction.
  6. Reintroduce the typed reference and extend drift coverage (G-14, R-4). This is the smallest synth-only change but unblocks the drift workflow. AWS impact: Synth-only on the typed reference; None on the drift-tool extension. Tests: the existing tools/drift-check.test.ts is updated to expect 4 items instead of 3.
  7. Add the reserved-words extension (G-17). Synth-only. Tests: existing ari-configuration.test.ts (or new) asserts the new constant’s contents and that any partition-zone slug-validation that consumes it rejects arda and the future Corporate slugs.
  8. Add the Corporate drift-detection workflow (G-16). AWS impact: None. Tests: workflow YAML lints; the driver script exercises a fixture asset list and produces the same report shape as the Phase 1 drift run.
  9. Run Phase A in PostmarkNonProd first as a smoke test, then in PostmarkProd. The Phase A code path is identical between the two; only the account credential differs. Captured in the operator companion at implementation time (G-18).
  10. Phase B — cdk deploy apps/Corporate/ — creates the Corporate zone, the NS-delegation record in Root’s ardamails.com, the Free Kanban Tool DNS records, and SPF / DMARC. AWS impact: Resource-touching. Surface a cdk diff summary in the PR before deploying; user-confirmation gate.
  11. Post-deploy verification, API-driven by default. The CLI invokes Postmark’s verifyDkim and verifyReturnPath endpoints to mark the Sender Signature verified. dig queries verify DNS propagation. The end-to-end smoke send (live mail to a non-owner address) is the only step that requires a human in the loop, and even there the CLI captures the Authentication-Results header from the bounce-handling endpoint when available. The Postmark Console click-through is reserved for sandbox-to-live approval if the account is still in sandbox at the time of deploy. AWS impact: None on the IaC side; the smoke send produces real live mail.
  12. Phase 3 documentation pages (G-19) and current-system retrofit. Pages are placed by content type: OAM material in current-system/oam/, runtime architecture in current-system/runtime/. No new functional or data-model pages (Phase 3 is all IaC). The current-system retrofit at project completion reconciles new pages with the broader trees; per-page verification.md is link-coverage.
  13. Operator companion (G-18) authored just-in-time during implementation. The companion captures only steps without an API equivalent. Authoring is part of implementation, not Pass 2 docs.
  14. Operator action (G-20) — create dmarc-reports@arda.cards in Arda’s Google Workspace before Phase B deploys. This is a prerequisite to the DMARC record (G-6) being meaningful.

The eight design questions surfaced during Pass 1 were resolved on 2026-05-06; each maps to a DQ-R1-NNN entry in decision-log.md under Round R1-Phase3 (DQ-R1-009..016). The table below preserves the question, options, and rationale as authored during Pass 1; the Decision column is populated from the resolved entries. The “Likely DQ #” column reflects the actual number assigned.

OQLikely DQ #QuestionOptionsRecommendationDecision
OQ-1DQ-R1-009Postmark domain-verification target: when the Free Kanban Tool’s sending sub-domain is brought up, does the operator click “Verify” in the Postmark Console for arda.ardamails.com (the Corporate-zone parent) or freekanban.arda.ardamails.com (the leaf)?(A) Verify each leaf sub-domain individually as it is created. (B) Verify once at the Corporate-zone parent (arda.ardamails.com); leaves inherit DKIM via the parent’s signing key.(B) Parent. Already pre-decided 2026-05-05 during the Phase 1 walkthrough; this entry formalizes the decision and is forward-referenced from the operator-domain-verification-checklist stub.Agreed
OQ-2DQ-R1-010Locus of Corporate’s NS-delegation write: Phase 3’s Corporate stack and Phase 2’s Root stack both deploy into the same AWS account (platformRoot) for the foreseeable future. WriteNSRecordsToUpstreamDns was designed for cross-account writes (it assumes a role across accounts). Does Corporate still go through the assume-role path, or write directly?(A) Always go through WriteNSRecordsToUpstreamDns and assume the role even when same-account; preserves the pattern uniformly across instance groups. (B) Branch the construct so same-account writes skip the assume-role hop (direct Route53 write). (C) Write the NS record from Root’s stack instead (revisits DQ-R1-006 for this case).(A) Uniform pattern. Cost: one extra STS call per deploy. Benefit: the construct’s behavior is invariant under the future Corporate-account migration (architecture-overview § 6.4). DQ-R1-006 said child writes upstream — preserve that even when same-account today.Agreed
OQ-3DQ-R1-011route-53-hosted-zone.tsdns-zone.ts migration shape: both names cannot survive long-term, but the existing Route53HostedZone has callers in production stacks.(A) Rename in place: dns-zone.ts replaces route-53-hosted-zone.ts; existing callers updated in the same PR. (B) Coexist for a transition window: dns-zone.ts is added; route-53-hosted-zone.ts becomes a thin re-export with a deprecation notice; followup PR removes the old name. (C) Leave the old construct, add the new one; the old continues to serve arda.cards-family callers.(A) Rename in place. The repo uses validateProps discipline so missed callers fail fast at synth, and the change is contained in a single PR. Coexistence (B) and side-by-side (C) accumulate construct sprawl.Agreed
OQ-4DQ-R1-012Corporate drift-workflow filename and scope: Phase 1 used external-resources-drift.yml. The Phase 3 phase plan currently names this workflow corporate-free-kanban-tool.yml (asset-specific). Going forward the Corporate group will hold more assets (HubSpot, marketing-site).(A) corporate-free-kanban-tool.yml (asset-specific, one workflow per asset). (B) corporate-drift.yml (instance-group-scoped, one workflow that exercises every Corporate asset). (C) <asset>-drift.yml per asset with a shared tools/corporate-drift.ts driver.(B) corporate-drift.yml — mirrors the Phase 1 naming style and keeps the workflow count proportional to instance groups, not assets. Driver script exercises every asset listed in instances/Corporate/.Agreed
OQ-5DQ-R1-013Phase A failure ordering for the Postmark server token: Phase A creates a Postmark server (which yields the token), writes the token to 1Password, and writes public values to cdk.context.json. If the 1P write fails after server creation, the token is unrecoverable from Postmark’s side (it is shown once at creation).(A) Write to 1P first, then cdk.context.json; rollback (delete the Postmark server) on 1P-write failure. (B) Persist the token to a process-local secret-handling buffer immediately on receipt; write to 1P with retries; fail loud on permanent 1P-write failure with the buffer’s redacted summary; manual operator action to recover.(B) Buffer + retries. The token is in memory only; cdk.context.json write is delayed until the 1P write succeeds. Postmark’s delete-server API exists but is destructive; (A)‘s rollback is a worse failure mode than (B)‘s alarm-and-investigate.Agreed
OQ-6DQ-R1-014cdk.context.json commit policy for Phase A’s outputs: Phase A writes postmark.free-kanban.serverId, .dkimSelector, .dkimKey, .returnPathTarget into cdk.context.json. Public values; safe to commit; published in DNS anyway.(A) Commit cdk.context.json — standard CDK practice; deterministic re-synth on a fresh checkout. (B) Local-only with .gitignore; CI re-runs Phase A to repopulate. (C) Commit, but exclude the postmark.* keys via a custom serializer.(A) Commit. The values are public, the standard CDK convention is to commit context, and a fresh checkout’s cdk synth should not require re-running Phase A.Agreed
OQ-7DQ-R1-015DMARC reporting mailbox (rua / ruf) for _dmarc.arda.ardamails.com: the policy at the Corporate zone’s _dmarc record needs an rua=mailto:... (and optionally ruf=mailto:...) address. Initial monitoring policy is decided (p=quarantine; sp=quarantine); the reporting address is not.(A) dmarc-reports@arda.cards (existing arda.cards-family inbox). (B) A new dmarc-reports@ardamails.com mailbox, hosted independently. (C) No rua / ruf in v1; revisit when DMARC reporting becomes a routine input.(A) dmarc-reports@arda.cards — least operational cost; mailbox already exists; reports are aggregated, not actioned in real time.Agreed, Operator must set up address in Arda’s Google Workspace.
OQ-8DQ-R1-016Reserved-name registry scope at arda.ardamails.com: Phase 3 reserves arda at the ardamails.com level. Should freekanban (and future Corporate sub-domain slugs) be reserved at the arda.ardamails.com level too, or is a Corporate-level registry documentation-only?(A) Register freekanban (and future slugs) in the constants; partition validators import them. (B) Documentation-only registry at arda.ardamails.com; partition validators do not import; collisions are caught by the Corporate CLI on Phase A entry.(B) Documentation-only. Partition tenants do not directly create slugs at the arda.ardamails.com level; the Corporate CLI is the only writer and can enforce the registry locally. Avoids cross-instance-group import coupling.(B). Additional requirement for the CLI tool to check pre-existing domains when creating one to avoid conflicts.
  1. PostmarkServer.Built exposes serverId (and DNS-publishable metadata) only — the Postmark Server API token is not on Built; it is persisted by Phase A directly to 1Password. PostmarkSendingDomain.Built exposes dkimSelector, dkimKey (public), and returnPathTarget. Both wrappers read public values from cdk.context.json in the interim mechanism; the same public surface is populated via the CR Lambda in the target mechanism (per architecture-overview § 5.3).

  2. Inputs: hosted zone, DKIM selector, DKIM key, Return-Path target, optional TTL (default 300s). Emits one DKIM TXT at <selector>._domainkey.<sub>.<zone> and one Return-Path CNAME at pm-bounces.<sub>.<zone>pm.mtasv.net. No env-var bridge; no file-artifact channel — props only.

  3. The dmarc-reports@arda.cards mailbox is provisioned by the operator pre-deploy (see G-20). The policy hardens to p=reject (and possibly to strict alignment) after Free Kanban Tool sending has bedded in — a post-Phase-3 operator task.

  4. Idempotent (list-then-create for Sender Signature, server, 1P item; cdk.context.json write follows the 1P write). Source comments name the phases explicitly so the eventual Custom-Resource migration is unambiguous.

  5. Asserts each asset’s Postmark server reachable, DNS records resolve, 1P item reachable. The driver enumerates instances/Corporate/ so future assets are picked up automatically. Issue label set mirrors Phase 1’s external-resources-drift.yml pattern.