feat: execution protocol, review agents, DNS and SES findings
Build and deploy / build-and-deploy (push) Failing after 6s

This commit is contained in:
Pouya Lajevardi
2026-08-26 09:51:24 -04:00
parent 19f7226661
commit e6abdf42e8
11 changed files with 804 additions and 6 deletions
+249 -4
View File
@@ -101,6 +101,8 @@ without an explicit instruction and a Change Log entry. `[verified 2026-08-25]`
| D13 | Licensure claims | **The site asserts the JD only.** No claim of licensure, call to the bar, or entitlement to practise law. The approved phrasing for the boutique role is **"active litigation exposure"** or **"involvement in litigation and ADR matters"** — never "practice". Pouya's direction, 2026-08-26. **Explicitly interim — see Standing Reminders §12** | Stating LSO licence status; the word "practice" |
| D14 | Fee structure | **Single published rate card, all mediation matters at one rate.** Confirmed by Pouya 2026-08-26; figures in `docs/07-fees.md`. No tribunal-secretary line | Two-tier card (recommended by Claude, declined); ranges; fees on request |
| D15 | Analytics | **Privacy-first and cookieless** (Plausible or Fathom). No cookie banner, nothing to consent to, one line in the privacy policy | GA4; no analytics at all |
| D18 | Intake email | **Two emails per submission** — notification to `info@smlcompany.ca`, and a **confirmation receipt to the inquirer**. Pouya, 2026-08-26. The receipt is what requires SES production access (Q19) | Notification only; no email; no form at all |
| D17 | Working method | **Pouya architects; Claude Code implements and then adversarially reviews its own work.** Six-phase loop encoded in `.claude/commands/build.md`; two independent review agents in `.claude/agents/`; extended thinking on by default. Standing agreement — not restated per prompt | Implement-and-report; single generic reviewer; review only on request |
| D16 | Naming the boutique | **Never named.** Referred to throughout as *a Toronto litigation and ADR boutique*. Pouya's decision, 2026-08-26 | Naming the firm |
## 4. Credential and claim register
@@ -221,7 +223,22 @@ the audience it targets. Revisit at month 1218. `[verified 2026-08-25 — dec
| Intake API | `adr-intake-api`, HTTP API `4tl0m5igkj`, endpoint `https://4tl0m5igkj.execute-api.ca-central-1.amazonaws.com` `[verified 2026-08-26]` |
| Intake Lambda | `adr-intake-handler`, `nodejs24.x` `[verified 2026-08-26]` |
| Intake table | `adr-intake-submissions` (DynamoDB, ca-central-1) `[verified 2026-08-26]` |
| SES identities | Domain `smlcompany.ca`; addresses `info@`, `intake@`, `adr@`. **Sending status unconfirmed — Q18/Q19** `[verified 2026-08-26]` |
| SES identities | Domain `smlcompany.ca` **verified for sending** `[verified 2026-08-26]`; addresses `info@`, `intake@`, `adr@` |
| SES account | **In the sandbox**`ProductionAccessEnabled: false` `[verified 2026-08-26]`. Production access required (Q19) |
| Mail hosting | **Google Workspace** — MX `1 smtp.google.com`; `google._domainkey` present, so Google DKIM is configured `[verified 2026-08-26 — DNS query]` |
| **SPF** | **ABSENT.** `smlcompany.ca` TXT holds only Anthropic and Google site-verification strings. No `v=spf1` record exists `[verified 2026-08-26 — DNS query]` |
| **DMARC** | **ABSENT.** No `_dmarc.smlcompany.ca` record `[verified 2026-08-26 — DNS query]` |
| Required DNS (Namecheap) | TXT `@``v=spf1 include:_spf.google.com include:amazonses.com ~all` · TXT `_dmarc``v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1` `[assumed]` — start at `p=none`, tighten after monitoring |
| SES DKIM — **the set that actually resolves** | `f5puwearz5ek47aiuoskk57ya6ir66ju`, `jdue2r22cxrk27laxlgnjcpby2fq4vxu`, `kznn3cklvwh4gs342rtf5euqymlger53` — all three answer NOERROR at `<token>._domainkey.smlcompany.ca` `[verified 2026-08-26 — DNS]` |
| SES DKIM — **the set that is BROKEN** | `3zsnvsjg3ddi6hjyndjy6jgjoitvhydl`, `jejgp7na3wdpprsanamct4uxxgw2tyqw`, `xpiwyftpodmgpnzfof3ee7t7fzmlmhnh` — listed in the SES console, present in Namecheap, but **NXDOMAIN**. They were entered with the full name in Namecheap's Host field, so the zone doubled the domain: `<token>._domainkey.smlcompany.ca.smlcompany.ca` resolves, the correct name does not `[verified 2026-08-26 — DNS]` |
| SES identity — **`ca-central-1` (the one that matters)** | `Verified: true`, `DkimStatus: SUCCESS`, tokens `kznn…` / `jdue…` / `f5pu…` — the set that resolves. **Healthy. Change nothing** `[verified 2026-08-26]` |
| SES identity — `us-east-1` (unused) | `Verified: false`, `DkimStatus: PENDING`, tokens `3zsn…` / `jejgp…` / `xpiwy…` — the doubled-domain set. A stray identity in a region this project does not use `[verified 2026-08-26]` |
| ⚠️ **SES sandbox is per-region** | Production access must be requested **in `ca-central-1`**. The AWS console defaults to `us-east-1`; requesting there grants access to the wrong region and the intake email still fails `[verified 2026-08-26]` |
| SES custom MAIL FROM | **Not configured** — SES reports "No MAIL FROM records found". This is why SES's SPF is unaligned and DMARC rests on DKIM. Optional future improvement, not required `[verified 2026-08-26]` |
| **DMARC alignment** | SES satisfies DMARC via **DKIM**, not SPF — without a custom MAIL FROM domain its envelope sender is at `amazonses.com` and is unaligned. The SPF record's real job is authenticating **Google Workspace** mail `[verified 2026-08-26]` |
| **Do not delete** | Two ACM validation CNAMEs → `jkddzztszm.acm-validations.aws`. They renew the `adr.smlcompany.ca` certificate; deleting them breaks HTTPS silently at the next renewal `[verified 2026-08-26]` |
| Root domain | `@` carries a Namecheap **URL Redirect Record** to `sml-coming-soon.ca`. TXT records coexist with it — adding SPF does not disturb it `[verified 2026-08-26]` |
| DNS confirmed | `adr.smlcompany.ca` CNAME → `d26v23dhgsp2ta.cloudfront.net` `[verified 2026-08-26]` |
| TLS | ACM certificate `[verified 2026-08-25 — AWS-Hosting-Guide.md]` |
| DNS | **Namecheap**, not Route 53 `[verified 2026-08-25 — AWS-Hosting-Guide.md]` |
| Intake backend | API Gateway (HTTP API) → Lambda → DynamoDB, notifications via SES `[verified 2026-08-25 — AWS-Hosting-Guide.md]` |
@@ -262,8 +279,10 @@ Nothing below can be invented. Each needs an answer from Pouya.
| ~~Q8~~ | **ANSWERED 2026-08-26.** ADRIC, ADRIO, OBA sections. Not OCNI. Not LSO (see §4) | — |
| ~~Q9~~ | **ANSWERED 2026-08-26.** Gitea, repo `adr-sml`, clone at `/Users/pouya/Dev/Websites/adr-sml` | — |
| ~~Q10~~ | **ANSWERED 2026-08-26.** Full inventory captured; values in §7 | — |
| Q18 | **Are the SES identities actually verified for sending?** `list-email-identities` returned `VerifiedForSendingStatus: None` for all six. If they are unverified, no intake email sends | Intake confirmation and notification email |
| Q19 | **Is the SES account still in the sandbox?** In sandbox, mail only reaches pre-verified addresses — the confirmation email to an inquirer would silently fail | `/contact/` going live |
| ~~Q18~~ | **ANSWERED 2026-08-26.** `smlcompany.ca` is verified for sending — `true`. The `None` in the earlier listing was a query artefact, not a fault | — |
| **Q19** | **CONFIRMED AS A BLOCKER 2026-08-26. Request it in `ca-central-1`, not the console's default `us-east-1` — the sandbox is per-region.** `ProductionAccessEnabled: false` — the SES account is in the **sandbox**, so mail reaches only pre-verified addresses. Confirmed with Pouya (D18) that the inquirer receipt is wanted, so production access **is** required. ~24 h AWS turnaround. **The only item on this project with external lead time** | The confirmation half of `/contact/` |
| ~~Q20~~ | **ANSWERED 2026-08-26.** Both records added by Pouya and independently verified: SPF `v=spf1 include:_spf.google.com include:amazonses.com ~all` on `@`, DMARC `v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1` on `_dmarc`. Both existing `@` TXT records, Google DKIM, MX, and the `adr` CNAME all confirmed intact | — |
| ~~Q21~~ | **ANSWERED 2026-08-26.** `ca-central-1` is healthy (`SUCCESS`) on the resolving token set. The broken records belong to a stray `us-east-1` identity this project does not use. **Nothing to fix.** The three dead CNAMEs are inert; leaving them is the low-risk choice | — |
| ~~Q11~~ | **ANSWERED 2026-08-26.** Privacy-first, cookieless — Plausible or Fathom. No GA4, no consent banner | — |
| ~~Q12~~ | **ANSWERED 2026-08-26.** `https://www.linkedin.com/in/pouyalajevardi/` | — |
@@ -278,7 +297,8 @@ Nothing below can be invented. Each needs an answer from Pouya.
| Cutover breaks the live site (D11 is a single-shot deploy) | Medium | Full pre-cutover checklist in `docs/06-deployment.md`; CloudFront can be rolled back to the prior origin path |
| Twenty pages of thin copy rank worse than six good ones | Medium | Each page must justify itself with substantive content. Ship fewer pages rather than padded ones |
| **Deploy-credential blast radius.** AWS account `327082975128` is not a single-project account. It also holds `meshkinilaw.ca` and its preview site, `demesne.media`, `orynenergy.ca`, `lajirugs.ca`, and **`mlp-clientdb-prod-backups`** — which by its name is a law firm's production client-database backups | **High** | A static deploy key for a personal website must never be able to reach a client database. The scoped IAM policy in `docs/06-deployment.md` grants four actions on one bucket and one distribution and nothing else — that narrowness is now load-bearing, not hygiene. Never widen it. Never reuse the `user/pouya` credentials in CI `[verified 2026-08-26 — inventory]` |
| SES not verified or still sandboxed | Medium | Q18/Q19. A silently undelivered confirmation email looks to the inquirer like being ignored |
| **SES is in the sandbox** — confirmed, not hypothetical | **High** | Q19. Until production access is granted, every confirmation email to an inquirer fails silently. The site appears to work; the inquirer concludes they were ignored. Request access early — it is the only dependency with an external turnaround |
| **No SPF and no DMARC on `smlcompany.ca`** | **High** | Q20. Affects SES *and* the existing Google Workspace mail — Pouya's business email is unauthenticated today, which is a live deliverability problem independent of this project. Gmail and Yahoo both penalise it. Add both records before requesting SES production access; AWS also weighs domain authentication when reviewing |
## 11. Glossary
@@ -322,6 +342,231 @@ never being raised again.
# Change Log
## 2026-08-26 (k) — Q21 resolved: nothing broken; SES sandbox is per-region
**Who:** Pouya ran the per-region check.
**Q21 closed — the good case.**
| Region | Verified | DKIM | Tokens |
|---|---|---|---|
| `ca-central-1` | `true` | **SUCCESS** | `kznn…` `jdue…` `f5pu…` — resolve correctly |
| `us-east-1` | `false` | PENDING | `3zsn…` `jejgp…` `xpiwy…` — doubled domain |
The region that matters is healthy. The three broken CNAMEs belong to a stray
`us-east-1` identity that nothing in this project uses; it is PENDING precisely
*because* those records were entered with the domain doubled. **No fix required.**
Recommendation recorded: **leave the three dead CNAMEs in DNS.** They are inert,
and the only way to cause harm is deleting the wrong row from a list of six
visually similar records. Optionally delete the `us-east-1` identity itself so it
stops reporting PENDING — that is a control-plane action with no DNS risk.
**New finding, and it would have cost a day: the SES sandbox is per-region.**
`ProductionAccessEnabled: false` was read from `ca-central-1`, which is where the
intake Lambda, DynamoDB, and API Gateway all live. The AWS console defaults to
`us-east-1`, and the panel Pouya screenshotted was `us-east-1` — so the natural
path would have been to request production access in the wrong region, wait a
day, and find the confirmation email still failing. Flagged in §7 and on Q19.
**Also worth noting for the record:** entry (j)'s correction was itself only
half the story. DNS proved which records resolved; it could not prove which
*mattered*. That needed the control plane. Neither source is sufficient alone —
the data plane says what exists, the control plane says what is expected, and a
discrepancy between them is the finding.
**Closed:** Q21. **Still blocking:** Q19 only.
---
## 2026-08-26 (j) — SPF/DMARC live; and a correction: three DKIM records are broken
**Who:** Pouya added the two TXT records. Claude verified independently.
**Q20 closed.** SPF and DMARC are both live and correct, confirmed by direct DNS
query rather than taken on report. Also confirmed unchanged: both existing `@`
TXT records, `google._domainkey`, `MX 1 smtp.google.com`, and
`adr.smlcompany.ca` → CloudFront. Nothing was disturbed.
**Correction to entries (h) and (i) — I had the two DKIM sets backwards.**
- Old → new: "`3zsn…`/`jejgp…`/`xpiwy…` are the live set; `f5pu…`/`jdue…`/`kznn…`
are inert orphans" → **the opposite.** The `f5pu` set resolves NOERROR at the
correct names. The `3zsn` set returns **NXDOMAIN**.
- **Root cause found.** `3zsnvsjg…._domainkey.smlcompany.ca.smlcompany.ca`
resolves; the correct name does not. Those three were entered into Namecheap's
Host field with the **full name including the domain**, so Namecheap appended
the domain a second time. This is precisely the gotcha flagged in entry (h) —
which was written before checking whether the existing records had already
fallen into it.
- **Why I got it wrong:** I inferred "live" from the SES console listing them and
did not verify against DNS. The console shows what SES *expects*, not what
exists. Lesson worth keeping: a control-plane listing is not evidence about the
data plane.
- **No harm done.** The advice in (i) was "leave both sets alone", which happened
to be the safe action either way.
**Q21 opened.** SES is regional, and `ca-central-1` reported
`VerifiedForSendingStatus: true` — only consistent with the `f5pu` set. So the
console panel screenshotted was probably a different region's identity. Until
that is confirmed per region, it is unknown whether the broken records matter.
Nothing should be deleted before that check.
**Closed:** Q20. **Opened:** Q21. **Still blocking:** Q19 (SES production access).
---
## 2026-08-26 (i) — SES DNS panel resolves the DKIM ambiguity; MAIL FROM absent
**Who:** Pouya sent the SES "Domain DNS records" panel.
**The six DKIM CNAMEs are now fully explained.** SES lists exactly three for
`smlcompany.ca`: `3zsnvsjg…`, `jejgp7na3…`, `xpiwyftpo…`. The other three in
Namecheap — `f5puwearz…`, `jdue2r22c…`, `kznn3cklv…` — are **not** referenced by
SES and are residue from an earlier verification of the same domain. Note the
TTLs corroborate this: the live set reads "1 min", the orphans "Automatic",
consistent with two separate additions.
**Recommendation recorded: leave the orphans in place.** They are inert. The only
way this becomes a problem is deleting the wrong three, which would break SES
DKIM and therefore DMARC. There is no upside to tidying a DNS zone.
**Custom MAIL FROM is not configured** — "No MAIL FROM records found". This
independently confirms the correction in entry (h): SES's envelope sender is at
`amazonses.com`, its SPF pass is unaligned with `smlcompany.ca`, and DMARC is
satisfied through DKIM alone. Configuring a custom MAIL FROM would add SPF
alignment as belt-and-braces; it is optional and not on the critical path.
**AWS's suggested DMARC value is `v=DMARC1; p=none;`** — valid but minimal, with
no `rua`, so it produces no reports. Using `v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1`
instead so the monitoring period actually yields data before tightening to
`quarantine`.
**Net action for Q20 is two TXT records and nothing else.**
---
## 2026-08-26 (h) — DNS inspected; earlier SPF reasoning corrected
**Who:** Pouya sent the Namecheap DNS panel, asking exactly what to change
without breaking anything.
**Correction to entry (g) — the SPF reasoning was wrong.** Old → new: "SES needs
SPF or mail lands in spam" → **SES authenticates via DKIM, not SPF.** Without a
custom MAIL FROM domain, SES's envelope sender sits at `amazonses.com`, so its
SPF pass is not aligned with `smlcompany.ca` and contributes nothing to DMARC.
The six `*.dkim.amazonses.com` CNAMEs already in Namecheap are what make SES
DMARC-compliant, and they are working. The SPF record's real value is
authenticating **Google Workspace** mail, which has none. `include:amazonses.com`
stays because it is harmless and becomes live if a custom MAIL FROM is added.
Entry (g) is left as written per rule 3; this is the correction.
**Also found in the DNS panel:**
- Six SES DKIM CNAMEs where three are needed — the domain was verified twice.
Harmless, but do not prune without checking SES first.
- Two ACM validation CNAMEs → `jkddzztszm.acm-validations.aws`. **Flagged as
never-delete**: they auto-renew the `adr.smlcompany.ca` certificate, and
removing them breaks HTTPS silently at the next renewal rather than
immediately. This is the only genuinely destructive mistake available in that
panel.
- `@` carries a Namecheap URL Redirect to `sml-coming-soon.ca`. TXT records
coexist with it, so adding SPF does not disturb the redirect.
**DMARC `rua` changed** from `pouya@` to `info@smlcompany.ca` — the mailbox
confirmed to exist.
**Still blocking:** Q19 (SES production access), Q20 (the two TXT records).
---
## 2026-08-26 (g) — SES purpose clarified; D18; SPF and DMARC found missing
**Who:** Pouya asked why SES is needed given he already has an `info@` mailbox.
Fair question — the answer exposed two things.
**The distinction, recorded because it will come up again.** A mailbox receives
mail and a person logs into it. SES lets *code* send mail — the intake Lambda has
no mailbox and no login. They are complementary, not alternatives: SES sends
**to** `info@smlcompany.ca`. Verifying the address in SES did not create a
mailbox; it authorised sending.
**D18 — two emails per submission (new decision).** The sandbox blocks only one
of the two: the notification to Pouya works (his address is verified), the
**receipt to the inquirer** does not. That receipt was Claude's addition in
`docs/05-backend-spec.md`, not part of the original AWS-guide build, so it was
put back to Pouya as a real choice rather than treated as a defect. **He confirmed
he wants it** — so Q19 is genuinely blocking rather than optional.
**Q20 opened — the domain has no email authentication at all.** A DNS query of
`smlcompany.ca` on 2026-08-26 returned:
- TXT: only `anthropic-domain-verification` and `google-site-verification`.
**No `v=spf1` record of any kind.**
- `_dmarc.smlcompany.ca`: **no record.**
- MX `1 smtp.google.com` and `google._domainkey` present — Google Workspace with
DKIM configured.
- `adr.smlcompany.ca` CNAME → `d26v23dhgsp2ta.cloudfront.net`, as expected.
Old → new (planned): no SPF, no DMARC → one SPF record covering **both** Google
and Amazon SES, plus a DMARC record starting at `p=none`.
**This is wider than this project.** Pouya's existing business email from
`smlcompany.ca` is sending unauthenticated today. Google Workspace's DKIM carries
it, but Gmail and Yahoo both penalise missing SPF and DMARC, so mail he sends as a
human is already more likely to be filtered than it should be. Raised to **High**
in §10 and flagged to him directly. Both records should go in before the SES
production-access request — AWS weighs domain authentication when reviewing, so
it improves the odds of first-time approval as well.
**Opened:** Q20. **Still blocking:** Q19, Q20.
---
## 2026-08-26 (f) — SES sandbox confirmed; execution protocol built into the repo
**Who:** Pouya. Claude recording.
**Q18 closed, Q19 confirmed as a live blocker.** `smlcompany.ca` **is** verified
for sending (`true`) — the earlier `None` was a query artefact, not a fault. But
`ProductionAccessEnabled: false`: **the account is in the SES sandbox.** Mail
reaches only pre-verified addresses, so an inquirer's confirmation email fails
silently. Raised from Medium to **High** in §10: this is the failure mode where
the site looks like it works and the inquirer concludes they were ignored.
Production access must be requested; ~24 h turnaround, and it is the only
dependency on this project with external lead time.
**D17 — working method, made structural (new decision).**
- Old → new: an implicit understanding that Claude Code implements what Pouya
decides → **an explicit six-phase loop encoded in the repository**, so it
applies without being restated in each prompt.
- Why: Pouya's direction — he architects, Claude Code programs and then
adversarially reviews, with deep reasoning on, permanently.
- Added:
- `.claude/agents/adversarial-reviewer.md` — code, accessibility,
crawlability, performance, security, simplicity. Instructed to **treat
uncertainty as a defect** and told that an approving review which misses a
real defect is a failure.
- `.claude/agents/claims-auditor.md` — every factual assertion traced to §4.
Separate agent on purpose: a generic reviewer consistently under-weights
professional-conduct checks, which is this project's highest-stakes failure
mode. It also audits JSON-LD, since a machine-readable misrepresentation is
still a misrepresentation.
- `.claude/commands/build.md` — the loop. `/review` — phase 3 alone.
`/wrap` — the session-end `AGENTS.md` ritual.
- `.claude/settings.json` — thinking on, high effort, and a read-deny on
`.env*` and `aws-inventory.txt`.
- `CLAUDE.md` — the protocol and the reasoning behind it.
- `docs/08-execution-protocol.md` — the prompt template Pouya writes from.
- **The design decision worth preserving:** the reviewers are given the diff and
the specs, and deliberately **not** the implementer's rationale. Explaining why
the work is correct anchors the reviewer and converts review into agreement.
Independence is the mechanism, not a courtesy. If a future change to this
protocol removes that, it removes the value.
**Closed:** Q18. **Still blocking:** Q19 (SES production access).
---
## 2026-08-26 (e) — AWS inventory captured; Q10 closed; blast-radius risk logged
**Who:** Pouya ran `scripts/aws-discover.sh`. Claude recording.