diff --git a/AGENTS.md b/AGENTS.md index 2862c3d..5f86d0c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -705,6 +705,7 @@ the audience it targets. Revisit at month 12–18. `[verified 2026-08-25 β€” dec | Thing | Value | |---|---| +| 🟒 **THE SITE IS LIVE** | **`https://adr.smlcompany.ca` β€” cutover executed 2026-09-02 by Pouya, `scripts/deploy-local.sh`, commit `67847d9`.** D11's single shot is spent; the old site is replaced. `[verified 2026-09-02 β€” 26 routes curled with the iteration count asserted]`: all 22 pages, `robots.txt`, `sitemap-index.xml` and `/pouya-lajevardi-bio.pdf` return **200**; an unknown path returns **404** with the styled Astro page (14,321 B), so the CloudFront function and custom error response are both in force. HTML `max-age=0, must-revalidate`; `_astro` `max-age=31536000, immutable`; PDF **89,496 B**, matching `public/` exactly. **All 22 live pages are byte-identical to a `dist/` rebuilt at `67847d9`** β€” SHA-256 per page, 22 same / 0 differ. Five `noindex` surfaces correct; sitemap 17 URLs. ⚠️ **THREE THINGS ARE LIVE AND NOT RIGHT, and `docs/06`'s callout carries them:** the intake form POSTs to `/api/intake`, which **403s with an empty body** (only `POST /submissions` exists), so a submitter gets a blank page; `/legal/privacy/` published while **Q60 is open** and its own source comment said it must not; and the **D20 claims pass ran after cutover and returned FAIL with 20 confirmed findings**. ⚠️ **`X-Robots-Tag: noindex` IS ABSENT ON THE PDF** `[verified 2026-09-02 β€” curl -I]` β€” `docs/06` carries that as an unticked item and the file is linked from `/about/`, so it is crawlable even though `/bio/` is not | | Framework | **Astro 7.2.9**, `output: 'static'` `[verified 2026-08-27 β€” npm view astro version, D1 as amended]`. `@astrojs/mdx` 7.0.8, `@astrojs/sitemap` 3.7.3, `sharp` 0.35.4 β€” all three at `latest`. Bumped from 7.2.7 at the step 1 β†’ step 2 phase boundary under R11: 7.2.8 published 2026-08-26 and 7.2.9 on 2026-08-27, i.e. **two patches appeared inside 48 hours**, which is the argument for checking at boundaries rather than on failure. `engines` unchanged at `node >=22.12.0`, `npm >=9.6.5` `[verified 2026-08-27 β€” npm view astro@7.2.9 engines]` | | **TypeScript β€” HELD at 6.x, and the hold is a peer constraint rather than a preference** | Pinned `^6.0.3`; `npm view typescript version` returns **7.0.2** `[verified 2026-08-31 β€” R11, re-run at the step 10 β†’ 11 boundary after the run added five dependencies: **18 of 19 pins current**, this the only hold, `npm audit` 0 vulnerabilities. The earlier run that day, at the step 6 β†’ 7 boundary, read 13 of 14]`. **The removal trigger was re-checked rather than recalled:** `npm view @astrojs/check@0.9.10 peerDependencies` still returns `{ typescript: '^5.0.0 \|\| ^6.0.0' }`, so the hold stands. ⚠️ **AND THERE ARE TWO GATES, NOT ONE β€” corrected 2026-08-31, and this row named only the first for two days.** `npm view typescript-eslint peerDependencies` returns `{ typescript: '>=4.8.4 <6.1.0' }`, which is **tighter**: widening `@astrojs/check` alone would not lift the hold, so the trigger as recorded was unreachable. **A second consequence, live:** the pin is a caret, so if a 6.1.x ever ships, a plain `npm install` moves the tree into a peer-range breach with nothing in the repo saying so. Nothing is breached today β€” `npm ls typescript` resolves **6.0.3**, which is the highest stable 6.x on the registry. Found by running R11's own check rather than reading the row. **One major behind, which is the exact shape D1 was amended over β€” so the reason is recorded rather than the hold being silent.** `@astrojs/check@0.9.10` declares `peerDependencies: { typescript: '^5.0.0 \|\| ^6.0.0' }` `[verified 2026-08-29 β€” npm view @astrojs/check@0.9.10 peerDependencies]`, and `npm run check` is `astro check`, which is the type gate the deploy path runs before it builds. **Removal trigger: `@astrojs/check` AND `typescript-eslint` both widen to include 7** β€” either alone is not enough. Re-check at every phase boundary under R11 β€” this is a hold on a dependency's schedule, not on a judgement, so it lifts without a decision | | Lint toolchain | ESLint **10.9.1**, `@eslint/js` 10.0.1, `globals` 17.11.0, `eslint-plugin-astro` **3.1.0**, `eslint-plugin-jsx-a11y` 6.10.2, `typescript-eslint` 8.68.0, `typescript` **6.0.3** β€” **every pin at the registry's `latest` except `typescript`** `[verified 2026-08-30 β€” npm view, all 14 pins, R11 at the step 5 β†’ 6 boundary; thirteen current, one held]`. `@eslint/js` and `globals` are declared explicitly; before 2026-08-26 `eslint.config.js` imported them and they resolved only by npm hoisting accident. **Accessibility linting is on** β€” `flat/jsx-a11y-recommended`, 36 rules `[verified 2026-08-26 β€” 7 rules fired on a deliberately inaccessible .astro file]`. `eslint-plugin-jsx-a11y@6.10.2` declares a stale `eslint ^3..^9` peer range; a one-line `overrides` entry in `package.json` resolves it. ⚠️ **`eslint-plugin-astro@3.1.0` declares `node ^22.22.3 \|\| ^24.16.0 \|\| >=26.3.0`**, which excludes Pouya's Node 25.6.0 β€” `npm install` prints EBADENGINE there. Dev-time only, and `nvm use` (Node 22 LTS, per `.nvmrc`) clears it. **`typescript` β€” the hold, and it was too wide by a whole major until 2026-08-27.** `latest` is **7.0.2** and it is unusable here: `typescript-eslint@8.68.0` peers `typescript >=4.8.4 <6.1.0` and `@astrojs/check@0.9.10` peers `^5.0.0 \|\| ^6.0.0`, so taking 7 breaks **both** the linter and `astro check` `[verified 2026-08-27 β€” npm view peerDependencies]`. **But this row previously read "held at 5.x", and 6.0.3 is a stable release both peers accept** β€” so the pin sat a full major behind for no reason the record could name, in a row whose whole job is to name the reason. Now at **6.0.3**, the newest version compatible with both peers; `npx tsc --version` reports 6.0.3 and the full gate is green `[verified 2026-08-27]`. The 6.x listing is mostly `-dev` and `-beta` tags; **6.0.2 and 6.0.3 are the only stable 6.x releases**, which is why `npm view typescript version` (7.0.2) is not the number to pin against here. Re-check at the next phase boundary: the hold ends when `typescript-eslint` widens its peer range past `<6.1.0` | @@ -751,7 +752,7 @@ the audience it targets. Revisit at month 12–18. `[verified 2026-08-25 β€” dec | Intake backend | API Gateway (HTTP API) β†’ Lambda β†’ DynamoDB, notifications via SES `[verified 2026-08-25 β€” AWS-Hosting-Guide.md]` | | Repository | **`adr-sml`**, self-hosted **Gitea**. Local clone at `/Users/pouya/Dev/Websites/adr-sml` `[verified 2026-08-26]` | | CI/CD | **Gitea Actions**, `.gitea/workflows/deploy.yml`. Instance **git.larsnolden.com, version 1.27.2** `[verified 2026-08-26 β€” /api/v1/version]` β€” well above the ~1.21 floor for the `vars` context. The GitHub OIDC workflow is kept as `docs/reference/github-actions-oidc.yml.example`, outside `.github/workflows/` so Gitea cannot fall back to it. **No OIDC available** β€” Gitea is not an AWS OIDC provider | -| **Deploy credential β€” PROVISIONED** | IAM user **`adr-sml-deploy`**, created **2026-08-26T15:45:18Z**, `arn:aws:iam::327082975128:user/adr-sml-deploy` `[verified 2026-08-28]`. **No managed policies, no group memberships.** One inline policy, `adr-sml-deploy-minimal`: `s3:ListBucket` on the site bucket, `s3:PutObject` + `s3:DeleteObject` on `bucket/*`, `cloudfront:CreateInvalidation` on the distribution this section records. **One active access key, created 2026-08-26, NEVER USED** β€” `AccessKeyLastUsed` returns `ServiceName: "N/A"` / `Region: "N/A"` with no `LastUsedDate` key at all, because deploys still run locally. **Verified by execution, not by reading** β€” `iam simulate-principal-policy`, **nine** checks: `allowed` for the four intended actions (`s3:PutObject`, `s3:DeleteObject`, `s3:ListBucket`, `cloudfront:CreateInvalidation`); `implicitDeny` for the five that matter more β€” `s3:ListBucket` and `s3:GetObject` on the client-database backup bucket, `s3:DeleteBucket` on its own bucket, `s3:PutObject` on `meshkinilaw.ca`, and `iam:CreateUser` on `*`. **The resource side is checked too:** `get-bucket-policy` on the backup bucket exits **254** with `NoSuchBucketPolicy`, so no resource-based grant exists and IAM is the only path in β€” and the same call against the *site* bucket exits 0 with a real CloudFront OAC policy, which is what makes the first result an absence rather than a broken command. **βœ… EVIDENCE IS IN THE REPOSITORY β€” R14 SATISFIED. `docs/reference/deploy-credential-verification.md`**, eighteen read-only calls, each with the command that produced it, the access key ID redacted; re-run 2026-08-28 rather than transcribed, and `adversarial-reviewer` independently reproduced every value including the `MatchedStatements` positions. Q52 closed. ⚠️ **GITEA ACTIONS SECRETS: `UNSET`** `[verified 2026-08-26]` β€” no access key has been placed in a repository secret on `git.larsnolden.com`, so the key exists in IAM and **nowhere on the jointly-administered instance**. That is a different fact from the key being unused: a key can sit on shared infrastructure without ever authenticating. **Rotation: Β§12 R17 carries the date and the procedure** β€” this row deliberately does not restate it | +| **Deploy credential β€” PROVISIONED** | IAM user **`adr-sml-deploy`**, created **2026-08-26T15:45:18Z**, `arn:aws:iam::327082975128:user/adr-sml-deploy` `[verified 2026-08-28]`. **No managed policies, no group memberships.** One inline policy, `adr-sml-deploy-minimal`: `s3:ListBucket` on the site bucket, `s3:PutObject` + `s3:DeleteObject` on `bucket/*`, `cloudfront:CreateInvalidation` on the distribution this section records. ⚠️ **THE KEY HAS NOW BEEN USED, AND THERE MAY BE TWO β€” UPDATED 2026-09-02 AT CUTOVER.** Pouya deployed the site to production from this machine via `scripts/deploy-local.sh` using this credential, and reports minting a **new access key** at launch `[Pouya's stated basis 2026-09-02 β€” reported, NOT re-read from IAM by this record]`. **Β§12 R17 carries the reset clock (next rotation 2026-12-02) and the warning that minting the second key is only half a rotation.** ~~One active access key, created 2026-08-26, NEVER USED β€” `AccessKeyLastUsed` returns `ServiceName: "N/A"` / `Region: "N/A"` with no `LastUsedDate` key at all, because deploys still run locally.~~ `[was verified 2026-08-28; falsified by the 2026-09-02 deploy]` **Verified by execution, not by reading** β€” `iam simulate-principal-policy`, **nine** checks: `allowed` for the four intended actions (`s3:PutObject`, `s3:DeleteObject`, `s3:ListBucket`, `cloudfront:CreateInvalidation`); `implicitDeny` for the five that matter more β€” `s3:ListBucket` and `s3:GetObject` on the client-database backup bucket, `s3:DeleteBucket` on its own bucket, `s3:PutObject` on `meshkinilaw.ca`, and `iam:CreateUser` on `*`. **The resource side is checked too:** `get-bucket-policy` on the backup bucket exits **254** with `NoSuchBucketPolicy`, so no resource-based grant exists and IAM is the only path in β€” and the same call against the *site* bucket exits 0 with a real CloudFront OAC policy, which is what makes the first result an absence rather than a broken command. **βœ… EVIDENCE IS IN THE REPOSITORY β€” R14 SATISFIED. `docs/reference/deploy-credential-verification.md`**, eighteen read-only calls, each with the command that produced it, the access key ID redacted; re-run 2026-08-28 rather than transcribed, and `adversarial-reviewer` independently reproduced every value including the `MatchedStatements` positions. Q52 closed. ⚠️ **GITEA ACTIONS SECRETS: `UNSET`** `[verified 2026-08-26]` β€” no access key has been placed in a repository secret on `git.larsnolden.com`, so the key exists in IAM and **nowhere on the jointly-administered instance**. That is a different fact from the key being unused: a key can sit on shared infrastructure without ever authenticating. **Rotation: Β§12 R17 carries the date and the procedure** β€” this row deliberately does not restate it | | **How deploys actually happen today** | **Locally, via `npm run deploy`** (`scripts/deploy-local.sh`) β€” same guard, same three sync passes, same cache headers, same invalidation as the workflow. Gitea Actions needs `[actions] ENABLED` in `app.ini` and a registered `act_runner`, both of which need the instance's second administrator (Q23). At this scale the pipeline changes only **how a deploy is triggered**, not what it does `[verified 2026-08-26]` | | **Interim auth β€” do not repeat in CI** | Pouya has been authenticating as **`user/pouya`**, the broadly-permissioned personal user the Q10 inventory captured. Acceptable for interactive work at a keyboard; **never** as a CI credential β€” see Β§10 `[verified 2026-08-26]` | | Analytics | **Plausible** β€” cookieless, no personal data, no consent banner, **all data held in the EU** `[verified 2026-08-26 β€” D15 as amended, Q31]`. `src/data/site.ts` sets `ANALYTICS.provider`. Not yet installed: no script is on any page, and `/legal/privacy/` (step 10) is where the processor gets named | @@ -936,7 +937,7 @@ never being raised again. | R14 | **Anything a spec makes a claim about must be reachable from the repository.** If the artefact lives only in Drive, in a console, or on someone's disk, no reviewer can check the claim and the claim is unverifiable by construction β€” see the rule now in `CLAUDE.md` | 2026-08-26 | **This has now cost twice.** Q24 was `AWS-Hosting-Guide.md`, the only record of how the infrastructure was hand-built, living outside the repo. Q32 was the infinity mark: Claude Code traced it from the old site's *loading placeholder*, wrote a scrupulous provenance comment recording exactly that doubt β€” and **two adversarial review passes still could not catch it**, because the artwork they would have had to compare against was not in the repo. The doubt was correctly stated and structurally uncheckable. That is the failure mode: not a missing warning, a missing artefact | | ~~R15~~ | βœ… **DISCHARGED 2026-08-31 β€” build step 7b. Seventeen pages and every future article now carry their own generated card; `/` and `/about/` keep the portrait, which Q40 called "not an interim" and which is unchanged.** `src/pages/og/[...slug].jpg.ts` renders them at build from `satori@0.33.4` + `sharp`; `src/data/og-cards.ts` is the registry; `SEO.astro` resolves which of the two kinds a page gets from its own pathname, so the decision lives in one data file rather than in nineteen call sites. `satori` over `astro-og-canvas@0.13.0` β€” both 0 vulnerabilities `[verified 2026-08-31]` β€” because `sharp` was already a dependency, so it is one library rather than a CanvasKit wasm blob, and it renders the site's real fonts and tokens instead of approximating them. **The interim note is deleted from `docs/04` and the cutover line from `docs/06`**, which is what this row's trigger asked for. ⚠️ **AND IT SURFACED A HOLE THAT IS WORTH MORE THAN THE ROW: TEXT BAKED INTO A JPEG CANNOT BE GREPPED BY `npm run check:claims`, WHICH UNDER D20 IS THE ONLY PER-STEP CLAIMS CONTROL THERE IS.** A card is the one surface on this site where new prose would face no mechanical check at all. So card copy is constrained structurally rather than editorially: **every headline is its page's own `

`, character for character**, the kicker renders `CREDENTIALS.designations`, and **`npm run og:proof` enforces both against the built HTML** β€” it fails if the registry and the page disagree in either direction, and it also confirms every `og:image` resolves to a file that exists. That check found a straight-versus-typographic apostrophe on its first run. **A page with no registry entry is a build error, not a fallback to the portrait** β€” R15's failure mode was never the wrong image, it was the wrong image shipping *invisibly* and reading as intentional, and a silent fallback recreates it exactly. **What is NOT discharged and is deliberately left open:** the check compares strings, so it cannot see a card that renders badly β€” a headline that overflows, a mark that clips. `npm run og:proof -- --sheet` writes a contact sheet of every card for that, and **a human still has to look**; `docs/06`'s cutover item says so. Two cards were read at step 7b (`/practice/energy/`, the longest headline, and `/practice/insurance/`) and rendered correctly | 2026-08-27 | Nobody on this project will ever see the defect. A link preview is rendered by LinkedIn, Slack and Teams for a reader who is not us, and `docs/04` requires a **unique** title and description per page β€” nineteen unique pages that all preview identically is the failure, and it is invisible from inside the repo. It cost nothing and worked, which is exactly the R13 shape | | ~~R16~~ | βœ… **DISCHARGED 2026-08-31 β€” build step 9, which is the trigger this row set. The one-page PDF exists, is committed at `public/pouya-lajevardi-bio.pdf`, and `/about/` links it.** Q45's two open sub-decisions are both taken, and the second one is what makes the first safe. **(a) Generated at build, or authored once? NEITHER β€” and the third option answers this row's actual objection.** The bio is a PAGE, `src/pages/bio.astro`, so every line of it is reviewed by the same apparatus as every other page: `astro check`, `npm run check:claims` on the built HTML, the per-step code review, and the cutover claims pass. `npm run bio:pdf` then RENDERS the PDF from the built page through Chrome, with **no new dependency** β€” `chrome-launcher` is already a devDependency because Lighthouse needs it. That is aimed squarely at what this row said the problem was: *"It is the one artefact class this project's review apparatus cannot reach… a PDF circulated with an appointment proposal is read once, by the reader who matters most, and never seen by a reviewer again."* A rendering of a reviewed page is back inside the apparatus. **It is deliberately NOT part of `astro build`** β€” CI has no Chrome, and a build step that cannot run in CI is Q22's shape again β€” so the PDF is committed, which R14 wants anyway. **(b) Does it carry anything the site does not? NO, and that is the answer that avoids the Β§4 question this row flagged.** Every line renders from `CREDENTIALS`, `ROLE`, `BOUTIQUE`, `PRACTICE_AREAS`, `FEES` and `CONTACT`. No matter list β€” which this row correctly said *"would collide with Β§4 Forbidden directly"* β€” no referees, and no figure that is not on `/fees/`. The fee summary IS there, because this row's own reasoning is that an appointment proposal needs the rate card as much as the bio. ⚠️ **`npm run bio:pdf` ASSERTS ONE PAGE and writes nothing if the count is wrong** β€” counted from the PDF's own page objects, cross-checked against the page tree's `/Count`. A one-page bio that silently becomes two is invisible from the source, because it depends on the print stylesheet, the paper size and how much Β§4 has grown. **And reading the rendered PDF caught a real breach the source review had not:** the opening sentence read *"I act as a neutral in commercial disputes β€” as a mediator, as an arbitrator in commercial matters…"*, whose leading clause scoped **mediation**, which Q56 leaves unscoped deliberately. `/bio/` is `noindex` and out of the sitemap: it is a condensed duplicate of `/about/` and `/fees/`. **What is NOT discharged:** nothing in the build regenerates the PDF. It must be re-rendered and re-committed whenever `/bio/`, Β§4, the rate card or the print styles change, and `docs/06`'s cutover checklist carries that | 2026-08-28 | It is the one artefact class this project's review apparatus cannot reach. A web page is re-reviewed by every audit and re-checked by every build; a PDF circulated with an appointment proposal is read once, by the reader who matters most, and never seen by a reviewer again | -| R17 | **THE DEPLOY KEY'S FIRST ROTATION IS DUE 2026-11-26, AND THAT DATE IS THE WHOLE ROW.** Access key for `adr-sml-deploy` created **2026-08-26**; D3 commits to **quarterly** rotation. Pouya's instruction when Q22 closed: *"Put the DATE in the R-series reminder, not the intention."* **Rotation procedure is in `docs/06-deployment.md` Β§Key rotation** β€” create the second key, update the Gitea secrets, verify a deploy, **then** delete the first; never delete before verifying, or the next push fails with no way to authenticate the fix. **Then set the next date here (2027-02-26) rather than deleting the row.** Also re-run **all nine** `simulate-principal-policy` checks after any policy edit (they are listed and reproducible in `docs/reference/deploy-credential-verification.md` Β§5) β€” the narrowness is the control (Β§10), and a widened policy is invisible from inside the repo. **The key is currently UNUSED β€” `AccessKeyLastUsed` returns `ServiceName`/`Region` = `N/A` and no `LastUsedDate`** β€” because deploys still run locally via `npm run deploy`; that does not pause the clock, and an unused long-lived key is exactly the one nobody remembers to rotate | 2026-08-28 | This is the reminder D3 has been waiting for a subject since 2026-08-26. Β§10 records the reason it matters more here than on a normal project: the secret lives on **jointly-administered** infrastructure, and the account also holds another business's client-database backups. A static key on shared infrastructure is only as safe as its age and its scope, and **nothing in the repo, the build, or AWS will prompt anyone on either.** Β§7 carries the inventory; this row carries the date | +| R17 | ⚠️ **THE CLOCK RESET AT LAUNCH β€” THE NEXT ROTATION IS DUE 2026-12-02, NOT 2026-11-26.** A **new access key was minted for `adr-sml-deploy` at cutover on 2026-09-02** and used for the first production deploy, so the quarterly clock restarts from that date on Pouya's instruction. `[Pouya's stated basis 2026-09-02 β€” reported, NOT verified in this repo]` β€” the key's existence, its creation date and **whether the 2026-08-26 key was deleted** have not been re-read from IAM by this record, and `docs/reference/deploy-credential-verification.md` still describes the 2026-08-26 key as the only one. ⚠️ **THE STEP THAT MATTERS IS STEP 4 AND IT IS THE ONE A LAUNCH SKIPS:** `docs/06` Β§Key rotation ends *"Delete the old key. Rotation that leaves the old key active is not rotation."* **Minting a second key is the first half of a rotation and looks exactly like a completed one from inside the repo.** Confirm with `aws iam list-access-keys --user-name adr-sml-deploy` and delete whichever is not in use; until that is done this row records TWO live keys, not one rotated one. **The previous text follows, unedited, because Β§12 rows are not rewritten silently.** THE DEPLOY KEY'S FIRST ROTATION IS DUE 2026-11-26, AND THAT DATE IS THE WHOLE ROW. Access key for `adr-sml-deploy` created **2026-08-26**; D3 commits to **quarterly** rotation. Pouya's instruction when Q22 closed: *"Put the DATE in the R-series reminder, not the intention."* **Rotation procedure is in `docs/06-deployment.md` Β§Key rotation** β€” create the second key, update the Gitea secrets, verify a deploy, **then** delete the first; never delete before verifying, or the next push fails with no way to authenticate the fix. **Then set the next date here (2027-02-26) rather than deleting the row.** Also re-run **all nine** `simulate-principal-policy` checks after any policy edit (they are listed and reproducible in `docs/reference/deploy-credential-verification.md` Β§5) β€” the narrowness is the control (Β§10), and a widened policy is invisible from inside the repo. ~~**The key is currently UNUSED β€” `AccessKeyLastUsed` returns `ServiceName`/`Region` = `N/A` and no `LastUsedDate`**~~ β€” **NO LONGER TRUE AS OF 2026-09-02: a key on this user has now authenticated a production deploy.** The reasoning it carried survives its own falsification and is why the row is struck rather than deleted: *because deploys still run locally via `npm run deploy`; that does not pause the clock, and an unused long-lived key is exactly the one nobody remembers to rotate* | 2026-08-28 | This is the reminder D3 has been waiting for a subject since 2026-08-26. Β§10 records the reason it matters more here than on a normal project: the secret lives on **jointly-administered** infrastructure, and the account also holds another business's client-database backups. A static key on shared infrastructure is only as safe as its age and its scope, and **nothing in the repo, the build, or AWS will prompt anyone on either.** Β§7 carries the inventory; this row carries the date | | R18 | βœ… **RE-CHECKED 2026-09-01 β€” the cutover fire. ALL SEVEN HOLD AND NO SHIPPED SENTENCE CHANGED.** Verified by Pouya (architect verification, Claude web) and recorded here with the sources, because the trigger is *"re-check before any cutover"* and this is that cutover. ⚠️ **THE STAMP IS TWO-TIER ON PURPOSE AND THE TIERS MUST NOT BE COLLAPSED: three limbs were re-verified against a source; four are held unchanged on a CADENCE JUDGEMENT rather than a fresh retrieval.** Writing all seven as "re-checked" would be the OCNI failure in miniature β€” a stamp that reads like a check and records a belief. ⚠️ **AND A CANDIDATE EIGHTH LIMB WAS FOUND WHILE STAMPING, FLAGGED RATHER THAN ADOPTED β€” (h)**: `/practice/construction/` publishes *"Ontario Power Generation … applied in March 2026 for a licence to operate it"*, a **pending application** that moves the way (a) moves. Not false today β€” the application was made, and a completed past act stays true β€” so it blocks nothing; but unlike (a) the sentence is **not time-anchored**, and a reader takes it as current status. **Pouya's call at the next re-check: adopt it as (h), or time-anchor the sentence and drop it.** **RE-VERIFIED AGAINST A SOURCE:** **(a) Bill C-36** β€” still at second reading in the House of Commons; latest completed stage **first reading, 2026-06-15**; no advance since `[re-checked 2026-09-01 β€” ]`. `/practice/technology/`'s *"was at second reading when this page was written"* stands. **This is the fastest mover of the seven and it needs no page edit while it sits, and one the day it moves.** **(d) the Tribunals Ontario annual report** β€” **no 2025-26 edition is published; FY2024-25 remains current** `[re-checked 2026-09-01 β€” ]`, so the figures `/practice/insurance/` publishes are still the latest. **This also closes an open item at the foot of `docs/reference/ontario-sabs-lat.md`** which had recorded *"NOT CONFIRMED either way… given today's date, one may well have been published"* β€” the honest gap is now answered. **(c) ERO 026-0853** β€” comment period to **2026-09-12** still open `[re-checked 2026-09-01]`. **HELD UNCHANGED ON A CADENCE JUDGEMENT, NOT RE-RETRIEVED β€” `[assumed 2026-09-01 β€” Pouya: unchanged by their nature at this cadence]`:** **(b)** the regulation under `Electricity Act` s. 28.1, **(e)** the SABS, **(f)** the ADRIC National Mediation Rules, **(g)** ADRIC's Code of Ethics. The quoted bytes in every extract are still the original retrieval and were not re-fetched; the digests in `adric-rules.md` were not recomputed, so that stamp says nothing about whether the page changed. ⚠️ **AND THE TRIGGER HAD NOWHERE TO FIRE, WHICH IS Q22'S SHAPE.** R18 names a cutover as its event and **`docs/06`'s cutover checklist carried no R18 item** β€” R10's was there, R18's was not, so a control documented here could not run where it was documented to run. **`docs/06` now carries one**, ticked for this cutover and re-armed for the next republish. Found 2026-09-02 while recording the re-stamp. ⚠️ **AND THE FIRST PASS STAMPED FIVE EXTRACTS OF SEVEN.** `ontario-construction-act.md` and `ontario-shareholder-remedies.md` carry the same standing "re-check before cutover" header and got no stamp, so a reader could not tell whether they were considered or missed β€” the same defect as this row having no checklist item, one notch smaller. Both are stamped now; `git grep -l "R18 re-check β€” cutover pass" -- docs/reference` returns **7**, and the set difference against `git grep -l "Re-check before cutover"` is **empty**. `adversarial-reviewer`, round 1. **THE ROW STAYS LIVE**: the trigger is an event and events recur. Original text follows. **THE SIX `docs/reference/` EXTRACTS BEHIND `/practice/*` ARE DATED 2026-08-29, AND SIX SHIPPED SENTENCES TURN ON FACTS THAT MOVE.** Build step 5 put statute, regulation, tribunal and bill status onto public pages β€” sourced, but **sourced as at one day**. The volatile ones, in order of how fast they move: **(a) federal Bill C-36** β€” `/practice/technology/` says it *"was introduced in June 2026 and was at second reading when this page was written"*; if it receives royal assent the page is wrong about the most load-bearing fact on it. **(b) the Ontario regulation under `Electricity Act` s. 28.1** β€” `/practice/energy/` says it *"had not been made as of August 2026"*. **(c) the ERO 026-0853 consultation**, comment period to **12 September 2026**. **(d) the Tribunals Ontario annual report** β€” `/practice/insurance/` publishes FY2024-25 figures and the extract records that a 2025-26 edition was never ruled out. **(e) the SABS**, amended with effect 1 July 2026. **(f) the ADRIC National Mediation Rules**, under review by ADRIC's own committee. **(g) ADRIC's Code of Ethics** β€” added 2026-08-30, build step 6. `/process/` Β§Confidentiality quotes it verbatim from `docs/reference/adr-institution-names.md` (retrieved 2026-08-29) **with a live link to ADRIC's page**, which is what makes it checkable and also what makes a stale quotation visible. It is the slowest-moving item here β€” a professional code, not a bill β€” so it does not change the cadence; it is listed because the trigger below says "all six" and there are now seven. **THE TRIGGER: re-check all seven extracts before any cutover, and before any republish that turns on one of them β€” the same event trigger R10 uses.** Then re-stamp the extract. **A page that was true when it was written and is false when it is read is still a false page**, and this is the first change set on the project to put that class of fact into public copy at volume | 2026-08-29 | Six sentences, six files, one retrieval date. Nothing here fires on its own; a fact with a shelf life and no owner is exactly what Β§12 exists for | | R19 | ⚠️ **DYNAMODB TTL BACKS A PUBLISHED PRIVACY PROMISE AND `/legal/privacy/` ASSERTS THE MECHANISM, NOT JUST THE PERIOD.** **Β§7 records the status and its stamp; this row deliberately does not restate it** β€” one place for a service status, or the copy that goes stale is the one nobody re-reads. **THE TRIGGER, and its two halves are not interchangeable: re-run `describe-time-to-live` and confirm `ENABLED`, THEN write a record with a near-future `ttl` and confirm it actually disappears.** `ENABLED` proves the setting; only the test record proves the behaviour. Writing the attribute proves neither β€” the handler's side is verified and is not what this row is about. Both halves are on `docs/06`'s cutover checklist and the question is Β§9 Q60. Re-stamp Β§7 the same day, **and when you do, sweep for the copies: this fact reached five files outside Β§7 in one change set and had to be pulled back.** Close this row only when the test record has been observed to vanish | 2026-08-31 | **This is R9's exact shape at higher stakes.** R9 exists because the SES alarms are configured and notify nobody until one subscription is confirmed β€” a control that reads as covered and is not. Here the control backs a **statement to the public on a privacy policy**, which is the one class of claim this project treats as unrecoverable, and the failure is silent in both directions: nothing in the repo, the build or AWS reports that records are accumulating forever. A cutover checklist fires once; Β§12 is read aloud every session | | R20 | ⚠️ **THE SEVENTH NAV ITEM ARMS TWO MEASURED HEADER DEFECTS, AND ITS TRIGGER IS A CONTENT EVENT RATHER THAN A DATE.** `SiteHeader` computes `showInsights` from the collection β€” Insights joins the masthead **automatically at two published articles** β€” so nothing in the build, the specs or a person's memory stands between publishing article #2 and arming both of these. With seven items **and fallback font metrics** (what a reader on `docs/04`'s Slow 4G profile sees during the `font-display: swap` window, at the DEFAULT text size, no reader setting involved) the header measures **141 px across a contiguous 1056–1091 px band** instead of 81 px: **(a)** it then collapses **60 px** when Geist swaps in, on all 22 pages, against the CLS < 0.05 budget; and **(b)** 141 px exceeds the 97 px `scroll-padding-top`, so "Skip to content" lands with **44 px of `#main` behind the sticky header** β€” and (b) is **new as of 2026-09-01**, the previous build's 86.97 px stayed under 97 px and covered 0. ⚠️ **HARDENED FROM A TRIGGER INTO A GATE β€” Pouya's ruling, 2026-09-01: NO SEVENTH NAV ITEM SHIPS UNTIL THE FALLBACK-METRICS DEFECT IS FIXED.** So fixing it is a **prerequisite of publishing the second Insights article**, not a follow-up to it, and *"font metric overrides on the fallback face or equivalent β€” to be designed then, not now"*. ⚠️ **AND THE GATE IS A BUILD FAILURE, NOT A CROSS-REFERENCE β€” corrected 2026-09-01 by `adversarial-reviewer`, round 2, in the same session that wrote the weaker version.** It was first implemented as three prose pointers, justified with the claim that the comment on `showInsights` in `SiteHeader.astro` is *"the only one of the three a person editing an article's front matter is likely to be reading"*. **That was backwards**: someone editing `src/content/insights/*.mdx` has no reason to open a header component. And it did not gate: with two articles flipped to `draft: false`, `npm run build` succeeded and `check`, `check:claims`, `og:proof`, `check:intake` and `lint` all exited 0 while both defects shipped. **`SiteHeader.astro` now THROWS when `published.length >= 2`**, with the measurements and the instruction in the message; it fires on both deploy paths, on the machine of whoever publishes. **Proven, not assumed:** two articles were temporarily published, `npm run build` exited **1** naming R20, and the files were restored and the restoration verified by `git diff --exit-code` plus an unchanged `dist` digest. The prose pointers remain β€” `docs/06`'s `/insights/` state item and its seventh-nav-item item under **Technical**, the latter deliberately unticked and marked NOT a cutover blocker β€” but they document the gate rather than being it. This project already knew the remedy: `content.config.ts` refuses `draft: false` without `reviewedByPouya: true` rather than trusting a comment, and `check:intake`/`og:proof` exist because a duplicated fact needs a mechanism. *Previous wording follows, and it was too weak: it asked for a re-measurement and a ruling at publication time, which leaves the defect shipping if the person publishing does not read this file.* **THE TRIGGER: before publishing the second Insights article, re-measure the masthead with seven items under blocked webfonts, and rule.** The two candidate fixes are raising the desktop breakpoint past 1091 px β€” which changes the normal-settings layout in that band β€” or giving Geist a metric-matched `size-adjust` fallback; both are outside the scope the header step was given, and both close (a) and (b) together. **Why this is a Β§12 row and not only a `docs/06` line:** a cutover checklist fires once, at cutover, and this arms itself later, on an editorial decision taken by someone who will not be reading the deployment spec. `docs/02` Β§Reflow carries the measurements | 2026-09-01 | It is latent today and invisible from inside the repo: six nav items never wrap, so every check passes, and the defect appears the day a second article ships. That is R13's shape β€” a temporary state that becomes permanent because nothing prompts anyone β€” with the added twist that the prompt would have to fire on a content event. Raised by `adversarial-reviewer`, round 2 | @@ -948,6 +949,233 @@ never being raised again. # Change Log +## 2026-09-02 (ar) β€” THE SITE IS LIVE. The D20 claims pass then ran against the shipped bytes and returned FAIL with 20 confirmed findings; the intake form is live and 403s with an empty body; and `/legal/privacy/` published carrying its own instruction not to + +**Cutover executed by Pouya on 2026-09-02** via `scripts/deploy-local.sh` with the +`adr-sml-deploy` credential, at commit `67847d9`. `https://adr.smlcompany.ca` +serves the rebuilt site; D11's single shot is spent. **This entry records the +launch and the claims pass that followed it, in that order, because that is the +order they happened in and the order is the finding.** + +### The launch, verified rather than transcribed + +Pouya reported 18 sampled routes. **Re-measured independently across 26 routes** +with the iteration count asserted, which mattered: the first sweep used +`for r in $ROUTES` and **iterated once** β€” the zsh parameter-expansion trap +`CLAUDE.md` records β€” and printed a single `000`. The assertion caught it; an +array fixed it. + +| Check | Result | +|---|---| +| All 22 pages, `robots.txt`, `sitemap-index.xml`, bio PDF | **200** β€” 26 iterated, 0 mismatches | +| Unknown path | **404**, styled Astro page, 14,321 B β€” not S3's XML | +| HTML cache-control | `public, max-age=0, must-revalidate` | +| `_astro` assets | `public, max-age=31536000, immutable` | +| Bio PDF | **89,496 B**, byte-identical to `public/` | +| **Live vs audited `dist/`** | **22 same / 0 differ / 0 errors**, SHA-256 per page against a rebuild at `67847d9` | +| `noindex` surfaces | `/bio/`, both `/legal/*`, both `/contact/` landings β€” all correct; sitemap 17 URLs | + +That last row is what makes the claims pass below meaningful: **the bytes audited +are the bytes served.** + +### The D20 cutover claims pass β€” FAIL, 20 confirmed findings + +Run at Pouya's instruction over all 23 built pages. Prior runs (2026-09-01 and +2026-09-02) predate the Q62/Q63 privacy rewrite, the `/med-arb/` strike and the +consent change, so **neither had read the shipped copy.** + +**Method, because the instrument is part of the result.** `dist/` is minified and +`claims-auditor` has only Read/Grep/Glob, so a **parse5** extraction produced the +visitor text, head metadata and pretty-printed JSON-LD of all 23 pages β€” +validated before use against known shipped strings at the counts Β§4 predicts +(the six conduct undertakings at 2/1/1/3/1/1 surfaces, the consent line at 1). +**13 auditors** β€” 8 page groups plus 5 cross-cutting lenses (adjacency, +gloss-vs-source, structured data, licensure, Β§4 Forbidden) β€” returned **41 raw +findings**, deduped to **31**, each then given to an **independent +`claims-auditor` instructed to refute it**. **20 CONFIRMED, 11 REFUTED.** Two +completeness critics added 13 more. 46 agents, 0 errors. + +**The brief that ran was the `bd282aa` version**, unchanged since before this +session began β€” checked, per `CLAUDE.md`'s rule that agent definitions load at +session start. + +**THE CREDENTIAL SPINE TRACED CLEAN FOR THE THIRD PASS RUNNING.** Not one of the +20 is a claim about Pouya, his credentials, his designations, his memberships or +the boutique. D13, D16, the Forbidden table and the Q.Arb stage vocabulary were +swept with every hit printed and read β€” `lso` was *"aLSO"*, `licenc` was *"a +licence question"* about software, every `leading` was the `--leading-*` token. +**Zero untraceable claims about the subject.** What failed is two other classes. + +#### Class 1 β€” glosses that assert more than their committed extract establishes + +The failure shape this project has now hit **five for five**: a correct verbatim +quotation with an over-reaching sentence beneath it. `/practice/energy/` calls +the IESO connection process **six stages** where the extract says *"up to six"* +and scopes it by connection type. `/practice/construction/` states the 30-day +adjudication determination without its *"Subject to subsection (2)"* extension, +which the same extract quotes immediately beneath. `/practice/technology/` +asserts Ontario *"has one AI instrument"* β€” a universal over the four instruments +its extract checked, which is the exact defect corrected on that page on +2026-08-31, recurring in different words. + +#### Class 2 β€” disclosures describing a backend that is not deployed + +**This is the serious half, and it is on the privacy policy of a live site.** + +- `/legal/privacy/` Β§How long it is kept: *"deleted automatically by the database + rather than by someone remembering to do it"* β€” **Q60 is open**, TTL's + behaviour has never been observed, Β§7 records the deployed handler as the + hand-built one that writes **no `ttl`**, and 4 pre-existing items will never + expire. +- `/legal/privacy/` Β§Where it is stored and `/contact/received/`: *"Two emails + are sent when you submit the form"*, *"A confirmation goes to the email address + you gave"* β€” **nothing is sent, because nothing can be submitted.** +- `/legal/privacy/`'s own meta description ends *"Written to match what is + built."* It is the one page where the register records that it does not. + +### πŸ›‘ The intake form is live and broken + +Found while verifying the launch, not by the claims pass. `/contact/` ships +`
`. **`POST /api/intake` returns HTTP +403 with `content-length: 0`** and an `apigw-requestid` header β€” the request +reaches API Gateway and is rejected because the only route is `POST /submissions` +(Β§7). **A visitor who fills in the intake form gets a blank page.** No styled +error, no message, no fallback. `docs/09` Part 6 is the fix. + +### πŸ›‘ `/legal/privacy/` published carrying its own instruction not to + +`src/pages/legal/privacy.astro:229` holds a live `TODO(pouya)` ending, in terms: + +> *"This page must not go public until a deletion has actually been seen."* + +**It is a JSX comment, so Astro strips it and it never reaches `dist/`.** That is +why `npm run check:claims`, the build and both deploy paths were all green over +it. **A publication gate that lives only in a stripped comment is not a gate** β€” +the same shape as Q22 and the Lighthouse row: a control everyone believed in that +could not fire. `docs/06`'s *"No `TODO(pouya)` remains in any shipped page"* is +the item that would have caught it, and it was never ticked. Verified directly: +`git grep -n 'TODO(pouya)' -- src/` β†’ 3 hits, exit 0. + +### What D20 rested on, and no longer has + +D20's reasoning is explicit that deferring the claims pass is safe because +*"nothing has shipped and there is no public site, so every claims finding to +date has been about a page no visitor can reach."* **That premise expired at +cutover, and the pass ran after it.** Recorded, not argued β€” whether D20 needs +amending is Pouya's. The honest version: the deferral bought nine build steps of +speed, and the bill came due on a live page. **Nothing was fixed in this change +set** β€” `claims-auditor`'s brief ends *"Never rewrite copy yourself. Report, and +let the implementer fix it"*, and Pouya's instruction was to report verbatim. + +### The 20 confirmed findings + +1. **[GLOSS OVERREACH]** `/practice/technology/` β€” Β§The backdrop, "What is actually in force, as of this page" (paragraph 2 lead) + > Ontario has one AI instrument, and it is mostly not switched on. +2. **[GLOSS OVERREACH]** `/practice/construction/` β€” Β§The machinery, "Interim adjudication" + > An adjudicator must determine the referred matter no later than 30 days after receiving the referring party's documents +3. **[ADJACENCY]** `/practice/construction/` β€” Β§The context, "Ontario is building, and building generates disputes" (second paragraph, immediately after the Darlington and Bruce C sentence) + > Programmes on that scale run for years, through dozens of trade contracts, and they produce exactly the disputes above. +4. **[GLOSS OVERREACH]** `/practice/energy/` β€” Β§The machinery, "Where the processes actually sit.", second paragraph + > Connection runs through the IESO, and it is not a queue. The IESO operates a six-stage connection process and calls it connection assessment and approval. +5. **[GLOSS OVERREACH]** `/practice/energy/` β€” Β§The machinery, "Where the processes actually sit.", third paragraph, under the bolded lead "And large loads now have their own gate." + > Section 28.1 of the Electricity Act, 1998 came into force on 11 December 2025 and creates a connection-approval requirement for a "specified load facility", a category defined to include data centres meeting criteria that may be set by regulation. +6. **[NOT IN REGISTER]** `/practice/insurance/` β€” Β§What comes up, "The disputes.", third card in the definition grid + > Treatment and assessment plans β€” Denied or partially approved plans, competing assessments, and disputes about the reasonableness and necessity of proposed treatment. +7. **[GLOSS OVERREACH]** `/practice/shareholder/` β€” Β§"The alternative", second paragraph, lead "And the end of the road." (src/data/practice-pages.ts:611) + > the Ontario Partnerships Act lets a partner apply to the court to dissolve a partnership on grounds that include conduct making it not reasonably practicable to carry on business together +8. **[NOT IN REGISTER]** `/bio/` β€” Β§The practice, first sentence (src/pages/bio.astro:141-142). The identical sentence is in the committed circulated artefact, public/pouya-lajevardi-bio.pdf, read and confirmed. + > I act as a neutral β€” as a mediator, as an arbitrator in commercial matters, and in med-arb where the parties want one neutral across both phases. +9. **[NOT IN REGISTER]** `/fees/` β€” Β§Arbitration, detail line on the "Documents-only or expedited β€” simple" row (src/pages/fees.astro:134). Its neighbour, on the "β€” complex" row, reads verbatim: "Flat fee. Which band applies is settled before the appointment." + > Flat fee, agreed in the first procedural order. +10. **[ADJACENCY]** `/fees/` β€” hero lede, immediately under the H1 "Published in full, including what overruns cost." Related on the same page: the Β§Also offered heading, "Three things beside the two processes." + > Every figure is on this page, and none of it has to be asked for. +11. **[NOT IN REGISTER]** `/legal/privacy/` β€” Β§How long it is kept + > 24 months from the date you send it, after which the record is deleted automatically by the database rather than by someone remembering to do it. +12. **[NOT IN REGISTER]** `/legal/privacy/` β€” Β§Where it is stored + > In a DynamoDB table in Amazon Web Services' Canada Central region, in Canada. It is encrypted at rest. Two emails are sent when you submit the form β€” a notification to the practice and a confirmation to you β€” using Amazon Simple Email Service, also in the same Canadian region. +13. **[NOT IN REGISTER]** `/legal/privacy/` β€” Β§Asking for a copy, or asking me to delete it + > Deletion removes the record. It does not retract the emails already sent, and if a conflicts check has already been run I will tell you what its outcome was rather than pretending the inquiry did not happen. +14. **[NOT IN REGISTER]** `/legal/privacy/` β€” Β§Cookies and analytics + > There is no tracking script on any page, nothing is stored on your device, and there is therefore nothing to consent to and no banner. +15. **[GLOSS OVERREACH]** `/practice/energy/` β€” Β§The machinery, under the heading "Named precisely, because two of these are routinely called something they are not." + > The IESO operates a six-stage connection process and calls it connection assessment and approval. +16. **[GLOSS OVERREACH]** `docs/01-architecture.md:533-535` β€” Β§`/practice/insurance/`, inside the box that directs what the page must say + > **Rule 14.3:** a **Member** presides and is then disqualified from the hearing panel; **Rule 14.6:** parties must attend. The neutral is the Tribunal's. +17. **[GLOSS OVERREACH]** `/practice/technology/` β€” Β§The backdrop, paragraph 2, sentence 2. Ships at dist/practice/technology/index.html:7; source src/data/practice-pages.ts:259 + > The Enhancing Digital Security and Trust Act, 2024 conditions each of its artificial-intelligence obligations on regulations prescribing who they apply to and when. +18. **[NOT IN REGISTER]** `/mediation/` β€” Β§Confidentiality ("What stays in the room"), final paragraph. Source src/pages/mediation.astro:229 + > Mediation is conducted on a without-prejudice basis. +19. **[NOT IN REGISTER]** `/legal/privacy/` β€” Β§Where it is stored + > Two emails are sent when you submit the form β€” a notification to the practice and a confirmation to you β€” using Amazon Simple Email Service, also in the same Canadian region. +20. **[NOT IN REGISTER]** `/legal/terms/` β€” Β§This site's own content + > Links out go to sources β€” statutes, regulators, tribunals and institutions. I do not control those sites and am not responsible for what they say. + +**The 11 refuted** are recorded in the full report rather than here; each was +dismissed against a Β§4 row or a committed extract read at audit time, and several +were refuted **because a Β§4 row carries an explicit carve-out** that would have +flagged correct copy if applied literally β€” the `SML Company Ltd.` terminal +period, the unscoped masthead *"Arbitration"* (ratified under Q33/Q50), and +*"each party should have their own legal advice"* (the ratified worked example of +a pass). + +### Also recorded + +- **Β§7 gains a `THE SITE IS LIVE` row** β€” the operational fact of record, with + the verification above and the three live defects. +- **Β§7's deploy-credential row: `NEVER USED` is struck.** A key on + `adr-sml-deploy` has now authenticated a production deploy. +- **Β§12 R17 β€” the rotation clock resets to 2026-09-02, next due 2026-12-02.** + Pouya reports minting a new key at launch. ⚠️ Recorded as + `[Pouya's stated basis]` and **not** re-read from IAM: whether the 2026-08-26 + key was deleted is unknown to this record, and `docs/06` Β§Key rotation ends + *"Rotation that leaves the old key active is not rotation."* **Minting the + second key is half a rotation and looks identical to a whole one from inside + the repo.** +- **`X-Robots-Tag: noindex` is absent on the PDF** `[verified 2026-09-02 β€” + curl -I]`. `/bio/` is `noindex` and out of the sitemap; the PDF it renders to + is linked from `/about/` and is crawlable. Unticked `docs/06` item, now live. +- **No credential reached the repo** β€” `git grep -nE 'AKIA[0-9A-Z]{16}| + aws_secret_access_key|ASIA[0-9A-Z]{16}'` exits **1**, 0 matches, and the tree + has no untracked files, which is what closes `git grep`'s known hole. +- **The headshot is soft on the live site, and it is not the master.** Pouya + raised it; measured rather than guessed. The build is faithful and the srcset + ladder is correct β€” 9 device profiles, **ratios 1.00–1.21, no upscaling + anywhere**, `sizes` (476 px) matching the measured rendered width exactly. + **The cause is that Astro passes no `quality`, so sharp's per-format defaults + apply β€” AVIF 50, WebP 80, JPEG 80 β€” and `formats={['avif','webp']}` puts AVIF + first, so every modern browser gets the quality-50 encode.** At 960 px it + retains **55%** of the reference's high-frequency energy against WebP's 85% and + JPEG's 95%. Sweep: q60 β†’ 76% at 33 KB, q65 β†’ 80% at 39 KB, q70 β†’ **90% at + 51 KB**, q80 β†’ 94% at 77 KB, against today's 21 KB. **This is a live decision + for Pouya, not a defect to fix silently**: the portrait is the LCP element from + 768 px up and `/`'s LCP already sits at 2.03 s against `docs/04`'s < 2.0 s, so + +30 KB needs a Lighthouse re-run. Three call sites are affected β€” + `index.astro`, `about.astro`, `InfinityMark.astro` β€” and none sets `quality`. +- **Two instrument failures caught before they became findings**, both in the + "uniformly good" direction `CLAUDE.md` names as the dangerous one. (1) A + `sharp(src).resize(800).resize(1600)` round trip returned **PSNR Infinity** β€” + the second `.resize()` **overrides** the first, so the image was compared with + itself; it read as *"the master carries no detail"* and would have sent Pouya + hunting for a new photograph. (2) A Chrome sweep reported **exactly 2.00x / + 3.00x upscaling on every DPR>1 profile** β€” `img.naturalWidth` is + **density-corrected** under `w` descriptors and merely echoes the `sizes` + value. Re-measured against the real on-disk widths: no upscaling at all. **The + instrument-failure list in `CLAUDE.md` stands at eight and both of these are + the same family; they are recorded here rather than added, because neither is a + new shape.** + +### Still open + +**Q60** β€” the retention mechanism, window started 2026-09-02, **check from +2026-09-04**, failure not called before 7 days. **The 20 claims findings** β€” none +fixed. **The intake form** β€” end-to-end test in progress (Pouya). **R17** β€” the +old key's state. **The post-launch group** β€” Gitea runner (Q23), the two +dependency majors, the iOS Safari / Android Chrome / Firefox pass, and **R5**'s +fee review. Β§12 **R1** surfaced and unchanged: `/about/` carries the full +credential spine on a live page with no licence status stated either way, and one +of the two completeness critics reached that finding independently, from the copy +alone. + ## 2026-09-02 (aq) β€” The favicon shipped with no transparency and is regenerated from the committed master; Pouya's full-site read-through completes with that as its only finding, and the cutover blockers drop to one **Pouya's read-through of every page against Β§4 is COMPLETE, 2026-09-02.** It is diff --git a/docs/06-deployment.md b/docs/06-deployment.md index 39f0c14..b4d1bfd 100644 --- a/docs/06-deployment.md +++ b/docs/06-deployment.md @@ -395,8 +395,25 @@ Then invalidate `/*`. > reversing them puts 22 of 23 pages behind a 403 for as long as a CloudFront > deployment takes. -> πŸ›‘ **ONE THING BLOCKS THIS ENTIRE LIST AS AT 2026-09-02, AND IT IS A WAITING -> PERIOD RATHER THAN A TASK: Q60.** +> βœ… **CUTOVER EXECUTED 2026-09-02 β€” THE SITE IS LIVE AT `https://adr.smlcompany.ca`.** +> Deployed by Pouya from this machine via `scripts/deploy-local.sh` with the +> `adr-sml-deploy` credential. **Verified independently the same day rather than +> transcribed**, 26 routes with the iteration count asserted (a `for r in $VAR` +> loop ran ONCE first β€” the zsh trap `CLAUDE.md` records, caught by the count): +> all 22 pages, `robots.txt`, `sitemap-index.xml` and the bio PDF return **200**; +> an unknown path returns **404** and serves the styled Astro page, 14,321 B, not +> S3's XML. HTML carries `max-age=0, must-revalidate`, `_astro` assets +> `max-age=31536000, immutable`, and the PDF is **89,496 B**, matching `public/` +> exactly. **All 22 live pages are byte-identical to a local `dist/` rebuilt at +> `67847d9`** β€” SHA-256 compared page by page, 22 same / 0 differ / 0 errors. The +> five `noindex` surfaces and the 17-URL sitemap are correct. +> +> πŸ›‘ **BUT THIS LIST WAS NOT CLEAN WHEN THE SITE PUBLISHED, AND THAT IS THE +> RECORD, NOT A REPROACH. THREE BLOCKING ITEMS WERE OPEN AT THE MOMENT OF +> CUTOVER AND TWO STILL ARE.** D11 is a single shot and the checklist exists +> because of it; a launch that crosses its own gates should be legible as one +> afterwards rather than smoothed over. **What follows is the state as at +> 2026-09-02, after the D20 pass ran against the shipped bytes.** > > βœ… **THE READ-THROUGH IS COMPLETE β€” Pouya, 2026-09-02, and it returned ONE > FINDING WHICH WAS NOT COPY.** `public/favicon.ico` shipped with no @@ -406,11 +423,15 @@ Then invalidate `/*`. > line, and `/med-arb/` **as shipped**. That discharges blocker 2 and every > wording sign-off that had been routed into it. > -> ⚠️ **THE COUNT NOW READS ONE AGAIN, AND THE EARLIER ONE WAS A DEFECT β€” READ -> THE REASON, NOT THE NUMBER.** It said ONE earlier on 2026-09-02 because an -> approval had gone **missing** from the list; it says ONE now because the pass -> that approval was routed into has been **done**. A tally cannot tell those -> apart, which is the point the note below has been making all day. +> ⚠️ **THE COUNT WENT UP, AND THAT IS THE FIRST TIME IT HAS β€” READ THE REASON, +> NOT THE NUMBER.** It read ONE for part of 2026-09-02 because an approval had +> gone **missing**; it then read ONE because the pass that approval was routed +> into had been **done**; it now reads **THREE**, because the site published and +> the D20 pass then ran against the shipped bytes and returned **FAIL**. Every +> earlier move in this note was a tally shrinking for a reason a tally could not +> show. This one grew, and the lesson is the same in the other direction: **the +> list did not get worse, the measurement finally happened.** Two of the three +> were true before cutover and unmeasured; one was invisible by construction. > > ⚠️ *(The count has moved repeatedly in one day and the DIRECTION is the only > part worth reading β€” the number of moves is deliberately not stated, because a @@ -428,14 +449,46 @@ Then invalidate `/*`. > pass, promoted here from the checklist below because that is where the last > reserved approval went missing.)* > -> 1. **Q60 β€” the retention MECHANISM has still not been observed.** TTL is -> `ENABLED` and no record has been watched to disappear, and -> `/legal/privacy/` asserts the mechanism, not merely the period. `docs/09` -> Part 10 is the test; its earliest useful reading is **48 hours** after the -> record is written and it does not call failure before **7 days**, so -> **start it before anything else on this page.** It is the one blocker -> that is a waiting period rather than a task. -> 2. βœ… **DONE 2026-09-02 β€” Pouya read every page against `AGENTS.md` Β§4.** The +> 1. πŸ›‘ **Q60 β€” the retention MECHANISM has still not been observed, AND +> `/legal/privacy/` PUBLISHED ANYWAY.** TTL is `ENABLED` and no record has been +> watched to disappear, and the page asserts the **mechanism**, not merely the +> period. `docs/09` Part 10 is the test; earliest useful reading **48 hours** +> after the record is written, failure not called before **7 days** β€” Pouya +> started the window 2026-09-02, so **check from 2026-09-04**. +> ⚠️ **THE PAGE CARRIED ITS OWN INSTRUCTION NOT TO DO THIS AND IT WAS +> INVISIBLE AT DEPLOY TIME.** `src/pages/legal/privacy.astro:229` holds a live +> `TODO(pouya)` ending, in terms: *"This page must not go public until a +> deletion has actually been seen."* It is a **JSX comment**, so Astro strips +> it and it never reaches `dist/` β€” which is exactly why `check:claims`, the +> build and both deploy paths were all green over it. **A publication gate +> that lives only in a stripped comment is not a gate.** The checklist item +> *"No `TODO(pouya)` remains in any shipped page"* below is the control that +> would have caught it and it was never ticked. +> 2. πŸ›‘ **THE INTAKE FORM IS LIVE AND BROKEN β€” a submitter gets a blank page.** +> `/contact/` ships ``; **`POST +> /api/intake` returns HTTP 403 with `content-length: 0`** and an +> `apigw-requestid` header, so the request reaches API Gateway and is rejected +> because the only route is `POST /submissions` (Β§7). No styled error, no +> message, no fallback. Measured against production 2026-09-02. `docs/09` +> Part 6 is the fix; Pouya has the end-to-end test in progress. +> ⚠️ **AND `/legal/privacy/` AND `/contact/received/` BOTH DESCRIBE THAT +> MECHANISM AS RUNNING** β€” *"Two emails are sent when you submit the form"* and +> *"A confirmation goes to the email address you gave"*. Nothing is sent, +> because nothing can be submitted. Found by the D20 pass; see item 3. +> 3. πŸ›‘ **THE D20 CLAIMS PASS HAS NOW RUN AGAINST THE SHIPPED BYTES AND RETURNED +> FAIL β€” 20 CONFIRMED FINDINGS ON LIVE PAGES.** Run 2026-09-02 at `67847d9`, +> after cutover, over all 23 built pages: 13 auditors (8 page groups + 5 +> cross-cutting lenses) β†’ 41 raw findings β†’ 31 distinct β†’ each adversarially +> verified by an independent `claims-auditor` instructed to refute it. **20 +> CONFIRMED, 11 REFUTED**, plus 13 further findings from two completeness +> critics. See the **`claims-auditor`** item below for the breakdown, and +> `AGENTS.md`'s Change Log entry of 2026-09-02 (ar) for the full list. +> **Nothing here is a claim about Pouya, his credentials or his designations β€” +> every one of those traced, for the third pass running.** The failures are +> over-reaches in glosses on sourced legal material, and disclosures on +> `/legal/privacy/` and `/contact/received/` that describe a backend which is +> not deployed. +> 4. βœ… **DONE 2026-09-02 β€” Pouya read every page against `AGENTS.md` Β§4.** The > human pass, the other half of D20 and not delegable. It was also where the > Β§Who can see it approval was routed, and his ruling that the read-through > *is* the approval means that sign-off is now discharged rather than @@ -609,7 +662,47 @@ the decision is re-readable rather than re-litigated. Change Log). **All corrected or declined; the pass is clean on findings and the item is open only on the human halves above.** - **Two things about that result are worth carrying forward.** The pass found + πŸ›‘ **AND IT HAS NOW RUN A THIRD TIME β€” 2026-09-02, AFTER CUTOVER, AGAINST + THE BYTES THAT ACTUALLY SHIP β€” AND RETURNED FAIL WITH 20 CONFIRMED + FINDINGS. THIS ITEM STAYS UNTICKED.** The two runs above predate the + Q62/Q63 privacy rewrite, the `/med-arb/` strike and the consent change, so + neither had seen the shipped copy. This one did: all 22 live pages were + confirmed **byte-identical** to a `dist/` rebuilt at `67847d9` before the + audit began, and the auditors read a parse5 extraction of the visitor text + and JSON-LD of all 23 pages, validated against known shipped strings first. + **13 auditors β€” 8 page groups and 5 cross-cutting lenses (adjacency, + gloss-vs-source, structured data, licensure, Β§4 Forbidden) β€” returned 41 + raw findings, deduped to 31, each then handed to an independent + `claims-auditor` instructed to REFUTE it. 20 confirmed, 11 refuted.** Two + completeness critics added 13 more. Full list: `AGENTS.md` Change Log + 2026-09-02 (ar). + + **The shape of the 20, because it is the same shape as run 2 and that is + the finding about the process rather than the pages.** Not one is a claim + about Pouya, his credentials, his designations, his memberships or the + boutique β€” **the credential spine has now traced clean three passes + running**, and D16, D13 and the Forbidden table were swept with every hit + printed and read. What failed is two classes: **glosses that assert more + than their committed extract establishes** (`/practice/energy/` calling the + IESO process six stages where the source says *"up to six"*; + `/practice/construction/` stating the 30-day adjudication deadline without + its *"Subject to subsection (2)"* extension; `/practice/technology/` + asserting Ontario *"has one AI instrument"*), and **disclosures describing a + backend that is not deployed** (`/legal/privacy/` on retention and on the + two emails, `/contact/received/` on the confirmation). The second class is + not a wording problem: it is the privacy policy of a live site describing a + mechanism that cannot run, which is the defect class `AGENTS.md` Q22 named. + + ⚠️ **AND THE PASS RAN AFTER THE SITE PUBLISHED, WHICH IS THE ONE THING D20 + RESTED ON AND NO LONGER HAS.** D20's reasoning is explicit that deferring + the claims pass is safe because *"nothing has shipped and there is no public + site, so every claims finding to date has been about a page no visitor can + reach."* That premise expired at cutover. It is recorded here rather than + argued: whether D20 needs amending is Pouya's call, and the honest version + of the trade is that the deferral bought nine build steps of speed and the + bill came due on a live page. + + **Two things about the earlier result are worth carrying forward.** The pass found no defect in any claim about Pouya, his credentials or his designations β€” every one traced. What it found was **five over-reaches in glosses on sourced legal material**, which is the failure mode a per-step claims run on