feat: park the two policy changes the pricing plan forbids; robots.txt stands in
Build and deploy / build-and-deploy (push) Failing after 4s

The third --apply of 2026-09-04 reached update-distribution and was rejected
atomically: "Distributions with the Free pricing plan can't have the following
features: Custom origin request policy, Custom response headers policy."
Pouya's ruling: both are PARKED as unavailable — a platform constraint, not a
defect.

The pre-flight added in the previous commit could not have caught this, and
that is the point: every limit in PAYLOAD_LIMITS is a property of the payload,
while this is a property of the account, reported only by the call the
pre-flight exists to avoid. Both sections now stop before creating anything.

The plan is not in the CloudFront API — checked across 167 operations, no
operation, shape, member or documentation string mentions one, and
PriceClass_All is the edge-location price class, not the plan. So the gate is a
constant, PLAN_ALLOWS_CUSTOM_POLICIES, and the two sections report as PARKED
under their own heading rather than as skips: the previous commit made a skip
exit 3, and a constraint true on every run would have made 3 permanent. Proven
with a shim that refuses every mutating verb: --apply now makes zero of them.

Substitute (a): Disallow: /pouya-lajevardi-bio.pdf in robots.txt, placed before
Allow:/ so first-match crawlers honour it too. It is not an equivalent and the
file says so — it stops the PDF being fetched, solving the duplicate-of-/bio/
problem, but does not de-index a URL linked from /bio/ and /about/. Verified:
syntax, a match simulation under both crawler semantics, and that the sitemap
does not list the PDF.

Substitute (b): the WAF web ACL CreatedByCloudFront-f8fbf256 is already
attached — 925 WCU, three AWS managed rule groups, no rate-based statement.
That corrects §9 Q65, which framed WAF as a cost decision about adding one and
named the now-unappliable header forwarding as its groundwork. The real
question is one rule on an ACL already paid for, and a rate-based rule matches
the viewer address directly, so the capability is superseded rather than lost.

Reviewed in two rounds by me rather than a separate agent, per instruction.

Nothing was applied to the distribution and nothing was deployed; robots.txt
needs one site deploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
This commit is contained in:
Pouya Lajevardi
2026-09-04 14:52:13 -04:00
co-authored by Claude Opus 5
parent 07a8ff6989
commit 17e316dc1d
6 changed files with 376 additions and 41 deletions
+44 -7
View File
@@ -1229,9 +1229,22 @@ the decision is re-readable rather than re-litigated.
renders a field nothing checks, and a handler ahead of the form checks a
field nothing renders. Both are inert, so the only cost of doing one and
not the other is that the mitigation is not yet in force
- [ ] **`CloudFront-Viewer-Address` forwarded on `/api/*`**⚠️ **WRITTEN
2026-09-04, NOT YET APPLIED. Same `configure.mjs --apply` run as the item
below; not a deploy.** `infra/cloudfront/configure.mjs` §5 creates a custom
- [x] **`CloudFront-Viewer-Address` forwarded on `/api/*` — CLOSED 2026-09-04
AS NOT AVAILABLE ON THIS PRICING PLAN, AND SUPERSEDED.** Pouya's ruling
after the third `--apply`: `update-distribution` rejected the change
atomically — *"Distributions with the Free pricing plan can't have the
following features: Custom origin request policy, Custom response headers
policy"* — so this is a **platform constraint, not a defect**. It is
**revisited only if the plan changes**; `configure.mjs` now parks section 5
instead of attempting it, and `AGENTS.md` §7 records the plan.
🛑 **SUPERSEDED, NOT MERELY PARKED: a WAF web ACL is already attached to
this distribution (`CreatedByCloudFront-f8fbf256`, §7), and that is where
any future per-IP rate rule belongs** — a forwarded viewer address was only
ever the means to an end this already provides. The original item is kept
below because its reasoning about the whitelist is what makes section 5
safe to un-park. ⚠️ **WRITTEN
2026-09-04, NOT APPLIED, AND NOW UNAPPLIABLE. Same `configure.mjs --apply`
run as the item below; not a deploy.** `infra/cloudfront/configure.mjs` §5 creates a custom
origin request policy `adr-sml-api-viewer-address` and points the `/api/*`
behaviour at it. Pouya's ruling of 2026-09-04, after the first real spam:
forward it **so per-IP measures become possible later — measured, not yet
@@ -1250,9 +1263,23 @@ the decision is re-readable rather than re-litigated.
Forwarding is infrastructure; **storing** the viewer address is a
`/legal/privacy/` change governed by `docs/09` §7.2's decision table, and
it is deliberately not made here
- [ ] **`X-Robots-Tag: noindex` on `*.pdf`**⚠️ **WRITTEN 2026-09-03, NOT YET
APPLIED. It needs a `configure.mjs --apply` run, not a deploy** — the same
run as the item above; one `--apply` does both.
- [x] **`X-Robots-Tag: noindex` on `*.pdf` — CLOSED 2026-09-04 AS NOT AVAILABLE
ON THIS PRICING PLAN. A SUBSTITUTE SHIPPED IN ITS PLACE.** Same rejection
as the item above: a custom response headers policy is not available on the
Free plan, so this is a **platform constraint, not a defect**, revisited
only if the plan changes.
**The substitute is `Disallow: /pouya-lajevardi-bio.pdf` in
`public/robots.txt`** — it needs a **site deploy**, not a `configure.mjs`
run. ⚠️ **IT IS NOT AN EQUIVALENT AND `public/robots.txt` SAYS SO IN THE
FILE.** `Disallow` stops the PDF being **fetched**, which solves the
duplicate-of-`/bio/` problem this item was raised for; it does **not**
de-index the URL, and the PDF is linked from `/bio/` and `/about/`, so a
bare listing remains possible. That residual is accepted deliberately.
`docs/04` §Crawlability carries the general rule this is the exception to.
The original item follows, because its reasoning is what makes section 4
safe to un-park. ⚠️ **WRITTEN 2026-09-03, NOT
APPLIED, AND NOW UNAPPLIABLE. It needed a `configure.mjs --apply` run, not
a deploy** — the same run as the item above; one `--apply` did both.
`infra/cloudfront/configure.mjs` §4 creates a response-headers policy
`adr-sml-pdf-noindex` and a `*.pdf` cache behaviour carrying it. ⚠️ **S3
OBJECT METADATA CANNOT DO THIS, which is the natural first reach and was
@@ -1468,7 +1495,17 @@ the decision is re-readable rather than re-litigated.
that spec line is struck and an aggregate route throttle ships instead
(`docs/09` Part 6.3). A rate-based WAF rule on the distribution is what
would do per-IP. Decide it on price, not on the spec's old wording — and do
not let anything describe the throttle that ships as per-IP
not let anything describe the throttle that ships as per-IP.
🛑 **THE PRICE QUESTION IS SMALLER THAN THIS ITEM ASSUMES, MEASURED
2026-09-04.** A web ACL is **already attached and already running** on the
distribution — `CreatedByCloudFront-f8fbf256`, 925 WCU, three AWS managed
rule groups, and **no rate-based statement** (`AGENTS.md` §7 and §9 Q65).
So this is not "buy WAF"; it is "add one rule to an ACL already being paid
for". ⚠️ **AND THE ROUTE THIS ITEM ASSUMED IS GONE:** the
`CloudFront-Viewer-Address` forwarding was parked as unavailable on the
pricing plan — but a rate-based rule matches on the viewer address itself
and never needed that header, so the capability is **superseded, not
blocked**
- [ ] ⚠️ **A FOOTER NAV LABEL OVERRUNS ITS COLUMN BY 24 px AT 640 px UNDER
MINIMUM FONT SIZE, WITH 7.7 px OF CLEARANCE TO THE NEXT COLUMN.** No document