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.
Executive Summary
Section titled “Executive Summary”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:
- Corporate IaC tree absent. No
apps/Corporate/, noinstances/Corporate/, nostacks/corporate/directories yet. Phase 3 creates the full subtree. - 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. - Generic DNS xgress constructs absent.
route-53-hosted-zone.tsisarda.cards-shaped and needs generalizing intodns-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 thePostmarkSendingDomainthin-wrapper). - 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 theHUMAN-STEPS.mdparser gate (perDQ-R1-004) means this orchestrator does not consume any human-steps document; its preconditions are checked at Phase A entry instead (perDQ-R1-016’s conflict-check). - 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’sverifyDkim/verifyReturnPathcalls) marks as verified. Sub-domains likefreekanban.arda.ardamails.cominherit DKIM from this parent (perDQ-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.
| # | Gap | Current state | Phase 3 target | AWS impact |
|---|---|---|---|---|
| G-1 | Postmark thin-wrapper constructs | No 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.1 | Synth-only |
| G-2 | Generic dns-zone.ts xgress construct | constructs/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-3 | Generic dns-email-records.ts xgress construct | No 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.2 | Synth-only |
| G-4 | arda.ardamails.com hosted zone | No 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-5 | NS-delegation record for arda in ardamails.com | No 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-6 | SPF and DMARC at arda.ardamails.com | No 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.3 | Resource-touching |
| G-7 | Postmark Sender Signature for arda.ardamails.com | No 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-8 | Free Kanban Tool sending-domain records | No 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-9 | Free Kanban Tool Postmark server | No 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-10 | Free-Kanban-Generator-Postmark-Server 1P item | Vault 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-11 | CorporateMailDns and FreeKanbanToolMailDns stacks | No 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-12 | apps/Corporate/index.ts + tools/cdk-corporate.ts | No 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-13 | instances/Corporate/free-kanban-tool.ts | No 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-14 | FREE_KANBAN_POSTMARK_ITEM typed reference | Removed 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-15 | tools/corporate-cli.ts | No 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).4 | None |
| G-16 | Drift-detection workflow for Corporate | Only 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.5 | None |
| G-17 | arda reserved at ardamails.com level | platform/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-18 | Operator 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-19 | Phase 3 documentation pages | No 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-20 | dmarc-reports@arda.cards Google Workspace mailbox | Mailbox 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”| # | Refactor | Current state | Phase 3 target | Notes |
|---|---|---|---|---|
| R-1 | Route53HostedZone → DnsZone | constructs/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-2 | Authoring 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-3 | Reserved-words list extension | platform/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-4 | Drift-check surface extension | tools/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)”| # | Concern | Where it belongs | Reasoning |
|---|---|---|---|
| O-1 | Per-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-2 | Per-partition Postmark account-token + encryption-key Secrets Manager entries | Phase 4. | These secrets gate the runtime ShopAccess/Email module’s per-tenant operations (Phase 5b). |
| O-3 | Backend ShopAccess/Email module in operations | Phase 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-4 | Tightening AllowCreatingNSRecordsRole.allowedParentHostedZoneIds from * to specific zone IDs | Out of scope of this project entirely. | Future security-hardening project. Phase 3 consumes the role as-is. |
| O-5 | Migration of the PostmarkServer thin-wrapper internals from “CLI Phase A” to a Custom-Resource Lambda | Out 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-6 | Helm release equivalent of the IaC patterns | Out 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 place | Source |
|---|---|---|
| K-1 | POSTMARK_PROD_ACCOUNT typed reference (with credentialReference, plan, API base URL, surface freshness metadata) | infrastructure/src/main/cdk/platform/postmark-service.ts (Phase 1). |
| K-2 | POSTMARK_PROD_ITEM and OAM_VAULT (Arda-SystemsOAM) typed references | infrastructure/src/main/cdk/platform/one-password.ts (Phase 1). |
| K-3 | Arda-CorporateOAM 1Password vault | Provisioned 2026-05-05 (operator action). Phase 3 creates the Free-Kanban-Generator-Postmark-Server item inside. |
| K-4 | ardamails.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-5 | AllowCreatingNSRecordsRole (Root-account role for cross-account NS writes) | Renamed-and-preserved through Phase 2. Exported as arda-allow-create-ns-record-role. |
| K-6 | WriteNSRecordsToUpstreamDns 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-7 | Drift-check shell + report shape | infrastructure/tools/drift-check.ts (Phase 1). Phase 3 extends ALL_OP_ITEMS with the new 1P item; the report shape is unchanged. |
| K-8 | Drift-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. |
Prioritized Recommendations
Section titled “Prioritized Recommendations”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.mdentry with one or moreV-CORP-NNNtest IDs. The Pass-2verification.mdis 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 viacorporate-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 (ghCLI), 1Password (SDK), and AWS (CDK /awsCLI).
- 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. - 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) andBuiltshape. - Add
instances/Corporate/free-kanban-tool.ts(G-13) and the Corporate stacks (G-11). The stack composition is value-flowing only —BuiltfromPostmarkServerflows intoDnsEmailRecords. 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). - Add
apps/Corporate/index.tsandtools/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 synthviatools/cdk-corporate.tsagainst a fixture context produces a valid template; CFN stack name is what the spec declares). - Author
tools/corporate-cli.ts(G-15). Two-phase shell: Phase A imperative (Postmark API + 1P write +cdk.context.jsonwrite); Phase Bcdk deployinvocation. Phase A entry includes the conflict-check fromDQ-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. - 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.tsis updated to expect 4 items instead of 3. - 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 rejectsardaand the future Corporate slugs. - 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.
- 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).
- Phase B —
cdk deploy apps/Corporate/— creates the Corporate zone, the NS-delegation record in Root’sardamails.com, the Free Kanban Tool DNS records, and SPF / DMARC. AWS impact: Resource-touching. Surface acdk diffsummary in the PR before deploying; user-confirmation gate. - Post-deploy verification, API-driven by default. The CLI invokes Postmark’s
verifyDkimandverifyReturnPathendpoints to mark the Sender Signature verified.digqueries 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 theAuthentication-Resultsheader 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. - Phase 3 documentation pages (G-19) and current-system retrofit. Pages are placed by content type: OAM material in
current-system/oam/, runtime architecture incurrent-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-pageverification.mdis link-coverage. - 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.
- Operator action (G-20) — create
dmarc-reports@arda.cardsin Arda’s Google Workspace before Phase B deploys. This is a prerequisite to the DMARC record (G-6) being meaningful.
Open Questions (resolved)
Section titled “Open Questions (resolved)”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.
| OQ | Likely DQ # | Question | Options | Recommendation | Decision |
|---|---|---|---|---|---|
| OQ-1 | DQ-R1-009 | Postmark 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-2 | DQ-R1-010 | Locus 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-3 | DQ-R1-011 | route-53-hosted-zone.ts → dns-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-4 | DQ-R1-012 | Corporate 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-5 | DQ-R1-013 | Phase 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-6 | DQ-R1-014 | cdk.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-7 | DQ-R1-015 | DMARC 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-8 | DQ-R1-016 | Reserved-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. |
References
Section titled “References”requirements.md— numbered requirements (Pass 2).specification.md— task contract with STOP points (Pass 2).verification.md— test catalogue (Pass 2).exports.md— cross-phase contracts produced (Pass 2).operator-domain-verification-checklist.md— stub authored on 2026-05-05 in Phase 1; expanded just-in-time during implementation per user direction.../goal.md— project intent.../architecture-overview.md— system structure (§ 5 IaC code hierarchy, § 6.3 Corporate, § 6.5 Reserved-name discipline).../cross-cutting-design.md— threats, secrets, OAM (§ 2.5 Free Kanban Tool blast radius, § 4.1 secret inventory).../phases.md— full phase plan (§ Phase 3 — Corporate Updates).../decision-log.md— existing decisions includingDQ-R1-006,DQ-R1-007,DQ-R1-008; newDQ-R1-009..016land here after open-question resolution.../1-external-resources/specification.md— Phase 1 contract (foundation for Phase 3’s external references).../2-root-updates/specification.md— Phase 2 contract (ardamails.comzone,AllowCreatingNSRecordsRole).
Footnotes
Section titled “Footnotes”-
PostmarkServer.BuiltexposesserverId(and DNS-publishable metadata) only — the Postmark Server API token is not onBuilt; it is persisted by Phase A directly to 1Password.PostmarkSendingDomain.BuiltexposesdkimSelector,dkimKey(public), andreturnPathTarget. Both wrappers read public values fromcdk.context.jsonin the interim mechanism; the same public surface is populated via the CR Lambda in the target mechanism (per architecture-overview § 5.3). ↩ -
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 atpm-bounces.<sub>.<zone>→pm.mtasv.net. No env-var bridge; no file-artifact channel — props only. ↩ -
The
dmarc-reports@arda.cardsmailbox is provisioned by the operator pre-deploy (see G-20). The policy hardens top=reject(and possibly to strict alignment) after Free Kanban Tool sending has bedded in — a post-Phase-3 operator task. ↩ -
Idempotent (list-then-create for Sender Signature, server, 1P item;
cdk.context.jsonwrite follows the 1P write). Source comments name the phases explicitly so the eventual Custom-Resource migration is unambiguous. ↩ -
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’sexternal-resources-drift.ymlpattern. ↩
Copyright: © Arda Systems 2025-2026, All rights reserved