Q32 — the traced mark was a WRONG SHAPE and had shipped. Pouya compared it against the master and rejected it. Two grounds reproduce from the path and are verified here: all four cubic branches meet the origin at exactly 90 degrees, so the loops are tangent rather than crossing and at stroke-width 28 render as two kissing circles (signed crossing number 0; the strokes fuse across 61% of the mark's height at 2rem); and the master's ink bbox is 2668x1704 = 1.5657:1. The path is deleted, not kept as a fallback. Pouya's 1.23:1 figure is reconciled rather than left dangling: it is the bounding box of the path's COORDINATES, not the curve. Control points sit at y +/-160 where the curve reaches +/-120, so the hull is 400x320 and with stroke 428x348 = 1.2299. A trap rather than a slip — x is monotone, so the control points give the right width and a 33% inflated height, and the "does the width look right" check passes. The real artwork is now in the repo: master, tight crop (the render source, so the file's aspect ratio IS the mark's), full lockup, and the SVG. InfinityMark renders AVIF/WebP; a Retina device takes 3,063 B. Favicons regenerated; favicon.svg deleted. Q33/Q36 — Pouya accepts arbitration appointments now. §4 gains an Offerings category: competence for an offering, permission for a credential, with an explicit boundary so it cannot become a route around D13. The masthead tagline is restored, and the footer designation strip now carries "Q.Arb — commenced August 2026" so §4's paired-disclosure condition is actually met on every page rather than only asserted. Two conventions added to CLAUDE.md, both earned this session: anything a spec makes a claim about must be reachable from the repo (R14 — the traced mark survived two review passes because the artwork was not here to compare against); and a command that did not run is not evidence of absence (`timeout` is not installed on macOS, so four Drive reads never executed and were reported as an empty directory). Reviews: claims-auditor FAIL/13 and adversarial-reviewer 2 blocking, all resolved. The severe one was self-inflicted — `flex: none` landed on the <img> while <Picture>'s <picture> wrapper is the flex item, so the logo compressed to 28.5x32 at 1024px with seven nav items. The page-level overflow check passed throughout because the brand block absorbed the deficit by crushing the mark. Harness now asserts rendered aspect ratio. Opened: Q38, Q39. Closed: Q32, Q33, Q36. Narrowed: Q35. Added: R13, R14. AGENTS.md entry (v) carries a RESUME HERE section. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XquaEq4BgWMCwUqLEyNkF
2892 lines
209 KiB
Markdown
2892 lines
209 KiB
Markdown
# Agent Working File
|
||
|
||
This file is a **living document** and a **full history tree** for this project —
|
||
not a snapshot. Any agent or person working here (Claude in chat, Claude Code,
|
||
Claude Cowork, or a human) maintains it by the rules below. Everything else in
|
||
this file varies by project; these rules do not.
|
||
|
||
## How to maintain this file
|
||
|
||
1. **Document everything.** On every change, record what was discussed, decided,
|
||
changed, or planned — decisions and plans included, not just executed work.
|
||
|
||
2. **Never overwrite or delete history.** When something changes, update the
|
||
relevant Current Truth section in place **and** append a dated Change Log
|
||
entry capturing old → new and why.
|
||
|
||
3. **Two parts, kept separate:** *Current Truth* is updated in place and always
|
||
reflects the present state; the *Change Log* is append-only, newest entry
|
||
first, and is never edited retroactively.
|
||
|
||
4. **Stamp facts** as `[verified YYYY-MM-DD]` or `[assumed]`. When you re-check a
|
||
fact, re-stamp it with today's date; a stale date means it needs re-verifying.
|
||
|
||
5. **Never prune the Change Log.** If the file gets unwieldy, ask before
|
||
archiving old entries to `AGENTS-history.md`. History is not destroyed.
|
||
|
||
---
|
||
|
||
# Current Truth
|
||
|
||
## 1. What this project is
|
||
|
||
A ground-up rebuild of **adr.smlcompany.ca**, the personal-brand website of the
|
||
alternative dispute resolution practice of **Pouya Lajevardi**. `[verified 2026-08-25]`
|
||
|
||
The brand's centre of gravity is the **practitioner**, not the firm and not
|
||
SML Company Ltd. Legal background, firm affiliation, and the operating company
|
||
are credibility signals referenced in support; they are not the subject.
|
||
`[verified 2026-08-25 — source: PL_ADR_Personal_Branding_Strategy_Brief.docx]`
|
||
|
||
**Positioning in one sentence:** a credentialed neutral who is also close to
|
||
live litigation and a practising machine-learning / infrastructure engineer — a
|
||
combination that is close to absent from Canadian ADR rosters, and which is the
|
||
practice's economic moat. `[verified 2026-08-25 — source: strategy brief §I, §V]`
|
||
*(Wording corrected 2026-08-26: "a working litigator" asserted conduct of
|
||
litigation, which §4 bars — see entry (q).)*
|
||
|
||
**Time horizon:** the strategy brief plans a 24-month compounding arc. The site
|
||
is built to be added to continuously, not shipped once. `[verified 2026-08-25]`
|
||
|
||
## 2. State of the thing being replaced
|
||
|
||
The site live at `adr.smlcompany.ca` as of this entry: `[verified 2026-08-25]`
|
||
|
||
> ⚠️ **Several of these measurements are contested — see Q34 (opened
|
||
> 2026-08-26).** A re-fetch of the deployed URL that day reproduced some of the
|
||
> list exactly and could not reproduce others *from the served HTML*, because
|
||
> the served document turns out to be a bundler harness whose application lives
|
||
> in nine UUID-named script files that were not fetched. **Do not cite any
|
||
> figure below as established, and do not put one in public copy, until Q34
|
||
> closes.** The items marked ⚠️ are the specific ones in doubt. This caveat sits
|
||
> here rather than only in §9 because a reader who arrives at a `[verified]`
|
||
> stamp will otherwise never learn it is under review.
|
||
|
||
- It is a **single 2.2 MB HTML file** containing an entire React application,
|
||
compiled **in the browser at runtime** by Babel Standalone loaded from unpkg.
|
||
*Confirmed on re-fetch 2026-08-26: the response is **2,206,032 bytes**, of
|
||
which **2,210,921** are inline `<script>`.*
|
||
- ⚠️ React and ReactDOM are loaded as **development** builds from a public CDN.
|
||
*Not reproducible 2026-08-26: the served file contains no reference to
|
||
`unpkg` or to a React development build. They may be inside the unfetched
|
||
bundles. Q34.*
|
||
- **Crawlers see almost nothing.** A server-side fetch of the URL returns only
|
||
`SML Company`, `DISPUTE RESOLUTION`, and the loading string `Unpacking...`.
|
||
Every word of real content is assembled client-side after three CDN round
|
||
trips. `[verified 2026-08-25 — direct fetch]`
|
||
- The deployed `<head>` has **no `<meta name="viewport">`**, no meta
|
||
description, no Open Graph or Twitter card tags, no canonical URL, and no
|
||
favicon. `[verified 2026-08-25]` — *still true of the head a crawler parses.
|
||
Note for accuracy: the harness carries a second, JSON-escaped `<head>` inside
|
||
a `<script type="__bundler/template">` which does contain a viewport tag. It
|
||
is assembled by JavaScript, so it reaches a browser and not a crawler, which
|
||
is the distinction that matters here.*
|
||
- `robots.txt` returns **403**. There is no sitemap. `[verified 2026-08-25]`
|
||
- ⚠️ The two logo PNGs are ~1 MB combined and are **base64-inlined** into the
|
||
HTML, so they cannot be cached separately from the page. `[verified
|
||
2026-08-25]` — **but a 2026-08-26 fetch found zero `base64,` occurrences
|
||
anywhere in the served document.** Q34. This figure is cited as the *reason*
|
||
for a standing convention in `CLAUDE.md` and `docs/02`, both of which now
|
||
carry the caveat. A related number, "470 KB PNG", appeared in `docs/02` with
|
||
no source anywhere in this repo and has been removed.
|
||
- `<title>` is still `SML Company · Dispute Resolution` — the pre-rebrand
|
||
placeholder title. `[verified 2026-08-25]`
|
||
- There is **no version control, no build step, and no test suite**. The
|
||
publishing mechanism is a Python script (`rebuild-standalone.py`) that inlines
|
||
the JSX components into the single file. `[verified 2026-08-25]`
|
||
- The May 2026 content brief was **partially applied** — the JSX components
|
||
under `components-standalone/` contain the corrected Pouya-branded copy, but
|
||
the document `<head>` and some structural defaults still carry placeholder
|
||
content. `[verified 2026-08-25]`
|
||
|
||
**Consequence, stated plainly:** for a site whose entire purpose is to be found
|
||
by counsel, general counsel, and appointing bodies searching for a neutral, the
|
||
current architecture is close to a worst case. This rebuild is justified on
|
||
architecture alone, independent of any content or design opinion.
|
||
`[verified 2026-08-25]`
|
||
|
||
## 3. Locked decisions
|
||
|
||
Decided with Pouya on 2026-08-25. Each of these is settled; do not re-open one
|
||
without an explicit instruction and a Change Log entry. `[verified 2026-08-25]`
|
||
|
||
| # | Decision | Chosen | Rejected alternatives |
|
||
|---|---|---|---|
|
||
| D1 | Framework | **Astro, static output, v7.x.** *Amended 2026-08-26:* the major is now pinned deliberately rather than inherited. The original `^5.0.0` pin was **recalled, not checked, and was two majors stale the day it was written**. Upgrading costs three config files now and compounds with every page; the v5 advisories were **high-severity XSS in the framework rendering a public site**, and "we don't use those authoring patterns" is a claim about all future code, not just today's. Starting two majors behind means being three behind at launch | Next.js; patching the single-file build; hand-written HTML; staying on v5 |
|
||
| D2 | Content scope | **Full re-architecture** — new IA, new pages, all copy rewritten | Copy rewrite on existing structure; audit only; technical layer only |
|
||
| D3 | Hosting & CI/CD | **Git repo + Gitea Actions → existing S3 + CloudFront.** *Amended 2026-08-26:* self-hosted **Gitea**, repo `adr-sml`. Gitea Actions uses GitHub Actions syntax, so the workflow ports nearly as is — but Gitea is not an AWS OIDC provider, so deploys authenticate with a **scoped IAM user**, rotated quarterly | GitHub Actions + OIDC; Terraform/CDK IaC; Amplify; manual deploys |
|
||
| D4 | Languages | **English only** | Full EN/FA bilingual; EN + one Farsi page; EN with FA scaffolding |
|
||
| D5 | Page structure | **Full multi-page**, ~20 URLs | Lean six-page; rich home + a few deep pages |
|
||
| D6 | Primary audience | **All four tiers**, served by different surfaces (see §5) | Single-audience focus |
|
||
| D7 | Visual design | **Keep palette and infinity mark; modernize the execution** | Faithful port; strip motion only; fresh design direction |
|
||
| D8 | Fees | **Full rate card published on a `/fees/` page** | Ranges only; on request; gated PDF |
|
||
| D9 | Insights | **Build it and launch with 3–5 drafted pieces**, every word reviewed by Pouya before publication | Launch empty; no Insights section |
|
||
| D10 | Intake | **Rebuilt intake form + calendar booking** for the confidential intake call | Structured questionnaire; form only; email/phone only |
|
||
| D11 | Rollout | **Build everything, one clean cutover.** The current site stays live untouched until replaced | Patch live site first; staging subdomain; take site down |
|
||
| D12 | Agent working file | **This file**, maintained under the constitution above | Ad hoc notes |
|
||
| D13 | Licensure claims | **The site asserts the JD only.** No claim of licensure, call to the bar, or entitlement to practise law. The approved phrasing for the boutique role is **"active litigation exposure"** or **"involvement in litigation and ADR matters"** — never "practice" **in that context**. "Practice" describing Pouya's own ADR practice — "the dispute resolution practice of Pouya Lajevardi" — is correct and stays. Pouya's direction, 2026-08-26. **Explicitly interim — see Standing Reminders §12** | Stating LSO licence status; the word "practice" |
|
||
| D14 | Fee structure | **Single published rate card, all mediation matters at one rate.** Confirmed by Pouya 2026-08-26; figures in `docs/07-fees.md`. No tribunal-secretary line | Two-tier card (recommended by Claude, declined); ranges; fees on request |
|
||
| D15 | Analytics | **Plausible.** Privacy-first and cookieless: no cookie banner, nothing to consent to, one line in the privacy policy. *Amended 2026-08-26 (Q31):* the original read "Plausible **or** Fathom", i.e. undecided, and a guessed `'plausible'` had already leaked into `src/data/site.ts` from it. **Decided, not defaulted** — Pouya checked rather than assumed and found his own assumption wrong: Fathom is Canadian-owned, which is the intuitive answer here, but it stores non-EU traffic on **US servers** and isolates in the EU only for EU visitors. Plausible keeps all data in the EU. For a practice whose privacy posture is part of the offer, EU-only beats US-hosted, and ownership is not the axis that matters | GA4; no analytics at all; **Fathom** — rejected on data residency, not on quality |
|
||
| D18 | Intake email | **Two emails per submission** — notification to `info@smlcompany.ca`, and a **confirmation receipt to the inquirer**. Pouya, 2026-08-26. The receipt is what requires SES production access (Q19) | Notification only; no email; no form at all |
|
||
| D17 | Working method | **Pouya architects; Claude Code implements and then adversarially reviews its own work.** Six-phase loop encoded in `.claude/commands/build.md`; two independent review agents in `.claude/agents/`; extended thinking on by default. Standing agreement — not restated per prompt | Implement-and-report; single generic reviewer; review only on request |
|
||
| D16 | Naming the boutique | **Never named.** Referred to throughout as *a Toronto litigation and ADR boutique*. Pouya's decision, 2026-08-26 | Naming the firm |
|
||
|
||
## 4. Credential and claim register
|
||
|
||
**This is the most important section in this file.** The previous version of this
|
||
site carried a fictitious founder, invented matter values, fabricated office
|
||
locations, and a testimonial attributed to a person who does not exist. Whatever
|
||
regulatory regime does or does not apply, publishing false credentials on a
|
||
professional's marketing site is the thing this register exists to prevent. That
|
||
reason is sufficient on its own, and it does not rest on a fact this file has
|
||
never established.
|
||
|
||
**Note on this paragraph's own history, kept as a caution.** It previously read
|
||
"Pouya is a licensed legal professional", then "a legal professional", then
|
||
asserted that LSO marketing rules apply — three progressively weaker forms of the
|
||
same unverified claim, each surviving a sweep meant to remove it. The register
|
||
was asserting what it could not vouch for, in the section written to stop exactly
|
||
that.
|
||
|
||
### Governing rule
|
||
|
||
> **No claim reaches a public page unless it appears in the Verified column
|
||
> below.** If a page needs a fact that is not here, stop and ask Pouya. Do not
|
||
> infer it, do not soften it into something defensible, and do not carry it over
|
||
> from the old site.
|
||
|
||
### Verified — may be published
|
||
|
||
Sourced from Pouya's own strategy brief of 2026-05-26. Self-reported by the
|
||
subject, which makes them reliable as to substance, but **currency is not
|
||
confirmed** — several are on a moving credentialing track and may have advanced
|
||
since May.
|
||
|
||
| Claim | Status |
|
||
|---|---|
|
||
| Pouya Lajevardi, JD, Bond University | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| **Licence status — NOT ESTABLISHED** | `[unestablished 2026-08-26]`. This record does not know whether Pouya is licensed. He instructed that licensure be left out of the site (D13) — an instruction about the site, **not a statement of fact either way.** Do not assert it, do not deny it, do not infer it from anything else here, and do not treat any regulatory regime as established. R1 tracks the reopening |
|
||
| ~~Lawyer; Law Society of Ontario member~~ | **DO NOT PUBLISH.** Pouya directed on 2026-08-26 that licensure is left out of the site entirely; the JD is asserted, nothing further. See D13 and the Forbidden table below `[verified 2026-08-26]` |
|
||
| Director of Firm Operations, Toronto litigation and ADR boutique | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| Active litigation **exposure**: personal injury, construction, regulatory (POA), insurance (SABS) | `[verified 2026-08-26]`. **Wording corrected** — this row previously read "practice", the exact word D13 bars in exactly the context it bars it. The register was quoting the strategy brief verbatim and contradicting its own rule. The underlying fact is unchanged |
|
||
| Q.Med designation through ADRIC / ADRIO | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| Has completed multiple sole mediations | `[verified 2026-08-25 — strategy brief §I]` — count deliberately not published, see below |
|
||
| Q.Arb credentialing pathway — **commenced August 2026** | `[verified 2026-08-26 — Pouya]`. Describe as newly commenced, never as held or nearing completion |
|
||
| C.Med-Arb stated as long-term designation goal | `[verified 2026-08-25 — strategy brief §I, §IV]` |
|
||
| Kompass Arbitration Certificate Program — completed | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| Stitt Feld Handy negotiation and ADR workshop sequence — completed | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| Practising machine-learning and DevOps / infrastructure engineer | `[verified 2026-08-25 — strategy brief §I, §V]` |
|
||
| Bilingual English and Farsi | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| Iranian-Canadian; cross-cultural fluency with diaspora business communities | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| Operator of SML Company Ltd. alongside the practice | `[verified 2026-08-25 — strategy brief §I]` |
|
||
| **SML Company Ltd — incorporated FEDERALLY, under the CBCA** | `[verified 2026-08-26 — Pouya, Q30]`. Two facts were being conflated and one of them was wrong: **jurisdiction of incorporation is federal (Canada)**; **place of business is Toronto, Ontario**. `src/data/site.ts` carried `'SML Company Ltd. · Ontario, Canada'`, which reads as a jurisdiction of incorporation and named the wrong one. **No corporation number** — none is held and the line does not need one. **Caution, and it is the point of this row:** "federally incorporated" says nothing about professional licensure, and nothing about where the practice may operate. It must not be read together with the **Licence status — NOT ESTABLISHED** row into an implication that neither row makes. **Not published:** on Pouya's direction the footer reads `© <year> SML Company Ltd` and nothing further — the fact is verified and available, it is simply not on a page |
|
||
| Memberships: **ADRIC**, **ADRIO**, **OBA — Construction & Infrastructure, ADR, and Civil Litigation sections**, and the **Canadian Tax Foundation** | `[verified 2026-08-26 — Pouya]`, **for now**. *CTF added 2026-08-26.* Both the OBA sections and the CTF renew yearly; every line here is a fact with a shelf life, tracked as R10. Re-confirm at renewal and before any page listing memberships ships. **CTF is a membership, not a practice area** — it is the one credential none of the six areas touch, and `docs/01-architecture.md` records why there is no seventh page at launch and when to revisit (R3) |
|
||
| ~~OCNI~~ | **Not current. Do not publish** `[verified 2026-08-26 — Pouya]` |
|
||
| ~~LSO~~ | **Do not publish.** Listing the Law Society among memberships implies licensure, which D13 bars. Excluded deliberately, not by oversight `[verified 2026-08-26]` |
|
||
| Toronto, Ontario; by appointment | `[verified 2026-08-26]` |
|
||
| Contact: `info@smlcompany.ca`; no public phone number; consultations by scheduled call | `[verified 2026-08-26 — Pouya]` |
|
||
| **Response time: inquiries answered within two business days** | `[verified 2026-08-26 — Pouya]`. A public commitment — it must read identically on `/contact/`, in the inquirer confirmation email, and in any bio |
|
||
| LinkedIn: `https://www.linkedin.com/in/pouyalajevardi/` | `[verified 2026-08-26 — Pouya]` |
|
||
| ~~The boutique may be named on the site~~ | **STRUCK 2026-08-26 (Q25).** Superseded by **D16 — never named.** Pouya said it *could* be named, then reversed; the permission survived the reversal and sat in the Verified table authorising exactly what D16 bars. Refer to it only as *a Toronto litigation and ADR boutique* |
|
||
|
||
### Offerings — what the practice offers, as distinct from what Pouya holds
|
||
|
||
**Added 2026-08-26 on Pouya's ruling (Q33, Q36).** Everything above this point
|
||
answers one question: *is this claim about Pouya true?* That is a question about
|
||
**credentials**. It turns out the register also has to answer a second, different
|
||
question: *does the practice offer this?* — and applying the credential test to
|
||
an offering produced a wrong answer twice in one session.
|
||
|
||
**What went wrong, because it explains the rule — stated correctly on the second
|
||
attempt.** `co-arbitration available now` was flagged across three specs on the
|
||
reasoning that §4 records the Q.Arb pathway as merely *commenced* and therefore
|
||
could not support an arbitration offering. **That** reasoning was wrong at the
|
||
root: it silently treated a **designation** as though it were a **licence**.
|
||
|
||
*The masthead objection was a different one, and this paragraph originally
|
||
misdescribed it.* Q33-orig did not say Pouya was not entitled to arbitrate. It
|
||
said the line had been **promoted to a masthead under his name on every page,
|
||
where nothing qualifies it and it reads as a designation strip** — an objection
|
||
about **placement and implication**, not entitlement. Answering the entitlement
|
||
objection does not answer the placement one, and the first version of this
|
||
section quietly substituted the easier question. `claims-auditor` caught the
|
||
substitution. The placement objection is answered by the paired-disclosure
|
||
condition below, which is a **condition**, not a formality.
|
||
|
||
**Pouya's ruling, and the reasoning to keep.** His stated basis: ADRIC and ADRIO
|
||
designations — Q.Med, Q.Arb, C.Med-Arb — are **voluntary credentials, not
|
||
licences**, and **commercial arbitral appointment in Ontario is not gated behind
|
||
a designation**. So the constraint on offering arbitration was never *may he
|
||
act* — only *will parties appoint someone at this stage of the arc*, which is
|
||
**positional** and his to answer. He answers it: **he accepts arbitration
|
||
appointments now**, and that — his acceptance — is what the rows below record.
|
||
|
||
> ⚠️ **The legal proposition underneath is the architect's, not this register's,
|
||
> and it is deliberately NOT stamped `[verified]`.** `[verified — Pouya]` means
|
||
> *the subject reported it*, which can establish what he offers and cannot
|
||
> establish a proposition of Ontario law. No statute or regulation is cited here
|
||
> and none is in the repo, so **no reviewer can check it** — R14 again, in a
|
||
> place that matters more than a logo.
|
||
>
|
||
> It is also **not a universal, and must never be restated as one.** An earlier
|
||
> draft of this paragraph wrote "Anyone may be appointed an arbitrator in
|
||
> Ontario. Nothing in law gates the role behind a designation." `claims-auditor`
|
||
> produced a counter-example from inside this repo: the site ships a
|
||
> **Shareholder & Family Business** practice area, and **family** arbitration in
|
||
> Ontario is understood to be one of the places where arbitrator qualification
|
||
> *is* prescribed. This record does not resolve that — it is **Q39** — but it
|
||
> will not carry an unqualified universal that the site's own nav may already
|
||
> contradict. The rows below are scoped to **commercial** arbitration for that
|
||
> reason.
|
||
|
||
### The offering test
|
||
|
||
> **For an offering, ask about COMPETENCE. For a credential, ask about
|
||
> PERMISSION.** A credential claim needs a row in the Verified table because it
|
||
> asserts a fact about Pouya that is either true or false. An offering needs two
|
||
> things, and they are answered by different people:
|
||
>
|
||
> 1. **Is the activity gated?** — *a question for THIS REGISTER, not for the
|
||
> subject.* It needs a source that a reviewer can check, and it is answered
|
||
> per activity and per jurisdiction, never as a class. Until it is answered,
|
||
> the offering does not ship.
|
||
> 2. **Is he competent to do it?** — *his call, and only his.* Not the
|
||
> register's, not an implementer's, not a reviewer's.
|
||
>
|
||
> **Question 1 is not his to answer**, and an earlier draft of this test said it
|
||
> was: it ended "That is his call, not the register's, and not an implementer's"
|
||
> attached to the *whole* preceding question, gating included. That hands a
|
||
> future writer the sentence *"he says this one is ungated, and §4 says that is
|
||
> his call"* — which is the walk-through the boundary below exists to prevent.
|
||
> Split, on `claims-auditor`'s finding, 2026-08-26.
|
||
|
||
### What counts as an offering, and the rule for publishing one
|
||
|
||
> **An offering may not be published until it has a row in "Offered now" below.**
|
||
> Same force as the governing rule for the Verified table. The register created
|
||
> this category; it has to gate it too, or it is a category that authorises
|
||
> everything by saying nothing.
|
||
|
||
**Scope: an offering is a PROCESS the practice conducts** — mediation,
|
||
arbitration, med-arb, early neutral evaluation, settlement counsel,
|
||
dispute-system design, pre-dispute technical advisory. Each needs its own row.
|
||
|
||
**A subject-matter area is NOT an offering.** *Construction*, *technology*,
|
||
*energy*, *insurance*, *shareholder*, *cross-cultural* are the contexts in which
|
||
the processes are conducted, not additional things offered. They are governed by
|
||
`docs/03`'s framing rule instead — *"Frame as positioning, not as history"* — and
|
||
the open half of **Q35** asks whether a one-word nav label can carry even that.
|
||
Do not read this scope note as clearing them; it says only which rule applies.
|
||
|
||
*Added 2026-08-26 on `claims-auditor`'s finding that the section had created a
|
||
category without defining either its extent or its gate — so `Med-Arb` was
|
||
sitting in the site-wide footer as an offering with no row, three sentences
|
||
below a paragraph saying not to infer exactly that. It has been removed from the
|
||
footer pending Q35(a).*
|
||
|
||
**THE BOUNDARY, AND IT IS NOT NEGOTIABLE.** This test applies **only where no
|
||
licence gates the activity.** It is not a general licence-free zone, and it must
|
||
never be read as one:
|
||
|
||
- **Mediation and arbitration are ungated.** No designation is required to be
|
||
appointed. The offering test governs, and Pouya has answered it.
|
||
- **Legal services are gated.** Providing legal services and practising law in
|
||
Ontario are restricted activities. For anything on that side of the line the
|
||
binding question is **permission**, the answer is **not established** (see the
|
||
**Licence status — NOT ESTABLISHED** row above), and **D13 bars the claim
|
||
outright.** "I offer legal representation — that is competence, not
|
||
permission" is exactly the misreading this paragraph exists to foreclose.
|
||
- **The test never converts a credential into an offering.** Q.Arb is still
|
||
*commenced August 2026* and must still never read as held. Offering
|
||
arbitration and holding Q.Arb are two different statements, and the site makes
|
||
the first while stating the second plainly.
|
||
|
||
### Offered now — may be published
|
||
|
||
| Offering | Status |
|
||
|---|---|
|
||
| **Mediation** — sole mediator | `[verified 2026-08-26 — Pouya]`. Q.Med held; §4 also verifies multiple completed sole mediations |
|
||
| **Arbitration — sole arbitrator** (commercial) | `[verified 2026-08-26 — Pouya, Q33/Q36]`. Offered **now**. Not gated by Q.Arb. **Commercial** — see Q39 on family arbitration |
|
||
| **Arbitration — party-appointed** | `[verified 2026-08-26 — Pouya, Q33/Q36]`. Offered **now** |
|
||
| **Arbitration — co-arbitration** | `[verified 2026-08-26 — Pouya, Q33/Q36]`. Offered **now**. This closes Q36, which had flagged the claim as asserted in three specs and resting on no §4 row — the row is here |
|
||
| ~~Tribunal secretary~~ | **Not offered.** D14 removed the rate and bars offering it |
|
||
|
||
**How the arbitration page must read** (`docs/01` §`/arbitration/`, step 4):
|
||
it offers all three today **and states plainly that the Q.Arb pathway commenced
|
||
August 2026, with C.Med-Arb as the endpoint.** Pouya's instruction: *being open
|
||
about the stage is the differentiator — do not hedge it into vagueness.* So
|
||
neither half may be dropped. Naming the stage is not a disclaimer bolted onto an
|
||
offering; it is the reason a sophisticated reader believes the offering.
|
||
|
||
**Still unanswered — do not infer from this section.** Whether **Med-Arb** is
|
||
offered now is not recorded here; Pouya named the three arbitration forms and
|
||
mediation, and nothing else. Med-Arb is ungated by the same reasoning, so the
|
||
question is his competence call alone — but it is still a question. See Q35.
|
||
|
||
### Forbidden — must not appear on the site
|
||
|
||
**Scope, clarified 2026-08-26.** This table governs **published pages** — anything
|
||
a visitor to `adr.smlcompany.ca` can read, JSON-LD included. It is not a word
|
||
filter over the repository's own documentation.
|
||
|
||
**Internal docs are bound by a related rule:** they must not assert as fact
|
||
anything this register has not verified. Reasoning built on an unverified premise
|
||
propagates into copy. Licensure is the live example — **the Verified table above
|
||
does not establish licence status either way**; its two licensure rows are
|
||
verified *directives not to publish*, not a verified status. So no file in this
|
||
repo may call Pouya "licensed", or soften it to "a legal professional", internal
|
||
or not. State the *reason* for a rule rather than a credential the register
|
||
cannot vouch for.
|
||
|
||
| Never publish | Why |
|
||
|---|---|
|
||
| Any count of matters closed, hours mediated, or years in ADR practice | Practice is new. Small true numbers do not persuade a sophisticated GC and invite scrutiny. Save for one-to-one appointment proposals where context travels with the figure. `[verified 2026-08-25 — content brief, "Rule of thumb on numbers"]` |
|
||
| Settlement rates, resolution percentages, median time to award | No underlying data exists |
|
||
| Aggregate value resolved; any dollar figure attached to past matters | No underlying data exists |
|
||
| Named or describable past matters | Confidentiality, and none are publishable |
|
||
| Testimonials, endorsements, quotes from counterparties | None exist. The prior site's testimonial was fabricated |
|
||
| "Since 2009", "sixteen years", London / New York offices, Co. № 07452218 | Artefacts of the placeholder template. All false |
|
||
| The name "S. M. Lawrence" | Fictitious founder from the template |
|
||
| Guarantees of outcome, or superlatives ("best", "leading", "top-rated") | Unverifiable as written, and they read as insecure to the audience this site is for. Marketing rules for regulated professions also treat them as suspect. *(Reason restated 2026-08-26: this row previously read "LSO marketing rules", which asserts the regulatory status the row above marks NOT ESTABLISHED. It was the last surviving instance, flagged in entries (o) and (q) and answered in Q26 as already fixed — it was not. **Restated by Claude Code; the rule itself is unchanged and the restatement needs Pouya's ratification.**)* |
|
||
| The word **"lawyer"** used of Pouya; "called to the bar"; "licensed"; "my law practice"; "my litigation practice"; any post-nominal implying a licence | D13. The site asserts the JD and nothing further |
|
||
| Any phrasing that *implies* entitlement to practise law without saying so — "acts for clients", "represents parties", "my clients", "legal advice" | Same rule. Implication is the risk, not just the word. Describe the role factually instead: *Director of Firm Operations at a Toronto litigation and ADR boutique*, and the matter types worked on |
|
||
| Q.Arb described as held, imminent, or "nearly complete" | It commenced August 2026 |
|
||
|
||
### The substitution principle
|
||
|
||
Wherever the design wants a "how much / how many" statistic, substitute a
|
||
**longer-arc credential** — something already substantial and true at launch that
|
||
does not grow by closing files. The approved stat set is `Q.Med` /
|
||
`JD + ML` / `EN · FA`, with `Q.Arb` as a fourth slot where one exists.
|
||
`[verified 2026-08-25 — content brief]`
|
||
|
||
## 5. Audience model
|
||
|
||
All four tiers matter, but they are served by different surfaces rather than by
|
||
diluting every page into a compromise. `[verified 2026-08-25 — decision D6]`
|
||
|
||
| Tier | Reads | Wants | Primary surface |
|
||
|---|---|---|---|
|
||
| In-house / general counsel | Home, practice pages | Credentials, subject-matter fluency, procedure, predictable cost | Home + `/practice/*` |
|
||
| Referring lawyers and litigation boutiques | Practice pages, fees, process | Availability, rules familiarity, rate card, confidence you won't embarrass them | `/fees/`, `/process/`, `/mediation/` |
|
||
| Appointing bodies and ADR institutions | About, credentials | A verifiable credential record and evidence of depth | `/about/` |
|
||
| Self-represented parties and SMEs | Plain-language layer | What mediation actually is, what it costs, what happens | `/for-parties/` |
|
||
|
||
Realistically, **referring counsel are the largest source of early
|
||
appointments**. Where two audiences conflict on a page, resolve toward counsel.
|
||
`[assumed]`
|
||
|
||
## 6. Architecture
|
||
|
||
Full sitemap, URL map, and per-page content outline: **`docs/01-architecture.md`**.
|
||
|
||
Summary: ~20 static pages. Home; About; three process pages (Mediation,
|
||
Arbitration, Med-Arb); six practice-area pages; Process; Fees; For Parties;
|
||
Insights index and articles; Contact; Privacy; Terms.
|
||
|
||
**Deliberate omission:** there is no Indigenous engagement / IBA practice page at
|
||
launch, though the strategy brief rates that niche as strategically the most
|
||
valuable. Publishing a practice page for it before the multi-year relationship
|
||
work described in brief §III.4 has been done would read as overreach to exactly
|
||
the audience it targets. Revisit at month 12–18. `[verified 2026-08-25 — decision recorded here, not yet discussed with Pouya]`
|
||
|
||
## 7. Environment and stack
|
||
|
||
| Thing | Value |
|
||
|---|---|
|
||
| Framework | **Astro 7.2.7**, `output: 'static'` `[verified 2026-08-26 — npm view astro version, D1 as amended]`. `@astrojs/mdx` 7.0.8, `@astrojs/sitemap` 3.7.3, `sharp` 0.35.4 |
|
||
| 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` 5.9.3 — **every pin at the registry's `latest` except `typescript`** `[verified 2026-08-26 — 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` is held at 5.x: `typescript-eslint` peers `<6.1.0` and `@astrojs/check` peers `^5 \|\| ^6`, so TypeScript 7.0.2 is unusable here `[verified 2026-08-26 — npm view]` |
|
||
| 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]` |
|
||
| Client JS | **Zero. Not "minimal" — none.** `dist/type-scale/index.html` contains **0 `<script>` tags** and `dist/_astro/` contains no JS bundle `[verified 2026-08-26 — built, counted, and confirmed in headless Chrome]`. The reveal was an inline `IntersectionObserver` in `<head>` until 2026-08-26; it was removed because `docs/05-backend-spec.md` specifies `script-src 'self'` with no `unsafe-inline`, so the only script on the site was the one thing the site's own CSP would refuse to run. `animation-timeline: view()` in `global.css` replaces it. **Both scroll-driven animations must use `animation-*` LONGHANDS** — Lightning CSS folds the `animation` shorthand together with `animation-timeline` into an invalid declaration and discards it, which works in `npm run dev` and is dead in `npm run build`. It happened twice in one session: once in the header condense, then again in the fix written for it. `/build` Phase 5 now greps `dist` for it. Islands remain available where a feature genuinely cannot be CSS or progressive HTML — none has been needed |
|
||
| Content | Astro **Content Layer API** collections, MDX for Insights. Config lives at **`src/content.config.ts`**; collections declare a `loader`, not a `type`; `z` imports from `astro/zod`. **Astro 5 introduced** the Content Layer API and this location; **Astro 6 removed** the legacy `src/content/config.ts` fallback — moving the file back now raises `LegacyContentConfigError` `[verified 2026-08-26 — reproduced by moving it back]` |
|
||
| Fonts | Instrument Serif + Geist + Geist Mono, **self-hosted**, `font-display: swap`. **Files present as of 2026-08-26** — six `.woff2` cuts, **123,804 bytes** total, in `public/fonts/` (`du -sh` says 136K; that is disk blocks, not transfer, and this row is read in a performance context); provenance, licences (all SIL OFL 1.1) and the reason they are committed rather than pulled at build are in `docs/reference/fonts-provenance.md`. Before this they were *declared* in `global.css` and **absent from disk**, so every face silently fell back to Georgia and the system sans `[verified 2026-08-26 — built and listed]`. Instrument Serif 400 latin (21,032 B) and Geist latin (29,400 B) are preloaded in `BaseLayout.astro` — about 50 kB at first paint; nothing else is. Every font URL carries **`?v=1`**, because the deploy script serves `/fonts/*` `immutable` for a year: replacing a cut means bumping that query in `global.css` and in the preload together `[verified 2026-08-26 — byte counts]` |
|
||
| AWS account | `327082975128` `[verified 2026-08-26 — inventory]` |
|
||
| Region | **`ca-central-1`** throughout — hosting, Lambda, DynamoDB `[verified 2026-08-26]` |
|
||
| S3 bucket | **`adr-smlcompany-site`** — versioning **Enabled**, so rollback works `[verified 2026-08-26]` |
|
||
| CloudFront | **`E1OK7G98KNKUTA`**, alias `adr.smlcompany.ca`, origin `adr-smlcompany-site.s3.ca-central-1.amazonaws.com`, Deployed `[verified 2026-08-26]` |
|
||
| ACM certificate | `arn:aws:acm:us-east-1:327082975128:certificate/2b6d5bdf-6790-430c-9b82-c00ab66e6d87` — ISSUED `[verified 2026-08-26]` |
|
||
| Intake API | `adr-intake-api`, HTTP API `4tl0m5igkj`, endpoint `https://4tl0m5igkj.execute-api.ca-central-1.amazonaws.com` `[verified 2026-08-26]` |
|
||
| Intake Lambda | `adr-intake-handler`, `nodejs24.x` `[verified 2026-08-26]` |
|
||
| Intake table | `adr-intake-submissions` (DynamoDB, ca-central-1) `[verified 2026-08-26]` |
|
||
| SES identities | Domain `smlcompany.ca` **verified for sending** `[verified 2026-08-26]`; addresses `info@`, `intake@`, `adr@` |
|
||
| SES account | **Production access GRANTED** — out of the sandbox in `ca-central-1`, confirmed by AWS in writing and effective immediately `[verified 2026-08-26 — Q19 closed]`. Mail now reaches unverified recipients, so the inquirer confirmation in D18 works |
|
||
| Mail hosting | **Google Workspace** — MX `1 smtp.google.com`; `google._domainkey` present, so Google DKIM is configured `[verified 2026-08-26 — DNS query]` |
|
||
| **SPF** | **PRESENT.** `v=spf1 include:_spf.google.com include:amazonses.com ~all` on `@`, added by Pouya and independently verified `[verified 2026-08-26 — DNS query, Q20]` |
|
||
| **DMARC** | **PRESENT.** `v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1` on `_dmarc`, added by Pouya and independently verified `[verified 2026-08-26 — DNS query, Q20]`. Still at `p=none`; tighten to `quarantine` only after reports come back clean |
|
||
| Required DNS (Namecheap) | TXT `@` → `v=spf1 include:_spf.google.com include:amazonses.com ~all` · TXT `_dmarc` → `v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1` `[assumed]` — start at `p=none`, tighten after monitoring |
|
||
| SES DKIM — **the set that actually resolves** | `f5puwearz5ek47aiuoskk57ya6ir66ju`, `jdue2r22cxrk27laxlgnjcpby2fq4vxu`, `kznn3cklvwh4gs342rtf5euqymlger53` — all three answer NOERROR at `<token>._domainkey.smlcompany.ca` `[verified 2026-08-26 — DNS]` |
|
||
| SES DKIM — **the set that is BROKEN** | `3zsnvsjg3ddi6hjyndjy6jgjoitvhydl`, `jejgp7na3wdpprsanamct4uxxgw2tyqw`, `xpiwyftpodmgpnzfof3ee7t7fzmlmhnh` — listed in the SES console, present in Namecheap, but **NXDOMAIN**. They were entered with the full name in Namecheap's Host field, so the zone doubled the domain: `<token>._domainkey.smlcompany.ca.smlcompany.ca` resolves, the correct name does not `[verified 2026-08-26 — DNS]` |
|
||
| SES identity — **`ca-central-1` (the one that matters)** | `Verified: true`, `DkimStatus: SUCCESS`, tokens `kznn…` / `jdue…` / `f5pu…` — the set that resolves. **Healthy. Change nothing** `[verified 2026-08-26]` |
|
||
| SES identity — `us-east-1` (unused) | `Verified: false`, `DkimStatus: PENDING`, tokens `3zsn…` / `jejgp…` / `xpiwy…` — the doubled-domain set. A stray identity in a region this project does not use `[verified 2026-08-26]` |
|
||
| ⚠️ **SES sandbox is per-region** | Recorded as a standing caution, not an open task — production access **was** granted in `ca-central-1` (Q19). The trap: the AWS console defaults to `us-east-1`, and granting access there leaves `ca-central-1` in the sandbox with the intake email still failing. Applies again to any future SES request `[verified 2026-08-26]` |
|
||
| SES custom MAIL FROM | **Not configured** — SES reports "No MAIL FROM records found". This is why SES's SPF is unaligned and DMARC rests on DKIM. Optional future improvement, not required `[verified 2026-08-26]` |
|
||
| SES monitoring — SNS topic | **`ses-alerts`**, `ca-central-1`. Email subscription to `info@smlcompany.ca` is **PENDING CONFIRMATION, not confirmed** `[verified 2026-08-26]`. **Until the subscription is confirmed the alarms below fire into nothing** — confirm the link in the subscription email |
|
||
| SES monitoring — alarms | **`SES-BounceRate-High`** (bounce rate ≥ `0.03`) and **`SES-ComplaintRate-High`** (complaint rate ≥ `0.001`), both `ca-central-1`, `treat-missing-data: notBreaching` `[verified 2026-08-26]` |
|
||
| SES bounce/complaint handling | **Email feedback forwarding** — SES's default, on unless disabled. **Deliberately not an SNS feedback topic:** at under 100 messages a month there is nothing to consume a programmatic feed. Revisit when code needs to act on a bounce `[verified 2026-08-26]` |
|
||
| **DMARC alignment** | SES satisfies DMARC via **DKIM**, not SPF — without a custom MAIL FROM domain its envelope sender is at `amazonses.com` and is unaligned. The SPF record's real job is authenticating **Google Workspace** mail `[verified 2026-08-26]` |
|
||
| **Do not delete** | Two ACM validation CNAMEs → `jkddzztszm.acm-validations.aws`. They renew the `adr.smlcompany.ca` certificate; deleting them breaks HTTPS silently at the next renewal `[verified 2026-08-26]` |
|
||
| Root domain | `@` carries a Namecheap **URL Redirect Record** to `sml-coming-soon.ca`. TXT records coexist with it — adding SPF does not disturb it `[verified 2026-08-26]` |
|
||
| DNS confirmed | `adr.smlcompany.ca` CNAME → `d26v23dhgsp2ta.cloudfront.net` `[verified 2026-08-26]` |
|
||
| TLS | ACM certificate `[verified 2026-08-25 — AWS-Hosting-Guide.md]` |
|
||
| DNS | **Namecheap**, not Route 53 `[verified 2026-08-25 — AWS-Hosting-Guide.md]` |
|
||
| 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 — NOT PROVISIONED** | `aws iam get-user --user-name adr-sml-deploy` returns **NoSuchEntity** `[verified 2026-08-26 — Q22]`. The scoped IAM user does not exist, no access key has been issued, and the Gitea secrets are unset. **No file may describe this credential as existing.** Creation commands are with Pouya; this is a to-do, not an open question |
|
||
| **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 |
|
||
|
||
## 8. Design system
|
||
|
||
Full token set, type scale, motion rules, and contrast results:
|
||
**`docs/02-design-system.md`**.
|
||
|
||
Palette and infinity mark carry over unchanged. Execution is modernized:
|
||
fluid type scale, self-hosted fonts, an 8 px spacing scale, motion gated behind
|
||
`prefers-reduced-motion`, and one hard accessibility constraint discovered by
|
||
measurement:
|
||
|
||
> **Gold `#c9a876` on cream `#faf7f2` measures 2.10:1 contrast.** That fails WCAG
|
||
> AA for body text (4.5:1) and for large text (3:1). Gold is a decorative and
|
||
> on-dark colour only. On maroon it measures 5.84:1 and on ink 8.00:1, both of
|
||
> which pass. `[verified 2026-08-25 — computed]`
|
||
|
||
## 9. Open questions — blocking
|
||
|
||
Nothing below can be invented. Each needs an answer from Pouya.
|
||
|
||
| # | Question | Blocks |
|
||
|---|---|---|
|
||
| ~~Q1~~ | **ANSWERED 2026-08-26.** Licensure is left out entirely; the site asserts the JD only. See D13 | — |
|
||
| ~~Q2~~ | **ANSWERED 2026-08-26.** Q.Arb commenced August 2026 | — |
|
||
| ~~Q3~~ | **ANSWERED 2026-08-26.** Email `info@smlcompany.ca`. No public phone — "By scheduled call". Location: Toronto · Ontario · By appointment | — |
|
||
| ~~Q4 / Q14~~ | **ANSWERED 2026-08-26.** Rate card confirmed by Pouya — see D14 and `docs/07-fees.md` | — |
|
||
| ~~Q13~~ | **ANSWERED 2026-08-26.** Self-hosted Gitea with Gitea Actions | — |
|
||
| ~~Q15 / Q16 / Q17~~ | **ANSWERED 2026-08-26.** Non-mediation hourly $500. Prep bundled: 2 h in the half day, 3 h in the full day, **stated on the page**. Overtime $500/h | — |
|
||
| ~~Q25~~ | **ANSWERED 2026-08-26. Struck.** The §4 row permitting the boutique to be named is removed; D16 governs alone | — |
|
||
| ~~Q26~~ | **ANSWERED 2026-08-26.** §4 now carries an explicit **Licence status — NOT ESTABLISHED** row, and its opening no longer asserts that LSO rules apply. The register's rationale stands on the fabricated-credentials history alone | — |
|
||
| ~~Q24~~ | **ANSWERED 2026-08-26.** `AWS-Hosting-Guide.md` copied into `docs/reference/` — it is the only record of how the hand-built infrastructure was created. Scanned for credentials before copying: no access keys, no secrets, no account ID | — |
|
||
| ~~Q22~~ | **ANSWERED 2026-08-26 — it does not exist.** `aws iam get-user --user-name adr-sml-deploy` returns `NoSuchEntity`. Recorded in §7 as **NOT PROVISIONED**; creation commands are with Pouya, so it is now a **to-do**, not a question. Consequence while it is outstanding: deploys run locally (`npm run deploy`), and the quarterly rotation obligation in D3 still has no subject | — |
|
||
| Q5 | Booking tool — **parked 2026-08-26 at Pouya's request.** Build `/contact/` with the form only and a clean slot for the embed. Now tracked as standing reminder R6 | `/contact/` — non-blocking |
|
||
| ~~Q6~~ | **ANSWERED 2026-08-26.** Supplied and committed: `src/assets/pouya-lajevardi.jpg` (1600×1600 master) and `src/assets/og-portrait.jpg` (1200×630 link-preview crop) | — |
|
||
| ~~Q7~~ | **ANSWERED 2026-08-26.** Pouya reverted to generic. The boutique is **never named**; refer to it as *a Toronto litigation and ADR boutique* throughout | — |
|
||
| ~~Q8~~ | **ANSWERED 2026-08-26.** ADRIC, ADRIO, OBA sections. Not OCNI. Not LSO (see §4) | — |
|
||
| ~~Q9~~ | **ANSWERED 2026-08-26.** Gitea, repo `adr-sml`, clone at `/Users/pouya/Dev/Websites/adr-sml` | — |
|
||
| ~~Q10~~ | **ANSWERED 2026-08-26.** Full inventory captured; values in §7 | — |
|
||
| ~~Q18~~ | **ANSWERED 2026-08-26.** `smlcompany.ca` is verified for sending — `true`. The `None` in the earlier listing was a query artefact, not a fault | — |
|
||
| ~~Q19~~ | **ANSWERED 2026-08-26. Production access granted** in `ca-central-1`, confirmed by AWS in writing, effective immediately. The per-region trap was real and was avoided — it was requested in `ca-central-1`, not the console's default `us-east-1`. **Nothing now blocks `/contact/`** | — |
|
||
| ~~Q20~~ | **ANSWERED 2026-08-26.** Both records added by Pouya and independently verified: SPF `v=spf1 include:_spf.google.com include:amazonses.com ~all` on `@`, DMARC `v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1` on `_dmarc`. Both existing `@` TXT records, Google DKIM, MX, and the `adr` CNAME all confirmed intact | — |
|
||
| ~~Q21~~ | **ANSWERED 2026-08-26.** `ca-central-1` is healthy (`SUCCESS`) on the resolving token set. The broken records belong to a stray `us-east-1` identity this project does not use. **Nothing to fix.** The three dead CNAMEs are inert; leaving them is the low-risk choice | — |
|
||
| ~~Q11~~ | **ANSWERED 2026-08-26.** Privacy-first, cookieless — Plausible or Fathom. No GA4, no consent banner | — |
|
||
| ~~Q12~~ | **ANSWERED 2026-08-26.** `https://www.linkedin.com/in/pouyalajevardi/` | — |
|
||
| ~~Q27~~ | **ANSWERED 2026-08-26 — two business days.** A public commitment, so it must read identically everywhere it appears: `src/data/site.ts`, `/contact/`, the inquirer confirmation email, and any bio | — |
|
||
| ~~Q28~~ | **ANSWERED 2026-08-26 — OBA Construction & Infrastructure, ADR, and Civil Litigation** `[verified 2026-08-26 — Pouya]`, **for now**: section membership renews yearly, so this is a fact with a shelf life. Tracked as R10 | — |
|
||
| ~~Q29~~ | **ANSWERED 2026-08-26 by implementation.** Guard widened on both counts: the two secrets are checked (emptiness only, never echoed), and `INTAKE_ENDPOINT` is promoted to job-level `env:` and guarded. Every `vars.*` and `secrets.*` the workflow consumes is now covered except `BOOKING_URL`, empty by decision (R6) | — |
|
||
| ~~Q30~~ | **ANSWERED 2026-08-26 — federally, under the CBCA**, and it does **not** appear in the footer. Recorded in §4 with the caution that it implies nothing about licensure or about where the practice may operate. The footer reads `© <year> SML Company Ltd` and stops there (Pouya's direction, given twice). `src/data/site.ts` un-nulled, `TODO(pouya)` removed. Note the spelling: §4 writes *SML Company Ltd.* with a terminal period; the rendered string has none, because that is how Pouya specified it | — |
|
||
| ~~Q31~~ | **ANSWERED 2026-08-26 — Plausible**, and decided rather than defaulted. Pouya checked and found his own assumption wrong: Fathom is Canadian-owned but stores non-EU traffic on US servers, isolating in the EU only for EU visitors; Plausible holds everything in the EU. D15 amended with that reasoning. The value that was guessed and nulled is now the value that was chosen — which is not the same thing, and the difference is why it was nulled | — |
|
||
| **Q35** | **NARROWED 2026-08-26 — two of the four items are resolved; two are not.** `Arbitration` is answered (Q33) and is now a **§4 Offerings** row. What remains: **(a) Med-Arb** — ungated by the same reasoning, so it is purely a competence call, and Pouya named mediation and the three arbitration forms without naming it. Do not infer it. **(b) `Energy, Grid & Regulatory` and `Shareholder & Family Business`** — subject-matter areas rather than gated activities, so the offering test asks only whether he is competent to mediate or arbitrate in them, which is his answer to give. `docs/03` already fixes how the pages must read once answered — *"Frame as positioning, not as history"* — so this is not a blocker on writing them, only on claiming capability in a one-word nav label before the page exists. Original framing follows | Med-Arb (step 4); the practice pages (step 5) |
|
||
| ~~Q35-orig~~ | **May the header nav and footer sitemap name a service or practice area the register does not establish as a present capability, before the page that qualifies it exists?** This is the architecture-versus-register question that `claims-auditor` has now raised twice, and it is not one an implementer should settle. The nav and footer ship on all nineteen pages and today carry **`Arbitration`**, **`Med-Arb`**, **`Energy, Grid & Regulatory`** and **`Shareholder & Family Business`** — none of which trace to a §4 row. Every one of them is also **locked architecture**: D5 and §6 fix the sitemap, and `docs/01` specifies each page. The specs already anticipate the tension and answer it at page level — `docs/03`: *"Frame as positioning, not as history"*; `docs/01` on energy: *"Write it as a genuine position, not a claim of existing volume"* — so the question is precisely whether a **one-word nav label**, which has no room to frame anything, inherits that licence. Three ways out: accept the labels as taxonomy and rely on the pages to qualify (status quo), rename toward capability-neutral wording, or hold items out of the nav until their page ships. **Claude Code declined to act unilaterally on either audit pass**; recorded so the judgement is visible rather than missed | The header and footer on every page; sharpest at step 5 |
|
||
| ~~Q36~~ | **ANSWERED 2026-08-26 — yes, and so are sole-arbitrator and party-appointed appointments.** Now a row in **§4 Offerings** rather than an unsourced assertion in three specs. `docs/01`, `docs/03` and `docs/07-fees.md` no longer need to cite an open question against it. Original text follows | — |
|
||
| ~~Q36-orig~~ | **Is co-arbitration work available now?** Asserted in three specs — `docs/01` §`/arbitration/`, `docs/03` §Voice as model copy, and `docs/07-fees.md`, which instructs *"The page must say so"* — and **it is in no §4 row.** §4 records the Q.Arb pathway as commenced August 2026 and nothing about what is available in the meantime. The sentence is offered under the heading *"Name the limits"*, so the unverified half travels inside advice about honesty, which is how it survived. All three files now cite this question; none has been deleted, because the *shape* of the sentence is right and only its second clause is unsourced. Raised by `claims-auditor`, 2026-08-26 | `/arbitration/` (step 4) and `/fees/` (step 9) |
|
||
| **Q37** | **Is "Law and engineering" the right label under `JD + ML`?** §4's substitution principle supplies the three credential-row **values** (`Q.Med` / `JD + ML` / `EN · FA`); the labels come from `docs/03` and are not themselves registered. `claims-auditor` reads this one as a D13 implication risk: "engineering" is shorthand for a **verified practising** claim, so the parallel invites "Law" to be read the same way, and D13 bars implication as hard as assertion. Left as written — it is approved copy and changing approved copy is not an implementer's call — with a `TODO(pouya)` on it in `src/data/site.ts`. Nothing renders it yet. Raised by `claims-auditor`, 2026-08-26 | The credential row on `/` (step 2) and `/about/` (step 3) |
|
||
| ~~Q33~~ | **ANSWERED 2026-08-26 — the question rested on a false premise.** It assumed the constraint on describing arbitration was about *entitlement*. ADR designations are voluntary credentials, not licences, and **anyone may be appointed an arbitrator in Ontario** — so the constraint was always positional, never legal, and it was Pouya's to answer. He accepts arbitration appointments now. `Mediation · Arbitration · Toronto` is **restored to the masthead one-liner**, and `docs/04`'s specified home `<title>` stands as written. See **§4 Offerings**, added for exactly this class of statement. The original question text follows for the record | — |
|
||
| ~~Q33-orig~~ | **How is arbitration described in site-wide copy before Q.Arb is conferred?** Not a wording question — a positioning one, and it recurs on every page. §4 records the Q.Arb pathway as **commenced August 2026**, "never as held or nearing completion". `SITE.tagline` reads `Mediation · Arbitration · Toronto`, and `docs/01` specifies that exact string as the **Home hero eyebrow** — where the hero copy beneath it can qualify it. Build step 1 promoted it to a masthead line under Pouya's name on every page, where nothing qualifies it and it reads as a designation strip; `claims-auditor` flagged it and **it has been removed from the header**. But the question it exposed is still open and lands at build step 2: does the hero eyebrow keep `Arbitration` as specified, qualify it, or hold it until designation? `/arbitration/` (step 4) is separately specced to state the position plainly, which is the honest model — the issue is the one-line forms that have no room to. **Second pass, same day:** the removal from the header did not sweep the proof sheet, where the same string was **hand-typed** as a type specimen and rendered ten times at up to 96px. Both defects fixed — the specimen is now a neutral pangram — and the lesson is the sweep rule again: a removal is a `grep`, not a memory. The nav item `Arbitration` also survives, deliberately; that is Q35. Raised by `claims-auditor`, 2026-08-26 | The Home hero (step 2); `docs/04`'s specified home `<title>`; any bio or one-line descriptor |
|
||
| **Q34** | **Do §2's measurements of the previous build still hold, and were they measured against the deployed file?** Two of `claims-auditor`'s findings were unverified old-site assertions published in copy, which sent Claude Code to measure the live URL on 2026-08-26. What came back does not straightforwardly match §2. **Reproduced:** the response is a single **2,206,032-byte** file, and `<title>` is still `SML Company · Dispute Resolution`. **Could not be reproduced from the served HTML:** there are **zero** `base64,` occurrences anywhere in it — §2 records "two logo PNGs ~1 MB, base64-inlined" — and no reference to `unpkg` or a React development build. **Why this is a question and not a correction:** the served document is a *bundler harness*. 2,210,921 bytes of it are inline `<script>`, the real `<head>` sits JSON-escaped inside a `<script type="__bundler/template">`, and the application is loaded from **nine UUID-named script files that were not fetched**. The logos and the CDN references may well be in those bundles. So §2 may be right, may be stale, or may have been measured against the local source rather than the deployment — this record cannot tell which. **Why it matters beyond tidiness:** `docs/02` and `CLAUDE.md` both cite the base64-logo figure as the *reason* for a standing convention, and §12 R8 exists because unverified premises propagate. Nobody should quote these numbers in public copy until the question closes. Raised by Claude Code, 2026-08-26 | Nothing structural. It governs whether §2's figures may be cited as fact |
|
||
| **Q39** | **Does the arbitration offering extend to FAMILY arbitration, and is that activity gated?** Raised by `claims-auditor` 2026-08-26 as a counter-example from inside this repo, and it is a good one. §4 Offerings rests on the proposition that arbitral appointment in Ontario is not gated behind a designation. The site ships a **`Shareholder & Family Business`** practice area — and **family** arbitration in Ontario is understood to be one of the places where arbitrator qualification *is* prescribed by statute and regulation. If so, the offering test's own boundary applies: where an activity is gated, the question is **permission**, and permission is not established. **This record does not resolve it** — Claude Code will not assert a proposition of Ontario law, and neither may a self-report. What is needed: (a) does the practice-area page contemplate family-law arbitration at all, or only shareholder, partnership and family-*business* disputes, which is a different thing; and (b) if it does, the gating question needs a checkable source in the repo, per R14. **Interim:** §4's arbitration rows are scoped to **commercial** arbitration and the universal has been removed from the reasoning. Nothing ships that turns on it | The `Shareholder & Family Business` page (step 5); `/arbitration/` (step 4) |
|
||
| **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]`. *An earlier draft of this row called it "a raster in a vector wrapper", which was unfair and is corrected.* What rules it out is **payload and composition**: 257,278 bytes against **3,063** for the AVIF a Retina browser actually takes — 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. 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 |
|
||
| **Q23** | **Is a Gitea `act_runner` registered, and are Actions enabled in `app.ini`?** *The version half is closed:* the instance reports **1.27.2** `[verified 2026-08-26 — /api/v1/version]`, so the `vars` context is supported and the first-step guard is belt-and-braces rather than load-bearing. **What remains is not a fact to look up but a dependency on a person:** git.larsnolden.com is **jointly administered**, and both enabling Actions and registering a runner need the second administrator. Until then `npm run deploy` is the path, and it is documented rather than treated as a workaround | How deploys are triggered — not what they do |
|
||
|
||
## 10. Risks
|
||
|
||
| Risk | Severity | Mitigation |
|
||
|---|---|---|
|
||
| A fabricated or unverifiable claim reaches a public page | **High** — professional-conduct exposure | §4 register; every claim traced to a source before it ships |
|
||
| Copy silently carried over from the placeholder template | High | Nothing is ported verbatim. All copy written fresh against `docs/03-content-spec.md` |
|
||
| Insights section launches and then goes stale | Medium | D9 commits to 3–5 pieces at launch and a monthly cadence. A dead blog is worse than no blog |
|
||
| Personal data in the intake pipeline without a retention policy | Medium — PIPEDA | `docs/05-backend-spec.md` sets retention, and `/legal/privacy/` states it |
|
||
| Cutover breaks the live site (D11 is a single-shot deploy) | Medium | Full pre-cutover checklist in `docs/06-deployment.md`; CloudFront can be rolled back to the prior origin path |
|
||
| Twenty pages of thin copy rank worse than six good ones | Medium | Each page must justify itself with substantive content. Ship fewer pages rather than padded ones |
|
||
| **`user/pouya` is a broadly-permissioned personal IAM user, and it is what has been authenticating to this account.** Q22 confirms the scoped deploy user does not exist yet | **High** | Fine at an interactive keyboard; **never** in CI, never in a Gitea secret, never in `scripts/deploy-local.sh`. That script refuses to run if `sts get-caller-identity` returns `user/pouya`. Create `adr-sml-deploy` before the first automated deploy `[verified 2026-08-26]` |
|
||
| **The deploy secret will live on jointly-administered infrastructure.** git.larsnolden.com has a second administrator. An instance admin can generally reach repository secrets, or register a runner that receives them — that is inherent to how Actions runners are fed credentials, not a flaw in this setup | **High** | This does not change the plan; it changes **what the scoped IAM policy is for.** The policy is no longer hygiene — it is the actual control standing between a shared Gitea instance and an AWS account holding another business's client-database backups. Four actions, one bucket, one distribution. **Never widen it**, and treat any request to as a security decision, not a convenience one `[verified 2026-08-26]` |
|
||
| **Deploy-credential blast radius.** AWS account `327082975128` is not a single-project account. It also holds `meshkinilaw.ca` and its preview site, `demesne.media`, `orynenergy.ca`, `lajirugs.ca`, and **`mlp-clientdb-prod-backups-327082975128`** (recorded in an earlier entry with the account suffix; the short form `mlp-clientdb-prod-backups` is used elsewhere in the docs) — which **by its name** holds production client-database backups. Only the name was ever observed; the contents were not inspected and the owner is not established | **High** | A static deploy key for a personal website must never be able to reach a client database. The scoped IAM policy in `docs/06-deployment.md` grants four actions on one bucket and one distribution and nothing else — that narrowness is now load-bearing, not hygiene. Never widen it. Never reuse the `user/pouya` credentials in CI `[verified 2026-08-26 — inventory]` |
|
||
| ~~SES is in the sandbox~~ | ~~High~~ → **RESOLVED 2026-08-26** | Q19 closed: production access granted in `ca-central-1`, confirmed in writing. Residual, tracked in §7: the `ses-alerts` SNS email subscription is **pending confirmation**, so the bounce and complaint alarms currently notify nobody `[verified 2026-08-26]` |
|
||
| ~~No SPF and no DMARC on `smlcompany.ca`~~ | ~~High~~ → **RESOLVED 2026-08-26** | Both records added by Pouya and independently verified (Q20). SPF authenticates Google Workspace mail; SES satisfies DMARC through DKIM alignment on the three resolving CNAMEs. DMARC sits at `p=none` — the residual task is to review reports and tighten to `quarantine`, which is monitoring, not a risk `[verified 2026-08-26 — DNS query]` |
|
||
|
||
## 11. Glossary
|
||
|
||
| Term | Meaning |
|
||
|---|---|
|
||
| ADR | Alternative dispute resolution |
|
||
| ADRIC | ADR Institute of Canada — national credentialing body |
|
||
| ADRIO | ADR Institute of Ontario — provincial affiliate |
|
||
| Q.Med | Qualified Mediator — ADRIC/ADRIO designation |
|
||
| Q.Arb | Qualified Arbitrator — ADRIC/ADRIO designation |
|
||
| C.Med-Arb | Chartered Mediator-Arbitrator — senior hybrid designation; the long-term goal |
|
||
| Med-Arb | Hybrid process: mediation that converts to binding arbitration if unresolved |
|
||
| SABS | Statutory Accident Benefits Schedule — Ontario auto insurance benefits |
|
||
| LAT | Licence Appeal Tribunal — hears Ontario SABS disputes |
|
||
| POA | Provincial Offences Act |
|
||
| ENE | Early neutral evaluation |
|
||
| LSO | Law Society of Ontario |
|
||
| OBA | Ontario Bar Association |
|
||
| IESO | Independent Electricity System Operator |
|
||
| OEB | Ontario Energy Board |
|
||
| SMR | Small modular reactor |
|
||
| PIPEDA | Personal Information Protection and Electronic Documents Act |
|
||
|
||
## 12. Standing reminders
|
||
|
||
**Surface these to Pouya at the start of any substantial session.** They are
|
||
open questions he has deliberately parked, not settled matters. The point of
|
||
this section is that a parked decision does not quietly become permanent by
|
||
never being raised again.
|
||
|
||
| # | Reminder | Raised | Why it must keep coming back |
|
||
|---|---|---|---|
|
||
| R1 | **Licensure.** No site exists yet — `src/pages/` does not exist. **As specified**, the copy will assert the JD only and describe the boutique role as *active litigation exposure*, never *practice*. Pouya flagged that as an interim position and asked to be reminded to change it. §4 now records licence status as `[unestablished]`, which is the honest state, not a resolution | 2026-08-26 | If he is licensed and in good standing, it is the first credential appointing bodies and opposing counsel look for, and its absence from a detailed credentials page is conspicuous. If licensure is in progress, the copy should be rewritten the moment it completes. Either way this is a **temporary** framing that will otherwise ossify |
|
||
| R2 | **Matter counts stay off the site until they are independently credible.** Revisit once there is a number a sophisticated GC would find persuasive on its own | 2026-08-26 | §4 forbids them now. That rule has an expiry date nobody has set |
|
||
| R3 | **The month 12–18 practice-area review — now two candidates, not one.** *(a)* **Indigenous engagement**, deliberately omitted at launch (§6). *(b)* **Tax-adjacent disputes**, added 2026-08-26 on the Canadian Tax Foundation membership: it is the one credential none of the six areas touch, and valuation, earn-out, indemnity and shareholder disputes that turn on a tax position are ADR territory. Reasoning for both is in `docs/01-architecture.md`. One review, two candidates | 2026-08-26 | The brief rates the Indigenous niche the most valuable single one, and omission there is a timing call rather than a permanent one. The tax case is the same shape and fails for the same reason today: a practice page is a claim of present capability, and a membership is a credential, not a caseload. Both stop being true at some point, and nothing will tell you when |
|
||
| R4 | **Insights cadence.** D9 commits to monthly. A blog that stops is worse than one that never started | 2026-08-26 | The section's whole value is compounding |
|
||
| 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 | **Annually-renewing memberships.** §4 verifies the **OBA sections** (Construction & Infrastructure, ADR, Civil Litigation) and the **Canadian Tax Foundation** as of 2026-08-26 — *for now*. Both renew yearly. *Widened 2026-08-26 when CTF was added; it was OBA-only before* | 2026-08-26 | A credential that lapses quietly is the failure mode §4 exists to prevent, and OCNI already did exactly this (§4 records it as "not current, do not publish"). Re-confirm at each renewal, and before any page listing memberships ships — `/about/` at build step 3 is the first one that will |
|
||
| 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.** `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 |
|
||
| 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 |
|
||
| ~~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 | — |
|
||
|
||
---
|
||
|
||
# Change Log
|
||
|
||
## 2026-08-26 (v) — The mark was wrong; §4 gains an Offerings category; and two of my own tools lied to me
|
||
|
||
**Who:** Pouya ruled on the mark (Q32), on arbitration (Q33, Q36), and on the
|
||
asset convention. Claude Code implemented; `adversarial-reviewer` and
|
||
`claims-auditor` ran on the result. **The claims audit returned FAIL with 13
|
||
findings, and the sharpest of them were against text written an hour earlier in
|
||
this same entry's work.** Most of what follows is those findings.
|
||
|
||
### Q32 — the infinity mark was a wrong shape, and it had shipped
|
||
|
||
Pouya rendered the committed path against the master and rejected it. Two of his
|
||
three grounds reproduce from the path alone and are now verified here:
|
||
|
||
- **TANGENT, NOT CROSSING.** All four cubic branches meet the origin at exactly
|
||
90°, so the loops are mutually tangent on a vertical line rather than crossing.
|
||
At stroke-width 28 that renders as **two kissing circles** — the one thing an
|
||
infinity mark must not be `[verified 2026-08-26 — tangent vector computed per
|
||
segment]`. This alone disqualifies the shape.
|
||
- **WRONG PROPORTION.** The master's ink bounding box is **2668 × 1704 =
|
||
1.5657:1** `[verified 2026-08-26 — measured]`.
|
||
- **FLAT.** Two uniform strokes standing in for a shaded ribbon: a band of
|
||
variable width twisting in three dimensions, maroon into champagne, passing
|
||
over itself at the crossing.
|
||
|
||
**The 1.23:1 figure is reconciled, and it was a real measurement.** It is the
|
||
bounding box of the path's **coordinates**, not of the **curve**: control points
|
||
sit at y = ±160 where the curve reaches only ±120, so the coordinate hull is
|
||
400 × 320, and with stroke-width 28 on all four sides **428 × 348 = 1.2299**.
|
||
Pouya's number exactly. It is a trap rather than a slip — x is monotone on every
|
||
segment, so the control points give the *right* width and a 33% inflated height,
|
||
and the usual "does the width look right?" check passes.
|
||
|
||
Two consequences worth keeping. **The direction was inverted:** measured from the
|
||
curve, the traced path is 6.4% *wider* than the master, not squatter. And the
|
||
declared viewBox 440:280 = 1.5714 sits within **0.35%** of 1.566 — so step 3 of
|
||
the ruling, re-tune the layout to the true aspect, was **0.18px of work at the
|
||
shipped 2rem**, and re-tuning to 1.23 would have *broken* it: for any
|
||
given width, 1.2299 allocates 1.5657/1.2299 = **27% more height** than the mark
|
||
actually occupies, so the header would have been rebuilt around a box a quarter
|
||
too tall. Where 1.566 does matter is step 2: a tight crop to ink is
|
||
exactly the measurement where 1.667 and 1.566 differ visibly, which is why the
|
||
render source is cropped to the master's ink box and pinned at `667 / 426` rather
|
||
than inheriting 11:7. **The ratio was never sufficient grounds on its own.
|
||
Tangency was, and it is exact** — both lobes are confined to their own
|
||
half-plane, so the signed crossing number is 0, and at 2rem the strokes fuse into
|
||
a single mass across 61% of the mark's height.
|
||
|
||
**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.* `public/favicon.svg` deleted with it; the favicon chain is regenerated
|
||
from the real artwork.
|
||
|
||
### Why two review passes could not catch it — and the rule that follows
|
||
|
||
`InfinityMark.astro` carried a scrupulous provenance comment stating that the
|
||
path was traced from the **old site's loading placeholder** and that a
|
||
placeholder is not necessarily canonical. It was correct, it was prominent, and
|
||
**it did not help**, because the artwork it would have to be checked against was
|
||
not in the repository. The doubt was stated and structurally uncheckable.
|
||
|
||
**New rule in `CLAUDE.md`, and 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 a laptop, the claim is **unverifiable by construction**, not
|
||
merely unverified. Second occurrence — Q24 was `AWS-Hosting-Guide.md`, the only
|
||
record of how the infrastructure was built, living outside the repo.
|
||
|
||
### Two process failures of my own, both in the same shape
|
||
|
||
1. **I reported the brand assets unreachable. They never were.** Four commands of
|
||
the form `timeout 60 ls "$DRIVE"` returned empty output; I read that as an
|
||
empty directory and told Pouya the ruling could not be executed. **`timeout`
|
||
is not installed on macOS** — the commands had never run. The Drive was fully
|
||
readable the whole time and the masters were found within a minute of using a
|
||
tool that exists.
|
||
2. **I reported malformed HTML** — `<picture="true">` and
|
||
`style="block-size:2rem"="true"` — and rewrote a component around it. It was
|
||
**my own regex**, stripping an attribute's name and leaving its value. The
|
||
markup was always valid. The rewrite was reverted.
|
||
|
||
Both are the same failure as *a sweep is a command, not a claim*: an assertion
|
||
resting on output that was never produced, or was produced by my own filter.
|
||
**New rule in `CLAUDE.md`:** *a command that did not run is not evidence of
|
||
absence* — check the tool exists, read exit status, not just stdout.
|
||
|
||
### What the mark is now
|
||
|
||
`src/assets/brand/` holds the master (3000 × 3000, alpha), the tight crop that is
|
||
the render source (2668 × 1704, so **the file's aspect ratio is the mark's**),
|
||
the full lockup, and the SVG Pouya added. `InfinityMark.astro` renders it through
|
||
Astro's `<Picture>`; a browser downloads **3,063 bytes** of AVIF on a Retina device.
|
||
|
||
**Correction inside this entry.** The SVG was first described here and in three
|
||
files as "a raster in a vector wrapper". That was unfair: rasterised at 8333 px
|
||
it **reproduces the master exactly, at the same 1.566:1** `[verified 2026-08-26]`.
|
||
What rules it out is **payload and composition** — 257,278 bytes against 9,468,
|
||
plus seven embedded base64 PNGs that would breach `CLAUDE.md`'s no-base64 rule.
|
||
Corrected in the component, `docs/02`, Q38 and `docs/reference/brand-assets.md`.
|
||
|
||
**`width={320}` on `<Picture>` is load-bearing.** Without it Astro emits the
|
||
untouched 2668 px master as the `<img src>` fallback — **1,146,406 bytes** — sitting
|
||
in `dist` looking like an optimisation had happened.
|
||
|
||
**Documented, temporary exception to `docs/02`'s "inline SVG, never a PNG"**,
|
||
tracked as **Q38** (a vector master that is faithful *and* light) with **R13** so
|
||
it cannot become permanent by neglect — Pouya's own warning when he made the
|
||
ruling.
|
||
|
||
### Q33 and Q36 — arbitration, and a new §4 category
|
||
|
||
**Both answered: Pouya accepts arbitration appointments now** — sole,
|
||
party-appointed and co-arbitration. His reasoning: ADR designations are
|
||
**voluntary credentials, not licences**, so the constraint was always
|
||
**positional**, never legal. `Mediation · Arbitration · Toronto` is **restored to
|
||
the masthead**; `docs/04`'s home `<title>` stands as specified.
|
||
|
||
**§4 gains an Offerings subsection** — a second category beside the credential
|
||
register, with the test: **competence for an offering, permission for a
|
||
credential.** Three specs that asserted the converse were swept
|
||
(`docs/01`, `docs/03`, `docs/07-fees.md`) — and that sweep had been *claimed* in
|
||
the Q36 closure before it was *run*, which `claims-auditor` caught. `docs/03`'s
|
||
model sentence was wrong in **both** halves and in opposite directions, which is
|
||
why it had survived two audits.
|
||
|
||
### What the claims audit found in the new section, and how it was resolved
|
||
|
||
Four structural defects in text written the same hour:
|
||
|
||
- **The masthead justification answered the wrong objection.** Q33-orig had
|
||
objected to **placement** — a line under his name on every page *"where nothing
|
||
qualifies it"* — not to entitlement. The section refuted an entitlement
|
||
objection nobody raised. Corrected, and the substitution is recorded rather
|
||
than tidied away.
|
||
- **§4's own paired-disclosure condition was unmet by the shipped build.** The
|
||
section says the site *"makes the first while stating the second plainly"* and
|
||
*"neither half may be dropped"* — yet the masthead shipped on every page while
|
||
the Q.Arb stage shipped on none, and `CREDENTIALS.inProgress` existed in
|
||
`site.ts` rendered nowhere. **The footer designation strip now reads
|
||
`Q.Med (ADRIC / ADRIO) · Q.Arb — commenced August 2026` on every page.**
|
||
- **The offering test handed the gating question to the subject.** Its closing
|
||
sentence, *"that is his call, not the register's"*, attached to the whole
|
||
preceding question including *whether the activity is gated* — a clean
|
||
walk-through for a future writer. Split in two: **gating is this register's
|
||
question and needs a checkable source; competence is his and only his.**
|
||
- **The category had no gate and no defined extent.** No rule said an offering
|
||
needs a row before it may be published, and `Med-Arb` was sitting in the
|
||
site-wide footer as an offering three sentences below a paragraph saying not to
|
||
infer exactly that. Both fixed: the publication rule is stated, scope is
|
||
defined (**an offering is a PROCESS; a subject-matter area is not**), and
|
||
**Med-Arb is removed from the footer pending Q35(a)**.
|
||
|
||
**And the finding that matters most: four unsourced propositions of Ontario
|
||
law.** *"Anyone may be appointed an arbitrator in Ontario. Nothing in law gates
|
||
the role behind a designation."* `[verified — Pouya]` means *the subject reported
|
||
it*, which can establish what he offers and **cannot establish a proposition of
|
||
law**. The auditor produced a counter-example from inside the repo: the site
|
||
ships a **Shareholder & Family Business** practice area, and **family**
|
||
arbitration in Ontario is understood to be one of the places where arbitrator
|
||
qualification *is* prescribed. §4 now carries the proposition as **the
|
||
architect's stated basis, deliberately unstamped**, scoped to **commercial**
|
||
arbitration, with the universal removed — and **Q39** opened. This record will
|
||
not assert Ontario law, and neither will a self-report.
|
||
|
||
### Questions and reminders
|
||
|
||
**Closed:** Q32 (the reconstruction was wrong), Q33, Q36.
|
||
**Narrowed:** Q35 — `Arbitration` resolved; **Med-Arb** and the two
|
||
subject-matter labels remain, now under the scope note that says which rule
|
||
governs them.
|
||
**Opened:** **Q38** (vector master), **Q39** (family arbitration and gating).
|
||
**§12:** **R13** (the raster is temporary), **R14** (assets must be in-repo).
|
||
|
||
### Verification — run, not asserted
|
||
|
||
`npm run check` 0/0/0 · `npm run lint` clean · `npm run build` 0 errors ·
|
||
`npm audit` **0 vulnerabilities**.
|
||
|
||
Measured in headless Chrome at 320/375/640/768/1000/1023/1024/1100/1199/1200/1201/1279/1280/1281/1440/1600,
|
||
**with a seventh nav item injected** for the step-7 state:
|
||
|
||
- **0 overflow, 0 focus-order inversions, 0 tap targets under 44 × 44** at every
|
||
width; closed `<details>` panels contribute 0 hidden tab stops
|
||
- Header a constant **81px** at every sticky width — 32 padding + 48 reserved
|
||
brand block + **the 1px border I had forgotten**, which is why `--header-h` is
|
||
measured rather than added up
|
||
- Restoring the tagline **broke the 64rem fit** (84px past the content box at
|
||
1024 with seven items) and was re-tuned: tagline gated at 75rem, nav gap moved
|
||
to **80rem so two things do not grow at one breakpoint** — 4px of spill at
|
||
exactly 1200px caught that
|
||
- **0 `<script>` tags, 0 `.js` files**; both scroll-driven animations attach live
|
||
- Print: all four revealed cards present · **760 words** with no JavaScript
|
||
|
||
### ⏸ RESUME HERE — session ended mid-flight, 2026-08-26
|
||
|
||
Pouya went mobile. The tree is committed and green; nothing is half-applied.
|
||
**Pick up at the numbered list below.**
|
||
|
||
**State:** `npm run check` 0/0/0 · `npm run lint` clean · `npm run build` 0
|
||
errors · `npm audit` 0 vulnerabilities. Both `adversarial-reviewer` blocking
|
||
findings on the mark are fixed and re-measured. Build step 1 is complete; step 2
|
||
is `/` (`docs/01` §Build order).
|
||
|
||
**Waiting on Pouya — nothing proceeds on these without a ruling:**
|
||
|
||
1. **Q35** — may the nav and footer name `Med-Arb`, `Energy, Grid & Regulatory`
|
||
and `Shareholder & Family Business`? Both halves go to him **together**;
|
||
`Med-Arb` was removed from the footer for a few hours and restored, because
|
||
acting on one half unilaterally was wrong and orphaned the page.
|
||
2. **Q39** — family arbitration. §4's arbitration rows are scoped to
|
||
**commercial** pending an answer. This one has a legal dimension the register
|
||
cannot resolve from a self-report.
|
||
3. **Q37** — is `JD + ML → "Law and engineering"` the right label? Answer before
|
||
step 2 renders the credential row.
|
||
4. **Q38 / R13** — the commissioned vector master. Until it lands the mark ships
|
||
as a raster under a documented exception.
|
||
5. **R1** — the licensure framing is interim and now carried by shipped pages.
|
||
|
||
**Unresolved review findings, all non-blocking, none shipped-defect:**
|
||
|
||
- `adversarial-reviewer` #7: `gap: 2px` and `font-size: 0.6875rem` in
|
||
`SiteHeader` are magic numbers; add `--text-2xs` / `--space-05` or record the
|
||
deviation.
|
||
- #8: Astro's `<Picture>` + `densities` emits a duplicate `1x` descriptor in
|
||
`srcset` — an HTML conformance error, harmless in browsers. Do not claim
|
||
"validator clean" on a page carrying it.
|
||
- #9: `--header-h` has 0.81 px of headroom at ≥76 rem. A font-metric change
|
||
pushes the brand past its 48 px reservation with no build error.
|
||
- #10: the masthead tagline duplicates the home hero eyebrow, so `/` will open
|
||
with the same words twice. Decide at step 2.
|
||
- #11: ~3.16 MB of brand binaries; the 1.1 MB derived crop is reproducible from
|
||
the committed master and could go if Astro could crop at build time.
|
||
- #12: `InfinityMark`'s `label` and `class` props have no call site.
|
||
|
||
**One correction owed to the record.** Entry (v)'s verification section says
|
||
"0 overflow at every width". That was true and **misleading** — the page-level
|
||
check passed *because* the brand block absorbed the deficit by crushing the
|
||
logo. The harness now asserts the rendered aspect ratio of every mark instance,
|
||
not just document overflow. Measure the elements, not only the page.
|
||
|
||
### Declined and deferred
|
||
|
||
- **The remaining Q35 labels** — locked architecture; not an implementer's call.
|
||
- **A verification workflow was run and half of it was wasted, which is worth
|
||
recording rather than hiding.** It was launched while the artwork was believed
|
||
unreachable, and briefed accordingly; the premise died four minutes later when
|
||
the assets were found. Its *design* strand — three proposals for coping without
|
||
the artwork — was answered by reality and discarded. Its *geometry* strand did
|
||
not depend on that premise at all, and it is what **reconciled 1.23:1** above,
|
||
proved the zero crossing number, and computed the fused-waist figure. The
|
||
lesson is not "don't delegate" but "scope a brief to what cannot change under
|
||
it": a premise about the environment goes stale, a question about the contents
|
||
of a committed file does not.
|
||
- **`Technology, AI & Data` and the other three practice labels**, raised as
|
||
offering claims with no rows: answered structurally rather than individually —
|
||
subject-matter areas are **not** offerings, so the Offerings table is not the
|
||
rule that governs them. `docs/03`'s framing rule and Q35 are.
|
||
- **`docs/reference/brand-assets.md` reproducing R14** — the auditor was right
|
||
that every measurement derived from a master outside the repo. **Fixed, not
|
||
declined:** the uncropped master is now committed, so the crop is re-derivable
|
||
in-repo, and every figure carries a stamp.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (u) — Build step 1. Zero JavaScript, and both review passes found defects in the first pass's fixes
|
||
|
||
**Who:** Pouya ruled on CTF, Q30, Q31 and the Lighthouse gap, and set the
|
||
direction for step 1. Claude Code implemented under `/build`;
|
||
`adversarial-reviewer` and `claims-auditor` each ran **twice**, and the second
|
||
pass is where most of this entry comes from.
|
||
|
||
### What Pouya decided
|
||
|
||
- **Canadian Tax Foundation membership** added to §4 and to
|
||
`CREDENTIALS.memberships`, `[verified 2026-08-26 — Pouya]`, **for now** — it
|
||
renews yearly, so **R10 was widened** from "OBA section membership" to
|
||
"annually-renewing memberships" covering both.
|
||
- **Not a seventh practice area at launch.** CTF is a credential none of the six
|
||
areas touch and tax-adjacent disputes are ADR territory, but there is no track
|
||
record to point at. Recorded in `docs/01` beside the Indigenous-engagement
|
||
omission, and **R3 widened from one candidate to two** — one month 12–18
|
||
review, two questions.
|
||
- **Q31 CLOSED — Plausible**, decided rather than defaulted. Pouya checked and
|
||
found his own assumption wrong: Fathom is Canadian-owned but stores non-EU
|
||
traffic on US servers, isolating in the EU only for EU visitors; Plausible
|
||
holds everything in the EU. **D15 amended** from "Plausible **or** Fathom" to
|
||
Plausible, with that reasoning and with Fathom recorded as rejected on data
|
||
residency rather than on quality.
|
||
- **Q30 CLOSED — SML Company Ltd is incorporated FEDERALLY, under the CBCA.**
|
||
Two facts were being conflated and one was wrong: `site.ts` carried
|
||
`'SML Company Ltd. · Ontario, Canada'`, which reads as a jurisdiction of
|
||
incorporation and named the wrong one. Jurisdiction of incorporation is
|
||
federal; place of business is Toronto, Ontario; they are different facts.
|
||
**Neither is published** — the footer reads `© <year> SML Company Ltd` and
|
||
stops, on Pouya's direction given twice. No corporation number: none is held
|
||
and none is needed. §4 carries the caution that "federally incorporated" says
|
||
nothing about licensure and nothing about where the practice may operate, and
|
||
must not be read together with the **Licence status — NOT ESTABLISHED** row
|
||
into an implication neither makes.
|
||
- **The Lighthouse gap, ratified and closed as a documented-control problem.**
|
||
Removing `@lhci/cli` was right — seven high-severity advisories for a tool
|
||
that could not run — but it left `docs/04`, `CLAUDE.md` and `/build` Phase 5
|
||
all requiring a check that had silently become impossible. That is Q22's
|
||
defect in a different costume. The unavailability is now stated in **six
|
||
places**, listed in §7, and `adversarial-reviewer` is told **not** to raise the
|
||
absence as a finding. **R11 carries the re-add trigger** for step 7, including
|
||
"check for a patched release; do not assume `0.15.1` is still the ceiling".
|
||
|
||
### What shipped
|
||
|
||
`src/layouts/BaseLayout.astro`, `src/components/{SEO,SiteHeader,SiteFooter,Button,InfinityMark}.astro`,
|
||
`public/fonts/` (six cuts), `public/favicon.{svg,ico}`, `public/apple-touch-icon.png`,
|
||
and a temporary proof sheet at `/type-scale/` that step 2 deletes.
|
||
|
||
**The fonts were never on disk.** `global.css` declared six `@font-face` rules
|
||
pointing at `/fonts/*.woff2` and `public/fonts/` did not exist, so every face had
|
||
been silently falling back to Georgia and the system sans. The design system had
|
||
never once rendered as specified. Six cuts committed, **123,804 bytes** (not the
|
||
136 K `du -sh` reports — that is disk blocks, and §7 is read in a performance
|
||
context), provenance and SIL OFL licences in `docs/reference/fonts-provenance.md`.
|
||
Every URL carries `?v=1` because the deploy script serves `/fonts/*` `immutable`
|
||
for a year and no invalidation reaches a browser cache.
|
||
|
||
**The infinity mark was recoverable, and is not a redrawing.** The deployed site
|
||
serves its own loading-thumbnail SVG labelled `smlMark`; `InfinityMark.astro` is
|
||
that path verbatim, with the source's `translate(60 0)` folded into the
|
||
coordinates. Stroke widths (28/6) and the 0.7 inner opacity are the source's.
|
||
Geometry verified to fit its viewBox with 6 units of margin, so it cannot clip at
|
||
any size. **Q32 opened** — a loading placeholder is not necessarily the canonical
|
||
file, and D7 says the mark carries over *unchanged*.
|
||
|
||
**The proof sheet computes rather than restates.** It reads `tokens.css` at build
|
||
time and runs the WCAG luminance formula over it. All eleven ratios reproduce
|
||
`docs/02`'s measured table exactly — 16.81, 11.75, 12.29, 8.95, 5.47, 3.11, 2.10
|
||
on cream; 16.81, 11.09, 8.00, 3.07 on ink. It cannot drift from the tokens
|
||
because it is derived from them.
|
||
|
||
**Insights is gated, not hardcoded.** `docs/01` keeps the section out of primary
|
||
nav until two pieces are live, so `SiteHeader` reads the collection count at
|
||
build time. It does not render today and appears by itself at step 7.
|
||
|
||
### Decisions taken during implementation
|
||
|
||
- **ZERO JavaScript, not "minimal".** The reveal was an inline
|
||
`IntersectionObserver` in `<head>`. `docs/05` specifies `script-src 'self'`
|
||
with no `unsafe-inline` — so the only script on the site was the one thing the
|
||
site's own CSP would refuse to execute, and a per-build hash drifts from the
|
||
policy pinning it. Replaced with `animation-timeline: view()` behind
|
||
`@supports`. **0 `<script>` tags and 0 `.js` files in `dist/`.** `docs/02`
|
||
§Motion amended: the `IntersectionObserver` route is now ruled out, not merely
|
||
second choice. `docs/05` gained the converse note — inline `style=` attributes
|
||
are now load-bearing, so hashing `style-src` would collapse the infinity mark.
|
||
- **Header sticky from 64rem, not `docs/02`'s "sticky".** Measured: the one-row
|
||
header must hold brand + **seven** nav items + CTA, which it does at 1024px
|
||
with 32px clearance and not below. Below 64rem the nav takes its own row and
|
||
the header is 137px (tablet) / 185px (phone) — more of a small viewport than a
|
||
sticky header is worth. `docs/02` amended with the measured numbers.
|
||
- **"Condenses on scroll" is now a rule and a shadow, not a size change.** A
|
||
`position: sticky` header stays in normal flow, so shrinking its padding
|
||
shortens its layout box at the top of the document and lifts every page below
|
||
it — a scroll-linked layout shift on every page, against the CLS < 0.05 budget.
|
||
- **`--section-y` corrected.** `6vw + 2rem` reaches its own upper bound only at a
|
||
2133px viewport, so the "160px desktop" half of `docs/02` was never delivered
|
||
(measured 108.8px at 1280, 128px at 1600). Now `9vw + 1rem` — 160px at 1600px.
|
||
- **The measure is opt-in.** A global `p { max-inline-size: 68ch }` capped every
|
||
paragraph in every card and footer, forced components to opt back out, and made
|
||
`.prose` a class with no effect. Moved onto `.prose`.
|
||
- **`robots.txt` disallows nothing.** `Disallow` + `noindex` on the same path
|
||
cancel out: a crawler forbidden to fetch a URL never reads the `noindex`. The
|
||
legal pages are footer-linked from every page, so the pair would likely have
|
||
produced bare-URL listings with the suppressing directive unread. `docs/04`
|
||
amended.
|
||
|
||
### What the reviews found — including in each other's fixes
|
||
|
||
**Round 1 — code (5 blocking, 7 should-fix).** All fixed and re-measured:
|
||
the `animation` shorthand silently collapsed by Lightning CSS (dead in every
|
||
production build, alive in dev); a seven-item nav that fit at no width; parent
|
||
styles that never reach a child component's root in Astro; gold text shipped at
|
||
**1.76:1**; 27 touch targets under 44px; revealed content printing blank; a skip
|
||
link landing behind the sticky header; 72px of overflow at 320px masked by
|
||
`body { overflow-x: hidden }`.
|
||
|
||
**Round 1 — claims (FAIL, 8).** The masthead tagline `Mediation · Arbitration ·
|
||
Toronto` — never specified for the header, and unqualified `Arbitration` beneath
|
||
a name reads as a held capability against §4's Q.Arb row. Removed; **Q33 opened**
|
||
for the form the question takes at step 2. A four-stage service timeline and two
|
||
unverified assertions about the previous build, both removed from a page that
|
||
exists to demonstrate CSS.
|
||
|
||
**Round 2 found defects in round 1's fixes — four of them.**
|
||
|
||
1. **The minifier bug was written back into its own fix.** Having just diagnosed
|
||
that `animation:` beside `animation-timeline` gets folded into an invalid
|
||
declaration, the replacement reveal used the same shorthand. Caught only
|
||
because the print test still failed. Both are longhand-only;
|
||
**`/build` Phase 5 now greps `dist` for it** and `CLAUDE.md` carries the rule.
|
||
2. **The claims fixes were silently lost.** The patch applying all four aborted
|
||
on a later assertion *before* writing the file, having already reported four
|
||
successes. The service timeline was still in the source and still in the
|
||
build.
|
||
3. **The colour-alone fix used the banned pairing.** Nav state was given a gold
|
||
underline so colour would not carry meaning alone — gold on cream is 2.10:1,
|
||
below WCAG 1.4.11's 3:1 for a state indicator, so it carried nothing and the
|
||
state reverted to colour alone. Now maroon at 12.29:1, 2px, dotted for section
|
||
and solid for current page.
|
||
4. **`--header-h` was stamped `[measured]` before anything was measured.** It
|
||
said 4.75rem. Actual is **77px** at every sticky width, six items and seven.
|
||
5. Plus: a retracted "470 KB PNG" figure surviving in a file written the same
|
||
day; two "measured" comments giving 34px and 14px for the same measurement;
|
||
`docs/02` amended to a 60rem threshold the code no longer used; flex `order`
|
||
putting focus order out of step with visual order between 640 and 1023px.
|
||
|
||
**Round 2 — claims (FAIL, 10).** The most serious was inside the review
|
||
apparatus: **`.claude/agents/claims-auditor.md` said "Memberships. ADRIC, ADRIO,
|
||
OBA sections **only**"** — contradicting a §4 that had gained CTF that morning.
|
||
It would have flagged a verified membership as unverified and would not have
|
||
noticed CTF being dropped. That file has now hosted a stale claim **twice**, so
|
||
the list was not updated — it was **removed**, replaced by an instruction to read
|
||
the §4 row at audit time. Also fixed: two specs instructing a public phone onto
|
||
pages against a §4 row that records none (Q3, answered four entries ago); a
|
||
`robots.txt` reproduced inline in `docs/04` that had already drifted; Q.Arb
|
||
described as "in progress" where §4 pins "commenced August 2026"; and headline
|
||
option 3 struck as asserting party, counsel *and* neutral.
|
||
|
||
**`ROLE` added to `site.ts`** — there was no constant for *"Director of Firm
|
||
Operations"* or *"active litigation exposure"*, the two highest-risk strings on
|
||
the site, and both were headed for hand-typing at step 3 and in the `Person`
|
||
JSON-LD. `npm run check` then caught a temporal-dead-zone error in that fix.
|
||
|
||
### Declined, with reasons
|
||
|
||
- **The nav and footer naming `Arbitration`, `Med-Arb`, `Energy` and
|
||
`Shareholder`** — raised on both claims passes. Every one is locked
|
||
architecture (D5, §6, `docs/01`), and §4 registers claims about Pouya rather
|
||
than enumerating service lines. Removing them is not an implementer's call.
|
||
**Opened as Q35** so the judgement is visible rather than missed.
|
||
- **Inlining critical CSS.** `docs/04` names it, but the bundle is 22.5 KB
|
||
against a 4 KB `inlineStylesheets: 'auto'` threshold; inlining it into all
|
||
nineteen pages costs more than the one round trip it saves and destroys
|
||
cross-page caching. Revisit at step 11 against real Lighthouse numbers.
|
||
- **Simplifying `PRACTICE_SLUGS` / `PRACTICE_AREAS`.** A fair simplicity finding,
|
||
but it touches the content schema a prior review round hardened, and rewriting
|
||
it as a rider on step 1 is exactly how defects 1 and 2 above happened.
|
||
Deferred to its own change before step 7.
|
||
- **The sitemap 404.** `<link rel="sitemap">` removed as inert (it is not a
|
||
registered link relation and no crawler consumes it); the `robots.txt`
|
||
`Sitemap:` line resolves at step 2. **Step 1 is not deployed** — no deploy
|
||
credential exists (Q22) and deploys are manual.
|
||
- **Second-person copy on the proof sheet.** `docs/03` bars it on counsel-facing
|
||
pages; an internal proof sheet is not one, and the page is deleted at step 2.
|
||
|
||
### Verification — run, not asserted
|
||
|
||
`npm run check` 0/0/0 · `npm run lint` clean · `npm run build` 0 errors ·
|
||
`npm audit` **0 vulnerabilities** · **R11 dependency sweep: 13 of 14 pins at
|
||
registry `latest`**, the only drift being the deliberate `typescript` hold.
|
||
|
||
Measured in headless Chrome across 320/375/640/768/900/1000/1023/1024/1100/1200/1280/1440/1600,
|
||
**with a seventh nav item injected** to test the step-7 state that does not exist yet:
|
||
|
||
- Header **77px, one row, sticky** at every width ≥1024 with six items and seven;
|
||
137px static below; **0 overflow at every width including 320px**
|
||
- **0 tap targets under 44×44** at any width
|
||
- **0 focus-order inversions** (`checkVisibility`-filtered); closed `<details>`
|
||
panels contribute **0** hidden tab stops
|
||
- State indicators **12.29:1**; `--section-y` reaches exactly **160px at 1600px**
|
||
- `scroll-padding-top` computes to **93px** = 77 + 16, matching `--header-h`
|
||
- **0 `<script>` tags, 0 `.js` files**; both scroll-driven animations attach live
|
||
(`header-lift`/`scroll()`, `reveal-in`/`view()`)
|
||
- Printed to PDF: all four revealed cards present (they were absent before the
|
||
print override)
|
||
- **755 words** of body text from `curl` with no JavaScript — the old site
|
||
returns three
|
||
|
||
### Sweeps — the commands, not the claims (R8)
|
||
|
||
```
|
||
$ grep -rE 'animation:[^;}]*(scroll\(\)|view\(\))' dist --include='*.css'
|
||
(no output)
|
||
|
||
$ grep -rn "OBA sections only" . --exclude-dir={node_modules,.git,dist,.astro}
|
||
.claude/agents/claims-auditor.md:58:enumerate "ADRIC, ADRIO, OBA sections only"; the Canadian Tax Foundation was
|
||
(the sole hit is the note recording its removal)
|
||
|
||
$ grep -c '<script' dist/type-scale/index.html -> 0
|
||
$ find dist -name '*.js' | wc -l -> 0
|
||
$ grep -c "Mediation · Arbitration · Toronto" dist/type-scale/index.html -> 0
|
||
```
|
||
|
||
The `470 KB` sweep is the one worth recording as a near-miss: after retracting
|
||
the figure from `docs/02`, `grep -rn "470 KB"` found it **still asserted in
|
||
`src/components/InfinityMark.astro`, a file written the same day**. Without the
|
||
grep this entry would have claimed a completed retraction. Three previous entries
|
||
made exactly that mistake.
|
||
|
||
### Questions
|
||
|
||
**Closed:** Q30 (federal, CBCA), Q31 (Plausible).
|
||
**Opened:** **Q32** (canonical vector for the infinity mark) · **Q33** (how
|
||
arbitration is described in one-line site-wide copy before Q.Arb) · **Q34**
|
||
(whether §2's measurements of the previous build still hold — a re-fetch found
|
||
zero `base64,` occurrences, but the real application is in nine unfetched
|
||
bundles, so this is a question and not a correction; §2 now carries the caveat
|
||
inline, and `CLAUDE.md`, `docs/02` and `adversarial-reviewer.md` all cite it
|
||
where they quote the figure) · **Q35** (may the nav name a service the register
|
||
does not establish) · **Q36** (is co-arbitration work available now — asserted in
|
||
three specs, in no §4 row) · **Q37** (is "Law and engineering" the right label
|
||
under `JD + ML`).
|
||
|
||
**§12:** R3 widened to two candidates, R10 widened to all annually-renewing
|
||
memberships, R11 given the `@lhci/cli` re-add trigger. **R1 remains live and was
|
||
surfaced** — nothing in this change describes Pouya as licensed or as a legal
|
||
professional, and the header tagline that triggered Q33 is verified absent from
|
||
`dist`.
|
||
|
||
### Commit shape
|
||
|
||
Split in two on `adversarial-reviewer`'s own recommendation: instructions that
|
||
narrow a reviewer's scope should not travel in the same commit as the work that
|
||
reviewer is checking. The `.claude/` changes go in their own `chore:` commit so
|
||
the narrowing reads as a decision rather than as a line in a feature diff.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (t) — Astro 5 → 7. The review found a defect in the fix, twice
|
||
|
||
**Who:** Pouya approved the upgrade and set the version-pin rule. Claude Code
|
||
implemented under `/build`; `adversarial-reviewer` and `claims-auditor` both ran
|
||
and both failed the first attempt.
|
||
|
||
**This entry resolves the escalation left open at the end of entry (s).** That
|
||
entry recorded the upgrade as *proposed, not done*; it is done now.
|
||
|
||
### D1 amended — the major is pinned, not inherited
|
||
|
||
Old → new: *"Astro, static output"* → **"Astro, static output, v7.x"**, with the
|
||
reasoning in the decision itself so the next reader knows the version was chosen.
|
||
|
||
Pouya's own account of the original pin, recorded at his instruction: `^5.0.0`
|
||
was **recalled, not checked, and was two majors stale on the day it was
|
||
written.** That is a different failure class from the sweep problem in entries
|
||
(l) through (o) — not a change that failed to propagate, but a fact asserted
|
||
that one command would have settled.
|
||
|
||
Verified with `npm view`, and the staleness was not confined to Astro:
|
||
|
||
| | was pinned | registry `latest` |
|
||
|---|---|---|
|
||
| `astro` | `^5.0.0` | **7.2.7** |
|
||
| `@astrojs/mdx` | `^4.0.0` | **7.0.8** — three majors |
|
||
| `eslint` | `^9.0.0` | **10.9.1**; npm flags 9.x deprecated |
|
||
| `sharp` | `^0.33.0` | 0.35.4 |
|
||
| `@lhci/cli` | `^0.14.0` | 0.15.1 |
|
||
|
||
**The rule is now in `CLAUDE.md`**: a version pin is verified against the
|
||
registry, never recalled. **R11** requires re-checking currency at every phase
|
||
boundary in the build order — ten remain.
|
||
|
||
A caveat the rule needs, learned here: **verified does not mean latest.**
|
||
`typescript` 7.0.2 exists and is unusable — `typescript-eslint` peers `<6.1.0`
|
||
and `@astrojs/check` peers `^5 || ^6`. `@eslint/js` latest is 10.0.1, but on
|
||
ESLint 9 it must track 9.x exactly. The check tells you the number; you still
|
||
have to know why you chose it. Both holds are now recorded in §7 so the next
|
||
agent running R11 does not re-derive them or break the build discovering them.
|
||
|
||
**The rule was then not applied to its own manifest**, which the second review
|
||
caught: `eslint-plugin-astro` was left at `^1.3.0` while §7 asserted a hold at
|
||
1.7.0, and `typescript` at `^5.7.0` against an installed 5.9.3. Both re-pinned.
|
||
Every one of the twelve pins is now verified against `npm view`.
|
||
|
||
### The migration
|
||
|
||
Astro 5 introduced the Content Layer API and `src/content.config.ts`; **Astro 6
|
||
removed the legacy `src/content/config.ts` fallback**. Verified by moving the
|
||
file back and reproducing `LegacyContentConfigError` — an earlier draft of this
|
||
entry attributed the move to Astro 6, which `claims-auditor` caught as a version
|
||
fact recalled rather than checked, *in the change that raised recalling-versions
|
||
to a rule.*
|
||
|
||
`type: 'content'` → `loader: glob(...)`, `z` from `astro/zod`. `schema: ({ image
|
||
}) => …` still works — verified, not assumed.
|
||
|
||
**An empty build proves nothing**, so a throwaway page and article exercised
|
||
`getCollection`, `render(entry)`, `entry.id`, MDX rendering, the `image()`
|
||
helper resolving to a hashed asset, and sitemap emission at `/sitemap-0.xml`
|
||
with no trailing-slash conflict. Then deleted.
|
||
|
||
### `compressHTML` — held at `true`, and the first rationale was wrong
|
||
|
||
Astro 7 changed the default to `'jsx'`. I asserted this would eat spaces in
|
||
prose, measured **MDX**, and found no difference — the premise was unsupported
|
||
as written. Measuring the `.astro` path instead:
|
||
|
||
```
|
||
'jsx': <em>inline</em><strong>pair</strong> ← space silently deleted
|
||
true: <em>inline</em> <strong>pair</strong>
|
||
```
|
||
|
||
Real hazard, wrong file. `adversarial-reviewer` reproduced both outputs
|
||
independently. The comment now names `.astro` templates specifically and records
|
||
that MDX is unaffected, so nobody inherits the original claim. **R12** makes the
|
||
deviation revisitable only with a measurement.
|
||
|
||
### What the review caught — seven defects in the fix itself
|
||
|
||
**Both reviewers, independently:** two source files cited *this entry* before it
|
||
existed. Current Truth had been rewritten in place with no append-only record,
|
||
and `astro.config.mjs` pointed at it as the authority for a measurement. Rules
|
||
1–3 of this file's own constitution, broken in the change that adds a rule about
|
||
not recalling things. Writing this entry is the fix.
|
||
|
||
**`z.coerce.date()` turned a plausible typo into 1970.** Reproduced: unquoted
|
||
`publishDate: 20260801` — valid YAML, the obvious slip for `2026-08-01` — coerces
|
||
from epoch milliseconds to **1970-01-01** with no error, and would ship as
|
||
`datePublished` in the article's JSON-LD and sort the piece last in a
|
||
reverse-chronological index. The reviewer also tested my justification for the
|
||
coercion and found it false: plain `z.date()` accepts unquoted YAML dates fine.
|
||
Replaced with a bounded union — `z.date()` or an ISO-shaped string — which
|
||
accepts both real forms and rejects numbers.
|
||
|
||
**The title bound made the SEO spec unsatisfiable.** `docs/04-seo-spec.md` puts
|
||
50–60 characters on the *rendered* `<title>`, whose pattern is
|
||
`"<headline> · Pouya Lajevardi"`. The suffix is 18 characters, so applying 50–60
|
||
to the headline guaranteed a rendered title of **68–78** — over the spec's own
|
||
ceiling on every article — while `.min(50)` hard-failed the build on a good short
|
||
headline (`"What Med-Arb Actually Is"`). The schema now enforces the *rendered*
|
||
length, adds an optional `seoTitle` override, and reports the computed title in
|
||
the failure message. `docs/01-architecture.md` updated to match.
|
||
|
||
**An article could ship an image with no alt text** — `image` and `imageAlt` were
|
||
independently optional, and the schema is the only gate that exists before the
|
||
`/insights/` route is written. Now coupled.
|
||
|
||
**Two comments asserted controls that did not exist**: "every piece must link to
|
||
a practice-area page" (`.min(1)` checks a *declaration*, not a link) and "an
|
||
article with `draft:false` and `reviewedByPouya:false` is a bug" (nothing
|
||
enforced it). The second is now true — a refinement fails the build. All four
|
||
invariants were then tested, each with a failing and a passing case; each fires.
|
||
|
||
**The schema diverged from two specs** — `docs/01` and `docs/02` both specify
|
||
`topics[]` plural and `readingTime`; the collection had singular `topic` and no
|
||
reading time. Pre-existing, carried through a wholesale rewrite without notice.
|
||
Fixed to the specs.
|
||
|
||
**`eslint.config.js` imported two undeclared packages**, one resolving by
|
||
hoisting accident: `globals` was 14.0.0 at the root against 16.5.0 nested under
|
||
the Astro plugin, so which major linting used depended on npm's hoisting.
|
||
|
||
### The second review pass found six more, four of them in the first round's fixes
|
||
|
||
The loop requires re-review after material fixes, and this is why.
|
||
|
||
**`frontmatterDate` accepted impossible dates** — the fix for the 1970 defect
|
||
introduced its own. The regex was unanchored at the end and nothing checked the
|
||
parsed result, so `2026-13-45` and `2026-08-01 nonsense` both **passed**, each
|
||
producing an `Invalid Date` bound for `datePublished` in the article's JSON-LD;
|
||
and `2026-02-30` silently rolled over to **2026-03-02** — a wrong date shipped
|
||
with no error, which is worse than a failed build. The comment claimed it
|
||
accepted "both real forms and nothing else." Now anchored, date-only, parsed as
|
||
UTC and **round-tripped** so the day that comes back must be the day written.
|
||
A time component is rejected rather than guessed at: quoted
|
||
`2026-08-01T10:00:00` parses as local time while the unquoted YAML form parses
|
||
as UTC, so the same frontmatter would mean different instants on a laptop and on
|
||
a CI runner.
|
||
|
||
**The title rule rejected all five planned launch articles.** The arithmetic was
|
||
right and the rule was still wrong. Every headline in `docs/03-content-spec.md`'s
|
||
launch slate is 50–67 characters; with the ` · Pouya Lajevardi` suffix they
|
||
render at 68–85 and **5 of 5 fail**, making `seoTitle` mandatory on every article
|
||
— a second title field to hand-keep in sync forever, on the highest-volume
|
||
content type. Two specs contradicted each other and the schema enforced one.
|
||
Resolved by making articles the documented exception: **an article's headline is
|
||
its `<title>`, with no suffix.** Under that rule 4 of 5 launch headlines pass and
|
||
`seoTitle` is a genuine exception. `docs/04-seo-spec.md` — which owns the title
|
||
rule and had not been swept — now states it.
|
||
|
||
**`PRACTICE_SLUGS` and `PRACTICE_AREAS` could drift silently.** Splitting one
|
||
derived source into two literals bought back the literal types but lost the
|
||
guarantee: deleting an area while leaving its slug declared passed `astro check`
|
||
cleanly, so an article could name a practice area with no page, no nav child and
|
||
no chip. My first completeness check was written as a conditional type and
|
||
**did not fire** — verified by deleting a row and getting 0 errors. Replaced with
|
||
an `AssertNever` constraint that does: a missing area now fails with
|
||
`ts(2344)`, and a typo'd slug fails with `ts(2820)` plus a "did you mean".
|
||
Zero runtime cost.
|
||
|
||
**Three narrower schema gaps**, each reproduced: `imageAlt: " "` passed, so an
|
||
image could ship with a whitespace-only accessible name — the exact thing the
|
||
coupling check exists to prevent; duplicate `topics` and `practiceAreas` passed
|
||
and would render duplicate pills; and `seoTitle` was bounded *and* re-checked,
|
||
so one mistake produced two errors. Trimmed, de-duplicated, and the field bound
|
||
dropped so the refinement is the single check.
|
||
|
||
**`scripts/deploy-local.sh` claimed to do "EXACTLY" what CI does.** It did not:
|
||
the workflow guards six values, the script guarded four, and the workflow runs
|
||
`npm run check` before building while the script ran neither check nor `npm ci`.
|
||
A local deploy — currently the **only** way this site ships, since Q22 and Q23
|
||
are open — was skipping the type and template gate. Guards brought to parity,
|
||
`npm run check` added, and the claim rewritten to name the two remaining
|
||
differences instead of denying them.
|
||
|
||
**Sweep misses:** `README.md` stated an engines floor of `>=22.12.0` after
|
||
`package.json` had moved to `>=22.13.0` for ESLint 10 — a duplicated version fact
|
||
of exactly the kind §7 exists to prevent, now replaced with a pointer.
|
||
`package-lock.json`'s root `engines` still carried the pre-bump value because the
|
||
bump was made without reinstalling. `engines.node` was described as "the
|
||
intersection of every dependency's floor" and was not one — `>=22.13.0` admits
|
||
Node 23.x, which ESLint 10 excludes; it now reads `^22.13.0 || >=24`.
|
||
`eslint.config.js` still called itself an "ESLint 9 flat config", and
|
||
`.gitea/workflows/deploy.yml` still carried the Gitea version as `[assumed]`
|
||
after Q23 verified it as 1.27.2.
|
||
|
||
**Three claims in `src/data/site.ts` bound for public pages**, all pre-existing
|
||
and all found by `claims-auditor`: `entity: 'SML Company Ltd. · Ontario, Canada'`
|
||
asserted a jurisdiction of incorporation §4 does not verify (**Q30**);
|
||
`provider: 'plausible'` was a guessed value where D15 records the choice as
|
||
undecided, in a file whose own header says not to guess one to make the build
|
||
pass (**Q31**); and `NO_RETAINER_NOTICE` was missing the third element
|
||
`docs/01-architecture.md` requires — that submitting the form does not itself
|
||
create a conflict check. The notice is fixed; the other two are nulled with
|
||
`TODO(pouya)` and numbered.
|
||
|
||
**One finding declined.** `claims-auditor` reported that no build order exists in
|
||
the repository and that R11's "ten more boundaries" was therefore untraceable. It
|
||
does exist — `docs/01-architecture.md` §Build order, eleven numbered steps. The
|
||
audit grepped lowercase `"build order"` and missed the capitalised heading. R11
|
||
now carries the file and section so the next reader does not repeat the search.
|
||
|
||
### Declined, with reasons
|
||
|
||
**Accessibility linting is ON, and I was wrong about it twice.**
|
||
`adversarial-reviewer` recommended `eslint-plugin-astro@3.1.0` for the
|
||
`eslint-plugin-jsx-a11y` peer. I tested that upgrade, hit ERESOLVE (v3 requires
|
||
`eslint >=10` *and* peers `jsx-a11y >=6.10.2`, whose latest peers `eslint ^3..^9`),
|
||
and concluded it was "not installable" — recording in §7 that
|
||
**"there is no automated accessibility linting"**.
|
||
|
||
Wrong the first time: **the already-installed `eslint-plugin-astro@1.7.0` ships
|
||
36 `jsx-a11y/*` rules and a `flat/jsx-a11y-recommended` config.** I had tested a
|
||
different upgrade path and generalised its failure to the capability. The stale
|
||
peer range is a *declaration*, not a runtime incompatibility.
|
||
|
||
Wrong the second time: having added a one-line `overrides` entry to fix that
|
||
declaration, I retested v3.1.0 — **and it installs cleanly.** The ERESOLVE was
|
||
never about v3; it was about the missing `overrides`. So the plugin is now on the
|
||
current major, not held two behind, and there is no stale pin left in the
|
||
manifest except `typescript`, whose hold is forced by peer ranges.
|
||
|
||
Verified by execution at each step — rules fired on a deliberately inaccessible
|
||
`.astro` file: `html-has-lang`, `alt-text`, `heading-has-content`,
|
||
`click-events-have-key-events`, `no-static-element-interactions`,
|
||
`no-noninteractive-tabindex`, `tabindex-no-positive`. Those are the checks
|
||
`docs/02-design-system.md` §Accessibility floor names.
|
||
|
||
One cost, recorded rather than discovered later: v3.1.0 declares
|
||
`node ^22.22.3 || ^24.16.0 || >=26.3.0`, which **excludes Node 25.6.0** — so
|
||
`npm install` prints EBADENGINE on Pouya's machine. Dev-time only; `nvm use`
|
||
clears it, and `.nvmrc` already says 22.
|
||
|
||
This mattered more than the ERESOLVE did. **§7 is the single source of
|
||
operational truth, and it carried a false capability claim on the eve of the
|
||
step that writes every landmark, heading and focus state**, under a CLAUDE.md
|
||
rule that accessibility is a build requirement rather than a polish pass.
|
||
|
||
**`@lhci/cli` advisories carried, not fixed.** All ten remaining findings trace
|
||
to it alone; 0.15.1 *is* `latest`, so there is no clean upgrade, and
|
||
`npm audit fix --force` installs `@lhci/cli@0.1.0` — a fourteen-minor downgrade,
|
||
which is not a fix. Dev-only, never on the CloudFront origin.
|
||
|
||
**Two `Consider` items taken rather than deferred**, both in files already being
|
||
edited: `image.service` was dead configuration (it set Astro's own default) under
|
||
a comment describing a convention it did not enforce — deleted, convention stays
|
||
in `CLAUDE.md`; and `sitemap({ lastmod: new Date() })` stamped every URL with the
|
||
build time, telling crawlers all 17 pages changed whenever one did, which spends
|
||
the signal `docs/04-seo-spec.md` wants. Removed, with step 7 named as the place
|
||
to reinstate it per-entry from `updatedDate`.
|
||
|
||
**Two `Consider` items declined:** the six extraneous `@img`/`@emnapi` packages
|
||
are cosmetic and `npm ci --dry-run` is clean; and no `.npmrc` with
|
||
`engine-strict` was added — the engines floor is a declaration, and making it
|
||
fatal is a decision with its own blast radius.
|
||
|
||
### `npm audit` — the actual result, and it is now zero
|
||
|
||
```
|
||
after the upgrade: 10 vulnerabilities (2 low, 1 moderate, 7 high)
|
||
npm audit --omit=dev: found 0 vulnerabilities
|
||
after removing @lhci/cli: found 0 vulnerabilities
|
||
```
|
||
|
||
**Every Astro advisory is cleared** — zero findings against `astro`,
|
||
`@astrojs/mdx`, `@astrojs/sitemap` or `sharp`. `adversarial-reviewer` confirmed
|
||
the rationale independently by installing `astro@^5.0.0` in a scratch directory
|
||
and reproducing **8 high-severity advisories, 6 of them distinct XSS**.
|
||
|
||
`npm ls` traced all 10 residual findings to `@lhci/cli` alone. It was first
|
||
carried as acceptable dev-only risk; on the second pass that was reconsidered
|
||
and it is now **removed**. The reasoning: 0.15.1 *is* `latest`, so there was no
|
||
clean upgrade; `npm audit fix --force` would install `@lhci/cli@0.1.0`, a
|
||
fourteen-minor downgrade; and the tool **cannot run at all today** — no pages,
|
||
no `lighthouserc`. Carrying seven high-severity advisories for an unusable tool
|
||
is not a trade worth making when deferring it to step 7 costs nothing. Recorded
|
||
in §7; R11 forces a fresh pin when it returns.
|
||
|
||
**A discrepancy with entry (s), stated rather than quietly dropped.** Entry (s)
|
||
recorded 16 vulnerabilities and attributed the high-severity set to Astro ≤
|
||
7.0.9. After the upgrade the high count was still 7, all from `@lhci/cli` — so
|
||
(s)'s attribution cannot have been right as written. (s) is append-only and
|
||
stands; this is the correction. What is verifiable now is the audit output
|
||
above.
|
||
|
||
### Verification
|
||
|
||
Run, not asserted: `npm run check` (0 errors, 0 warnings, **0 hints** — three
|
||
`ZodIssueCode is deprecated` hints appeared mid-change and were fixed to the
|
||
string literal), `npm run build`, `npm run lint` (ESLint clean, Prettier clean).
|
||
`npm audit` and `npm audit --omit=dev` as above. `npm install --dry-run` for both
|
||
candidate lint upgrades. Each of the four schema invariants exercised with a
|
||
failing and a passing article. A deliberately broken `.astro` file linted to
|
||
confirm the plugin is live under ESLint 10. `node -v` → v25.6.0.
|
||
|
||
Not run: Lighthouse (removed — see above), the no-JavaScript render check (no
|
||
pages), link resolution (no pages). Those belong to build step 1
|
||
(`docs/01-architecture.md` §Build order), which entry (s) began and which
|
||
continues after this.
|
||
|
||
**Still open:** Q23 (runner registration — needs the second administrator), R6.
|
||
**Standing reminders surfaced at the start of this build:** R1 (licensure — the
|
||
layout about to be written is what carries it), R9 (the SES alarms still notify
|
||
nobody until the `ses-alerts` subscription is confirmed), R6, R10.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (s) — Q22/Q23/Q27/Q28 answered; toolchain installed; build step 1 begun
|
||
|
||
**Who:** Pouya answered four questions mid-session and corrected one earlier
|
||
instruction. Claude Code implemented and started build step 1.
|
||
|
||
### Q22 — ANSWERED, and the answer is that it does not exist
|
||
|
||
`aws iam get-user --user-name adr-sml-deploy` returns **`NoSuchEntity`**. §7 now
|
||
carries **Deploy credential — NOT PROVISIONED**, and it is a **to-do** rather
|
||
than a question: the creation commands are with Pouya. Swept by command; no file
|
||
describes the credential as existing.
|
||
|
||
**Pouya has been authenticating as `user/pouya`** — the broadly-permissioned
|
||
personal user the Q10 inventory captured. Recorded in §7 as interim, and in §10
|
||
as a **High** risk: fine at an interactive keyboard, never as a CI credential.
|
||
`scripts/deploy-local.sh` **refuses to run** if `sts get-caller-identity` returns
|
||
that ARN, so the rule is enforced rather than merely written down.
|
||
|
||
### Q23 — correction: the version is verified, and the blocker is a person
|
||
|
||
**Old → new:** entry (r) recorded the Gitea version `[assumed]` on Pouya's
|
||
instruction. It is now **1.27.2** at git.larsnolden.com
|
||
`[verified 2026-08-26 — /api/v1/version]`, comfortably above the ~1.21 floor for
|
||
the `vars` context. The first-step guard is therefore belt-and-braces rather than
|
||
load-bearing — which is the right outcome: it was built so the answer would not
|
||
matter, and now it does not.
|
||
|
||
**What remains is not a fact to look up.** The instance is **jointly
|
||
administered**, so enabling Actions in `app.ini` and registering an `act_runner`
|
||
both depend on a second administrator. Q23 is rewritten to say so.
|
||
|
||
**So the local path is documented, not treated as a workaround.**
|
||
`npm run deploy` → `scripts/deploy-local.sh` (new) performs *exactly* what the
|
||
workflow performs: same guard, same three sync passes in the same order with the
|
||
same cache headers, same invalidation. At this scale the pipeline changes only
|
||
**how a deploy is triggered**, not what it does. `docs/06` leads with it, and
|
||
both files say the script and the workflow are one artefact in two places.
|
||
|
||
### §10 — a new risk that changes what the IAM policy is for
|
||
|
||
The deploy secret will live in a repository on **jointly-administered
|
||
infrastructure**, where an instance admin can generally reach repo secrets or
|
||
register a runner that receives them. That is inherent to how Actions runners are
|
||
fed credentials, not a flaw in this setup, and it does not change the plan.
|
||
|
||
It changes the **reading** of the scoped policy. Four actions, one bucket, one
|
||
distribution is no longer hygiene — it is the actual control standing between a
|
||
shared Gitea instance and an AWS account holding another business's
|
||
client-database backups. §10 now says so, and says that any request to widen it
|
||
is a security decision rather than a convenience one.
|
||
|
||
### Q27 and Q28 — answered, and both are facts with obligations attached
|
||
|
||
**Q27 — two business days.** In `src/data/site.ts` as `responseTime`, with a
|
||
derived `responseTimeShort` so the confirmation email cannot drift from the page.
|
||
Added to §4 Verified as a **public commitment** that must read identically on
|
||
`/contact/`, in the inquirer email, and in any bio. `docs/05` says to render it
|
||
from `SITE`, never retype it. Swept for the superseded "one business day": no
|
||
occurrences.
|
||
|
||
**Q28 — OBA Construction & Infrastructure, ADR, and Civil Litigation**
|
||
`[verified 2026-08-26 — Pouya]`, **for now**. §4's row and `site.ts` both carry
|
||
the "for now". Section membership renews yearly, so this is a fact with a shelf
|
||
life — **R10** added, with OCNI as the precedent: §4 already records it as
|
||
lapsed and not publishable, which is exactly this failure arriving a year late.
|
||
|
||
### Build step 1 — started
|
||
|
||
**Dependencies installed; `package-lock.json` exists.** That alone closes a
|
||
blocker documented since entry (m): `npm ci` had no lockfile, so the CI pipeline
|
||
could not get past its first step.
|
||
|
||
**`npm run check` and `npm run build` both pass** — 0 errors, 0 warnings. Worth
|
||
recording because entry (m) F6 asserted both would *fail* on an empty
|
||
`src/pages/`. `adversarial-reviewer` doubted that and was right: `astro build`
|
||
emits zero pages and exits 0. **Correcting it here rather than editing (m).**
|
||
|
||
**ESLint and Prettier are now wired**, closing the "not yet wired" rows:
|
||
|
||
- `eslint.config.js` — flat config, `js.configs.recommended` +
|
||
`typescript-eslint` + `eslint-plugin-astro`. **`typescript-eslint` added as a
|
||
devDependency** because `.astro` frontmatter *is* TypeScript and the plugin
|
||
cannot parse a component without it. Deliberately not type-aware: `astro check`
|
||
already type-checks, and running both would be slower and would disagree at the
|
||
edges.
|
||
- `.prettierrc.json` — 80 columns, single quotes, `prettier-plugin-astro`.
|
||
- **`.prettierignore` excludes `*.md` and `src/styles/tokens.css`, deliberately.**
|
||
Prettier's reformat of `AGENTS.md` alone is an **892-line diff** with no reading
|
||
benefit, and it collapses the aligned comment column in `tokens.css` that lets
|
||
the measured contrast ratios be scanned down the page — the one thing that file
|
||
exists for. Both exclusions carry their reason in the file.
|
||
|
||
`npm run lint` now passes clean. `npm run lighthouse` remains unwired, and now
|
||
honestly so: there are no pages to measure.
|
||
|
||
**`npm audit` reports 16 vulnerabilities (7 high) — not fixed, flagged.** The
|
||
high-severity set is Astro ≤ 7.0.9 (XSS via `define:vars`, spread attribute
|
||
names, `transition:*` values, slot names; SSRF in a prerendered error page) plus
|
||
transitive `esbuild`/`sharp`, and a `cookie` advisory reached only through
|
||
`@lhci/cli`. `npm audit fix --force` wants **Astro 7.2.7, a major upgrade**, and
|
||
`@lhci/cli@0.1.0`, a catastrophic downgrade. Most of the Astro advisories need
|
||
authoring patterns this project does not use (no server islands, no
|
||
`define:vars`, no view transitions, static output, no spread props yet). **This
|
||
needs a deliberate decision, not an automated fix** — see the escalation below.
|
||
|
||
### Escalated
|
||
|
||
**Astro major upgrade.** The project pins `astro@^5.0.0`; the advisories are
|
||
fixed in 7.x. Doing it now, before any pages exist, is far cheaper than doing it
|
||
after — there is nothing to migrate. Doing it later means shipping on a
|
||
known-vulnerable major. This is a D-series decision (D1 names Astro but not a
|
||
version), so it is yours.
|
||
|
||
### Verification
|
||
|
||
`npm run check` — 0 errors, 0 warnings, 5 files. `npm run build` — completes,
|
||
0 pages, as expected. `npm run lint` — ESLint clean, Prettier clean.
|
||
`bash -n scripts/deploy-local.sh` — syntax OK. `node --check astro.config.mjs` —
|
||
OK. `package.json` re-parsed as JSON after editing. Sweeps run as commands with
|
||
output read: `adr-sml-deploy` (no file claims it exists), `one business day` (no
|
||
occurrences), operational identifiers in `docs/*.md` (no matches).
|
||
|
||
**One error of mine, caught by my own sweep and recorded rather than quietly
|
||
fixed:** the three new §7 rows initially landed in the **§3 decisions table**,
|
||
overwriting D15's label, because the anchor I matched on (`| Analytics |`)
|
||
appears in both sections. The `adr-sml-deploy` sweep surfaced it. §3 is restored
|
||
— `git diff` over the decisions table is empty — and the rows are in §7. An
|
||
anchor that is not unique is not an anchor.
|
||
|
||
**Closed:** Q22, Q27, Q28, and the version half of Q23. **Still open:** Q23
|
||
(runner registration — needs the second administrator), R6 (parked). **Added:**
|
||
R9 (SES subscription pending), R10 (OBA renewal).
|
||
|
||
---
|
||
|
||
## 2026-08-26 (r) — Q19 closed; SES monitoring recorded; §7 made the single source of operational truth
|
||
|
||
**Who:** Pouya closed Q19, supplied the monitoring configuration, ruled on Q29,
|
||
and made the structural call below. Claude Code implemented.
|
||
|
||
### Q19 — CLOSED. Nothing now blocks `/contact/`
|
||
|
||
**Production access granted** in `ca-central-1`, confirmed by AWS in writing,
|
||
effective immediately. §7's SES account row and §9's Q19 both record it; §10's
|
||
**High** risk row for the sandbox is struck as resolved.
|
||
|
||
The per-region trap is kept in §7 as a **standing caution rather than an open
|
||
task** — it was avoided, and it applies again to any future SES request. Deleting
|
||
the row would delete the lesson.
|
||
|
||
### Monitoring exists, and one part of it does not work yet
|
||
|
||
Recorded in §7 (the facts) and `docs/05-backend-spec.md` (why it matters):
|
||
|
||
- **SNS topic `ses-alerts`**, `ca-central-1`. The email subscription to
|
||
`info@smlcompany.ca` is **PENDING CONFIRMATION**, stamped that way deliberately.
|
||
An unconfirmed SNS subscription **drops every message**, so as things stand the
|
||
alarms below fire into nothing. That is now a cutover checklist item in its own
|
||
right, checked with `sns list-subscriptions-by-topic` rather than assumed.
|
||
- **`SES-BounceRate-High`** (≥ `0.03`) and **`SES-ComplaintRate-High`**
|
||
(≥ `0.001`), both `ca-central-1`, `treat-missing-data: notBreaching`.
|
||
- **Bounce and complaint handling is SES email feedback forwarding** — the
|
||
default — **not an SNS feedback topic**, deliberately. Under 100 messages a
|
||
month there is nothing to consume a programmatic feed, and an unused topic is
|
||
one more thing to keep correct. Revisit when code needs to *act* on a bounce:
|
||
suppression lists, retry logic, marking a record undeliverable.
|
||
|
||
`docs/05` records why these are a real control rather than a formality: **SES
|
||
suspends sending above roughly a 5% bounce rate, and at this volume five bounces
|
||
crosses it.** An intake form is exactly where mistyped addresses arrive. The
|
||
alarms sit well below that line so there is room to react.
|
||
|
||
### The structural fix — §7 is the single source of truth for operational facts
|
||
|
||
**Old → new.** Specs in `docs/` carried their own copies of resource IDs,
|
||
regions, DNS records, and service state. They now **cite** §7 instead.
|
||
|
||
The reasoning is entry (q)'s DKIM inversion, generalised: the same operational
|
||
fact lived in §7 and in `docs/05`, a correction reached one of them, and the
|
||
stale copy ended up instructing an operator to delete the three records that
|
||
authenticate outbound mail — under the heading "Never delete". Same class as the
|
||
D3 amendment surviving three sweeps. **A duplicated fact is a fact that will
|
||
eventually be wrong in one place, and the copy that goes stale is the one nobody
|
||
re-reads.**
|
||
|
||
The rule is now in `CLAUDE.md` under Conventions, with that incident as its
|
||
stated reason so it is not softened later by someone who does not know the cost.
|
||
|
||
**Swept.** `docs/05` no longer restates the DKIM token sets, the DNS records, the
|
||
MX, the region, or the table name — it cites §7 and keeps only the two facts it
|
||
*depends* on, stated as dependencies. `docs/06` no longer restates the region,
|
||
bucket, distribution ID, intake endpoint, or account ID; its variable table now
|
||
points at §7 rows, and the IAM substitutions say explicitly that they are not
|
||
repeated and why. Verified by command:
|
||
|
||
```
|
||
grep -rn "ca-central-1\|adr-smlcompany-site\|E1OK7G98KNKUTA\|4tl0m5igkj\|327082975128\|adr-intake-submissions\|d26v23dhgsp2ta\|jkddzztszm" docs/*.md
|
||
```
|
||
|
||
Output read: **no matches.** Every operational identifier in `docs/` is now a
|
||
reference.
|
||
|
||
### Q29 — the guard is widened on both counts
|
||
|
||
Both gaps `adversarial-reviewer` found are closed:
|
||
|
||
- **`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are guarded.** By Q22 nobody
|
||
has confirmed the IAM user or its key exists, so an unset key is the single
|
||
likeliest first-run failure — excluding it defeated the guard's stated purpose.
|
||
**Only emptiness is tested and no value is ever echoed**, so nothing can leak
|
||
into a run log.
|
||
- **`INTAKE_ENDPOINT` promoted to job-level `env:` and guarded.** An empty one
|
||
does not fail a build; it **ships a live contact form posting to nothing**. A
|
||
silent production defect is worse than a failed build.
|
||
|
||
The build step still reads `${{ vars.INTAKE_ENDPOINT }}` rather than the `env`
|
||
context — depending on Gitea's expression-context support is the exact thing the
|
||
guard exists not to do. Coverage checked against every `vars.*` and `secrets.*`
|
||
the workflow consumes: all guarded except `BOOKING_URL`, which is empty by
|
||
decision (R6).
|
||
|
||
### Also done
|
||
|
||
**`docs/06`: the account ID is out of the backup-bucket callout**, pointing at
|
||
§10 instead — the same removal entry (m) made in `README.md`. Operational
|
||
usefulness does not outweigh pairing a live account number with a statement about
|
||
what the account holds, in a file that gets pasted around. The billing check now
|
||
derives the account from `sts get-caller-identity` rather than hard-coding it.
|
||
|
||
**`astro.config.mjs`: `prefetch` removed entirely** — recorded here as a decision,
|
||
not a silent deletion. *Any* prefetch setting ships Astro's prefetch script to
|
||
every page, against CLAUDE.md's "default to zero JS", for a marginal gain on a
|
||
small static site already served from CloudFront. **Revisit only against real
|
||
Lighthouse numbers**, once pages exist to measure. Entry (q) had set it to
|
||
`hover`; that was still JS on every page.
|
||
|
||
**Gitea version stays `[assumed]`** at Pouya's direction — he is running the
|
||
check. The guard is designed not to need the answer.
|
||
|
||
### Verification
|
||
|
||
The widened guard's `run:` block was **executed** under `sh -e` across four input
|
||
states — all set, all empty, secret empty, intake empty. Exit 0 only when
|
||
everything is present; exit 1 naming exactly the missing items otherwise; no
|
||
value printed in any case. `node --check astro.config.mjs` passes. The
|
||
duplication sweep was run as the `grep` above and its output read — no matches.
|
||
|
||
`npm run lint`, `check` and `build` still **cannot run**: `node_modules` is
|
||
absent and `npm ci` needs a lockfile. Installing dependencies is the first
|
||
concrete task of build step 1.
|
||
|
||
**Closed:** Q19 (and with it the last blocker on `/contact/`). **Still open:**
|
||
Q22 (deploy credential), Q23 (runner registration), Q27, Q28, R6 (parked).
|
||
**Q29 closed by implementation.**
|
||
|
||
---
|
||
|
||
## 2026-08-26 (q) — Guard built; and the audit found an inverted DKIM table that would have broken production mail
|
||
|
||
**Who:** Pouya ruled on entry (o)'s escalations, ratified R7, promoted R8, and
|
||
reversed his own F5. Claude Code implemented, ran `claims-auditor` and
|
||
`adversarial-reviewer` in parallel, and resolved 31 + 17 findings.
|
||
|
||
### The six rulings, implemented
|
||
|
||
1. **Q23 hardened, not checked.** `.gitea/workflows/deploy.yml` now runs a guard
|
||
as `steps[0]` — before checkout, before `npm ci`, before any AWS call — that
|
||
fails the run naming any of `AWS_REGION`, `S3_BUCKET`,
|
||
`CLOUDFRONT_DISTRIBUTION_ID` that is empty. It tests the env names
|
||
(`AWS_DEFAULT_REGION`) and reports the **Gitea variable** names, which is the
|
||
mapping most likely to be got wrong. `adversarial-reviewer` executed the
|
||
extracted block under `bash -e`, `sh -e`, and `bash -euo pipefail` across
|
||
all-unset / all-set / one-empty / whitespace-only and confirmed it fails
|
||
closed and is POSIX-clean.
|
||
2. **R7(3) dropped** — the `aws s3 ls` pre-flight is out; the guard supersedes it.
|
||
3. **R7(1) ratified** — the cache table stays matched to the pipeline.
|
||
4. **R7(2) reasoning corrected.** Old → new: "the intended cover is an S3
|
||
lifecycle rule" → **no lifecycle rule exists and is not the cover**; the cover
|
||
is that `aws s3 sync` only goes multipart above 8 MB. Corrected once more
|
||
after audit: the figure now measures **what is uploaded**, with
|
||
`src/assets/pouya-lajevardi.jpg` at 357,627 bytes `[verified — stat]` as the
|
||
largest source asset and an instruction to re-measure `./dist` after the first
|
||
build. The old wording measured the repository, which is not what syncs.
|
||
5. **R8 promoted to a rule** in `CLAUDE.md`, `/build` Phase 6, `/wrap` step 3.
|
||
6. **R1 put in the conditional** — there is no site; `src/pages/` is empty.
|
||
|
||
### The finding that mattered most was nowhere near the diff
|
||
|
||
**`docs/05-backend-spec.md` had the two SES DKIM sets exactly inverted.** It
|
||
labelled `3zsn…` / `jejgp…` / `xpiwy…` "**Live.** Matches SES exactly. Never
|
||
delete", and `f5pu…` / `jdue…` / `kznn…` "Orphans from an earlier verification.
|
||
Inert." §7 records the opposite, `[verified 2026-08-26 — DNS]`: the `f5pu` set is
|
||
the one that **resolves** and backs the healthy `ca-central-1` identity; the
|
||
`3zsn` set is NXDOMAIN and belongs to a stray `us-east-1` identity.
|
||
|
||
Entry (j) corrected this. **The correction reached §7 and never reached
|
||
`docs/05`.** An operator tidying DNS from that table deletes the three CNAMEs
|
||
that make SES DKIM work — and since SES has no custom MAIL FROM, DKIM is the
|
||
*only* thing satisfying DMARC. Outcome: intake mail from `ca-central-1` starts
|
||
failing authentication, silently, and the table said "Never delete" over the
|
||
wrong three records. Both rows rewritten from §7. The "six CNAMEs" claim below
|
||
them now says three, which is how many answer.
|
||
|
||
### Current Truth was stale in three more places
|
||
|
||
**§7 SPF and DMARC still read "ABSENT"** `[verified 2026-08-26]`, and §10 still
|
||
carried a live **High** risk row for it — while Q20, in the same file, records
|
||
both records added and independently verified the same day. Rule 2 requires
|
||
Current Truth updated in place; it was not. Both §7 rows now record the live
|
||
values; the §10 risk row is struck as **RESOLVED**, with the residual
|
||
`p=none` → `quarantine` tightening described as monitoring rather than risk.
|
||
`docs/05` opened with "**it is not in place**" and a table headed "Add both of
|
||
these" for records that already exist — rewritten, with the superseded state kept
|
||
visible as a caution.
|
||
|
||
**§9 carried Q24, Q25 and Q26 twice each, struck and open, with opposite
|
||
verdicts.** The live Q25 still asserted §4's boutique-naming permission stands —
|
||
against D16 — after entry (p) had struck it. This is the duplicate-question
|
||
defect entry (o) fixed for Q22, reintroduced three times in one session: entry
|
||
(p) added struck rows rather than striking the existing ones. The three
|
||
superseded open rows are deleted.
|
||
|
||
### The guide added in entry (p) was a live procedure for the architecture being replaced
|
||
|
||
`docs/reference/AWS-Hosting-Guide.md` was added **untracked** — so Q24's answer
|
||
("copied into `docs/reference/`") was true of one working tree and false of the
|
||
repository — with no reference-only marker, in fully executable imperative voice.
|
||
Following it creates an IAM user with `AdministratorAccess` in account
|
||
`327082975128`, rebuilds the site through the standalone-HTML pipeline D1 and D3
|
||
replace, and wires SES to `adr@` / `intake@smlcompany.ca` against D18's
|
||
`info@`. It is now `git add`ed and carries a **do-not-execute** banner tabulating
|
||
seven contradictions with Current Truth, each resolved in Current Truth's favour.
|
||
|
||
**It also called Pouya a lawyer** — "**Audience:** You (a lawyer, not a
|
||
sysadmin)" — and proposed public copy reading "does not create a lawyer-client
|
||
relationship", which is what `NO_RETAINER_NOTICE` in `src/data/site.ts` was
|
||
written to avoid. Both corrected.
|
||
|
||
**The R8 sweep command recorded in entry (o) could not have caught either.**
|
||
`grep -rn -i "licensed\|legal professional"` does not match "lawyer". The rule
|
||
promoted this session is right; the pattern it was demonstrated with was
|
||
incomplete. The corrected sweep is:
|
||
|
||
```
|
||
grep -rn -iE "\blawyer\b|licensed|legal professional|litigator" \
|
||
--include="*.md" --include="*.ts" --include="*.astro" .
|
||
```
|
||
|
||
Run, output read. Every surviving hit is rule text, Change Log history, or
|
||
"lawyer" referring to the *reader* or a third party (`docs/03:20`, `docs/03:161`,
|
||
`docs/01:291`, `docs/08:111`) — none describes Pouya.
|
||
|
||
### Copy defects one step from shipped pages
|
||
|
||
- **"a working litigator"** — `AGENTS.md` §1's positioning sentence and
|
||
`docs/03-content-spec.md:71`, the line designated for the hero, the About page
|
||
and the PDF bio. Not in §4; in Ontario it asserts conducting litigation, which
|
||
§4 Forbidden bars. §1's own line asserted "each element is verified"; it was
|
||
not. Both now read "close to live litigation". §1's correction is annotated in
|
||
place rather than silently rewritten.
|
||
- **"an active litigation practice"** — `docs/03:118`, `docs/01:211`,
|
||
`docs/01:246`. §4's Verified row was itself corrected from "practice" to
|
||
"**exposure**" in entry (l); the copy decks kept the barred word. Fixed to the
|
||
register's own substitute.
|
||
- **`LegalService` JSON-LD** — `docs/04-seo-spec.md` specified it for the home
|
||
page. schema.org defines `LegalService` as a business providing legal advice
|
||
and **representation**: it asserts in machine-readable form precisely what D13
|
||
bars. Changed to `ProfessionalService`, with the reason recorded so it is not
|
||
changed back. `worksFor` on the `Person` node had no register-safe value —
|
||
populating it either names the boutique (D16) or misstates the employer — so
|
||
it is dropped in favour of `jobTitle`.
|
||
- **Tribunal-secretary work** — `docs/01:173,180` and `docs/03:32` instructed
|
||
offering it; `docs/07-fees.md:116` records Pouya removing the rate and barring
|
||
the offer. Three files against one; the one was right.
|
||
- **§4's Forbidden table and `docs/03:52`** justified the superlative ban with
|
||
"LSO marketing rules" — the **fourth** surviving form of the licensure claim,
|
||
two rows below the `NOT ESTABLISHED` row. Restated on grounds that do not
|
||
depend on licence status. *(The §4 instance is the architect's to apply.)*
|
||
- **`docs/07-fees.md`** opened with "pending Pouya's sign-off — Q14" and
|
||
"**Nothing in this document publishes until Pouya confirms the figures**",
|
||
then contradicted itself 85 lines later with "This is the card. Build `/fees/`
|
||
from it." An implementer reading top-to-bottom blocks the page. Header
|
||
rewritten to D14 as confirmed.
|
||
- **Nine stale `Q` blockers** across five spec files — Q4, Q10, Q11, Q12, Q14,
|
||
Q20, Q5 — all answered or re-tracked, three of them gating `/fees/`, whose
|
||
figures are confirmed. Swept.
|
||
|
||
### Code defects
|
||
|
||
- **`src/data/site.ts`** published `responseTime: 'Inquiries are answered within
|
||
one business day.'` — a service level a reader can hold him to, not in §4. Set
|
||
`null` with a `TODO(pouya)`; **Q27**.
|
||
- **The memberships block** carried a blanket `[verified]` stamp over the OBA
|
||
section names, which §4 marks `[assumed]`. Downgraded, flagged; **Q28**.
|
||
- **`src/content/config.ts`** — `/** Drafts are excluded from the build, the
|
||
index, and the sitemap. */` describes three controls, **none implemented**;
|
||
the sitemap filter covers `/legal/` only. Same defect class as Q22. Restated
|
||
as intent with a deadline. Its `title`/`description` bounds also contradicted
|
||
`docs/04` (max 70 / min 70 against 50-60 / 140-160); tightened to the spec.
|
||
- **`astro.config.mjs`** set `prefetch: { prefetchAll: true, defaultStrategy:
|
||
'viewport' }`, injecting Astro's prefetch script and an IntersectionObserver
|
||
into **every** page and speculatively fetching every link that scrolls into
|
||
view — against CLAUDE.md's "default to zero JS" and §7's "most pages ship zero
|
||
JS", with no Change Log entry. Set to `prefetchAll: false` /
|
||
`defaultStrategy: 'hover'`, the setting the written conventions imply. If
|
||
prefetch-all is wanted, it needs a decision entry.
|
||
- **`src/styles/tokens.css`** attached the ink contrast figure (11.09:1) to both
|
||
ink and maroon; on maroon `--gold-l` measures 8.11:1. Both pass AA, so no
|
||
accessibility consequence — but the token file is what developers read.
|
||
|
||
### Also corrected
|
||
|
||
`docs/06` cited a billing **alarm** verified by `cloudwatch describe-alarms`; the
|
||
guide set up an AWS **Budget**, which that command never returns — and nothing
|
||
records either as created. Now names `budgets describe-budgets` and says confirm,
|
||
do not assume. The booking cutover check is marked conditional on R6, since
|
||
`BOOKING_URL` is empty by decision. `scripts/aws-discover.sh` and `docs/06`
|
||
claimed "every call is a list or describe" — four are not (`sts
|
||
get-caller-identity`, `apigatewayv2 get-apis`, `s3api get-bucket-versioning`,
|
||
`configure get`); all are genuinely read-only, so the substance held and the
|
||
wording did not. Four stale `AWS-Hosting-Guide.md` paths repointed at
|
||
`docs/reference/`. The reference workflow still said "Two passes" above three
|
||
sync steps — the one file entry (o)'s three-pass sweep missed, and a
|
||
twelve-character grep would have caught it. The IAM procedure had 22 lines of
|
||
multipart prose between "attach this policy" and "create an access key", which
|
||
also broke the ordered list in CommonMark; moved below step 3.
|
||
|
||
### Declined, with reasons
|
||
|
||
**The guard was not widened.** `adversarial-reviewer` showed it does not cover
|
||
the secrets — and by Q22, an unset key is the *most likely* first-run failure, so
|
||
the stated benefit ("one second instead of a full build") does not apply to the
|
||
likeliest case — nor `vars.INTAKE_ENDPOINT`, whose absence would **ship a live
|
||
form posting to an empty endpoint**. Both are real. Neither was done: the ruling
|
||
named three variables, and `INTAKE_ENDPOINT` additionally needs promoting to
|
||
job-level `env:`. **What was fixed instead is the guard's comment**, which
|
||
claimed it converted the problem "on every Gitea version" into a clean failure —
|
||
broader than what it implements. It now states its scope and points at **Q29**.
|
||
|
||
**The Gitea 1.21 claim is stamped `[assumed]`, not verified.** The version could
|
||
be confirmed with one command on the instance. It was not run here; the guard is
|
||
designed to work without knowing the answer, so the stamp is the honest record.
|
||
|
||
**`docs/06` still pairs the account ID with the backup-bucket callout** — the
|
||
pairing entry (m) removed from `README.md` as an inference vector. `docs/06` is
|
||
an operational document where the account ID is load-bearing, unlike the README.
|
||
Flagged rather than changed; if you want it out, it moves to `aws-inventory.txt`,
|
||
which is gitignored.
|
||
|
||
### Verification
|
||
|
||
The guard's `run:` block was extracted from parsed YAML and **executed** by
|
||
`adversarial-reviewer` under three shells and four input states — fail-closed
|
||
confirmed. Both workflow YAMLs parse. The barred-word sweep was run as the
|
||
`grep -iE` above and its output read. `stat` gave 357,627 bytes. `npm run lint`,
|
||
`check` and `build` were **not run**: `node_modules` is absent and `npm ci`
|
||
cannot install without a lockfile, so the `astro.config.mjs`, `site.ts` and
|
||
`config.ts` edits are **unverified by any type-checker** — they are small and
|
||
syntactically simple, and that is the whole of the assurance.
|
||
|
||
**Opened:** Q27, Q28, Q29. **Still open:** Q22, Q19 (blocking), Q23 (runner
|
||
half), Q5/R6 (parked). **Closed by entry (p), confirmed here:** Q24, Q25, Q26.
|
||
|
||
**Lesson.** Entry (o) said the sweep must be a command whose output is read.
|
||
This entry adds what that costs when the command is wrong: the pattern
|
||
`licensed|legal professional` cannot match "lawyer", so a compliant sweep still
|
||
missed a file added in the same session. **And the largest finding of the session
|
||
was in a file nothing had asked either reviewer to look at.** Both reviewers were
|
||
pointed at a diff; the destructive defect was three files away, left by a
|
||
correction that reached one file and stopped. Scope the audit to the *decision*
|
||
that changed, not the lines that changed.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (p) — Q24, Q25, Q26 answered; R7 ratified; R8 promoted to a rule
|
||
|
||
**Who:** Pouya relaying Claude Code's session close. Claude (architect) deciding.
|
||
|
||
**Q25 — struck. This was the dangerous one and it was mine.** §4's Verified table
|
||
carried "The boutique **may be named on the site**", authorising precisely what
|
||
**D16 — never named** forbids. Sequence: Pouya said it could be named, then
|
||
reversed; D16 recorded the reversal; **the permission was never removed.** A
|
||
locked decision and the table copy is written from disagreed, and the table was
|
||
the more permissive of the two. Struck.
|
||
|
||
The general defect: reversing a decision requires removing the permission it
|
||
granted, not only recording the new position. Same shape as the D3 amendment that
|
||
went unswept three times.
|
||
|
||
**Q26 — answered, and the third form of the same claim is gone.** §4's opening
|
||
asserted "Every public claim on this site is subject to Law Society of Ontario
|
||
marketing rules" — which asserts regulatory status by implication, since those
|
||
rules bind licensees. Claude Code was right that this is the same half-measure as
|
||
the "legal professional" reversal, one level further down.
|
||
|
||
Old → new: the register's rationale no longer rests on licensure at all. It rests
|
||
on the fabricated-credentials history, which is documented, sufficient, and true
|
||
regardless of licence status. The paragraph's own history is kept in place as a
|
||
caution — three progressively weaker forms of one unverified claim, each
|
||
surviving a sweep.
|
||
|
||
**§4 now carries a `Licence status — NOT ESTABLISHED` row.** Stamped
|
||
`[unestablished]`, a new stamp for this case. It records the honest position: this
|
||
file does not know, Pouya's instruction was about the *site* and not a statement
|
||
of fact either way, and nothing may assert, deny, or infer it. Claude Code was
|
||
also right that the two licensure rows were stamped `[verified]` as *directives*,
|
||
which is a different thing from a verified fact — this row separates the two.
|
||
|
||
**Q24 — closed.** `AWS-Hosting-Guide.md` copied to `docs/reference/`. It is the
|
||
only record of how the hand-built infrastructure was created, so it belongs in
|
||
the repo rather than in a Drive folder the repo cites but cannot reach. Scanned
|
||
before copying: no access keys, no secrets, no account ID.
|
||
|
||
**Q23 — answered by hardening rather than by version-checking.** The risk is real
|
||
— on Gitea below ~1.21, `${{ vars.* }}` interpolates empty and the sync target
|
||
becomes `s3://`. But a version check is a fact that goes stale. Ruling: add a
|
||
**pre-flight guard step** to `.gitea/workflows/deploy.yml` that fails the run if
|
||
any required variable is empty, before any AWS call. That converts a silent
|
||
destructive misfire into a clean failure on every Gitea version, and it
|
||
subsumes R7's third item.
|
||
|
||
**R7 ratified, with one amendment.**
|
||
1. Cache-policy table amended to match the pipeline — **accepted.** Documenting
|
||
what the pipeline does beats documenting an intention. `robots.txt` and
|
||
`sitemap*.xml` at `max-age=0, must-revalidate` is immaterial at this size.
|
||
2. `s3:AbortMultipartUpload` omitted, lifecycle rule as intended cover —
|
||
**accepted, with the reasoning corrected.** The lifecycle rule does not exist,
|
||
so it is not the cover. The actual cover is that `aws s3 sync` only goes
|
||
multipart above 8 MB and the largest asset in the repo is a 357 KB portrait.
|
||
Record *that*, and add a note to revisit if any asset ever approaches 8 MB.
|
||
Do not document a control that does not exist — that is Q22's defect again.
|
||
3. `aws s3 ls` pre-flight documented but not implemented — **superseded** by the
|
||
guard step above, which covers more.
|
||
|
||
**R8 promoted from a reminder to a rule.** *"A sweep is a command whose output is
|
||
read, not a claim."* Entries (l), (m) and (n) each asserted a completed sweep;
|
||
instances survived all three — one of them inside `claims-auditor.md`, the
|
||
definition of the agent whose job is to catch it. A reminder is the wrong
|
||
instrument for something this reliable. It belongs in `/build` Phase 6 and
|
||
`/wrap`: any claim that a change was applied across files must cite the command
|
||
and be made only after reading its output.
|
||
|
||
**R1's wording corrected:** it says "the site currently asserts the JD only".
|
||
There is no site — `src/pages/` is empty. Restated in the conditional.
|
||
|
||
**Closed:** Q23, Q24, Q25, Q26. **Still blocking the build:** nothing.
|
||
**Still blocking `/contact/`:** Q19. **Still open:** Q22, Q5 (parked).
|
||
|
||
---
|
||
|
||
## 2026-08-26 (o) — Five rulings implemented; both reviewers run; the D3 sweep finally completed
|
||
|
||
**Who:** Pouya ruled on entry (m)'s escalations and reversed his own F5 ruling.
|
||
Claude Code implemented, ran `claims-auditor` **and** `adversarial-reviewer` in
|
||
parallel on the diff, and resolved 27 + 17 findings.
|
||
|
||
### The five rulings, implemented
|
||
|
||
1. **§4 and §10 "licensed"** — the architect's, done in entry (n).
|
||
2. **Reversal on "a legal professional's"** — cut from `README.md:35` and
|
||
`CLAUDE.md` (twice). The sentences now assert nothing about professional
|
||
status and state the reason instead.
|
||
3. **`s3:PutObjectAcl` dropped** from the policy in `docs/06-deployment.md`. §10's
|
||
"four actions" is now accurate without §10 being touched, and every file that
|
||
states a count agrees with the policy: `README.md:77`, `docs/06:82`,
|
||
`.gitea/workflows/deploy.yml:13`, `AGENTS.md` §10.
|
||
4. **`.github/workflows/deploy.yml` → `docs/reference/github-actions-oidc.yml.example`**
|
||
via `git mv`; `.github/` removed entirely. It held exactly one file — no
|
||
CODEOWNERS, no dependabot, no templates — and nothing referenced the path.
|
||
References updated in `README.md`, `CLAUDE.md`, `docs/06`, `.gitea`, and §7.
|
||
5. **Q22** left open with the designed-not-confirmed phrasing intact.
|
||
|
||
### The reviewers found that the D3 sweep had never actually reached `docs/06`
|
||
|
||
Both agreed on the same blocking defect, independently. `docs/06-deployment.md`
|
||
is the file `README.md` points to as "Full procedure, IAM policy, runner setup" —
|
||
and it still opened with:
|
||
|
||
- `Authority: AGENTS.md §3 D3 (git + GitHub Actions → ...)` — D3 as amended says
|
||
Gitea;
|
||
- a topology diagram, **the first content block in the file**, reading
|
||
`GitHub push to main → GitHub Actions → assume AWS role via OIDC (no stored
|
||
keys)`. That is the exact sentence entry (l) called "the serious one", six
|
||
lines above the section that contradicts it;
|
||
- an entire 19-line section, `## Why OIDC and not access keys`, in the
|
||
imperative — *"IAM → Identity providers → add OIDC provider..."* — with **no
|
||
marker that it does not apply**, naming a third principal `adr-site-deploy`
|
||
that conflicts with `adr-sml-deploy`, and citing Q9, which is answered.
|
||
|
||
`adversarial-reviewer` named the concrete failure: an operator working the
|
||
document top-to-bottom adds a GitHub federation trust to account `327082975128`
|
||
— the account §10 rates High blast-radius — that nothing will ever use, and
|
||
plausibly never creates `adr-sml-deploy` at all. Two mutually exclusive
|
||
credential procedures in one file with no signpost saying which is live.
|
||
|
||
**Fixed.** Authority line cites D3 as amended. Topology rewritten to Gitea +
|
||
static key. The OIDC section is retitled *Why OIDC would have been better — and
|
||
why it is unavailable*, opens with a **do not execute** block, and its executable
|
||
steps are gone.
|
||
|
||
### The "licensed" sweep had missed three more files, including the auditor's own persona
|
||
|
||
Entry (n) scoped the reversal to "`README.md`, `CLAUDE.md` (twice), and this
|
||
file." Five instances survived in three files it did not name:
|
||
|
||
- **`.claude/agents/claims-auditor.md:8`** — *"You audit public copy for a
|
||
**licensed legal professional's** marketing site."* This is the system prompt
|
||
of the agent that enforces the rule. Its opening sentence asserted the exact
|
||
claim its own §"Licensure (D13)" instructs it to flag.
|
||
- **`.claude/agents/adversarial-reviewer.md:9`** — *"a legal professional's
|
||
dispute resolution practice"*, the precise phrase entry (n) reverses.
|
||
- **`docs/08-execution-protocol.md:63`** — *"a licensed professional's public
|
||
marketing page"*. **This file was edited in that same diff**, eleven lines
|
||
above the surviving phrase.
|
||
|
||
Entry (n) said "twice in two entries that a claim about a sweep was made without
|
||
the sweep being verified. The pattern, not the instances, is the finding." This
|
||
was the third. It is now swept by command, not by recall:
|
||
|
||
```
|
||
grep -rn -i "licensed\|legal professional" --include="*.md" --include="*.yml" .
|
||
```
|
||
|
||
Every surviving hit is either rule text stating the prohibition, or Change Log
|
||
history, which rule 3 forbids editing.
|
||
|
||
### Also fixed
|
||
|
||
**Register and rule accuracy.** §4's internal-docs rule cited a `[assumed]` stamp
|
||
on licensure that **does not exist** — the two licensure rows are stamped
|
||
`[verified]` as *directives not to publish*, which is a different thing. A reader
|
||
following the pointer found nothing there, in the sentence stating the rule.
|
||
Reworded here and in `CLAUDE.md:30` to say what is true: the Verified table does
|
||
not establish licence status either way. **Duplicate `Q22`** — two rows, two
|
||
scopes, one number, one of them mine; merged into the fuller statement, keeping
|
||
the architect's `aws iam get-user` check command. **§7 Node** re-stamped
|
||
`[verified]` — it was `[assumed]` for something directly checkable.
|
||
|
||
**Claims that overstated what exists.** `CLAUDE.md:164` still said *"deploys use
|
||
OIDC role assumption"* — verbatim the F1 claim, in a file both prior sweeps
|
||
touched; it and `CLAUDE.md:119` are corrected. `.gitea/workflows/deploy.yml` and
|
||
`CLAUDE.md` asserted the deploy credential in the present tense; both now cite
|
||
Q22. `docs/06` asserted five blocking PR checks — the workflow has **no
|
||
`pull_request` trigger** and neither `lint` nor `lighthouse` is wired, so nothing
|
||
gates a merge today; retitled *planned, not implemented*. `README.md:5` and
|
||
`docs/06`'s "every push deploys" both hedged against the never-run-green fact the
|
||
same files establish elsewhere.
|
||
|
||
**The moved reference file carried the defect the move was meant to contain.**
|
||
Its line 16 still read *"OIDC role assumption — no long-lived AWS credentials in
|
||
this repository"*: present tense, about this repository, and false. It also
|
||
carried a live `TODO(pouya): AGENTS.md Q9, Q10` against two struck rows — which
|
||
`CLAUDE.md`'s definition of done treats as a build failure — for a variable that
|
||
will never be set. Both fixed, along with its claim that the file is usable on
|
||
GitLab (different CI schema entirely; it is the design there, not the
|
||
implementation).
|
||
|
||
**Two security-posture claims narrowed.** `docs/06` said the workflow's
|
||
`aws sts get-caller-identity` pre-flight means "a credential problem fails loudly
|
||
and early rather than halfway through a sync." `sts:GetCallerIdentity` requires
|
||
**no IAM permission** — it succeeds for any valid key regardless of policy. It
|
||
catches a missing or revoked key and misses an under-scoped one, which is exactly
|
||
the risk this diff introduced by removing an action. Narrowed, with the one-line
|
||
`aws s3 ls` check that would make it load-bearing. And `README.md` had re-dropped
|
||
§10's *"which by its name is"* hedge on `mlp-clientdb-prod-backups` — the defect
|
||
entry (m) F2 records as fixed — as had `docs/06:113`, escalated in entry (n) and
|
||
not done. Both restored.
|
||
|
||
**Documentation matching implementation.** The cache section described **two**
|
||
sync passes; the workflow runs **three**, and two ordering dependencies are
|
||
load-bearing and undocumented — pass 3 re-walks the tree and preserves pass 2's
|
||
image headers only because `sync` skips just-uploaded objects, and pass 3's
|
||
excludes also exclude those prefixes from `--delete`, which is why hashed assets
|
||
from old deploys are kept deliberately. Both now stated. The `robots.txt` /
|
||
`sitemap*.xml` row claimed `max-age=3600`; nothing implements it, so the table
|
||
now records what the pipeline does. Two conflicting variable tables for the same
|
||
five names — one under **Secrets** with real values, one under **Variables** with
|
||
placeholders — merged; entering those as secrets would have masked them in the
|
||
logs the doc says to read them from. Duplicate booking references (Q5 vs R6)
|
||
resolved to R6. `README.md` said "no `src/pages/`"; the directory exists and is
|
||
empty, which is not what a reader would find.
|
||
|
||
**`docs/08-execution-protocol.md`** — entry (n)'s inserted block landed between
|
||
*"`claims-auditor` reads the copy..."* and its follow-up *"It is a separate agent
|
||
on purpose"*, leaving "It" twelve lines from its antecedent and reading as though
|
||
the frontmatter were a separate agent. Block moved below.
|
||
|
||
### Declined, with reasons
|
||
|
||
**`s3:AbortMultipartUpload` not added.** `adversarial-reviewer` traced every AWS
|
||
call in the workflow against the policy and found the happy path fully covered,
|
||
with this one gap: objects over 8 MB upload multipart, and an interrupted
|
||
transfer cannot clean up its own parts. It recommended against adding the
|
||
permission — an S3 lifecycle rule aborting incomplete uploads after 7 days solves
|
||
it without widening the policy. Agreed, and left as an operational item below
|
||
rather than a policy change.
|
||
|
||
**The `aws s3 ls` pre-flight is documented, not implemented.** Adding it changes
|
||
the deploy pipeline, which was not in scope. It is written into `docs/06` as the
|
||
fix for the narrowed claim.
|
||
|
||
**§4's tables untouched.** Two findings land in the register itself; both are
|
||
escalated below rather than edited. §4 is the architect's instrument.
|
||
|
||
### Escalated — for the architect
|
||
|
||
*(All four now tracked: 1 and 2 as **Q25** and **Q26** in §9; 3 folded into Q26;
|
||
4 left in R1, which already owns the licensure framing.)*
|
||
|
||
1. **§4's Verified table authorises what D16 forbids.** `AGENTS.md:153` reads
|
||
*"The Toronto litigation and ADR boutique **may be named on the site** ... the
|
||
name itself is pending, see Q7."* D16 says **"Never named."** Q7 is answered:
|
||
*"Pouya reverted to generic. The boutique is never named."* A **Verified** row
|
||
currently permits publication of the exact thing a locked decision bars — the
|
||
same shape as the F5 defect in entry (l), in the same table, and the register
|
||
is where copy is written from.
|
||
2. **§4's opening asserts regulatory status by implication.** *"Every public
|
||
claim on this site is subject to Law Society of Ontario marketing rules"* —
|
||
LSO marketing rules bind licensees. Stating flatly that they govern this site
|
||
asserts the licensure §4 declines to verify, in the sentence that replaced
|
||
"Pouya is a licensed legal professional". A half-measure of the same shape as
|
||
the "legal professional" reversal.
|
||
3. **Consider an explicit §4 row** — *"LSO licence status | UNKNOWN / not
|
||
verified either way"* — which would make the internal-docs rule's pointer land
|
||
somewhere real instead of being reworded around.
|
||
4. **R1 (§12)** describes "the site currently asserts the JD only". There is no
|
||
site; `src/pages/` is empty. R1 is the reminder that must not ossify, so it
|
||
should be accurate about what it describes.
|
||
5. **Operational:** add an S3 lifecycle rule aborting incomplete multipart
|
||
uploads after 7 days; and once Q22 is answered, run
|
||
`aws s3 sync ./dist s3://adr-smlcompany-site --dryrun` under the new key to
|
||
confirm no `s3:GetBucketLocation` call is needed.
|
||
6. **Minor:** `.yml.example` is not a Prettier-recognised extension, so the moved
|
||
reference file has silently dropped out of `prettier --check` coverage. Frozen
|
||
reference material, so near-harmless — but nothing validates it as YAML now.
|
||
|
||
**Q23–Q26 opened, none blocking the build.** Q23: is an `act_runner` registered, and is the Gitea
|
||
instance ≥ 1.21 for the `vars` context the workflow depends on throughout? On an
|
||
older instance `vars.S3_BUCKET` interpolates empty and the sync target becomes
|
||
`s3://`. Q24: `AWS-Hosting-Guide.md` is cited by `docs/06` as the source for
|
||
existing infrastructure and two cutover checks, and **does not exist in the
|
||
repository**; the two dependent checks have been restated self-containedly, so
|
||
nothing is blocked. Q25 and Q26 carry the two §4 escalations above.
|
||
|
||
**R7 and R8 added to §12.** R7 records three spec decisions made on Pouya's
|
||
behalf and not ratified — the amended cache-policy row, the omitted
|
||
`s3:AbortMultipartUpload`, and the documented-not-implemented `aws s3 ls`
|
||
pre-flight. R8 records the sweep-verification lesson as a standing discipline
|
||
rather than a lesson buried in one entry, which is what let it recur three times.
|
||
|
||
### Verification
|
||
|
||
`npm run lint`, `npm run check`, and `npm run build` were **not run** —
|
||
`node_modules` is absent, and `npm ci` cannot install without a lockfile. No
|
||
JavaScript, TypeScript, or Astro source was touched. The `.gitea` workflow and
|
||
the `.yml.example` were not YAML-parsed either: PyYAML is unavailable in this
|
||
environment and the project has no YAML tooling installed. Both were changed only
|
||
in comment blocks, and the indentation of every edited comment was checked by
|
||
eye. The "licensed" sweep was executed as the grep above and its output read, not
|
||
asserted. Every prose line added to `README.md` and `CLAUDE.md` is ≤ 80 columns.
|
||
|
||
**Lesson.** Entry (m) recorded that a correction is a change and gets reviewed
|
||
like one. This pass adds the sharper version: **the sweep must be a command, and
|
||
its output must be read.** Three consecutive entries claimed a completed sweep;
|
||
three times instances survived, and the third time one of them was inside the
|
||
definition of the agent whose job is to catch it. The `/wrap` step should require
|
||
pasting the grep, not describing it.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (n) — Register corrected; a declined finding reversed on appeal
|
||
|
||
**Who:** Pouya relaying Claude Code's re-audit. Claude (architect) recording.
|
||
|
||
**Correction to entry (l), which was false.** It claimed all three files had been
|
||
rewritten off "licensed". Two were. **This file still said it twice** — §4's own
|
||
opening sentence ("Pouya is a licensed legal professional") and §10's risk row
|
||
("professional-conduct exposure for a licensed practitioner"). The register was
|
||
asserting as established fact the very thing it marks `[assumed]`, in the section
|
||
that exists to prevent exactly that. Entry (l) stands as written per rule 3; this
|
||
is the correction. Both lines are now fixed.
|
||
|
||
That is twice in two entries that a claim about a sweep was made without the
|
||
sweep being verified. The pattern, not the instances, is the finding.
|
||
|
||
**Reversal — "a legal professional's" is out too.** `claims-auditor` wanted the
|
||
phrase cut entirely, arguing that in Ontario it reads as an LSO licensee and so
|
||
trips the same bar as "licensed". Claude Code declined it, correctly, because it
|
||
was my explicit ruling — and recorded the reasoning rather than silently keeping
|
||
it, which is what let it be revisited.
|
||
|
||
**On appeal the auditor is right and I was wrong.** "Legal professional" was a
|
||
half-measure: a weaker assertion of the same unverified thing. The fix is to
|
||
assert nothing about professional status and state the reason instead — the
|
||
previous site carried fabricated credentials, which is true regardless of licence
|
||
status and carries the full force of the rule. Applies to `README.md`,
|
||
`CLAUDE.md` (twice), and this file.
|
||
|
||
Worth preserving: **the mechanism worked because the decline was reasoned rather
|
||
than silent.** A finding dropped quietly is a finding that cannot be appealed.
|
||
|
||
**Q22 opened — does `adr-sml-deploy` exist?** Claude Code caught that its own
|
||
README rewrite asserted a control nothing records as in place. `docs/06` is
|
||
written imperatively — "Create the user", "Create an access key" — a procedure,
|
||
not a record. Nothing in this file says it was performed. Its framing of this as
|
||
"same failure class, inverted" is exactly right: the original README understated
|
||
the credential risk, its replacement overstated the control. **The §10 rotation
|
||
obligation has no subject until Q22 is answered.**
|
||
|
||
**Accepted — §10's "four actions" resolves by tightening the policy, not editing
|
||
the count.** `docs/06` grants five: `ListBucket`, `PutObject`, `PutObjectAcl`,
|
||
`DeleteObject`, `CreateInvalidation`. `s3:PutObjectAcl` is unused by `aws s3 sync`
|
||
without `--acl`, and inert under OAC with ACLs disabled. Dropping it makes §10
|
||
accurate **and** the policy tighter — strictly better than correcting the number.
|
||
Handed to Claude Code.
|
||
|
||
**Accepted — move the GitHub workflow out of `.github/`.** Gitea falls back to
|
||
`.github/workflows` when `.gitea/workflows` is absent, so a live `push:` trigger
|
||
sitting there is conditionally inert, not inert. Moving it to
|
||
`docs/reference/github-actions-oidc.yml.example` makes it structurally inert.
|
||
Handed to Claude Code.
|
||
|
||
**Also noted, no action:** Claude Code reported that `npm run lint` and
|
||
`npm run check` could not run because `node_modules` is absent, and said so
|
||
rather than reporting a check it did not run. That is the Phase 5 rule working.
|
||
|
||
**Opened:** Q22. **Still blocking the build:** nothing. **Still blocking
|
||
`/contact/`:** Q19.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (m) — README corrected against D3-as-amended; second audit found nine more
|
||
|
||
**Who:** Pouya ruled on the five findings in entry (l); Claude Code implemented,
|
||
re-ran `claims-auditor` on the result, and resolved the second pass.
|
||
|
||
### The architect's rulings, applied
|
||
|
||
**F5 — no change, as ruled.** D13 bars "practice" for the boutique role, not for
|
||
Pouya's own ADR practice. `README.md:3` and `package.json:5` stand.
|
||
|
||
**F2 — scope corrected, wording changed.** The Forbidden table governs published
|
||
pages, not repository documentation; that scope is now stated in §4. The real
|
||
defect was different and the architect identified it: licensure is `[assumed]`
|
||
in §4, so no file should call Pouya "licensed". `README.md:36` now reads *a legal
|
||
professional's public marketing surface* — the clause that follows, about the
|
||
previous site's fabricated credentials, already carries the reasoning.
|
||
|
||
**F1, F3, F4 — Deployment section rewritten** against §7 and
|
||
`docs/06-deployment.md`. The section had been written against D3 as originally
|
||
decided and never swept when D3 was amended to Gitea. It now names
|
||
`.gitea/workflows/deploy.yml` as the live pipeline, says Gitea Actions rather
|
||
than GitHub Actions, states plainly that a long-lived AWS credential is involved
|
||
and why (Gitea is not an AWS OIDC provider), and names the quarterly rotation
|
||
obligation. `format` and `lighthouse` added to the scripts table.
|
||
|
||
### The second audit found nine findings in the replacement text
|
||
|
||
Seven fixed, one declined, one escalated. The re-audit was worth running: the
|
||
rewrite traded the original defect for its mirror image.
|
||
|
||
**F1 (fixed) — the replacement asserted a control state that may not exist.**
|
||
The new text said deploys *do* authenticate as IAM user `adr-sml-deploy` with a
|
||
key in Gitea secrets. Only the **decision** is verified (D3 as amended, §7).
|
||
`docs/06-deployment.md` is written entirely in the imperative — "Create the
|
||
user", "Create an access key" — i.e. a procedure to perform, not a record of one
|
||
performed, and `adr-sml-deploy` appears nowhere else in this file. The old README
|
||
understated the credential risk; the new one asserted a control that nothing
|
||
records as in place. Same failure class, inverted. Now phrased as designed-not-
|
||
confirmed, and **raised as Q22**.
|
||
|
||
**F2 (fixed) — a hedge was dropped, and an account number was disclosed.** §10
|
||
says `mlp-clientdb-prod-backups` is "**which by its name is** a law firm's
|
||
production client-database backups" — only the bucket name was ever observed.
|
||
The README stated the contents flatly. It also paired the live account ID with
|
||
that statement in the repo's most pasteable file, and D16 says the boutique is
|
||
never named — "this account holds a law firm's client database" is an inference
|
||
vector that costs nothing to remove. The account number and the bucket name are
|
||
both now out of the README; the security reasoning stays, pointing at §10.
|
||
|
||
**F3 (fixed).** The bullet led with "**The key is rotated quarterly**" — a
|
||
description of an operating control — and conceded four lines later that the
|
||
task has no owner. Now an obligation: *must be rotated quarterly, and nobody
|
||
owns that yet.*
|
||
|
||
**F4 (fixed).** "It does not run. Editing it changes nothing." is conditional,
|
||
not absolute: Gitea falls back to `.github/workflows` when `.gitea/workflows` is
|
||
absent, and that file carries a live `push` trigger on `main`. Realised risk is
|
||
low — it would fail on `role-to-assume` with no OIDC provider rather than
|
||
mis-deploy — but describing a trigger-eligible file as inert invites someone to
|
||
treat it as safe. Now states the condition. **The auditor's better fix was
|
||
declined for scope:** moving it to `docs/reference/github-oidc-deploy.yml.example`
|
||
would make the claim structurally true instead of dependent on Gitea's
|
||
resolution order. Worth doing; not done here.
|
||
|
||
**F6 (fixed) — "Pushes to `main` build and deploy automatically" was not true.**
|
||
Verified against the filesystem, not inferred: there is no `package-lock.json`,
|
||
so `.gitea/workflows/deploy.yml:46`'s `npm ci` exits non-zero at step one; there
|
||
is no `src/pages/`, so both `npm run check` and `npm run build` fail; and whether
|
||
an `act_runner` is registered is recorded nowhere, which `docs/06:125-130` warns
|
||
"looks exactly like a broken pipeline." The README now says the pipeline has
|
||
never run green and names all three reasons.
|
||
|
||
**F7 (fixed).** "No wildcard actions, no wildcard resources" is literally false —
|
||
the policy uses `arn:aws:s3:::BUCKET_NAME/*`, correctly scoped but a wildcard. In
|
||
a paragraph whose entire job is to stop a future maintainer widening the policy,
|
||
an overstatement is the wrong error: the first reader who notices it discounts
|
||
the rest. Now: no `Action: "*"`, no `Resource: "*"`, nothing outside one bucket
|
||
and one distribution.
|
||
|
||
**F8 (fixed).** `npm run lint` and `npm run lighthouse` are listed in a table a
|
||
reader takes as "these work." Neither does — there is no ESLint flat config, and
|
||
ESLint 9 (pinned `^9.0.0`) errors without one, so the command dies before
|
||
Prettier runs; and there is no `lighthouserc`, so `lhci autorun` has no budgets
|
||
even though `docs/06:221-222` makes Lighthouse a blocking check. Both rows are
|
||
now marked **not yet wired**.
|
||
|
||
**F9 (fixed).** Two precision defects. The README restated the project's single
|
||
most important rule more narrowly than §4 states it — "no factual claim **about
|
||
the practice**" versus §4's "him, his credentials, his experience, or his
|
||
practice" — in the file a new contributor reads first. And it said `/build` runs
|
||
two review agents unconditionally, where `.claude/commands/build.md:52-53` allows
|
||
skipping `claims-auditor` when no copy changed. Both now mirror their sources.
|
||
|
||
**F5 (declined) — the auditor asked to cut "a legal professional's" entirely,**
|
||
on the grounds that in Ontario the phrase reads as an LSO licensee and so trips
|
||
§4's bar on "phrasing that *implies* entitlement to practise law." Declined
|
||
because the architect ruled this exact wording four hours earlier, having already
|
||
considered and rejected "licensed", and because the same phrase now appears
|
||
deliberately in `CLAUDE.md`. Recorded rather than silently kept: the auditor's
|
||
reasoning is not obviously wrong, and if the phrase is judged to imply licensure
|
||
it needs changing in three files at once, not one.
|
||
|
||
### Escalated to the architect — not fixed here
|
||
|
||
1. **Entry (l) records `CLAUDE.md`, `AGENTS.md`, and `README.md` as all rewritten
|
||
off "licensed". Two of the three were.** `CLAUDE.md` is clean. **This file
|
||
still says it twice** — line 110, "Pouya is a licensed legal professional",
|
||
in the opening sentence of §4 itself, and line 303, "professional-conduct
|
||
exposure for a licensed practitioner", in §10. The register is asserting as
|
||
fact the thing it marks `[assumed]`, in the section whose whole purpose is to
|
||
stop that. Not edited here because §4 is the architect's instrument.
|
||
|
||
2. **§10 says the deploy policy "grants four actions on one bucket and one
|
||
distribution."** It grants five: `s3:ListBucket`, `s3:PutObject`,
|
||
`s3:PutObjectAcl`, `s3:DeleteObject`, `cloudfront:CreateInvalidation`. The
|
||
discrepancy resolves in a useful direction — `s3:PutObjectAcl` is unused by
|
||
`aws s3 sync` and inert under OAC with ACLs disabled (`docs/06:205-206`), so
|
||
the policy is one action wider than it needs to be. Dropping it makes the
|
||
§10 count correct and the policy tighter at the same time. `docs/06:108` also
|
||
drops §10's "by its name" hedge on the backup bucket.
|
||
|
||
3. **The `.github` workflow's location** — see F4 above.
|
||
|
||
### Verification
|
||
|
||
`npm run lint` and `npm run check` were **not run**: `node_modules` is absent, so
|
||
neither can execute. This change touches only `README.md`, which is outside
|
||
`astro check` and ESLint; Prettier has no config in the repo, so its default
|
||
`proseWrap: "preserve"` leaves markdown prose unreflowed. Line widths were
|
||
checked by hand and every line Claude Code wrote is ≤ 80 columns.
|
||
|
||
**Lesson, extending entry (l)'s.** (l) recorded that amending a decision requires
|
||
sweeping every file that referenced the original. This pass adds the corollary:
|
||
**a correction is a change, and gets reviewed like one.** Seven of the nine
|
||
findings above were in text written specifically to fix an audit finding. The
|
||
first rewrite of a false claim is the most dangerous moment for it, because
|
||
everyone involved is now confident the area is clean.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (l) — First adversarial review run; it found defects in the specs
|
||
|
||
**Who:** Pouya ran `claims-auditor` against `README.md` to verify the agents load.
|
||
It loaded, and returned five findings before any site code exists.
|
||
|
||
**The agents are confirmed working.** R1 was surfaced unprompted, as §12 requires.
|
||
F2 and F5 were escalated rather than silently edited — the behaviour the brief
|
||
asks for on judgement calls.
|
||
|
||
**Two register-level defects fixed here (architect's, not Claude Code's):**
|
||
|
||
**F5 root cause — §4 contradicted D13.** Old → new: the Verified table read
|
||
*"Active litigation practice: personal injury, construction, regulatory (POA),
|
||
insurance (SABS)"* → **"Active litigation exposure: …"**. The row quoted the
|
||
strategy brief verbatim and used the exact word D13 bars in exactly the context
|
||
D13 bars it. Anyone writing copy from the register would have inherited the
|
||
violation **from the register itself** — the worst place for it to live.
|
||
|
||
D13 now also states the distinction explicitly: "practice" is barred **for the
|
||
boutique role**, not for Pouya's own ADR practice. So "the dispute resolution
|
||
practice of Pouya Lajevardi" is correct — `README.md:3` and `package.json:5` need
|
||
no change. **F5 resolved, no edit to the README.**
|
||
|
||
**F2 — the Forbidden table's scope was overbroad.** Old → new: heading read "must
|
||
not appear **anywhere**" → **"must not appear on the site"**, with the scope
|
||
stated: published pages including JSON-LD, not repository documentation.
|
||
|
||
The auditor was right that something was wrong, though the rule it cited was the
|
||
wrong one. `README.md`, `CLAUDE.md`, and this file all called Pouya "a licensed
|
||
professional" while §4 marks licensure **`[assumed]`**. Internal docs were
|
||
asserting as fact what the register cannot vouch for. A new rule now covers this
|
||
directly. The three phrases are rewritten to state the *reason* for the
|
||
strictness — the previous site carried fabricated credentials — rather than a
|
||
credential we cannot confirm.
|
||
|
||
**F1, F3, F4 — handed to Claude Code.** One root cause: `README.md`'s Deployment
|
||
section was written against D3 as originally decided and never updated when D3
|
||
was amended to Gitea. `CLAUDE.md`, `docs/06`, and this file were all updated; the
|
||
README was missed.
|
||
|
||
F1 is the serious one. "Deploys via OIDC — there are no long-lived AWS credentials
|
||
in this repository" is **false**, and dangerous in the specific way the auditor
|
||
identified: it is narrowly defensible, since the key is not tracked in git — which
|
||
is exactly why it would suppress the quarterly rotation obligation in D3, in an
|
||
account §10 rates High blast-radius because it holds `mlp-clientdb-prod-backups`.
|
||
|
||
**Lesson recorded.** Amending a decision requires sweeping every file that
|
||
referenced the original, not the files that happen to come to mind. The review
|
||
caught it; the amendment process should have. Worth adding to `/wrap`.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (k) — Q21 resolved: nothing broken; SES sandbox is per-region
|
||
|
||
**Who:** Pouya ran the per-region check.
|
||
|
||
**Q21 closed — the good case.**
|
||
|
||
| Region | Verified | DKIM | Tokens |
|
||
|---|---|---|---|
|
||
| `ca-central-1` | `true` | **SUCCESS** | `kznn…` `jdue…` `f5pu…` — resolve correctly |
|
||
| `us-east-1` | `false` | PENDING | `3zsn…` `jejgp…` `xpiwy…` — doubled domain |
|
||
|
||
The region that matters is healthy. The three broken CNAMEs belong to a stray
|
||
`us-east-1` identity that nothing in this project uses; it is PENDING precisely
|
||
*because* those records were entered with the domain doubled. **No fix required.**
|
||
|
||
Recommendation recorded: **leave the three dead CNAMEs in DNS.** They are inert,
|
||
and the only way to cause harm is deleting the wrong row from a list of six
|
||
visually similar records. Optionally delete the `us-east-1` identity itself so it
|
||
stops reporting PENDING — that is a control-plane action with no DNS risk.
|
||
|
||
**New finding, and it would have cost a day: the SES sandbox is per-region.**
|
||
`ProductionAccessEnabled: false` was read from `ca-central-1`, which is where the
|
||
intake Lambda, DynamoDB, and API Gateway all live. The AWS console defaults to
|
||
`us-east-1`, and the panel Pouya screenshotted was `us-east-1` — so the natural
|
||
path would have been to request production access in the wrong region, wait a
|
||
day, and find the confirmation email still failing. Flagged in §7 and on Q19.
|
||
|
||
**Also worth noting for the record:** entry (j)'s correction was itself only
|
||
half the story. DNS proved which records resolved; it could not prove which
|
||
*mattered*. That needed the control plane. Neither source is sufficient alone —
|
||
the data plane says what exists, the control plane says what is expected, and a
|
||
discrepancy between them is the finding.
|
||
|
||
**Closed:** Q21. **Still blocking:** Q19 only.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (j) — SPF/DMARC live; and a correction: three DKIM records are broken
|
||
|
||
**Who:** Pouya added the two TXT records. Claude verified independently.
|
||
|
||
**Q20 closed.** SPF and DMARC are both live and correct, confirmed by direct DNS
|
||
query rather than taken on report. Also confirmed unchanged: both existing `@`
|
||
TXT records, `google._domainkey`, `MX 1 smtp.google.com`, and
|
||
`adr.smlcompany.ca` → CloudFront. Nothing was disturbed.
|
||
|
||
**Correction to entries (h) and (i) — I had the two DKIM sets backwards.**
|
||
|
||
- Old → new: "`3zsn…`/`jejgp…`/`xpiwy…` are the live set; `f5pu…`/`jdue…`/`kznn…`
|
||
are inert orphans" → **the opposite.** The `f5pu` set resolves NOERROR at the
|
||
correct names. The `3zsn` set returns **NXDOMAIN**.
|
||
- **Root cause found.** `3zsnvsjg…._domainkey.smlcompany.ca.smlcompany.ca`
|
||
resolves; the correct name does not. Those three were entered into Namecheap's
|
||
Host field with the **full name including the domain**, so Namecheap appended
|
||
the domain a second time. This is precisely the gotcha flagged in entry (h) —
|
||
which was written before checking whether the existing records had already
|
||
fallen into it.
|
||
- **Why I got it wrong:** I inferred "live" from the SES console listing them and
|
||
did not verify against DNS. The console shows what SES *expects*, not what
|
||
exists. Lesson worth keeping: a control-plane listing is not evidence about the
|
||
data plane.
|
||
- **No harm done.** The advice in (i) was "leave both sets alone", which happened
|
||
to be the safe action either way.
|
||
|
||
**Q21 opened.** SES is regional, and `ca-central-1` reported
|
||
`VerifiedForSendingStatus: true` — only consistent with the `f5pu` set. So the
|
||
console panel screenshotted was probably a different region's identity. Until
|
||
that is confirmed per region, it is unknown whether the broken records matter.
|
||
Nothing should be deleted before that check.
|
||
|
||
**Closed:** Q20. **Opened:** Q21. **Still blocking:** Q19 (SES production access).
|
||
|
||
---
|
||
|
||
## 2026-08-26 (i) — SES DNS panel resolves the DKIM ambiguity; MAIL FROM absent
|
||
|
||
**Who:** Pouya sent the SES "Domain DNS records" panel.
|
||
|
||
**The six DKIM CNAMEs are now fully explained.** SES lists exactly three for
|
||
`smlcompany.ca`: `3zsnvsjg…`, `jejgp7na3…`, `xpiwyftpo…`. The other three in
|
||
Namecheap — `f5puwearz…`, `jdue2r22c…`, `kznn3cklv…` — are **not** referenced by
|
||
SES and are residue from an earlier verification of the same domain. Note the
|
||
TTLs corroborate this: the live set reads "1 min", the orphans "Automatic",
|
||
consistent with two separate additions.
|
||
|
||
**Recommendation recorded: leave the orphans in place.** They are inert. The only
|
||
way this becomes a problem is deleting the wrong three, which would break SES
|
||
DKIM and therefore DMARC. There is no upside to tidying a DNS zone.
|
||
|
||
**Custom MAIL FROM is not configured** — "No MAIL FROM records found". This
|
||
independently confirms the correction in entry (h): SES's envelope sender is at
|
||
`amazonses.com`, its SPF pass is unaligned with `smlcompany.ca`, and DMARC is
|
||
satisfied through DKIM alone. Configuring a custom MAIL FROM would add SPF
|
||
alignment as belt-and-braces; it is optional and not on the critical path.
|
||
|
||
**AWS's suggested DMARC value is `v=DMARC1; p=none;`** — valid but minimal, with
|
||
no `rua`, so it produces no reports. Using `v=DMARC1; p=none; rua=mailto:info@smlcompany.ca; fo=1`
|
||
instead so the monitoring period actually yields data before tightening to
|
||
`quarantine`.
|
||
|
||
**Net action for Q20 is two TXT records and nothing else.**
|
||
|
||
---
|
||
|
||
## 2026-08-26 (h) — DNS inspected; earlier SPF reasoning corrected
|
||
|
||
**Who:** Pouya sent the Namecheap DNS panel, asking exactly what to change
|
||
without breaking anything.
|
||
|
||
**Correction to entry (g) — the SPF reasoning was wrong.** Old → new: "SES needs
|
||
SPF or mail lands in spam" → **SES authenticates via DKIM, not SPF.** Without a
|
||
custom MAIL FROM domain, SES's envelope sender sits at `amazonses.com`, so its
|
||
SPF pass is not aligned with `smlcompany.ca` and contributes nothing to DMARC.
|
||
The six `*.dkim.amazonses.com` CNAMEs already in Namecheap are what make SES
|
||
DMARC-compliant, and they are working. The SPF record's real value is
|
||
authenticating **Google Workspace** mail, which has none. `include:amazonses.com`
|
||
stays because it is harmless and becomes live if a custom MAIL FROM is added.
|
||
Entry (g) is left as written per rule 3; this is the correction.
|
||
|
||
**Also found in the DNS panel:**
|
||
- Six SES DKIM CNAMEs where three are needed — the domain was verified twice.
|
||
Harmless, but do not prune without checking SES first.
|
||
- Two ACM validation CNAMEs → `jkddzztszm.acm-validations.aws`. **Flagged as
|
||
never-delete**: they auto-renew the `adr.smlcompany.ca` certificate, and
|
||
removing them breaks HTTPS silently at the next renewal rather than
|
||
immediately. This is the only genuinely destructive mistake available in that
|
||
panel.
|
||
- `@` carries a Namecheap URL Redirect to `sml-coming-soon.ca`. TXT records
|
||
coexist with it, so adding SPF does not disturb the redirect.
|
||
|
||
**DMARC `rua` changed** from `pouya@` to `info@smlcompany.ca` — the mailbox
|
||
confirmed to exist.
|
||
|
||
**Still blocking:** Q19 (SES production access), Q20 (the two TXT records).
|
||
|
||
---
|
||
|
||
## 2026-08-26 (g) — SES purpose clarified; D18; SPF and DMARC found missing
|
||
|
||
**Who:** Pouya asked why SES is needed given he already has an `info@` mailbox.
|
||
Fair question — the answer exposed two things.
|
||
|
||
**The distinction, recorded because it will come up again.** A mailbox receives
|
||
mail and a person logs into it. SES lets *code* send mail — the intake Lambda has
|
||
no mailbox and no login. They are complementary, not alternatives: SES sends
|
||
**to** `info@smlcompany.ca`. Verifying the address in SES did not create a
|
||
mailbox; it authorised sending.
|
||
|
||
**D18 — two emails per submission (new decision).** The sandbox blocks only one
|
||
of the two: the notification to Pouya works (his address is verified), the
|
||
**receipt to the inquirer** does not. That receipt was Claude's addition in
|
||
`docs/05-backend-spec.md`, not part of the original AWS-guide build, so it was
|
||
put back to Pouya as a real choice rather than treated as a defect. **He confirmed
|
||
he wants it** — so Q19 is genuinely blocking rather than optional.
|
||
|
||
**Q20 opened — the domain has no email authentication at all.** A DNS query of
|
||
`smlcompany.ca` on 2026-08-26 returned:
|
||
|
||
- TXT: only `anthropic-domain-verification` and `google-site-verification`.
|
||
**No `v=spf1` record of any kind.**
|
||
- `_dmarc.smlcompany.ca`: **no record.**
|
||
- MX `1 smtp.google.com` and `google._domainkey` present — Google Workspace with
|
||
DKIM configured.
|
||
- `adr.smlcompany.ca` CNAME → `d26v23dhgsp2ta.cloudfront.net`, as expected.
|
||
|
||
Old → new (planned): no SPF, no DMARC → one SPF record covering **both** Google
|
||
and Amazon SES, plus a DMARC record starting at `p=none`.
|
||
|
||
**This is wider than this project.** Pouya's existing business email from
|
||
`smlcompany.ca` is sending unauthenticated today. Google Workspace's DKIM carries
|
||
it, but Gmail and Yahoo both penalise missing SPF and DMARC, so mail he sends as a
|
||
human is already more likely to be filtered than it should be. Raised to **High**
|
||
in §10 and flagged to him directly. Both records should go in before the SES
|
||
production-access request — AWS weighs domain authentication when reviewing, so
|
||
it improves the odds of first-time approval as well.
|
||
|
||
**Opened:** Q20. **Still blocking:** Q19, Q20.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (f) — SES sandbox confirmed; execution protocol built into the repo
|
||
|
||
**Who:** Pouya. Claude recording.
|
||
|
||
**Q18 closed, Q19 confirmed as a live blocker.** `smlcompany.ca` **is** verified
|
||
for sending (`true`) — the earlier `None` was a query artefact, not a fault. But
|
||
`ProductionAccessEnabled: false`: **the account is in the SES sandbox.** Mail
|
||
reaches only pre-verified addresses, so an inquirer's confirmation email fails
|
||
silently. Raised from Medium to **High** in §10: this is the failure mode where
|
||
the site looks like it works and the inquirer concludes they were ignored.
|
||
Production access must be requested; ~24 h turnaround, and it is the only
|
||
dependency on this project with external lead time.
|
||
|
||
**D17 — working method, made structural (new decision).**
|
||
- Old → new: an implicit understanding that Claude Code implements what Pouya
|
||
decides → **an explicit six-phase loop encoded in the repository**, so it
|
||
applies without being restated in each prompt.
|
||
- Why: Pouya's direction — he architects, Claude Code programs and then
|
||
adversarially reviews, with deep reasoning on, permanently.
|
||
- Added:
|
||
- `.claude/agents/adversarial-reviewer.md` — code, accessibility,
|
||
crawlability, performance, security, simplicity. Instructed to **treat
|
||
uncertainty as a defect** and told that an approving review which misses a
|
||
real defect is a failure.
|
||
- `.claude/agents/claims-auditor.md` — every factual assertion traced to §4.
|
||
Separate agent on purpose: a generic reviewer consistently under-weights
|
||
professional-conduct checks, which is this project's highest-stakes failure
|
||
mode. It also audits JSON-LD, since a machine-readable misrepresentation is
|
||
still a misrepresentation.
|
||
- `.claude/commands/build.md` — the loop. `/review` — phase 3 alone.
|
||
`/wrap` — the session-end `AGENTS.md` ritual.
|
||
- `.claude/settings.json` — thinking on, high effort, and a read-deny on
|
||
`.env*` and `aws-inventory.txt`.
|
||
- `CLAUDE.md` — the protocol and the reasoning behind it.
|
||
- `docs/08-execution-protocol.md` — the prompt template Pouya writes from.
|
||
- **The design decision worth preserving:** the reviewers are given the diff and
|
||
the specs, and deliberately **not** the implementer's rationale. Explaining why
|
||
the work is correct anchors the reviewer and converts review into agreement.
|
||
Independence is the mechanism, not a courtesy. If a future change to this
|
||
protocol removes that, it removes the value.
|
||
|
||
**Closed:** Q18. **Still blocking:** Q19 (SES production access).
|
||
|
||
---
|
||
|
||
## 2026-08-26 (e) — AWS inventory captured; Q10 closed; blast-radius risk logged
|
||
|
||
**Who:** Pouya ran `scripts/aws-discover.sh`. Claude recording.
|
||
|
||
**Q10 closed.** All identifiers in §7. Bucket `adr-smlcompany-site`,
|
||
distribution `E1OK7G98KNKUTA`, region `ca-central-1`, intake API
|
||
`4tl0m5igkj`. Certificate ISSUED. **S3 versioning is already Enabled**, so the
|
||
rollback path in `docs/06-deployment.md` works as written — no change needed.
|
||
|
||
**Data residency confirmed.** Hosting, Lambda, and DynamoDB are all in
|
||
`ca-central-1`. `docs/05-backend-spec.md` treated Canadian residency as a
|
||
selling point conditional on the table's region; it is now a verified fact and
|
||
`/legal/privacy/` can state it plainly.
|
||
|
||
**New risk logged — deploy-credential blast radius.** The inventory shows this is
|
||
a shared account across at least five unrelated properties, including
|
||
`mlp-clientdb-prod-backups-327082975128`. A Gitea runner holding a static AWS key
|
||
for this site sits in the same account as a law firm's client-database backups.
|
||
Nothing about the plan changes — the scoped IAM policy was already correct — but
|
||
its narrowness is now load-bearing rather than good practice, and that is
|
||
recorded so nobody widens it later for convenience.
|
||
|
||
**Q18 and Q19 opened — SES.** `list-email-identities` returned
|
||
`VerifiedForSendingStatus: None` for all six identities, and sandbox status was
|
||
not checked. Either condition means the intake confirmation email silently fails.
|
||
This is the failure mode where the site appears to work and inquirers think they
|
||
have been ignored, so it is on the cutover checklist, not the nice-to-have list.
|
||
|
||
**Closed:** Q10. **Opened:** Q18, Q19.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (d) — Memberships, analytics, prep time, firm naming; one blocker left
|
||
|
||
**Who:** Pouya. Claude recording.
|
||
|
||
**Q16 closed — preparation time is bundled and stated.** 2 h in the half day,
|
||
3 h in the full day, printed on `/fees/`. The carried-forward assumption is now a
|
||
decision. At $4,000 a day counsel will assume preparation happened; saying so
|
||
converts the assumption into a selling point and forecloses an argument about
|
||
what the fee covered.
|
||
|
||
**Q15 and Q17 closed.** Non-mediation hourly (ENE, settlement counsel,
|
||
dispute-system design, technical advisory) and mediation overtime both **$500/h**,
|
||
matching the arbitration rate.
|
||
|
||
**Q8 closed — memberships.** Old → new: five `[assumed]` bodies from the strategy
|
||
brief → **ADRIC, ADRIO, and OBA sections, verified.** OCNI is **not current** and
|
||
must not be published; the brief listed it as aspirational positioning for the
|
||
nuclear niche, which is a different thing from a membership.
|
||
|
||
- **LSO was deliberately not offered as an option and is not published.** Listing
|
||
the Law Society in a credentials block implies licensure, which D13 bars. Worth
|
||
recording explicitly so a later reader does not "helpfully" add it back.
|
||
- Which OBA sections remains `[assumed]` — the brief names Construction &
|
||
Infrastructure, ADR, and Civil Litigation. Low stakes; confirm in passing.
|
||
|
||
**D15 — analytics (new decision).** Old → new: unchosen → **privacy-first and
|
||
cookieless**, Plausible or Fathom. No GA4, no consent banner, no personal data
|
||
leaving the site, one honest line in the privacy policy. Consistent with a
|
||
practice whose privacy posture is part of its offer, on a site where visitors are
|
||
often describing live disputes.
|
||
|
||
**D16 — the boutique is never named (new decision).** Old → new: "may be named,
|
||
name pending" → **generic throughout**: *a Toronto litigation and ADR boutique*.
|
||
Pouya reversed his earlier answer. Trade-off, recorded for the record: naming a
|
||
recognisable firm is a meaningful credibility signal to appointing bodies, and
|
||
the generic phrasing gives that up in exchange for zero coordination cost and no
|
||
dependency on anyone else's sign-off. Do not infer the name from his email domain
|
||
or from anything else.
|
||
|
||
**Closed:** Q7, Q8, Q11, Q15, Q16, Q17. **Opened:** none.
|
||
|
||
**Q10 is now the only blocker.** Everything else needed to build is answered.
|
||
R6 (booking) stays parked by choice and does not block `/contact/`.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (c) — Contact, headshot, LinkedIn; booking parked
|
||
|
||
**Who:** Pouya. Claude recording.
|
||
|
||
**Closed.** Q3 — contact is `info@smlcompany.ca`, no public phone, consultations
|
||
by scheduled call. Q6 — headshot supplied; committed as
|
||
`src/assets/pouya-lajevardi.jpg` (1600×1600 master, for Astro to derive AVIF and
|
||
WebP from at build) and `src/assets/og-portrait.jpg` (1200×630, cropped high so
|
||
the face is not centred under the fold of a link preview). Q12 — LinkedIn URL,
|
||
which now feeds `sameAs` in the `Person` JSON-LD.
|
||
|
||
**Q5 parked, not closed.** Old → new: "choose a booking tool" → **deferred at
|
||
Pouya's request**; `/contact/` ships with the intake form and a reserved slot for
|
||
an embed, so adding one later is a drop-in rather than a rebuild. He asked to be
|
||
reminded, so it is now **R6** in §12 rather than a question that quietly expires.
|
||
Worth restating when it comes back up: D10 chose booking because it removes the
|
||
scheduling back-and-forth that loses appointments, and the form alone only
|
||
half-solves that.
|
||
|
||
**Q7 partially answered.** The boutique may be named. The name itself was not
|
||
given. His email domain (`meshkinilaw.ca`) points to **Meshkini Law**, but an
|
||
inferred firm name is exactly the kind of thing §4 exists to stop — asked for
|
||
confirmation rather than published.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (b) — Fees confirmed; Gitea; licensure wording; standing reminders
|
||
|
||
**Who:** Pouya, answering the second round. Claude recording.
|
||
|
||
**D14 closed — rate card confirmed, and it is not what was recommended.**
|
||
- Old → new: two-tier card (Tier A insurance/SABS at $1,800/day, Tier B
|
||
commercial at $3,200/day) → **one rate for all mediation matters: $2,000 half
|
||
day, $4,000 full day, $500 per party beyond two.** Arbitration $500/h, hearing
|
||
day $4,000, documents-only flat $6,500 simple / $9,500 complex. Tribunal-
|
||
secretary line removed. Cancellation terms adopted as recommended.
|
||
- Why: Pouya's decision.
|
||
- **Recorded dissent, for the 12-month review (R5).** A single premium rate puts
|
||
the insurance / SABS / LAT segment out of reach — the published market there
|
||
runs roughly $800–$1,200 a day, and $4,000 is three to five times it. That is
|
||
the segment the strategy brief (§IV.7) identifies as the highest realistic
|
||
near-term volume, flowing directly from the firm's existing accident-benefits
|
||
work. The trade is deliberate if it is deliberate: a premium specialist
|
||
position that forgoes volume. It is a mistake if the volume was being counted
|
||
on. Flagged once, implemented as directed.
|
||
- Three parameters were not specified and are carried forward as assumptions,
|
||
logged as Q15–Q17: non-mediation hourly ($500), bundled prep hours (2 h half
|
||
day / 3 h full day), mediation overtime ($500/h).
|
||
|
||
**D13 refined — approved wording for the boutique role.**
|
||
- Old → new: "describe the role factually" → the specific approved phrases are
|
||
**"active litigation exposure"** and **"involvement in litigation and ADR
|
||
matters"**. The word **"practice"** is barred in that context.
|
||
- Pouya asked to be **continuously reminded** that this is interim. That request
|
||
is now structural rather than a note — see the new §12.
|
||
|
||
**New §12, Standing Reminders.** Five items (R1–R5) that any agent must surface
|
||
at the start of a substantial session. Added because a parked decision otherwise
|
||
becomes permanent by never being raised again — and R1 is Pouya's explicit
|
||
instruction to keep raising it.
|
||
|
||
**D3 amended again — Gitea.**
|
||
- Old → new: "private git server, software unknown" → **self-hosted Gitea with
|
||
Gitea Actions**, clone at `/Users/pouya/Dev/Websites/adr-sml`.
|
||
- Consequence: Gitea Actions uses GitHub Actions syntax, so the workflow ports
|
||
almost unchanged into `.gitea/workflows/deploy.yml`. But **Gitea is not an AWS
|
||
OIDC provider**, so the OIDC role assumption is replaced by a scoped IAM user
|
||
whose key lives only in Gitea's secret store. The IAM policy stays exactly as
|
||
narrow. Quarterly rotation is now an operational obligation that OIDC would
|
||
have made unnecessary — noted in `docs/06-deployment.md`.
|
||
|
||
**Closed:** Q4, Q9, Q13, Q14. **Opened:** Q15, Q16, Q17.
|
||
**Still open:** Q3, Q5, Q6, Q7, Q8, Q10, Q11, Q12, Q15, Q16, Q17.
|
||
|
||
---
|
||
|
||
## 2026-08-26 (a) — Licensure decision; Q.Arb dated; git host changed; fees researched
|
||
|
||
**Who:** Pouya, answering the blocking questions from §9. Claude recording.
|
||
|
||
**Date correction.** The entry below is stamped 2026-08-25. The correct date of
|
||
that work was **2026-08-26** — the session clock was a day behind. Per rule 3 the
|
||
entry is left as written rather than edited; this note is the correction. Facts
|
||
touched today are re-stamped `[verified 2026-08-26]`.
|
||
|
||
**D13 — licensure left out of the site (new decision).**
|
||
- Old → new: "confirm LSO licence status before launch" → **the site asserts the
|
||
JD and makes no licensure claim at all.**
|
||
- Why: Pouya's direction.
|
||
- **Consequence, and it is not small.** The strategy brief describes an *"active
|
||
litigation practice in personal injury, construction, regulatory (POA), and
|
||
insurance (SABS) matters."* Copy written that way would imply entitlement to
|
||
practise law without stating it — which is the risk the decision is meant to
|
||
avoid, restated in different words. So the Forbidden table now bars implication
|
||
as well as assertion, and the About page describes the role factually:
|
||
*Director of Firm Operations at a Toronto litigation and ADR boutique*, plus
|
||
the matter types worked on. **Flagged back to Pouya**: if he is in fact
|
||
licensed and in good standing, that is a material credential for exactly the
|
||
audience this site targets, and omitting it costs more than it protects.
|
||
|
||
**Q.Arb dated.** Old → new: `[assumed]`, stage unknown → **commenced August
|
||
2026** `[verified 2026-08-26]`. Describe as newly commenced. Not as held, not as
|
||
nearing completion.
|
||
|
||
**D3 amended — the repository is not on GitHub.**
|
||
- Old → new: "git repo + GitHub Actions" → **repo `adr-sml` on a private git
|
||
server**; the GitHub Actions workflow in `.github/workflows/` is retained as a
|
||
working reference implementation, to be translated once the server software is
|
||
known (**Q13**).
|
||
- Why: Pouya is running his own git server.
|
||
- Knock-on: GitHub's OIDC provider is unavailable. GitLab CE can federate to AWS
|
||
by OIDC; Gitea/Forgejo and bare git cannot, so those need a scoped IAM user
|
||
with rotated keys, or deploys run from Pouya's machine. The choice is real and
|
||
waits on Q13.
|
||
|
||
**D14 — fee structure recommended (new decision, pending sign-off).** Market
|
||
research completed against the Ontario mandatory-mediation tariff, ADR Chambers
|
||
published ranges, and four published Ontario practitioner rate cards. A two-tier
|
||
structure is recommended so the price-sensitive SABS/LAT volume work does not
|
||
anchor the commercial rate. Figures are in the response to Pouya and are **not
|
||
written into any page until he signs off (Q14).**
|
||
|
||
**Still open:** Q3, Q5, Q6, Q7, Q8, Q10, Q11, Q12, Q13, Q14.
|
||
|
||
---
|
||
|
||
## 2026-08-25 — Project initiated; architecture decided; repo bundle authored
|
||
|
||
**Who:** Pouya Lajevardi with Claude (Cowork session), acting as architects. Claude Code to implement.
|
||
|
||
**What was discussed.** Pouya asked for a modernization of `adr.smlcompany.ca`
|
||
going beyond appearance — content and codebase included — with Claude Code doing
|
||
the implementation and deploying to AWS.
|
||
|
||
**What was found.** An audit of the live site and the local sources established
|
||
the facts in Current Truth §2. The headline finding: the site is a browser-
|
||
compiled single-file React bundle that serves crawlers three words of content.
|
||
For a personal-brand site whose function is discoverability, this is the
|
||
dominant problem, ahead of any question of looks or copy.
|
||
|
||
**What was decided.** Twelve decisions, D1–D12, recorded in §3. In summary: an
|
||
Astro static rebuild, full content re-architecture, ~20 pages, English only,
|
||
git + GitHub Actions onto the existing S3/CloudFront, palette and infinity mark
|
||
retained with a modernized execution, a published rate card, an Insights section
|
||
launching with drafted content, a rebuilt intake form plus booking, and a single
|
||
clean cutover.
|
||
|
||
- Old → new (framework): browser-compiled React single file → Astro static site
|
||
- Old → new (publishing): manual `rebuild-standalone.py` + console upload → git + GitHub Actions with OIDC
|
||
- Old → new (structure): one scrolling page → ~20 pre-rendered pages
|
||
- Old → new (content): partially-corrected placeholder template → written fresh against a claim register
|
||
- Old → new (record-keeping): none → this file, under the AGENTS constitution
|
||
|
||
**Why.** Discoverability is the practice's growth constraint and the current
|
||
architecture forecloses it. Every other decision follows from fixing that, with
|
||
the content decisions following from the strategy brief of 2026-05-26.
|
||
|
||
**Also decided, not yet discussed with Pouya:** no Indigenous engagement practice
|
||
page at launch (§6), on the reasoning recorded there. Flag this to him.
|
||
|
||
**Planned, not yet done.** Astro implementation; all page copy; 3–5 Insights
|
||
drafts; intake backend rework; CI/CD wiring; pre-cutover audit. Twelve blocking
|
||
questions are open in §9 — several pages cannot be written until they are
|
||
answered.
|