feat: build step 4 — /mediation/, /arbitration/, /med-arb/; source ADRIC's rules
Build and deploy / build-and-deploy (push) Failing after 4s

Three pages, five in the build, zero JavaScript. /arbitration/ carries §4's
paired-disclosure condition on four surfaces and Q39's struck universal appears
in no form. /med-arb/ meets the procedural-fairness objection at the level of
process design and ships deliberately without Pouya's own protocol commitments,
which are Q54.

docs/01 directed the mediation page to name the "ADRIC Model Mediation Rules".
No such document exists — 0 occurrences across all four of ADRIC's rules pages
against 10 for "National Mediation Rules"; "Model" belongs to the Model Dispute
Resolution Clause inside the rules. Caught only because R14 requires the source
before the claim. docs/reference/adric-rules.md + adric-extract/ carry it, with
the digest drift measured rather than assumed: the HTML changes per request, the
text extracts are byte-stable, so the extracts are the artefact.

Four review passes, 21 defects, and the pattern was mine: I wrote the Q54 gate
into the page and then breached it four times, then round 2 found two survivors
of round 1's own fixes and one defect round 1's fix created. Also removed a
<title> naming a practised role §4 does not grant, a habitual presupposing awards
issued, and a claim about what ADRIC's rules permit that my own reference doc
says is unsupported.

Two instrument failures caught before they became conclusions: touch targets
measured over file:// with no CSS loaded (uniform 18px, including on a .btn with
a 44px floor), and a schema.org validator call that parsed nothing and returned
0 warnings for everything. Both re-run with the instrument validated first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0148NztQskLKKApP5SzAA78e
This commit is contained in:
Pouya Lajevardi
2026-08-28 17:06:27 -04:00
co-authored by Claude Opus 5
parent 2282183b4b
commit f3138a0a79
18 changed files with 2496 additions and 18 deletions
+204
View File
@@ -701,6 +701,7 @@ Nothing below can be invented. Each needs an answer from Pouya.
| ~~Q51~~ | **ANSWERED 2026-08-28 — KEEP THE OBA SECTIONS. The distinction is structural, not evidentiary.** Pouya: *"the Law Society is the regulator, so membership IS licensure; the OBA is a voluntary association. Structural distinction, independent of eligibility details."* That last clause is what closes it: the question was raised as unanswerable inside this repo **because OBA eligibility is not established here** — and the ruling makes eligibility irrelevant. A voluntary association admits members it does not license, so listing it carries no licensure implication; a regulator's membership roll *is* the licence. Recorded in §4's memberships row and in `.claude/agents/claims-auditor.md` so it is not re-litigated, and removed from `docs/06`'s cutover checklist. **R1 is unaffected** — same page, same subject, different question, still live | — |
| ~~Q52~~ | **CLOSED 2026-08-28 — committed, and RE-RUN rather than transcribed.** Pouya: *"YES — commit the simulate results, the inline policy, and the `NoSuchBucketPolicy` response, access key ID redacted."* `docs/reference/deploy-credential-verification.md` — eighteen read-only calls, each with the command that produced it, generated from captured output with the key ID replaced by `AKIA…REDACTED`. **Every value in §7's row reproduced**, including all nine `simulate-principal-policy` decisions (four `allowed`, five `implicitDeny`). **Two things the re-run added that the original verification did not have.** (1) A **contrast call**: `get-bucket-policy` on the *site* bucket returns exit 0 and a real policy, which is what makes exit **254** / `NoSuchBucketPolicy` on the backup bucket a genuine absence rather than a command that failed to start — the rule `CLAUDE.md` records twice, applied to the evidence that closes a High risk. (2) A correction to §7's own wording on the key's `LastUsed` field shape. The **secret** access key was never requested; the API cannot return it | — |
| ~~Q53~~ | **ANSWERED 2026-08-28 — EMIT IT. The withholding is dropped.** Pouya: *"the memberships are already crawlable in `/about/`'s HTML, so withholding the triple reduces no exposure and only makes the graph less complete than the page."* Option (a) of the three the question offered. `src/data/schema.ts` now emits `memberOf` — the four §4 memberships as `Organization` nodes — **on `/about/` only**, which is where the page shows them, so the graph and the visible page assert the same set. **This ends a judgement that was restated three times and wrong twice:** cacheability proved too much (equally true of `hasCredential`, which ships), volatility did not survive the page already carrying the claim, and the widened *"yearly renewing"* premise it rested on was itself a §4 misstatement found on re-audit | — |
| **Q54** | **Six conduct commitments are drafted and NONE ships. Confirm them, rewrite them, or decline them — three pages are the poorer for their absence and none of them may carry a commitment you have not made.** `docs/03` requires `/med-arb/` to meet the procedural-fairness objection *"head-on"* and rates that section the reason the page is worth reading; `/mediation/` and `/arbitration/` have the same gap in smaller form. **All three pages now answer at the level of PROCESS DESIGN** — what an agreement has to settle, what a rule set does and does not fix — because that describes the process and needs no §4 row. **What none of them carries is what YOU will do**, which is the half a reader weighs when choosing a neutral. Under **Q43** these are **service commitments**, not facts about you, so they are publishable **the moment you have said them** — the bar is that you said them, not that §4 rows them. Drafted, and each is a separate yes: **(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."* **(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."* **(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."* **(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."* **(e) arbitration, procedure** — *"I will not run a process whose shape nobody agreed to in advance."* **(f) arbitration, the award date** — *"The date the award is due is fixed in the first procedural order rather than left open."* **(c) is the one to weigh hardest** — the strongest available answer to the fairness objection and by far the most expensive commitment; a neutral who would not honour it must not publish it. ⚠️ **(d) and (e) SHIPPED for one pass and were removed.** `claims-auditor` found them: the gate was applied to `/med-arb/` in the same change set that wrote them, and not applied one file over. Raised by Claude Code 2026-08-28 | The fairness section on `/med-arb/`, and one paragraph each on `/mediation/` and `/arbitration/`. Nothing else — all three pages ship |
| **Q38** | **A true vector master for the infinity mark.** The mark is a shaded ribbon — variable-width band, maroon flowing into champagne, twisting in three dimensions and passing over itself at the crossing. That is gradient-mesh artwork, and there is no honest way to express it as the flat vector paths `docs/02` assumes. An SVG **is** held — `src/assets/brand/sml-logo-source.svg` — and **it renders faithfully**: rasterised at 8333 px it reproduces the master exactly, at the same 1.566:1 `[verified 2026-08-26 — rendered and measured]`. *The characterisation has now moved twice and Pouya has settled it.* This row first called the file **"a raster in a vector wrapper"**; a later draft withdrew that as unfair. **The withdrawal went too far, and Pouya's ruling of 2026-08-27 restores the substance:** *"It renders faithfully because it IS the raster. Your first characterisation was accurate and the walk-back went too far."* Both things are true at once and the row must hold both — the file is a faithful rendering **and** it is faithful because seven base64 PNGs are carried inside it, which is why fidelity was never the question. **The Canva SVG does not close this question.** Pouya: *"Keep it committed, keep the AVIF render path. R13 stays open for a true vector master."* What rules it out is **payload and composition**: 257,278 bytes against **3,063 B** for the AVIF a Retina browser takes in the header — **84×** — plus **seven embedded base64 PNGs** and a 1,225-stop gradient mesh, so inlining it would breach `CLAUDE.md`'s rule against base64-inlining images. *Restated 2026-08-27, because the single number stopped covering the case:* build step 2 added a **232 px** call site (the home page's approach section, rendering at 225.5 px) beside the existing 64 px one, and at that size a DPR-2 device takes **14,555 B** and DPR-3 **22,639 B** — a ratio of ~11×, not 84×. `adversarial-reviewer` caught the three copies of the old figure going stale together, which is the SES-DKIM duplication in a new place. Both ladders are in `docs/reference/brand-assets.md`; **quote the size with the call site, never on its own.** Also worth knowing before someone reads it as a regression: the PNG fallback at 232 px runs **38,094 / 91,618 / 150,215 B**. Nothing that negotiates content types takes it — a DPR-3 device with neither AVIF nor WebP does not exist in practice — and the AVIF above is what real clients fetch. Accepted deliberately rather than capped, because capping it would blunt the mark on the high-DPI desktops that are the only place the 696 px variant is used at all. What is wanted is a master that is faithful **and** light. **Pouya is commissioning a proper vector master.** Until it lands, `InfinityMark.astro` renders an optimised raster and that is a **documented, temporary exception to `docs/02`'s inline-SVG rule** (R13 keeps it from becoming permanent). When the master arrives: replace the component's `<Picture>` with inline SVG, regenerate the favicons from it, and delete the exception from `docs/02` and this row. Raised by Claude Code 2026-08-26; scoped by Pouya the same day | Nothing — the mark renders correctly. This is fidelity and payload, not function |
| ~~Q32~~ | **ANSWERED 2026-08-26 — and the answer was that the reconstruction was WRONG.** Pouya rendered the committed path against the master (`SML Company Just Logo Transparent.png`, 3000×3000) and rejected it on three counts. Two reproduce from the path alone: **(a) TANGENT, NOT CROSSING** — all four cubic branches meet the origin at exactly 90°, so the loops are mutually tangent on a vertical line and at stroke-width 28 render as *two kissing circles*, the one thing an infinity mark must not be `[verified 2026-08-26 — tangent vectors computed per segment, independently reproduced]`. Both lobes are strictly confined to their own half-plane (x is monotone on every segment), so the signed crossing number is **0** — they cannot cross anywhere, not merely at the node. Worse at the size actually shipped: the two strokes stay fused into one mass wherever the centreline separation `y²/192` is under 28, i.e. across **61% of the mark's height** — at 2rem that is a 16.8px blob out of a 27.4px-tall mark. No stroke-width, viewBox or scale change can produce a crossing from this centreline; **(b) WRONG PROPORTION** — the master's ink bounding box is **2668 × 1704 = 1.5657:1** `[verified 2026-08-26 — measured]`, and **(c)** the master is a shaded ribbon where the trace was two flat uniform strokes. ***1.23:1 — RECONCILED, and it was a real measurement, not a slip.*** It is the bounding box of the path's **coordinates** rather than of the **curve**. The control points sit at y = ±160 while the curve only reaches ±120 — the standard 3/4 pull-in of a cubic — so a coordinate-hull box is 400 × 320 = 1.2500, and with stroke-width 28 applied on all four sides it is **428 × 348 = 1.2299**. Pouya's figure to four significant figures, from one method applied consistently `[verified 2026-08-26 — derived]`. **It is a trap rather than a slip:** because x is monotone on every segment, the control points give the *correct* width (±200) and a 33% inflated height, so the obvious sanity check — "does the width look right?" — passes. Any tool that reads a bbox from parsed coordinates lands here; `getBBox()` would have returned 400 × 240. **And the direction is inverted from how it read:** measured from the curve, the traced path is 6.4% *wider and flatter* than the master, not squatter. **Consequence worth keeping:** the declared viewBox 440:280 = 1.5714 is within **0.35%** of the master's 1.566, so re-tuning the layout to the true ratio was ~0.18px of work at the shipped 2rem — and **re-tuning to 1.23 would have actively broken it** — at any given width 1.2299 allocates 1.5657/1.2299 = **27% more height** than the mark occupies, so the header would have been rebuilt around a box a quarter too tall. The ratio was never sufficient grounds on its own; **(a) tangency is, and it is exact.** **The traced path is deleted, not kept as a fallback**, on Pouya's instruction: *a wrong mark that renders is worse than a missing one, because it stops looking wrong.* Now closed by Q38 for the vector master | — |
| ~~Q32-orig~~ | *(superseded — the original wording of this question is preserved in entry (v))* | `src/components/InfinityMark.astro` is built from geometry lifted verbatim from the deployed site's own loading-thumbnail SVG — the element it labels `smlMark`, fetched from `https://adr.smlcompany.ca/` on 2026-08-26. Path, both stroke widths (28 / 6) and the 0.7 inner opacity are the source's; the only change is folding a `translate(60 0)` into the coordinates. So it is SML's own artwork rather than a redrawing — but a loading placeholder is not necessarily the canonical file, and D7 says the mark carries over *unchanged*. If a master SVG or AI/EPS exists, supply it and the component gets replaced. Raised by Claude Code, 2026-08-26 | Nothing — the mark renders. This is about fidelity, not a blocker |
@@ -803,6 +804,209 @@ never being raised again.
# Change Log
## 2026-08-28 (aa) — Build step 4: `/mediation/`, `/arbitration/`, `/med-arb/`. ADRIC's rules sourced, and `docs/01` had the wrong name for one of them
**Three pages, five in the build, zero JavaScript.** `/arbitration/` carries §4's
paired-disclosure condition on four surfaces; `/med-arb/` meets the
procedural-fairness objection at the level of process design and ships
deliberately **without** Pouya's own protocol commitments, which is **Q54**.
### The fetch earned itself before a word of copy was written
`docs/01` directed `/mediation/` to name the **"ADRIC Model Mediation Rules"**.
No such document exists. Measured across all four of ADRIC's rules pages:
```console
$ for f in *.txt; do printf '%-42s Model=%s National=%s\n' "$f" \
"$(grep -o -F 'Model Mediation Rules' "$f" | wc -l)" \
"$(grep -o -F 'National Mediation Rules' "$f" | wc -l)"; done
rules-codes.txt Model=0 National=2
rules-codes__national-mediation-rules.txt Model=0 National=10
rules-codes__arbrules.txt Model=0 National=2
rules-codes__adric-med-arb-rules.txt Model=0 National=2
```
The document's own heading is *"The ADRIC National Mediation Rules"*. **"Model"
belongs to the Model Dispute Resolution Clause**, a contract clause *inside* the
rules. This is the `Chartered Mediator-Arbitrator` shape a second time — a wrong
institutional name sitting in a spec, waiting to be copied onto a public page —
and it was caught only because R14 requires the source before the claim.
`docs/reference/adric-rules.md` + `adric-extract/` carry it, with a difference
from the ADRIO fetch that is measured rather than assumed: **the HTML digests
drift on every request** (page-builder class suffix, a rotating sponsor
carousel, Cloudflare email obfuscation, a challenge token — read out of a diff
of two fetches three minutes apart) while **the text extracts are byte-stable**.
So the extracts are the artefact and the digests only record what was fetched.
It also turned up the **ADRIC Med-Arb Rules**, which `docs/01` did not know
about, and corroborated `Chartered Med-Arbitrator` from a second body.
### What the four review passes found — 21 defects, and the pattern is mine
**The gate I built, breached four times.** `/med-arb/`'s header comment states
the rule — a first-person commitment about how Pouya runs an engagement is a
claim about his practice with no §4 row — and a `TODO(pouya)` sits ten lines
above it. The page then shipped *"What I will not do is agree to one where the
parties have not settled the switch in writing first"*, which is Q54(a). Three
more shipped on the neighbouring pages. Both agents found it independently.
Round 2 then found **two survivors of round 1's own fix**: *"I will work from a
bespoke protocol"* on `/mediation/`, and *"carry what I am given to carry"*
Q54(d) in compressed positive form, in the hero lede.
**Three that would have reached a public page:**
- **`<title>`: "Arbitration · Pouya Lajevardi · Sole Arbitrator · Toronto"** —
`Name · Role · City` is a designation strip, and §4 grants exactly one
practised role. It is the form struck from `jobTitle` and `description` the
day before, reintroduced in the one string a search result renders. Now
*"Commercial Arbitration · Pouya Lajevardi · Toronto"*.
- **"Every award I issue"** — a habitual presupposing awards issued, where the
register records none. Now *"An award should be…"*.
- **"The rules are a starting point. They are not a constraint on how the day is
run"** — a claim about what ADRIC's rules permit. `adric-rules.md` Finding 4,
written the same hour, says the fetch reached landing pages and supports **no
statement of what any rule requires or permits**. The constraint was written
and then crossed two files later.
**And the arb-med definition survived being fixed.** *"the order of the phases is
the whole difference"* was replaced with *"the phases run in a different order"*
which, on a page that defines med-arb as mediation→arbitration, **still defines
arb-med, by inversion**. The differentia is gone entirely now.
### Two instrument failures, caught before they became conclusions
Both mine, and both would have produced confident wrong numbers.
1. **Touch targets measured over `file://`** returned a uniform **18 px**
including on a `.btn` carrying a 44 px floor. `file://` cannot resolve
`/_astro/…`, so **the page had no CSS at all**. Re-run over HTTP with the
instrument validated first (2 stylesheets, `.btn` = exactly 44 px, cream
background), the real numbers matched `adversarial-reviewer`'s to the pixel.
2. **`validator.schema.org` returned 0 warnings for every page**, including one
with a known-bad field. The response said `fetchError: NOT_FOUND`,
`numObjects: 0` — the wrong parameter name, so nothing was ever parsed.
Calibrated against a deliberately invalid probe (`Service` +
`availableLanguage` → 2 `UNKNOWN_FIELD`), the real result was **3 out-of-domain
fields on `/` and one on each new page**.
`availableLanguage` is removed from `Service` **and** from
`ProfessionalService` — the same defect existed on `/` and fixing it next door
while leaving it in place would have been worse. `serviceType` and `provider`
remain out of domain on `ProfessionalService`; they carry real information, the
modelled fix is a different node shape, and it is recorded in `schema.ts` for
the step-7 SEO pass rather than left as folklore.
⚠️ **The post-fix validator re-run on `/` is NOT RUN** — Google returned HTTP 302
to `/sorry/` (rate limited) mid-verification. What is verified by reading `dist`
is that the field is gone from all five pages.
### Structural changes the review produced
- **`DefinitionGrid.astro`** — `/mediation/`'s `.formats` and both of
`/arbitration/`'s `.cols` were one component under two names, with `.cols`
already serving two content types on one page. `/practice/*` wants it a fourth
time.
- **`.grid-autofit` in `global.css`** — five copies of the same five-line comment
explaining five identical declarations, plus a sixth differently-worded copy on
`/about/`. One home for the `min(Nrem, 100%)` guard and its measurement.
- **`/arbitration/`'s `.arc*` renamed to `.stage*`** — the names collided with
`/about/`'s `.arc` block, which renders differently, and one of the two rules
was dead on arrival because Astro's scoping kept them apart.
- **`serviceType` is a union type**, not a `string`. Four lines of comment asking
a caller not to pass an unscoped `"Arbitration"` became a build error.
- **`CREDENTIALS.goalName`** — the designation name that was wrong on a public
page last week is now read from the constant on both new pages instead of
hand-typed a third and fourth time.
### Measured, not asserted
Reflow at a 200% default font size (root 32 px), validated instrument, over HTTP:
| root 32px | 320 | 360 | 390 | 4141024 |
|---|---|---|---|---|
| `/mediation/` | 63 | 23 | 3 | 0 |
| `/arbitration/` | 63 | 23 | 3 | 0 |
| `/med-arb/` | 63 | 23 | **0** | 0 |
Identical before and after the grid refactor, so it changed no rendering. At the
**default** root, 0 at all eleven widths. Touch targets: `/arbitration/` and
`/med-arb/` have **no** link under 44 px; `/mediation/`'s six are the
`PracticeCard` chips, whose real hit area is the **342 × 284 px** card via
`::after { inset: 0 }` — measured, and byte-identical to `/`.
`docs/02`'s 1280 px figure was **`/`-only and generalised**: every non-home page
measures **944**, because `SiteHeader` gates the tagline on `!isHome`. `/about/`
was already 944 when it joined that table. Corrected there.
Em-dashes per 1000 words: `/med-arb/` 13.3 → **3.8**, now the lowest of the four
content pages (`/about/` 8.2). Second person on `/arbitration/` 7 → 3, the
remainder being procedural (*"whatever your contract names"*) plus the shared
`ContactBand` line that `/` and `/about/` also carry.
### Verified
`npm run check` 0/0/0 · `eslint` + `prettier` clean · 5 pages · **0 `.js`** ·
non-JSON-LD `<script>` **0** · minifier guard **0**, `animation-timeline` **5**
surviving · one `<h1>` per page, no skipped levels · `Chartered
Mediator-Arbitrator` **0** in `dist` · `TODO(pouya)` **0** in `dist` · JSON-LD:
`Service` with scoped `serviceType` and `provider` resolving in-document,
`FAQPage` built from the same array the page renders (3/3 questions and answers
exact-match), no `BreadcrumbList`, no `worksFor`, no `memberOf` off `/about/`.
**NOT run:** Lighthouse — tool unavailable until step 7 (R11). HTML validator.
The `/` schema.org re-run — rate limited, stated above.
### Round 2 found four more, and two were defects in round 1's fixes
D19's cap exists for exactly this, and it showed up in the change set that
introduced D19.
- **`DefinitionGrid` re-implemented the guard the extraction removed.**
`.grid-autofit` uses `--grid-min`/18rem; the new component wrote its own
`minmax(min(var(--def-min), 100%), 1fr)` with a different variable and a
different default. A correction to the guard would have reached five call
sites and silently missed three — the exact failure the extraction was
justified by, reintroduced inside the extraction.
- **`.stage-item` matched no CSS rule at all.** Dead markup on three list items,
where `/about/`'s `.arc-item` counterpart carries a real rule. Removed.
- **Second person, four instances**, on two pages where `/` and `/about/` have
zero — and `docs/01`'s new annotation *quoted* one of them, so one in-scope
spec recorded the phrasing another in-scope spec bars. All five pages now
measure **0** excluding the shared `ContactBand` line.
- **`PublishableServiceType`'s comment said its members are §4's rows
"verbatim".** They are `docs/04`'s strings; §4's rows read *"Arbitration —
sole arbitrator (commercial)"*. An implementer widening the union "verbatim
from §4" would write the unscoped `Arbitration` the union exists to bar — in
the comment that **is** the gate.
**And it caught a number of mine that does not reproduce.** `docs/02`'s new
block said the element sweep names *"six `PracticeCard` chips"*. Re-run: **three**
(`right > clientWidth` — Construction, Technology, Shareholder). Six is the count
of `article.card` with `scrollWidth > clientWidth` — a different predicate on a
different element. Corrected, with the predicate stated, because a number in that
table has to be re-runnable.
**One process finding accepted in full: the tree moved during the review.** Three
of its drafted findings were resolved by edits landing while it measured, which
makes a reviewer's *negative* results worthless. It published md5s; all six match
the committed tree, so this review is against what shipped. **Freeze the tree for
the review window** — that is a `/build` Phase 3 lesson, not a code one.
**Declined, with the reason:** four hand-written copies of the auto-fit guard
remain in `index.astro` and `about.astro`. Converting them after the review cap
is an unreviewed change to live CSS on two shipped pages. They are **listed by
line number** in `global.css` so nobody reads that comment and believes the guard
has one home, and they move at step 5 when `/practice/*` is in the same files.
### Open after this entry
**Q54** — six drafted conduct commitments, none shipping, each a separate yes.
**Q23**, **Q34**, **Q38** unchanged. `/fees/`, `/practice/*`, `/process/`,
`/contact/` still 404 by build order (D11: nothing deploys until step 11).
---
## 2026-08-28 (z) — D19 bounds the review loop; nine rulings applied; and the loop immediately proved the point by finding a sweep that was claimed and never run
**The session's own shape is the argument for D19.** Two review rounds, four