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
276 lines
15 KiB
Markdown
276 lines
15 KiB
Markdown
# CLAUDE.md — operating instructions for Claude Code
|
|
|
|
## Read this first
|
|
|
|
1. **`AGENTS.md` is the source of truth for this project.** Read it in full
|
|
before your first edit in any session. It carries the locked decisions, the
|
|
credential register, the open questions, and the full history.
|
|
2. **You are required to maintain `AGENTS.md`** under the constitution written at
|
|
the top of it. Update *Current Truth* in place; append to the *Change Log*,
|
|
newest first; never edit a past entry; never delete history. Record decisions
|
|
and plans, not only executed work. Stamp facts `[verified YYYY-MM-DD]` or
|
|
`[assumed]`.
|
|
3. Update it **at the end of every working session**, not only when something
|
|
ships. A session that produced a decision and no code still produces a Change
|
|
Log entry.
|
|
4. **Read `AGENTS.md` §12 Standing Reminders at the start of every substantial
|
|
session and surface anything live to Pouya.** These are decisions he parked
|
|
deliberately, not settled matters — R1 in particular is his explicit
|
|
instruction to keep raising the licensure wording. A parked decision that
|
|
stops being raised has quietly become permanent, which is the failure mode
|
|
§12 exists to prevent.
|
|
|
|
## The one rule that matters more than the code
|
|
|
|
This is Pouya's public marketing surface, and the site it replaces carried
|
|
fabricated credentials. **No factual claim about him, his credentials, his
|
|
experience, or his practice may appear on a public page unless it is in the
|
|
Verified table in `AGENTS.md` §4.**
|
|
|
|
(§4 does not verify licensure either way — so do not describe him as
|
|
"licensed", or as a "legal professional", anywhere, this file included. State
|
|
the reason for the rule, not a credential the register cannot vouch for.)
|
|
|
|
If a page needs a fact you do not have:
|
|
|
|
- Do not infer it from context.
|
|
- Do not soften it into something defensible ("extensive experience", "years of").
|
|
- Do not carry it over from the old site — the old site contained a fictitious
|
|
founder, invented matter values, and a fabricated testimonial.
|
|
- **Leave `TODO(pouya): <the exact question>` in the source, and add the question
|
|
to `AGENTS.md` §9.** A build that fails on an unanswered question is a correct
|
|
build.
|
|
|
|
Read the Forbidden table in §4 before writing any statistic, number, or
|
|
superlative.
|
|
|
|
## How work is executed here
|
|
|
|
Pouya is the architect. He makes the decisions and hands you the task. **You
|
|
implement, then you adversarially review your own work before calling it done.**
|
|
This is the standing agreement — it applies to every substantial change without
|
|
being restated in the prompt.
|
|
|
|
**Run `/build <task>` for any substantive change.** It encodes the loop:
|
|
|
|
1. **Plan** — read `AGENTS.md` (including §12 Standing Reminders, and surface
|
|
anything live), read the governing specs, name the decisions the task touches,
|
|
and **stop and ask on any conflict**. A blocked build is a correct build.
|
|
2. **Implement** — following the conventions below.
|
|
3. **Adversarial review** — invoke `adversarial-reviewer` and `claims-auditor` in
|
|
parallel on the diff.
|
|
4. **Resolve** — fix each finding or decline it with a stated reason. Re-review
|
|
material fixes.
|
|
5. **Verify** — run the checks. Never report a check as passing that you did not
|
|
run.
|
|
6. **Record** — append the `AGENTS.md` Change Log entry.
|
|
|
|
`/review` runs phase 3 alone. `/wrap` runs phase 6 at session end.
|
|
|
|
**Think deeply before acting.** Extended thinking is on by default for this
|
|
project (`.claude/settings.json`), and `/build` and `/review` request it
|
|
explicitly. The planning and review phases are where it earns its cost — a defect
|
|
reasoned out before implementation is far cheaper than one found after.
|
|
|
|
### Why the review is adversarial, and what would break it
|
|
|
|
Two rules make the difference between a review and a rubber stamp:
|
|
|
|
**Do not brief the reviewers on why your work is correct.** Give them the diff
|
|
and the specs, nothing else. Your rationale anchors them, and an anchored
|
|
reviewer produces agreement rather than review. They must form an independent
|
|
view from the artefact — that independence *is* the mechanism.
|
|
|
|
**The reviewers are instructed to treat uncertainty as a defect.** They will
|
|
sometimes be wrong, and that is the intended trade. Explaining why a finding is
|
|
mistaken costs minutes; a missed defect on this project's public marketing
|
|
pages costs considerably more — the site this replaces carried fabricated
|
|
credentials, and that is the standard being corrected. Do not read a finding as
|
|
an accusation, and do not argue a reviewer down — either fix it, or record the
|
|
reason you declined it so a later reader can see the judgement was made rather
|
|
than missed.
|
|
|
|
**Two reviewers, because they catch different things.** `adversarial-reviewer`
|
|
reads the code. `claims-auditor` reads the copy against the §4 register and knows
|
|
nothing about whether the code is elegant. A generic reviewer consistently
|
|
under-weights the professional-conduct check, which is the highest-stakes failure
|
|
mode on this project — so it gets its own pass.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev # local dev server
|
|
npm run build # static build to ./dist
|
|
npm run preview # serve ./dist locally
|
|
npm run check # astro check — type and template errors
|
|
npm run lint # eslint + prettier check
|
|
npm run format # prettier — rewrite files in place
|
|
npm run deploy # build + deploy from this machine (see docs/06)
|
|
```
|
|
|
|
## Where things live
|
|
|
|
```
|
|
AGENTS.md living project record — read first, maintain always
|
|
docs/ the specs you build from
|
|
01-architecture.md sitemap, URL map, per-page content outline
|
|
02-design-system.md tokens, type scale, motion, contrast constraints
|
|
03-content-spec.md voice, copy rules, per-page copy deck
|
|
04-seo-spec.md metadata, structured data, sitemap, crawlability
|
|
05-backend-spec.md intake form, Lambda/DynamoDB/SES, booking, PIPEDA
|
|
06-deployment.md S3/CloudFront, Gitea Actions, IAM, cutover checklist
|
|
src/
|
|
content.config.ts content collections — Content Layer API, NOT content/config.ts
|
|
styles/tokens.css design tokens — the single source of colour and scale
|
|
styles/global.css reset, base type, utilities
|
|
layouts/ page shells
|
|
components/ UI components
|
|
pages/ routes (file-based)
|
|
content/insights/ Insights MDX only; the config sits above, not in here
|
|
data/site.ts site-wide constants, nav, contact details
|
|
public/ static assets served as-is
|
|
```
|
|
|
|
## Conventions
|
|
|
|
**Framework.** Astro **7.x**, `output: 'static'` (D1 as amended). Never introduce
|
|
a server runtime without a Change Log entry recording why. The major is pinned
|
|
deliberately — check `npm view astro version` before changing it.
|
|
|
|
**JavaScript.** Default to zero. Reach for an Astro island only when a feature
|
|
genuinely cannot be CSS or progressive HTML. If you add a `client:*` directive,
|
|
say why in the Change Log. A `<details>` element beats a JS accordion.
|
|
|
|
**Styling.** Plain CSS with custom properties. No Tailwind, no CSS-in-JS, no
|
|
utility framework. Every colour, space, and font size comes from a token in
|
|
`tokens.css` — no raw hex values and no magic numbers in component styles.
|
|
|
|
**Accessibility is a build requirement, not a polish pass.** Semantic landmarks,
|
|
one `<h1>` per page, heading levels never skipped, visible focus states, all
|
|
interactive elements reachable by keyboard, `prefers-reduced-motion` honoured on
|
|
every animation. Gold `#c9a876` never sits on cream — it fails contrast at
|
|
2.10:1. See `docs/02-design-system.md`.
|
|
|
|
**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 laptop, no
|
|
reviewer can compare the claim against it and the claim is **unverifiable by
|
|
construction** — not merely unverified. Commit the artefact, or commit a faithful
|
|
extract with its provenance and the command that produced it.
|
|
|
|
This has cost twice. `AGENTS.md` Q24 was the AWS hosting guide, the only record
|
|
of how the infrastructure was hand-built, living outside the repo. Q32 was the
|
|
infinity mark: it was traced from the old site's *loading placeholder*, the
|
|
source comment said so in as many words — and **two adversarial review passes
|
|
still could not catch that the shape was wrong**, because the real artwork was
|
|
not in the repo to compare against. Stating a doubt is not enough when the thing
|
|
that would resolve it is unreachable. Tracked as R14.
|
|
|
|
**A command that did not run is not evidence of absence.** Check that a tool
|
|
exists before trusting its silence, and read exit status, not just stdout. This
|
|
project ran `timeout 60 ls "$DRIVE"` four times, got empty output each time, and
|
|
reported the brand assets unreachable — `timeout` is not installed on macOS, so
|
|
the command had never executed and the directory was fully readable all along.
|
|
Empty output from a command that failed to start looks exactly like empty output
|
|
from a command that found nothing. Same family as *a sweep is a command, not a
|
|
claim*: the claim must rest on output you actually read, from a command that
|
|
actually ran.
|
|
|
|
**A parent cannot style a child component's root element.** Astro does not pass
|
|
a parent's scope attribute down, so `<Button class="header-cta" />` compiles the
|
|
parent's rule to `.header-cta[data-astro-cid-<parent>]` while the rendered `<a>`
|
|
carries only `<Button>`'s own cid. **The rule silently never matches** — no
|
|
error, no warning, and the CSS looks correct in the source. Wrap the child in an
|
|
element the parent owns (`<div class="header-cta"><Button …/></div>`), or reach
|
|
it deliberately with `:global()` from a parent-scoped ancestor. Inherited
|
|
properties (`white-space`, `color`, `font-*`) do cross the boundary and are the
|
|
exception. This cost a header CTA that was documented as hidden on mobile,
|
|
was not hidden, and sat 75 px short of the right edge on desktop — both found by
|
|
measurement, neither by reading. It will recur with `PracticeCard`,
|
|
`ArticleCard`, and `Pill`.
|
|
|
|
**Never write the `animation` shorthand beside `animation-timeline`.** Longhands
|
|
only — `animation-name`, `animation-duration`, `animation-timing-function`,
|
|
`animation-fill-mode`, then `animation-timeline` and `animation-range`.
|
|
`scroll()` and `view()` are not legal components of the shorthand, and Lightning
|
|
CSS folds the two declarations together on minify into something invalid, which
|
|
is then discarded whole. **It works in `npm run dev` and is dead in
|
|
`npm run build`** — the worst shape a defect can take. It happened twice in one
|
|
session, the second time inside the fix for the first. `/build` Phase 5 greps
|
|
`dist` for it; do not remove that check.
|
|
|
|
**Images.** Astro `<Image>` with explicit width and height. AVIF/WebP with
|
|
fallback. Never base64-inline an image into HTML — the old site did this with
|
|
~1 MB of logo PNGs — a figure `AGENTS.md` Q34 is now open against, so treat the
|
|
rule as standing on its own merits rather than on that number.
|
|
|
|
**Fonts.** Self-hosted, subset, `font-display: swap`, preloaded. No Google Fonts
|
|
request at runtime — it costs a round trip and adds a third-party call to a
|
|
page that collects legal inquiries.
|
|
|
|
**Every page ships with:** a unique `<title>` and meta description, a canonical
|
|
URL, Open Graph and Twitter card tags, and appropriate JSON-LD. See
|
|
`docs/04-seo-spec.md`. A page without these is not finished.
|
|
|
|
**A version pin is verified against the registry, never recalled.** Before you
|
|
write or change any dependency version, run `npm view <pkg> version` and pin
|
|
against what it returns. One second of checking; a stale pin costs a migration.
|
|
This rule exists because `astro: "^5.0.0"` was written from memory and was
|
|
**two majors stale on the day it was written** — which meant shipping a
|
|
framework carrying high-severity XSS advisories. The same check applies to
|
|
every pin in `package.json`, not just the framework.
|
|
|
|
Re-check currency at each phase boundary in the build order (`AGENTS.md` R11),
|
|
not only when something breaks.
|
|
|
|
**`AGENTS.md` §7 is the single source of truth for operational facts.** Resource
|
|
IDs, regions, DNS records, credential state, service status — these live in §7
|
|
and nowhere else. Specs in `docs/` **cite** §7; they do not restate it. Write
|
|
"the region `AGENTS.md` §7 records", not the region. Same for bucket names,
|
|
distribution IDs, DKIM tokens, endpoints, and account identifiers.
|
|
|
|
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. This rule exists because
|
|
`docs/05-backend-spec.md` carried its own copy of the SES DKIM table, a
|
|
correction reached §7 and never reached it, and the stale copy ended up telling
|
|
an operator to delete the three records that authenticate outbound mail —
|
|
under the heading "Never delete".
|
|
|
|
**A sweep is a command, not a claim.** Any statement that a change was applied
|
|
across files — a phrase removed everywhere, a path updated everywhere, a
|
|
decision swept through the docs — must cite the command that proves it, and be
|
|
written only after reading that command's output. Paste the `grep` into the
|
|
Change Log entry. Three consecutive entries on this project asserted a completed
|
|
sweep; instances survived all three, and one of them was inside
|
|
`.claude/agents/claims-auditor.md` — the definition of the agent whose job is to
|
|
catch exactly that. Recall is not evidence.
|
|
|
|
**Commits.** Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`).
|
|
One logical change per commit. Never commit secrets, `.env` files, or AWS
|
|
credentials. Gitea is not an AWS OIDC provider, so the deploy key is designed as
|
|
a static IAM access key to be held in Gitea Actions secrets — whether it has
|
|
actually been provisioned is `AGENTS.md` Q22. It must never reach the repo.
|
|
|
|
**Performance budget.** Lighthouse ≥ 95 on all four categories, on mobile, for
|
|
every page. Under 100 KB of JS on any route. LCP under 2.0 s on a simulated
|
|
Slow 4G connection. Treat a budget breach as a failing build.
|
|
|
|
**Lighthouse cannot currently be run.** `@lhci/cli` was removed on 2026-08-26
|
|
(it carried 7 high-severity advisories, `0.15.1` is `latest`, and it had no
|
|
pages and no `lighthouserc` to work with). The budget stands; the instrument is
|
|
missing. It is re-added at build step 7 under `AGENTS.md` R11 — with a freshly
|
|
verified pin, not on the assumption that `0.15.1` is still the ceiling. **Say
|
|
"not run — tool unavailable" rather than silently omitting it.** A documented
|
|
control that no longer exists is precisely the defect Q22 turned out to be.
|
|
|
|
## What "done" means for a page
|
|
|
|
- [ ] Copy written from `docs/03-content-spec.md`, every claim traceable to `AGENTS.md` §4
|
|
- [ ] No `TODO(pouya)` left unlogged in §9
|
|
- [ ] Unique title, meta description, canonical, OG/Twitter tags, JSON-LD
|
|
- [ ] Semantic HTML; keyboard navigable; reduced-motion honoured
|
|
- [ ] Lighthouse ≥ 95 mobile, all four categories — **UNAVAILABLE until step 7**
|
|
(see the performance budget above). Report it as not run; do not tick it
|
|
- [ ] Renders correctly with JavaScript disabled
|
|
- [ ] `AGENTS.md` Change Log entry appended
|