Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit c21344c6650011774cdef32063429cd56db25fd6
parent 609033036978a1c2121be5257ece368f04900a93
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Mon, 28 Sep 2026 03:13:58 -0400

plans: slice O2 as shipped — the fitted custom hex, the sixth chart slot (validated; the gone question), the forced-colours outline, the synthetic homepage e2e summary; FACTS' colour entry; dated notes in brand-and-themes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mplans/FACTS.md | 9++++++---
Mplans/brand-and-themes.md | 8+++++++-
Mplans/release-11.md | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 188 insertions(+), 4 deletions(-)

diff --git a/plans/FACTS.md b/plans/FACTS.md @@ -6720,9 +6720,12 @@ S4, as shipped"; `release-10.md` "Slice L2 / L1, as shipped". Every anchor below it goes through `siteColor` (`common/lib/siteColor.ts`): a NAMED accent is `var(--swatch-<id>)` (per base) from the summary's `accentId`, used only when `hub-sites.json`'s hex (on-dark only) is absent or equal to the summary's; the homepage's `ArchiveCards` use the - same helper. With no accent the fallback is the site's CHART colour, `siteChartColors(sites)`: - the `--chart-N` of its accent's hue family (blue 1, green 2, violet 3, brass 4, sakura 5), else - `seriesColor(i)` when free, else the lowest free slot — never two sites in one colour. The growth + same helper. A CUSTOM hex is fitted per base (release 11, slice O2): `perBaseColor(resolveAccent(hex))`, + one `rgb(calc(…))` over tokens.css's one-hot `--base-light|sepia|dark`. With no accent the + fallback is the site's CHART colour, `siteChartColors(sites)`: the `--chart-N` of its accent's hue + family (blue 1, green 2, violet 3, brass 4, sakura 5, vermilion 6 — `--chart-6` is a rust since + release 11), else `seriesColor(i)` when free (the first SIX are `var(--chart-1..6)`), else the + lowest free slot — never two sites in one colour, and any six wear the six validated slots. The growth chart, its legend and `/stats` (`SiteGrid`, `homepageChartData`, the By-site leaderboard) wear it; the accents themselves fail the dataviz validator as a chart palette (no five with Brass and Blue pass). `useHubSites` lists the official diff --git a/plans/brand-and-themes.md b/plans/brand-and-themes.md @@ -69,6 +69,11 @@ tile, following its corner (`border-radius` 21.875% = rx 112 of 512), in the pal (`#6b5d47`, `#586977`: 3.09:1 and 3.48:1 on the ink page), as a box-shadow under `.dark` only, so no layout moves and light and sepia are unchanged. The icon files carry no ring. +**Under forced colours the tile has a 1px `CanvasText` outline, on every base** (2026-09-28, +release 11, slice O2; `release-11.md`): the reader's Canvas replaces the page there and may be as +dark as the tile. Same place and corner as the ring (outline offset 0); outside forced colours the +classes match nothing, and the marks render pixel-identical on light, sepia and dark. + Icon variants: - `any`: `rx=112`. - `maskable`: full-bleed ground, the mark scaled 0.8 about (256,256). @@ -119,7 +124,8 @@ Each base declares all 48 colour tokens listed in `homepage/e2e/theme.spec.ts:63 - **Sepia** derives its status, panel and chart values from the archive paper block, darkened for a light ground. - **Dark** adds the `--destructive-soft` and `--state-gone(-soft)` it lacks today. -- **Charts do not follow the accent:** each base has its own fixed `--chart-1..5`. Re-validate +- **Charts do not follow the accent:** each base has its own fixed `--chart-1..5` (`--chart-6`, a + rust, since release 11: light and sepia `#823c10`, dark `#a54a08`; `release-11.md`, slice O2). Re-validate chart-3 against `--state-gone` with the dataviz skill's validator (FACTS.md ~6442 records a real past collision), and keep chart-1 ≠ Signal. diff --git a/plans/release-11.md b/plans/release-11.md @@ -394,6 +394,181 @@ mounted is fine as a rule, keep `untouched` optional tonight). the new row; with `persistKept` counting every return, the new persistKept test fails; the other 8 in the two files pass. +### Slice O2, as shipped — homepage and brand lows (2026-09-28) + +Release 10 left four lows on the homepage and the mark: AC's "a custom hex is painted unfitted on +every base" and "a sixth published site would nearly repeat Anilyzer's magenta", and MR's "the ring +is a box-shadow, which forced-colours mode removes" and "a fresh worktree's homepage e2e skips 8 +specs". Branch `r11/homepage-lows` off `main` `2162db92`, `main` `c1d4790a` merged in (a fast-forward) +before the first commit, worktree `/home/user/Projects/r11-homepage-lows` (block #3), one Opus +implementer. Scratch files `o2-*` in the job's `tmp/overnight`. + +**What shipped** + +- **A custom hex is fitted per base, on the homepage cards and the hub's official cards.** + - `siteColor` paints one through `resolveAccent`, the fit the site's own pages use: 4.5:1 on the + ground and with its ink. + - The three values travel as ONE CSS value, `perBaseColor` (`common/lib/siteColor.ts`): each + channel is a `calc()` over new one-hot tokens `--base-light|sepia|dark`. They are 1 on their own + base and 0 on the others, declared in each base block of `tokens.css`. The browser resolves the + value to a plain `rgb()` and switches it with the base. + - A single string was needed because `siteColor`'s result goes, as is, into every hub surface: + card, chip, result stripe. So the fix is in the shared function, and no file under + `export/app/components/hub/**` changed. + - The fallbacks paint the light value on a page with no token sheet. + - The e2e's pale `#f4c2d7` (1.43:1 on the light ground as published) paints `#846974` on light, + `#7c636e` on sepia and `#f4c2d7` on dark. +- **A sixth validated chart slot, `--chart-6`, a rust.** Light and sepia `#823c10`, dark `#a54a08`. + - `seriesColor(5)` is `var(--chart-6)` (`CHART_SLOTS = 6`), no longer the golden angle's + `hsl(328 64% 55%)`. + - `ACCENT_CHART_SLOT.vermilion = 5`. The rust is Vermilion's hue family: 8–10° of OKLCH hue from + its swatches, and 27–33° from the nearest other slot (amber). + - So Jasolyzer, once published, wears the rust at any rank, and any six sites wear the six + validated slots. Signal, a custom hex and no accent still have no family; they take the lowest + free slot, and the sixth when it is free. + - `REQUIRED_TOKENS` is 50 and `--color-chart-6` is in `@theme`. The homepage `theme.spec` checks + that every base declares it. +- **Forced colours: a 1px `CanvasText` outline on the mark's tile, on every base.** It is two classes + on `BRAND_MARK_RING_CLASS`: + - `forced-colors:outline` and `forced-colors:outline-[color:CanvasText]`. + - They compile to `@media (forced-colors:active){…outline-width:1px…outline-color:canvastext}`. + - The outline has offset 0, so it sits where the dark ring does and follows the same corner. + - Outside forced colours the two classes match nothing. + - Every `BrandMark` has it: the export header and footer credit, the hub, the homepage and the + editor sidebar. +- **The homepage e2e reads a synthetic summary.** + - `homepage/e2e/fixture-summary.ts` replaces `fixture-accents.ts`. It runs the real + `buildHomepageSummary` over made-up recordings and is deterministic (`FIXTURE_NOW` 2026-09-15). + - Its six public sites are, in order: Brass; the pale custom hex; three with no accent; Vermilion. + - The first site has a 12,000-transcript week in May, so the All-time axis reaches 10K. Every site + transcribes every day of the last 200. + - `playwright.config.ts` writes it to `e2e/.e2e-summary.json` before the server starts. That path is + gitignored, as is a killed run's temp copy, and is not linked. It always passes + `HOMEPAGE_SUMMARY_FILE`. + - Nothing is read from `public/` or from the primary. + - `stats.spec` no longer skips, and its site count is the fixture's six. `marketing.spec` reads the + fixture, the file the server reads. `instance-colours.spec` checks the fitted pale hex per base, + Vermilion's card in its rust layer's hue, and the six legend swatches as the six validated slots. + +**The sixth slot, validated.** Slice AC's method (`ac-validate.sh` / `ac-layers.mts`): the dataviz +skill's validator on each base's own values, read from `tokens.css`. It runs over all pairs, since +the homepage order moves with transcript counts. Contrast is checked against `--chart-surface` AND +the page ground. The command, from `common/`, is `pnpm exec tsx $T/o2-validate.mts`. For every run it +calls `node $T/dataviz/scripts/validate_palette.js "<chart-1..6>" --mode light|dark --surface <chart +surface | page ground> --pairs all|adjacent`. Log: `o2-validate.log`. + +| base | `--chart-1..6` | surface / ground | band | chroma | CVD, all pairs | normal, all pairs | contrast | exit | +|---|---|---|---|---|---|---|---|---| +| light | `#3a7de0 #2f8a57 #5e3aa8 #a8741a #c24a8a` **`#823c10`** | `#ffffff` / `#f3f6f7` | PASS | PASS | WARN 6.2 (amber↔green) | PASS 15.4 | PASS / PASS | 0 / 0 | +| sepia | `#3574d6 #2a7d4f #5e3aa8 #a8741a #bb4585` **`#823c10`** | `#faf4e6` / `#f4ecd8` | PASS | PASS | WARN 6.1 (amber↔green) | PASS 15.3 | PASS / PASS | 0 / 0 | +| dark | `#3561c8 #3fa577 #9a7ee6 #b98a2a #c24c8a` **`#a54a08`** | `#16110a` / `#0c0a08` | PASS | PASS | WARN 6.9 (magenta↔green) | PASS 15.4 | PASS / PASS | 0 / 0 | + +- **The WARN pairs are the five's own floor band, which release 10 recorded; none involves the + sixth.** The sixth's own pairs, each run alone against `--chart-1..5`, are: + - worst CVD 12.5 / 9.1 / 13.9 (light / sepia / dark; against green, green, magenta); + - worst normal 16.6 / 16.6 / 16.3 (against amber); + - all above the target of 8 and the floor of 15. +- **Contrast:** 8.1 / 7.4 / 3.2 to 1 on the chart surface and 7.4 / 6.9 / 3.4 to 1 on the ground. +- **The stack (adjacent), as `siteChartColors` resolves it** for the operator's mapping, with + Jasolyzer (Vermilion) at each of the six places: PASS on every base. Worst CVD 12.5 / 9.1 / 11.0, + normal 16.6 / 15.3 / 16.3. Before this slice the same run could not even resolve: + `jasolyzer→hsl(328 64% 55%)` (`o2-validate-pre.log`). +- **Why a rust.** A search over every OKLCH hue took each base's best L and C inside the band at + ≥ 3:1 on both the surface and the ground (`o2-chart6-search.mjs`, `-hue`, `-margin`; logs of the + same names). Only the red-browns, h ≈ 15–60, clear every pair with `--chart-1..5` on all three + bases. With 0.01 of L margin and ≥ 3.2:1, the best is h 48. The other hues fail: + - teal fails CVD against blue and green (4.0–6.4), and would sit on Signal; + - olive fails the normal floor on light and sepia (11–14.9); + - an electric blue or violet fails on dark, or reads as a second chart-1 or chart-3. +- **A question for the operator: the rust sits near `--state-gone`.** The validator FAILs that pair, + so it is information, not a gate: + - normal ΔE 8.4 / 7.2 / 13.1, CVD 5.4 / 3.6 / 11.7; + - release 10 (S2's review) accepted amber and magenta at 11.8–14.5, because gone is only ever + labelled text, never a chart mark; the rust is closer; + - no sixth hue clears both `--chart-1..5` and gone ≥ 15 on every base; the gone-constrained + search's best, an electric violet, fails the palette's own normal floor (14.4 on light and + sepia; `o2-chart6-hue-gone.log`); + - on the homepage, gone is the FamilyStats figure and the rail's badge. + +**Forced colours, as rendered** (`o2-shots.mjs`, `o2-shots.log`, `o2-forced.png`). +- **Normal modes are unchanged.** Each mark was shot as built and again with the two new classes + removed, which is `main`'s class string, then compared by pixel (`magick compare -metric AE`). + All 15 pairs differ by **0 px**: the homepage header, the site header and footer, and the hub header + and footer, on light, sepia and dark. +- **The same pairs under forced colours differ,** on a light (white Canvas) and a dark (black Canvas) + forced palette: all 18. +- **The worst case before, row 2 of `o2-forced.png`.** A site's ink tile on light or sepia under a + black Canvas had no edge at all. Now it has a white outline. +- **Found: Chromium keeps the svg's box-shadow in forced colours.** Its UA sheet gives `svg` + `forced-color-adjust: preserve-parent-color`, so MR's premise ("which forced-colours mode removes") + does not hold there. The dark ring stays, and the outline is painted over the same pixel ring. What + the mode does do is put the reader's Canvas under the tile on every base, which is why the outline + is on every base. +- **Playwright applies the forced palette** (`page.emulateMedia({ forcedColors: "active" })`). This + version's `test.use` has no `forcedColors`. + +| sha | what | +|---|---| +| `fbd6318a` | `common:` `--chart-6` + the base flags (`tokens.css`), `seriesColor` six, `ACCENT_CHART_SLOT.vermilion`, `perBaseColor`, `siteColor` fits a custom hex; `REQUIRED_TOKENS` 50; unit tests (siteColor, hubSummary, themeTokens); the homepage chart and card headers' notes | +| `e253e244` | `common:` the forced-colours outline on `BRAND_MARK_RING_CLASS`; `BrandMark.test.ts` +1; the homepage `brand.spec` forced-colours test + outline none in the normal-mode test | +| `c37d620e` | `homepage(e2e):` `fixture-summary.ts` (replaces `fixture-accents.ts`), `playwright.config.ts`, `stats`/`marketing`/`instance-colours` specs, `.gitignore` | +| `7685e701` | `changelogs:` export and editor `[Unreleased]` bullets (the existing sections), the homepage's four | +| _this_ | `plans:` this record; `FACTS.md`'s official-instance colour entry; dated notes in `brand-and-themes.md` (mark, charts) | + +**Gates** (logs `o2-*.log`): +- **tsc** clean before each code commit (33–71 s; `o2-tsc{1..4}.log`). The one failure along the way + was `forcedColors` in `test.use`; the spec now uses `emulateMedia`. +- **Units:** + - common **2,056/2,056** (2,051 on `main` + 5); + - editor unit **85/85**, `test:scripts` **175 + 1 skipped**, mcp **269/269**, homepage unit **2/2**. +- **Builds:** + - Timings: editor ok (38 s), export site ok (23 s), export hub ok (25 s; `out/ask` present), + homepage ok (12 s; no `public/` data, so the no-data page and the header mark). + - Each stylesheet carries the `forced-colors:active` outline rule, `--chart-6` (×2 `#823c10`, + ×1 `#a54a08`) and the three flags per base. + - The primary's `export/public` is byte-identical before and after the builds and the e2e: 392 + entries, 380 files, tree md5 `5c90d8e63753`, content md5 `4a0c46b39eeb`. +- **e2e** (queued behind O1 for about a minute; nothing failed, so no re-runs): + - homepage FULL, from this worktree with NO `homepage/public` data (only its tracked `_headers`): + **30 passed, 0 skipped** (42 s). Release 10's fresh-worktree run was 21 passed / 8 skipped, and + 29 passed with the primary's data copied in; 30 is those 29 plus the forced-colours test. + - export `brand` + `site-branding` + `theme` + `theme-accent`: **20 passed** (50 s). + - `e2e:hub`: **26 passed** (1.0 min). + - editor `branding.spec`: **4 passed** (25 s). + +**They bite** (`o2-bite.sh`, `o2-bite*.log`). Each item was reverted to the old code on its own, +with the new tests kept: +1. **Custom hex as published.** Unit: 4 fail (the two `officialInstances` custom-hex cases, the fitted + test, and the unrecognised-id fall-through). e2e `instance-colours`: expected `rgb(132, 105, 116)`, + received `rgb(244, 194, 215)`. +2. **Five slots, no vermilion family, no `--chart-6`.** Unit: 6 fail (the required tokens, the + charts test, the family slots, `seriesColor` six, the seventh-site case, and any-six-wear-six). + e2e: expected `var(--chart-6)`, received `hsl(328 64% 55%)`. +3. **No outline.** Unit: 1 fails. e2e forced-colours test: `isCanvasText: false`. +4. **`main`'s e2e setup** (its specs, config and `fixture-accents.ts`) in this worktree: **21 passed, + 8 skipped**. The skips are `stats.spec` ×7 and `instance-colours.spec`. + +**Found and left** +- **The hub's ADDED archives still paint a custom hex as set.** This is `ArchiveShelf`'s + `site.accent || FALLBACK_ACCENT`, and the chips and stripes that read the registry's raw `accent`. + It is `export/app/components/hub/**`, O1's, so it was not touched here. The fix is one call where + it parses: `perBaseColor(resolveAccent(hex))`. For O1 or the parent. +- **The rust near `--state-gone`:** the operator question above. +- **Two things do not reach the sixth slot:** + - `ChartView.tsx` (export and editor charts) cycles `var(--chart-${(i % 5) + 1})`, so it never + wears `--chart-6`; its sixth series repeats chart-1, as before. Not a homepage surface. + - Past six, `seriesColor`'s golden angle still gives hue 105 (7th) and 243 (8th, near chart-1's + blue). Only `/stats`' by-channel views reach them (up to 8 channels plus Other). +- **The no-data paths have no spec now.** The e2e server always has the fixture, so `/` and `/stats` + without a summary go untested; `marketing.spec` keeps its absent branches. Before, those paths ran + only in a fresh worktree, where the data specs skipped instead. A second web server without the + fixture would cover them. +- **O6's `E2E_` rename will meet `HOMEPAGE_SUMMARY_FILE`,** which `app/lib/summary.ts` reads and + `playwright.config.ts` sets. +- **The live sites carry none of this until rebuilt:** every export site and the hub (the outline, + and the hub's fitted hex), the homepage (all four), and the editor on its next restart (the + sidebar outline). + ## Rollout Nothing is rolled out tonight. The morning runbook lists what is owed: the :3001 editor restart,