fix: resolve adversarial review round 2 — 9 findings, 8 of them in round 1's fixes
Build and deploy / build-and-deploy (push) Failing after 4s

D19 caps the loop at two rounds, and this is what the second round is for.

BLOCKING. Round 1 made NO_RETAINER_NOTICE a requireEnv and added it to no
document, while the fix's own comment claimed docs/06 named it. The deployment
list said five variables for a handler that needs six, so an operator following
the cutover checklist would have deployed a function that throws at cold start
on every invocation — 5xx from API Gateway, every inquiry lost from the moment
/api/* was wired, loud in CloudWatch and silent to Pouya. docs/05 and docs/06
now name all six, and the comment that asserted the documentation existed is
corrected rather than deleted.

The intake route check added in round 1 could not fail: curl -w already prints
000 on a failed transfer, so `|| echo 000` double-appended and the failure arm
was unreachable, and the pass arm accepted anything that was not literally 404 —
including the 403 CloudFront returns when the /api/* behaviour is missing, which
is the one distinction the check exists to draw. It now sends the correct Origin
and asserts a positive: 303 to /contact/could-not-send/, which the handler
returns before any DynamoDB write or email. Probed on refused/501/403/303; the
old version passed the first three. Fixed in both deploy paths.

Removing priceRange left three statements saying it was present or pending, one
of them the stated reason /fees/ emits no Offer node. Deleting
overtimeStartsAfterSessionHours left AGENTS.md §9 naming it and left Q59
recorded as open. The Google-as-processor fix was applied to the privacy
policy's "Where it is stored" and not to "Who can see it", which still read
"Nobody else has access".

And the variable removal was justified with a path-scoped git grep — which also
cannot see untracked files. The unscoped sweep found docs/06's variable table,
the OIDC example, and .env.example still carrying them; .env.example also
restates the execute-api hostname, falsifying a live claim in intake.ts that has
been corrected. That file is not edited here: this environment denies read
access to it, and nothing may edit a file it cannot read. It is in the batched
list.

Also: og:image:alt was the page title rather than the card's headline on 20
pages; og-card.ts documented the wrong path and invocation for the contact
sheet; deploy-local.sh still said Q22's deploy credential "does NOT yet exist";
and the round-1 fix comments were trimmed per D19, though the ratio held at 0.44.

Round 2 also confirmed the round-1 fixes by measurement: all 56 .btn instances
across 22 pages, the consent checkbox's computed accessible name, the radio
labels hit-tested at 44px, and og:proof exercised against synthetic article
pages in a sandbox.

Verified: check/build/check:claims/og:proof/check:intake/lint/bio:pdf all exit 0
on a clean build; 22 pages; Lighthouse 99-100 / 100 / 100 / 100, CLS 0.000.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
This commit is contained in:
Pouya Lajevardi
2026-08-31 11:21:07 -04:00
co-authored by Claude Opus 5
parent 9f2d83c32f
commit 9f2d2eeb04
18 changed files with 360 additions and 180 deletions
+35 -20
View File
@@ -165,35 +165,50 @@ jobs:
# header requires the two paths to match on everything that determines
# what gets published - and this replaced the INTAKE_ENDPOINT guard.
#
# The contact form posts to the same-origin path /api/intake, which only
# works if a CloudFront behaviour routes /api/* to the HTTP API origin
# AGENTS.md §7 records. Nothing in the build can know whether it exists.
# It ASSERTS A POSITIVE. The first version excluded one status code and
# passed on everything else; `adversarial-reviewer` round 2 measured it
# passing on a refused connection (curl -w already prints 000, so the
# `|| echo 000` double-appended and made $code "000000") and on a real 501.
# It would also have passed the case that matters most: with the /api/*
# behaviour MISSING, the POST falls to the S3 default behaviour and
# CloudFront answers 403 for a disallowed method - indistinguishable from
# the handler's Origin refusal, which is the one distinction this check
# exists to draw.
#
# 404 means not routed. 403 means routed and REFUSED, which is the correct
# answer here: the handler checks the Origin header and this request sends
# none, so it is rejected before any DynamoDB write or any email. That is
# why the probe is safe to run against production.
# With the correct Origin and an empty submission the handler validates,
# rejects, and redirects 303 to /contact/could-not-send/ - BEFORE any
# DynamoDB write and before any email, which is what makes it safe against
# production. Probed on four cases: refused, 501, 403, and the real 303.
#
# It warns rather than failing: the site is already deployed by this point,
# and failing the job would not un-deploy it.
- name: Intake route check
run: |
url="https://adr.smlcompany.ca/api/intake"
code=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
--max-time 15 \
-H "Origin: https://adr.smlcompany.ca" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'deploy-route-probe=1' \
"https://adr.smlcompany.ca/api/intake" || echo 000)
case "$code" in
404|000)
echo "WARNING: POST /api/intake returned $code."
echo "The contact form posts there. 404 means the CloudFront /api/*"
echo "behaviour is missing; 000 means the request did not complete."
echo "See docs/06-deployment.md's cutover checklist."
;;
*)
echo "POST /api/intake -> $code (routed; 403 is the Origin check)"
;;
esac
--data 'deploy-route-probe=1' "$url")
rc=$?
location=$(curl -sS -o /dev/null -w '%{redirect_url}' -X POST \
--max-time 15 \
-H "Origin: https://adr.smlcompany.ca" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'deploy-route-probe=1' "$url" 2>/dev/null || true)
if [ "$rc" -ne 0 ]; then
echo "WARNING: the POST to /api/intake did not complete (curl exit $rc)."
echo "The site is deployed and the contact form is unverified."
echo "See docs/06-deployment.md's cutover checklist."
elif [ "$code" = "303" ] && case "$location" in *"/contact/could-not-send/") true;; *) false;; esac; then
echo "POST /api/intake -> 303 -> $location (routed, validating)"
else
echo "WARNING: POST /api/intake returned $code, expected 303 to"
echo "/contact/could-not-send/; redirect was '${location:-none}'."
echo "404 means the CloudFront /api/* behaviour is missing. 403 can"
echo "mean the same thing, or the handler refusing the Origin."
echo "See docs/06-deployment.md's cutover checklist."
fi
- name: Summary
run: echo "Deployed to https://adr.smlcompany.ca — commit ${GITHUB_SHA:0:7}"