feat: build step 6 — /process/ and /for-parties/, the first step under D20
Build and deploy / build-and-deploy (push) Failing after 4s

Two pages, 14 in dist/ (was 12), zero JavaScript, one <h1> each, no skipped
heading levels. Both were dangling links in SECONDARY_NAV since step 1; the
footer is now whole on every page.

/process/ — the five steps from intake to minutes of settlement or an award,
with PROCESS_FRAMING rendered adjacent to the timings rather than in a section
lede above them, which is the condition Q43 attached to publishing them at all.
Conflicts says WHEN the check runs and WHAT it needs, and then stops: any
sentence naming the outcome would be a seventh conduct undertaking, and §4's
gate requires that Pouya has made it in terms. Logged as Q57.

/for-parties/ — second person, grade 9, for a party arriving without counsel.
NEUTRAL_ROLE_LINE gets its own section above the FAQ rather than a slot in it.
Eight questions in one array feeding both the visible page and the FAQPage node,
so a question cannot reach the structured data without appearing on the page.
The word "lawyer" appears nowhere, deliberately, and the reason is in the file
header.

Three defects worth recording.

The link colour on a dark band measured 1.88:1, and 1.10:1 on hover. `--link`
is documented at 8.95:1 ON CREAM and was never overridden for an inverse
ground; SiteFooter sets its own colour, which is the only reason this had not
fired before — /process/ shipped the site's first body link on a dark band.
That is worse than the gold-on-cream 2.10:1 this project treats as canonical.
Fixed in global.css, where the next inverse-ground link will hit the same hole.
The `:not(.btn)` guard is load-bearing, not tidiness: the new rule's
specificity (0,2,1) beats `.btn-gold`'s (0,2,0), so without it the repair would
have recoloured every gold button on a dark band.

The fee shipped as "with preparation included", which docs/07 forbids in terms:
the allowance is CAPPED, so the unqualified form sells an uncapped one, and
/mediation/ already promises the site does the opposite. `prepIncluded` had
been declared in site.ts with no consumer anywhere.

/process/ §Confidentiality asserted that confidentiality is settled in the
terms of appointment — a claim about Pouya's engagement documents that §4 does
not row and no reader can check. Replaced with ADRIC's Code of Ethics quoted
from the committed extract, plus the §4-verified membership rendered from
MEMBERSHIP_ORGS. The replacement then said "on this it is one sentence", a
completeness claim about a third party's Code that the extract cannot support —
the same file records a separate instrument eighteen lines above. The cited
source supplied its own counter-example.

check:claims failed the build on compliant copy: five uses of "lawyer" about
the READER, which §4 permits since it bars the word used OF Pouya. There is no
allowlist, and the freeze bars narrowing the pattern, so the copy moved — the
same call and the same direction as "my client" to "our client" on /med-arb/.
Both replacements are better than what they replaced.

Q58 opened rather than guessed: docs/07 reads "up to 3.5 h, INCLUDING 2 h
preparation", which makes 3.5 the billed envelope and the room time 1.5 h,
against which 3.5 and 7 being exactly 2x makes no sense. A draft had answered
"What happens on the day?" with "about 3.5 hours" — the envelope presented as
the day, to the reader least able to check it. The sentence was removed.

Two review rounds, 16 findings, all applied. Verified: check 0 errors, lint 0,
build 0 (14 pages), check:claims 0, npm audit 0, minifier tripwire clean.
Lighthouse not run — tool unavailable until build step 7.

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 07:33:58 -04:00
co-authored by Claude Opus 5
parent 2ea4c0f8ac
commit 13b97841b9
9 changed files with 917 additions and 36 deletions
+307 -6
View File
@@ -608,10 +608,10 @@ do not re-open it against the quoted ruling above.**
| Undertaking | Where it ships | Status |
|---|---|---|
| **(a) Med-arb — the switch.** *"The switch is agreed in writing before the mediation phase begins, or I do not take the appointment. I will not convert a mediation into an arbitration on the day because the room has run out of road."* | `/med-arb/` §What I undertake | `[verified 2026-08-29 — Pouya, Q54]` |
| **(a) Med-arb — the switch.** *"The switch is agreed in writing before the mediation phase begins, or I do not take the appointment. I will not convert a mediation into an arbitration on the day because the room has run out of road."* | `/med-arb/` §What I undertake **and `/process/` §If it does not settle** (added 2026-08-30, build step 6 — it is the answer to the question that section exists to answer, and it is rendered from the constant, not retyped) | `[verified 2026-08-29 — Pouya, Q54]` |
| **(b) Med-arb — caucus material.** *"If a party tells me something in caucus they are not prepared for me to rely on as arbitrator, they say so at the time, and it does not enter the arbitral record."* | `/med-arb/` §What I undertake | `[verified 2026-08-29 — Pouya, Q54]` |
| **(c) Med-arb — the hard one.** *"If I cannot decide a remaining issue without relying on something said to me in confidence, I say so and step out of the arbitral phase rather than decide on it."* | `/med-arb/` §What I undertake | `[verified 2026-08-29 — Pouya, Q54]`. **SHIPS AS DRAFTED, and this row carries his reasoning because it is the only one that cost anything to make.** *"It is the strongest available answer to the med-arb objection, and cheaper in practice than it sounds — the arbitral phase runs on the evidentiary record, not the caucus, so the case where a neutral genuinely cannot decide without confidential material is uncommon. `/med-arb/` was raising the hardest question about med-arb and answering it only at the level of process design."* Q54 warned that *"a neutral who would not honour it must not publish it"*; he published it. **This is the row where softening would be worst and least visible** — a version that says he "may" step out, or that he "considers" stepping out, is a different commitment wearing the same words |
| **(d) Mediation — caucus confidentiality.** *"What a party tells me in caucus stays in that caucus until they tell me I may use it, and I do not carry a number across the hall that I was not given to carry."* | `/mediation/` §Confidentiality | `[verified 2026-08-29 — Pouya, Q54]`. Shipped for one pass at step 4 and removed by `claims-auditor` — correctly: the gate had been applied to `/med-arb/` in the same change set and not one file over. It is here now because it is answered, not because the gate relaxed |
| **(d) Mediation — caucus confidentiality.** *"What a party tells me in caucus stays in that caucus until they tell me I may use it, and I do not carry a number across the hall that I was not given to carry."* | `/mediation/` §Confidentiality, **`/process/` §Confidentiality, and `/for-parties/` §Questions** — the last both visibly and inside that page's `FAQPage` node (all added 2026-08-30, build step 6). **Three surfaces.** The row recorded two for one pass; `adversarial-reviewer` found the third, and an incomplete "where it ships" on the one class Pouya flagged as failing *silently* is this column doing the opposite of its job | `[verified 2026-08-29 — Pouya, Q54]`. Shipped for one pass at step 4 and removed by `claims-auditor` — correctly: the gate had been applied to `/med-arb/` in the same change set and not one file over. It is here now because it is answered, not because the gate relaxed |
| **(e) Arbitration — procedure.** *"I will not run a process whose shape nobody agreed to in advance."* | `/arbitration/` §Rules | `[verified 2026-08-29 — Pouya, Q54]`. Same one-pass history as (d). **It REPLACED a third-person sentence rather than joining it** — the page already ended that paragraph *"What does not is a process whose shape nobody agreed to in advance"*, the observation form of the same proposition, and keeping both would have set the undertaking beside its own paraphrase |
| **(f) Arbitration — the award date.** *"The date the award is due is fixed in the first procedural order rather than left open."* | `/arbitration/` §Awards | `[verified 2026-08-29 — Pouya, Q54]`. Same replacement as (e), of *"The date an award is due belongs in the first procedural order"*. **The sentence after it is unchanged and is doing different work:** *"No number is published here"* is §4 Forbidden's bar on a time-to-award statistic, which is not what this undertaking is |
@@ -703,8 +703,8 @@ the audience it targets. Revisit at month 1218. `[verified 2026-08-25 — dec
| Thing | Value |
|---|---|
| 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-29 — R11, step 45 phase boundary]`. **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` widens the range to include 7.** 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-27 — npm view, all 14 pins]`. `@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 <pkg> 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` |
| **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-30 — R11, step 56 phase boundary; unchanged from 2026-08-29]`. **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. **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` widens the range to include 7.** 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 <pkg> 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` |
| Lighthouse CI | **Not installed, and that is now stated wherever the check is claimed.** `@lhci/cli` was the sole source of all 10 `npm audit` findings (7 high, via `lighthouse → puppeteer-core → extract-zip`), 0.15.1 is `latest` so there was no clean upgrade, and it cannot run at all — no pages, no `lighthouserc`. Removed 2026-08-26; **`npm audit` now reports 0 vulnerabilities** `[verified 2026-08-26 — npm audit]`. Removing it left a **documented control that no longer exists**`docs/04`, `CLAUDE.md` and `/build` Phase 5 all required a run that had silently become impossible, which is Q22's defect in a different costume. Fixed 2026-08-26: the unavailability is stated in `docs/04-seo-spec.md` (budget table, the callout under Performance, and the post-launch checklist), `CLAUDE.md` (performance budget and the definition of done), `.claude/commands/build.md` Phase 5, `docs/06-deployment.md` (PR checks and the cutover checklist), and `.claude/agents/adversarial-reviewer.md` §4 — which is also told **not** to raise the absence as a finding. **Re-add at build step 7** with a pin verified that day and a check for a patched release; do not assume 0.15.1 is still the ceiling (R11) |
| Node | **22 LTS line**, `.nvmrc` holds `22` so it tracks the latest 22.x rather than freezing at a floor. `package.json` declares **`engines.node` = `^22.13.0 || >=24`** (verbatim — it excludes Node 23, an EOL odd line, which a flat `>=22.13.0` would have admitted) and **`engines.npm >=9.6.5`** — the intersection of every dependency's own floor: Astro 7 and `@astrojs/mdx` 7 need `>=22.12.0`, ESLint 10 needs `^20.19.0 \|\| ^22.13.0 \|\| >=24` `[verified 2026-08-26 — npm view astro@7.2.7 engines, npm view eslint@10.9.1 engines]`. **Pouya's machine reports Node v25.6.0** `[verified 2026-08-26 — node -v]`, an odd-numbered non-LTS line; it satisfies the floor, but `nvm use` keeps local and CI on the same runtime |
| Styling | Plain CSS with custom properties. No Tailwind, no CSS-in-JS `[verified 2026-08-25]` |
@@ -771,6 +771,8 @@ Nothing below can be invented. Each needs an answer from Pouya.
| # | Question | Blocks |
|---|---|---|
| **Q58** | **DOES `hours` IN THE MEDIATION RATE CARD MEAN THE LENGTH OF THE DAY, OR THE BILLED ENVELOPE INCLUDING PREPARATION?** `docs/07-fees.md` reads *"Half day — **up to 3.5 h, including 2 h preparation**"* and *"Full day — up to 7 h, including 3 h preparation"*. Taken at face value, 3.5 is the whole billed envelope and the **time in the room is 1.5 h** for a half day and **4 h** for a full day. **Against that reading:** 3.5 and 7 are exactly 2×, which they would not be if preparation sat inside them (1.5 vs 4 is not 2×). So either the card's wording is wrong in the one document that is the authority on money, or `FEES.mediation.*.hours` in `src/data/site.ts` does not mean what a page would naturally publish it as. **This was one sentence from shipping.** A draft of `/for-parties/` answered *"What happens on the day?"* with *"A half day is about 3.5 hours"* — the envelope presented as the day, to the reader least able to check it. The sentence was removed rather than resolved by inference; the page now says only that you book a half day or a full day. **What is needed:** one line from Pouya saying which the 3.5 and 7 are. Then `docs/07`'s two rows or `site.ts`'s field gains the correction, and the semantics go in the constant's doc comment (a warning is there now). **`/fees/` at build step 9 publishes this table and cannot be built without the answer.** Raised by `adversarial-reviewer`, 2026-08-30 | **`/fees/` (step 9), and any sentence anywhere stating how long a mediation day is.** Nothing currently shipping — no page states a duration |
| **Q57** | **WHAT HAPPENS WHEN A CONFLICTS CHECK TURNS SOMETHING UP?** `/process/` §Conflicts ships saying **when** the check runs (the intake call, before anything is agreed) and **what it needs** (full legal names of the parties, the parent or affiliate actually behind the dispute, counsel on each side). It stops there, and the stop is deliberate: **any sentence naming the outcome is a SEVENTH conduct undertaking**, and §4's gate for that class is one line — *"an undertaking may be published only where Pouya has made it in terms. Not 'would obviously agree to', not 'follows from the process' — said."* *"If a conflict appears I decline the appointment"* is exactly what that gate refuses to let this repository infer, however obvious it looks. **What is needed:** one sentence from Pouya, in his words, or a decision that the page says nothing further. `TODO(pouya)` sits at `src/pages/process.astro` §Conflicts. Raised at build step 6, 2026-08-30 | **The outcome half of `/process/` §Conflicts.** The section ships and is accurate; a reader who asks the obvious next question does not find the answer on the page |
| ~~Q56~~ | **RULED 2026-08-30 — MEDIATION IS *NOT* SCOPED COMMERCIAL.** Pouya: *"Correct the four 'Commercial Mediation' surfaces to 'Mediation'; leave §4's mediation row unscoped."* **And the asymmetry with arbitration is DESIGNED, not an oversight — the reason is now recorded beside both §4 rows so nobody tidies them into a matching pair.** **Arbitration is scoped commercial because of a LEGAL GATE:** Q39 — family arbitration in Ontario requires prescribed training, and Pouya has excluded it by choice. **Mediation has no such gate**; he mediates commercial, construction, insurance/SABS, shareholder and cross-cultural matters, and the practice pages say so. So the site-wide "commercial" framing was **under-describing a wider offering**, which is why it is corrected rather than ratified as a positioning choice. `/practice/insurance/`'s SABS framing needs no reconciliation: it was never outside the offering. **The question as raised is preserved below.** ⚠️ **IS THE MEDIATION OFFERING SCOPED COMMERCIAL, OR NOT? THE SITE SAID BOTH.** §4 Offerings rows arbitration three times, each **scoped commercial**; the mediation row is `**Mediation** — sole mediator`, **unscoped**. Shipped output scopes it anyway: `/mediation/`'s `<title>` is *"Commercial Mediation"*, its `Service` node is `name: "Commercial mediation"`, and `/` and `/about/` both say *"I mediate commercial disputes"*. Meanwhile **`/practice/insurance/` offers mediation in matters that are not commercial on any ordinary reading** — *"Disputes between an insured person and an insurer under the Statutory Accident Benefits Schedule"*, with *"What I offer is private mediation"*. An individual claimant against their own insurer is not a commercial dispute. **Nothing here is a false claim** — scoping a page to commercial mediation asserts *less* than the unscoped §4 row grants, and narrower than the row is always safe. **The problem is that the two halves cannot both be the whole picture**: either the practice takes non-commercial mediations (and the site-wide "commercial" framing under-describes it, including on the page an appointing body reads), or it does not (and `/practice/insurance/` is offering something outside the offering). **What is needed:** either a §4 Offerings row scoping mediation, with `/practice/insurance/`'s SABS framing reconciled to it — or a decision that mediation is deliberately unscoped, in which case the site-wide "commercial" wording is a positioning choice and should be recorded as one rather than read as a limit. **Pre-existing, not created 2026-08-30** — but this change set newly wrote the claim onto `/med-arb/` and it has been removed again pending this answer. Raised by `adversarial-reviewer`, 2026-08-30 | **Nothing — ruled the same day it was raised.** No page was wrong; the register was silent where the site was specific, and the ruling makes the site match the register rather than the other way round |
| ~~Q55~~ | **CLOSED 2026-08-30 — NOT RESOLVED, AND THE DIFFERENCE IS THE RULING.** Pouya: *"The date is not published and nothing depends on it. Your split stamp was right: `[verified]` on the status, `[Pouya's stated basis]` on the date, the 2026-08-26 record noted as unreconciled. A register that says 'two inconsistent reports of an unpublished fact, neither relied on' is complete. Do not put it to Pouya."* **So this row is not a question any more and must not be re-raised as one.** §4's Q.Arb row now carries the split stamp explicitly and marks the 2026-08-26 (a) report **unreconciled, permanently**. **Why closing beats answering here:** the register's job is to say what it can vouch for, and it can vouch for exactly this — that he reported a July acquisition, that he had reported an August commencement three days earlier, and that no published claim rests on either. Asking again would buy a third self-report about a fact the site does not state. **The question as raised is preserved below, because the evidence is the record.** ⚠️ **WHEN WAS Q.Arb ACTUALLY OBTAINED? THE REGISTER HOLDS TWO DATES FROM THE SAME SOURCE AND THEY CANNOT BOTH BE TRUE.** (a) **2026-08-26 (a)**, Change Log, `[verified 2026-08-26]`: *"Q.Arb dated. Old → new: `[assumed]`, stage unknown → **commenced August 2026**"*. (b) **2026-08-29**, Pouya's amendment, now the §4 Verified row: *"Obtained July 2026"*. A designation cannot have been obtained in July from a pathway reported as commencing in August, and (a) was reported three days before (b). One self-report is wrong; the register cannot say which, and **inferring it would be exactly the guessed-explanation failure `CLAUDE.md` bars**. **THIS DOES NOT BLOCK, and that is the whole reason it is a question rather than a hold.** The published claim is *held***which is faithful to his most recent instruction, and that is a different thing from correct.** §4's premise is that `[verified — Pouya]` establishes *what he reported*, never the fact, and this is the one row where the register holds documented evidence that a self-report about this credential is wrong. The date is deliberately unpublished, so no page turns on the answer. What turns on it is whether §4 can vouch for its own highest-stakes new row. **What is needed:** one line from Pouya giving the correct date, and whether (a) was a misreport, a different milestone, or something else. Then re-stamp the §4 row and correct or annotate (a) — the Change Log is append-only, so (a) gets a pointer, never an edit. Raised by `claims-auditor`, 2026-08-30 | **Nothing — and closed on that basis rather than despite it.** The site states what Pouya most recently instructed, and no page carries the date |
| ~~Q1~~ | **ANSWERED 2026-08-26.** Licensure is left out entirely; the site asserts the JD only. See D13 | — |
@@ -915,7 +917,7 @@ never being raised again.
| R5 | **Fee review at 12 months.** Published rates are sticky; the right moment to move them is deliberate, not reactive | 2026-08-26 | D14 is priced for where the practice is going, not where it is |
| R6 | **Booking tool.** Parked by Pouya on 2026-08-26; `/contact/` ships with the intake form and a reserved slot for an embed | 2026-08-26 | He asked to be reminded. D10 committed to booking because it removes the back-and-forth that loses appointments — the form alone is a partial answer |
| R9 | **The SES alarms notify nobody until the `ses-alerts` email subscription is confirmed.** `SES-BounceRate-High` and `SES-ComplaintRate-High` are configured and live; the SNS email subscription to `info@smlcompany.ca` is **pending confirmation**, and an unconfirmed subscription drops every message | 2026-08-26 | A monitoring control that exists but does not deliver is worse than none, because it reads as covered. At this volume five bounces can cross the ~5% suspension threshold. Tracked in §7 and on the cutover checklist, but a one-click task nobody owns is exactly what §12 is for |
| R10 | **DISCHARGED AS WRITTEN 2026-08-28 — AND RE-ARMED WITH AN EVENT TRIGGER INSTEAD OF A DATE. STILL LIVE.** Pouya re-confirmed all four memberships as current (Q44), which discharges the prohibition this row carried, and `/about/` now publishes the Memberships group. **The row does not close, because he declined renewal-date tracking**, and that was his instruction for what to do about it: *"Without renewal months it cannot fire on a date, so make it fire on an event: re-confirm memberships before any cutover or major republish, and re-stamp §4 when confirmed."* **THE TRIGGER: re-confirm before any cutover, and before any major republish. Then re-stamp §4 the same day.** **His reason, kept verbatim because it is the general principle and not a membership detail:** *"§4 already carries OCNI as lapsed and unpublishable, and that was found roughly a year late. A stamp with no trigger behind it goes stale silently, which is exactly how OCNI got onto a list of things to feature."* **Two things the discharge did NOT license.** (1) **No currency warranty on the page** — list the memberships, promise nothing about their future state; the struck sentence stays struck and nothing replaces it. (2) ~~`memberOf` stays out of the JSON-LD~~**SUPERSEDED. Q53, ruled 2026-08-28: EMIT IT.** `/about/`'s Person node now carries the four memberships as `Organization` nodes. Pouya took `adversarial-reviewer`'s argument: they are already crawlable in `/about/`'s HTML, so withholding the triple reduced no exposure and only made the graph less complete than the page. **The consequence for THIS reminder is that it now covers two surfaces** — re-confirming before a cutover means `src/data/schema.ts` as well as the visible list, and they must not be allowed to diverge. **Renewal periods, stated once and not widened again:** the OBA sections and the CTF renew yearly; §4 records **nothing** about ADRIC's or ADRIO's period, and the widened form ("all four renew yearly") reached four files before it was swept. *Previous text described the prohibition and the withheld group; it held for one session and did its job.* | 2026-08-26 | A credential that lapses quietly is the failure mode §4 exists to prevent, and OCNI already did exactly this. The group is on a public page now, which raises the cost of a lapse rather than lowering it — *(This rationale ended by pointing at **Q48** as a possible widening of the row. Q48 closed 2026-08-28 as not site-relevant — ADRIO retention governs whether Pouya keeps a designation, not what the site may say about holding one — so the clause is struck. §12 is read aloud every session; a live reminder pointing at a struck row produces a false surface every time.)*, not just a list |
| R10 | ⚠️ **A THIRD SURFACE, 2026-08-30: `/process/` §Confidentiality renders `MEMBERSHIP_ORGS[0]` ("I am a member of the ADR Institute of Canada").** It is rendered from the constant rather than typed, so the sweep this reminder prescribes reaches it — that was `adversarial-reviewer`'s finding and the fix, in that order. **DISCHARGED AS WRITTEN 2026-08-28 — AND RE-ARMED WITH AN EVENT TRIGGER INSTEAD OF A DATE. STILL LIVE.** Pouya re-confirmed all four memberships as current (Q44), which discharges the prohibition this row carried, and `/about/` now publishes the Memberships group. **The row does not close, because he declined renewal-date tracking**, and that was his instruction for what to do about it: *"Without renewal months it cannot fire on a date, so make it fire on an event: re-confirm memberships before any cutover or major republish, and re-stamp §4 when confirmed."* **THE TRIGGER: re-confirm before any cutover, and before any major republish. Then re-stamp §4 the same day.** **His reason, kept verbatim because it is the general principle and not a membership detail:** *"§4 already carries OCNI as lapsed and unpublishable, and that was found roughly a year late. A stamp with no trigger behind it goes stale silently, which is exactly how OCNI got onto a list of things to feature."* **Two things the discharge did NOT license.** (1) **No currency warranty on the page** — list the memberships, promise nothing about their future state; the struck sentence stays struck and nothing replaces it. (2) ~~`memberOf` stays out of the JSON-LD~~**SUPERSEDED. Q53, ruled 2026-08-28: EMIT IT.** `/about/`'s Person node now carries the four memberships as `Organization` nodes. Pouya took `adversarial-reviewer`'s argument: they are already crawlable in `/about/`'s HTML, so withholding the triple reduced no exposure and only made the graph less complete than the page. **The consequence for THIS reminder is that it now covers two surfaces** — re-confirming before a cutover means `src/data/schema.ts` as well as the visible list, and they must not be allowed to diverge. **Renewal periods, stated once and not widened again:** the OBA sections and the CTF renew yearly; §4 records **nothing** about ADRIC's or ADRIO's period, and the widened form ("all four renew yearly") reached four files before it was swept. *Previous text described the prohibition and the withheld group; it held for one session and did its job.* | 2026-08-26 | A credential that lapses quietly is the failure mode §4 exists to prevent, and OCNI already did exactly this. The group is on a public page now, which raises the cost of a lapse rather than lowering it — *(This rationale ended by pointing at **Q48** as a possible widening of the row. Q48 closed 2026-08-28 as not site-relevant — ADRIO retention governs whether Pouya keeps a designation, not what the site may say about holding one — so the clause is struck. §12 is read aloud every session; a live reminder pointing at a struck row produces a false surface every time.)*, not just a list |
| R11 | **Re-check dependency currency at every phase boundary in the build order** (`docs/01-architecture.md` §Build order, 11 steps). Run `npm view <pkg> version` across **every** pin in `package.json` and compare; do not wait for something to break. Verified does not mean latest — record the reason for any deliberate hold in §7. **Carries one specific re-add trigger: at step 7, put `@lhci/cli` back.** Check for a patched release — do not assume `0.15.1` is still the ceiling just because it was on 2026-08-26 — pin it against `npm view` that day, write a `lighthouserc` with the budgets from `docs/04-seo-spec.md`, and delete the UNAVAILABLE notices this repo now carries in six places (§7 lists them). If the advisories are still unfixed, that is a decision to take deliberately, not a reason to leave the gap unstated | 2026-08-26 | `astro: "^5.0.0"` was recalled rather than checked and was two majors stale the day it was written, which meant a framework carrying high-severity XSS advisories. Between phases is cheap; after a phase of pages is written is not. The build order has ten more boundaries |
| R12 | **`compressHTML: true` is a deliberate deviation from the Astro 7 default (`'jsx'`).** Measured 2026-08-26: in an `.astro` template an inline pair split across two lines renders as `<em>a</em><strong>b</strong>` under the default — the space is silently deleted. MDX prose is unaffected | 2026-08-26 | It is a deviation, and undocumented deviations become folklore. Revisit **with a measurement**, not a preference — and re-measure after any Astro major, since the behaviour could change again |
| R13 | **The infinity mark ships as a RASTER, and that is temporary. RAISED 2026-08-27; Pouya ruled the committed SVG does NOT close it***"Keep it committed, keep the AVIF render path. Your own measurement is the reason: 257 KB wrapping seven embedded base64 PNGs. It renders faithfully because it IS the raster."* So the exception stands and the reminder stays live. `InfinityMark.astro` renders an optimised AVIF/WebP from `src/assets/brand/sml-infinity-mark.png` — a deliberate, documented exception to `docs/02`'s "inline SVG, never a PNG", because the mark is gradient-mesh artwork and no true vector master exists yet (Q38). **Removal trigger: the commissioned vector master lands.** Then replace the `<Picture>` with inline SVG, regenerate `favicon.ico` and `apple-touch-icon.png` from it, and delete the exception from `docs/02`, from the component, and from Q38 | 2026-08-26 | Pouya flagged this himself when he made the ruling: *an interim raster is exactly the kind of temporary measure that becomes permanent by never being raised.* It costs ~8 KB and works, which is precisely why nobody will notice it again. There is no build error to prompt anyone — only this row |
@@ -923,7 +925,7 @@ never being raised again.
| R15 | **Every page shares ONE Open Graph image — the portrait — and that is an interim for seventeen of the nineteen. RULED, DEFERRED, AND TRACKED HERE ON POUYA'S INSTRUCTION:** *"Record the trigger so it cannot quietly become permanent."* Q40, 2026-08-27. The portrait is the **decided** card for `/` and `/about/` — a face is the strongest social preview for a personal brand. It is the wrong card for the other seventeen, where a typed card carrying the page title does the work. **Removal trigger: build step 7**, where Insights needs per-article cards anyway — one generator (`satori` or `astro-og-canvas`, pinned against `npm view` that day per R11), one dependency, one review. Then delete the interim note from `docs/04` and the cutover line from `docs/06`. **Blocks cutover; does not block steps 36** | 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 costs nothing and works, which is exactly the R13 shape |
| R16 | **The one-page PDF bio does not exist, and it is deferred to build step 9 — not dropped. RULED AND TRACKED HERE ON POUYA'S INSTRUCTION:** *"Record the trigger so it doesn't become another quiet interim."* Q45, 2026-08-28. His reasoning: it is a **derived artefact**, so building it before `/about/` and `/fees/` are final means building it twice — and an appointment proposal needs the fee card as much as the bio. **Removal trigger: build step 9, alongside `/fees/`.** Two decisions travel with it and are **not** settled by the deferral: (a) generated at build (a dependency and an `npm audit` surface, against R11) or authored once as a designed artefact; (b) whether it carries anything the site does not — a matter list, a fee summary, referees — **each of which is a §4 question of its own**, and the matter list would collide with §4 Forbidden directly. Then delete `docs/01` §`/about/` item 7's pending note | 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. `/about/` currently ships with no link to it, which is correct — a dead link on the page an appointing body reads is worse than the absence — and that means **nothing on the site will ever prompt anyone about this.** Only this row will |
| 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 |
| R18 | **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. **THE TRIGGER: re-check all six 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 |
| R18 | **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 |
| ~~R7~~ | **RATIFIED / SUPERSEDED 2026-08-26.** (a) Cache-policy table matching the pipeline — **accepted**; documenting what the pipeline does beats documenting an intention. (b) `s3:AbortMultipartUpload` omitted — **accepted, reasoning corrected**: the lifecycle rule does not exist and is therefore not the cover; the actual cover is that `aws s3 sync` only goes multipart above 8 MB and the largest asset is a 357 KB portrait. Recorded in `docs/06-deployment.md` with a revisit trigger. (c) The `aws s3 ls` pre-flight — **superseded** by the variable guard now running as the workflow's first step | 2026-08-26 | — |
| ~~R8~~ | **PROMOTED TO A RULE 2026-08-26.** A reminder was too weak for a pattern that survived three entries. *A sweep is a command, not a claim* now sits in `CLAUDE.md` under Conventions, in `/build` Phase 6, and in `/wrap` step 3: any claim that a change was applied across files must cite the command and be written only after reading its output | 2026-08-26 | — |
@@ -931,6 +933,305 @@ never being raised again.
# Change Log
## 2026-08-30 (af) — Build step 6: `/process/` and `/for-parties/`, the first step under D20 — and the frozen `check:claims` failed the build on compliant copy, which is the freeze working
**Two pages, 14 in `dist/` (was 12), zero JavaScript, one `<h1>` each, no skipped
heading levels.** The rulings they were built under are entry (ae) below.
**Reviewed by `adversarial-reviewer` only — D20, first application.**
`claims-auditor` was **not** run; it runs once, at cutover. Saying which agent
ran is now required by `/build` Phase 3, because "reviewed" reads as both.
### `/process/` — the shape of an engagement
`PROCESS` and `PROCESS_FRAMING` rendered from `src/data/site.ts`, so `/` and
`/process/` cannot drift on the five timings. **Q43's framing sits directly under
the last step**, not in the section lede — verified in the built bytes, not
asserted: the sentence follows *"Day 30 onward · Minutes of settlement — or an
award"* with nothing between them.
Conflicts, confidentiality and the non-settlement outcome are the three things
`docs/01` requires beyond the steps. Two of the three close with a **conduct
undertaking rendered from the constant**, never retyped — (d) on confidentiality
and (a) on the switch — and §4's "Where it ships" column is updated for both.
### The third one stops early, and the stop is the point — Q57
`/process/` §Conflicts says **when** the check runs and **what it needs**. It does
not say what happens when one turns something up, because **that sentence would
be a seventh conduct undertaking** and §4's gate for that class is one line:
*"an undertaking may be published only where Pouya has made it in terms. Not
'would obviously agree to', not 'follows from the process' — said."*
*"If a conflict appears I decline the appointment"* is exactly what that gate
refuses to let this repository infer, however obvious it looks. `TODO(pouya)` in
the source, **Q57** in §9. One sentence from him closes it.
### `/for-parties/` — and it is the page `docs/03` warns about
The one page in the second person. `NEUTRAL_ROLE_LINE` gets **its own section
above the questions** rather than a slot inside them: `docs/03` requires this
page to say it explicitly, and the sentence a party most needs is not one to make
them scroll for.
Eight questions, written as questions a party actually asks, so `FAQPage` is
honest — `docs/04` allows the node here *"only where the visible page genuinely
is Q&A"*. **One array feeds both the visible page and the node**, so a question
cannot enter the structured data without appearing on the page. The caucus
undertaking renders through `<Undertaking>` visibly and is joined into the
answer text for the node, which is plain text by schema — one source, two shapes,
neither typed twice. **Fee figures are interpolated from `FEES`**, so the page a
party reads cannot drift from D14's card.
**Reading level, measured rather than claimed, and the instrument checked both
ways.** `docs/03` requires grade 9. A syllable heuristic that **undercounts**
(it merges `ia`/`io` and eats `-le`) returns FleschKincaid **4.5**; one
corrected to split those vowel groups **overcounts** and returns **4.9**. Both
bracket the true value well under the requirement, and the syllable-independent
metric agrees: **9.6 words per sentence** in `<main>`, longest sentence 36 words.
Reported as a range because a single estimate here would be a claim about the
counter.
### `check:claims` failed the build on compliant copy, and the copy moved
The page shipped *"Do I need my own lawyer?"*, *"coming without a lawyer"* and
*"you are not a lawyer"* — every one of them about the **reader**. §4 bars the
word *used of Pouya*, so all three are compliant on the register's own wording.
**`licensure-of-pouya` failed the build on five matches anyway**, because a bare
`\blawyer\b` cannot tell whose.
**The copy moved, not the gate**, and there were three reasons rather than one:
1. **There is no allowlist.** `mustNotMatch` fixtures only assert in the
self-test that a pattern stays silent on approved strings; they are **not**
applied when scanning `dist/`. Adding one would not have unblocked the build
— it would have made the self-test fail instead. Worth recording, because
"add it to the approved list" is the obvious first move and it does not exist.
2. **The precedent runs this way.** When `/med-arb/` shipped *"the neutral who
heard my client in caucus"* — counsel's voice, not Pouya's — the copy changed
to *"our client"* and the pattern widened. Same shape, same call.
3. **The freeze.** Narrowing `\blawyer\b` to exclude reader-owned uses is a
refactor of the strongest pattern on the site, on the page `docs/03` names as
where the barred phrasing is most tempting. That is the coverage work the
freeze bars.
The replacements are better copy, which is the part worth noting: *"Should I get
my own legal advice?"* uses `NEUTRAL_ROLE_LINE`'s own wording, and the hero lede
went from *"you are not a lawyer"* to *"nobody has explained what one is"*
which targets the need rather than the reader's credential. The constraint is
recorded in the page header so the next editor does not re-introduce it.
### Two dangling internal links closed, on every page
`SECONDARY_NAV` in `src/data/site.ts` has linked `/process/` and `/for-parties/`
from the footer since step 1, and neither page existed. **Every one of the 12
previously built pages therefore shipped two broken internal links.** `site.ts`
is unchanged in this change set — the pages simply arrived. All internal links
now resolve except `/contact/`, `/fees/`, `/insights/` and `/legal/*`, which are
build steps 710.
### Structured data
`/process/` emits **`Person` only** — a new `pageGraph()`, which
`practiceIndexGraph()` now delegates to. **No `Service` node, deliberately**: the
five timings are publishable only under Q43's framing sentence, and a `Service`
node would carry the numbers into a field where no framing travels with them.
`/for-parties/` emits `Person` + `FAQPage`; `faqNode()` is factored out and
`medArbGraph()` now calls it instead of carrying its own copy of the mapping.
### R11 at the step 5 → 6 phase boundary — same one pin, same recorded reason
`npm view` run across all fourteen pins, exit status read. **Thirteen are
current.** `typescript` is pinned `^6.0.3` against a latest of `7.0.2` — the same
deliberate hold recorded at the step 4 → 5 boundary, and the trigger was
**re-verified rather than recalled**:
```
$ npm view @astrojs/check@0.9.10 peerDependencies
{ typescript: '^5.0.0 || ^6.0.0' }
```
`astro check` is the type gate both deploy paths run, so the hold stands.
**Removal trigger unchanged: `@astrojs/check` widens the range.** A hold on a
dependency's schedule, not on a judgement.
### The code review — 9 findings, 2 blocking, all accepted
`adversarial-reviewer` only, per D20. **`claims-auditor` was not run.**
**BLOCKING 1 — a body link shipped at 1.88:1, which is worse than the defect this
project treats as canonical.** `/process/`'s Code-of-Ethics link sits on
`.section-inverse` and inherited the global `a { color: var(--link) }`
`--maroon-l`, a token whose own comment says *"links on cream 8.95:1"*. Nothing
overrode it for a dark ground. **Measured independently before acting**, and the
instrument was validated by reproducing three documented values exactly (8.95:1
maroon-l on cream, 11.09:1 gold-l on ink, 16.81:1 cream on ink):
```
1.88:1 --link (maroon-l #7a2a2c) on --bg-inverse (ink #1a1614)
1.10:1 :hover (maroon-d #3d1112) on the same ground
```
`CLAUDE.md` records gold-on-cream at **2.10:1** as the thing that must never
ship. This was worse, on hover by a factor of two. **`/process/` is the site's
first body link on a dark band** — `SiteFooter` colours its own, which is the
only reason the hole had never opened. Fixed in `global.css` beside the
`:focus-visible` rule that exists for exactly this reason, not in the page.
**And the `:not(.btn)` in that fix is load-bearing, not decoration.**
`.section-inverse a:not(.btn)` has specificity (0,2,1); `.btn-gold[data-astro-cid]`
has (0,2,0). Without the exclusion the new rule would have **overridden every
gold button on an inverse ground**, which is the repair introducing a second
defect — the exact shape `/build` Phase 4 exists to catch. Verified after the
fix by enumerating every non-`.btn` anchor inside any `.section-inverse` or
`.section-accent` across all 14 built pages: **one, and it is this link.**
**BLOCKING 2 — the fee shipped as "with preparation included", which `docs/07`
forbids in terms.** *"Preparation time is bundled and **must be stated on the
page** — 'including 2 hours of preparation'… **Do not quietly fold it into the
hours figure.**"* The allowance is **capped**, so the unqualified form sells an
uncapped one, to the reader least able to check it — and `/mediation/` already
ships the promise that the site does the opposite. `grep -n prepIncluded src/`
returned **only the declaration**: the constant existed for this exact sentence
and had no consumer anywhere. Now interpolated on both figures.
**And one finding I could not resolve, so it became Q58 rather than a guess.**
`docs/07`'s card reads *"Half day — up to 3.5 h, **including** 2 h
preparation"*, which makes 3.5 the **billed envelope** and the time in the room
**1.5 h**. Against that: 3.5 and 7 are exactly 2×, which they would not be if
preparation sat inside them. One of the two is wrong, and a draft of
`/for-parties/` had already answered *"What happens on the day?"* with *"A half
day is about 3.5 hours"* — the envelope presented as the day. **The sentence was
removed, not reconciled**; the page now says only that you book a half day or a
full day. `site.ts`'s `hours` field carries a warning against publishing it as a
duration. **`/fees/` at step 9 cannot be built without the answer.**
**The other six, all accepted:**
1. ***"on this it is one sentence"* was a completeness claim about a third
party's Code that the cited artefact contradicts.** The extract is a scrape
of one web page carrying three quoted lines, and **eighteen lines above** it
records a separate instrument — ADRIC's **Code of Conduct for Mediators**
bearing on the same subject. The source cited supplied the counter-example.
Fourth instance of the named gloss pattern, after the LAT ellipsis, the IESO
"connection allocation" and the `/med-arb/` ADRIC gloss — **and the first one
caught before shipping.** Now reads *"and it says:"*.
2. **"Caucus" was undefined jargon on the one page whose spec bars jargon.** Used
twice, never defined; §11 has no entry; `/mediation/` — the *counsel*-facing
page — actually glosses it and `/for-parties/` did not. Glossed in the page
copy, **never inside `CONDUCT_UNDERTAKINGS.mediationCaucus`**. Note the
measurement's blind spot: FleschKincaid put the page at grade 47, well
inside the grade-9 requirement, **because the formula cannot see a single
unfamiliar noun.** A reading-level score is not a jargon check.
3. **`docs/01` still instructed *"not your lawyer"*** — struck in `docs/03` on
2026-08-28 with a two-point explanation, and the sweep never reached
`docs/01`. **This change set edited `docs/01` and did not fix it.** The page
avoided the trap; the spec that directs the page did not. Replaced with a
pointer to `NEUTRAL_ROLE_LINE` and the struck forms.
4. **§4 undertaking row (d) recorded two surfaces and there are three** —
`/for-parties/` publishes it visibly and inside the `FAQPage` node. An
incomplete "where it ships" on the one class Pouya flagged as failing
*silently* is that column doing the opposite of its job.
5. **`/process/` hand-typed the ADRIC membership.** Character-identical to
`MEMBERSHIP_ORGS[0]`, and R10's re-confirm trigger sweeps the constants, not
the pages — so after a lapse this page would have kept asserting it. The OCNI
shape exactly. Now interpolated, and R10 records the third surface.
6. **`practiceIndexGraph` had become a pure alias** — one caller, zero
behaviour, 13 lines of comment over 3 lines of code, and the comment's
reasoning is about `/practice/` rather than about graph shape. My churn from
an hour earlier. Deleted; `practice.astro` imports `pageGraph` and the
reasoning now sits beside the call that makes the decision.
### One defect I introduced while fixing them, and what caught it
Removing the duration turned a template literal into a plain string — **but I
replaced the closing backtick and left the opening one**, so the frontmatter
fence broke and TypeScript read the rest of the file as one string. **`astro
check` returned exit 1 with 50 errors**, and both deploy paths run `check` before
`build`, so nothing could have shipped. Recorded because it is the case
`CLAUDE.md`'s truncation rule is about from the other side: the verdict was read
as an **exit status**, not as the tail of a pipe.
### Two limits on this pass, stated rather than omitted
- **Lighthouse: not run — tool unavailable.** `@lhci/cli` removed 2026-08-26,
returns at step 7 under R11.
- **Mobile layout at 390px: not measured.** Headless Chrome lays out at a wider
viewport and crops to the requested width; the reviewer validated that by
checking already-reviewed pages clip identically, so the clipping is the
instrument rather than the pages. Element-level widths at 390px remain
unmeasured and need a real driver — `puppeteer` is not installed, only an
empty `node_modules/@puppeteer` directory exists.
### And a sweep caveat worth keeping
**`git grep` silently misses untracked files.** Both new pages were untracked for
the whole of this build, so any `git grep` across step 6 returned a clean result
that meant nothing. Use plain `grep -rn` until the files are staged. Same family
as R8: a real command, read honestly, and the wrong command.
### And working the `docs/03` checklist against the pages found the checklist wrong
Not a review finding — this came from actually running the compliance checklist
rather than assuming it. Item 2 read *"No matter counts, settlement rates,
**dollar figures**, or time-to-award stats"*, and `/for-parties/` is **the first
page on the site to publish a fee** (`grep -roh '\$[0-9][0-9,]*' dist` returns
`$2,000` and `$4,000`, both on that page and nowhere else). Read literally, the
checklist forbade the page `docs/01` commits to and the rate card D8 commits to.
**§4's own row was never wrong** — it reads *"Aggregate value resolved; any
dollar figure attached to **past matters**"*. The checklist **dropped the
qualifier when it mirrored it**, which is `CLAUDE.md`'s duplicated-fact rule
exactly: the copy that goes stale is the one nobody re-reads. Item scoped, with
§4's wording cited rather than restated.
**Third instance of an unscoped checklist line flagging the copy the spec
requires** — the other two are the testimonials item and the capacity-phrasing
item, both corrected the same way, and `docs/03` records that the checklist *"is
what gets grepped"*.
### Verification — exit statuses read directly, never through a pipe
```
npm run check exit 0 0 errors / 0 warnings / 0 hints (34 files)
npm run lint exit 0
npm run build exit 0 14 pages
npm run check:claims exit 0 11 patterns, 26 approved strings, 14 pages
npm audit exit 0 0 vulnerabilities
```
Also run and read: the minifier trap (`grep -rE 'animation:[^;}]*(scroll\(\)|
view\(\))' dist --include='*.css'` → **exit 1, no match**); a secret scan whose
**own** exit status was read rather than a pipeline's (**exit 1, no match** —
the first attempt piped through `head` and reported `head`'s status, which is
the failure `CLAUDE.md` names); all 14 pages for one `<h1>`, no skipped heading
levels, canonical, OG/Twitter, JSON-LD parse, and title/description inside
5060 / 140160 (**14/14 in band, all unique**); every internal link resolved
except `/contact/`, `/fees/`, `/insights/` and `/legal/*`, which are steps 710;
zero `client:*` directives and zero non-JSON-LD `<script>` on either new page.
**`PROCESS_FRAMING` verified in the shipped bytes**, not asserted: it follows
*"Day 30 onward · Minutes of settlement — or an award"* with nothing between.
### And the `set -- $pair` bug recurred, in the direction that is dangerous
`CLAUDE.md` records a zsh digest-comparison loop that printed **`DIFFER` on all
five rows** because `set -- $pair` gave the loop body one argument. The same loop
shape was used here to confirm nine files matched a saved copy, and it printed
**`same` on all nine** — a uniformly *good* result from a comparison that never
ran. `shasum` was invoked as `shasum -a 256 "AGENTS.md AGENTS.md"`, errored, and
left **both** variables empty, so `[ "$a" = "$b" ]` compared `""` to `""` and
passed.
**The `DIFFER` version announces itself; this one does not.** "Uniformly bad"
gets re-checked because it is alarming. **"Uniformly good" is what you were
hoping for, and it ends the check.** Re-run in Python with an `os.path.isfile`
assertion on both sides — the step the shell loop skipped — and the nine files
are genuinely byte-identical. Add to the sweep discipline: **a comparison must
assert that both things it compares exist.**
---
## 2026-08-30 (ae) — D20 moves the claims pass to cutover; Q56 rules mediation UNSCOPED and records why the asymmetry with arbitration is designed; Q55 is closed without being resolved; and `check:claims` is frozen
**Three rulings and two conventions, all Pouya's, recorded in one entry because