Brand OS — Changelog
Status: active · Owner: Head of Design · Scope: Aleris Group · Version: v0.1 · Updated: 2026-09-18
Project history. Decisions, milestones, and structural changes.
This is the only changelog. baseline/planning/baseline-changelog.md was merged in and superseded on 2026-07-28; its entries sit at the end of this file, before this file's own earliest entry, because they predate it. Do not create a second changelog — the last one died in two days and left Baseline's history four months behind the project's.
2026-09-18 (sixteenth entry) – The published design system is linked from the nav, and it takes the slot that was reserved for the kit
It had been published and unreachable. The realization's four screens, its storybook and the fourteen generated token cards have been serving at /design-system/ since 2026-09-17, and a search of the other 55 pages for an href into that path returned nothing. Published, in the sense that the host answers; not published, in the sense that a reader would have to be told the URL by someone. It is now the fourth entry in the main navigation, on all 55 pages, and a card on the front page.
It takes the slot that said "Kit", and it is not labelled Kit. That entry was reserved for the starter-kit Storybook of board card 99 — still unbuilt, still waiting on three rulings — and rendered as text with "Not built yet" beside it. What sits there now is labelled Reference screens, because Torfinn ruled on 2026-09-17 that an API surface is off scope: Brand OS publishes a rendering to look at, not components to import, and the page itself closes with that sentence. A nav label promising a kit would have been the site making the offer the ruling had just declined. The label is Torfinn's to overturn in one line, and so is what happens to this slot if card 99 is ever built.
And the rules section was renamed rather than this one. The first version put Reference screens next to Reference in the header, and two adjacent entries opening with the same word cost a reader a second look. Torfinn's call was to rename the other one, which is the right half to move: reference describes what a section is for rather than what is in it, and by that reading almost everything on this site is reference material. It is Rules now — the nine constitutional pages, how the system is documented, and the language filters — and its landing page is a constitutional rule, so the label and the destination agree.
The mechanism is the part that generalises. assertSectionsResolve() fails the build when a nav entry points at a route the site does not produce, and it checks the content map — which cannot see this route, because /design-system/ is written by scripts/build-design-system.mjs after astro build has finished. The easy fix is an allowlist entry, which is a promise with nothing behind it. Instead the section declares generatedBy, and the assertion checks the two things that are knowable at that moment: the generator still exists, and site:build still runs it. Both failure modes were proved by mutation — the generator moved away, then unwired from the script — and each fails the build with the reason named. Whether it actually emitted the route is answered afterwards by site/link-audit.mjs against the tree it wrote: 3,983 paths served, every href resolving.
One stale claim found on the way, and it was a claim about the page it sat on. The front page said "Three of the six sections above have nothing to open yet" and listed their reasons; the real number was two before this change and is one after it, and the clause about the design system "still lives on the current site while it is moved to this one" had been false since the cutover. It is now counted from the same data the cards render from, so the sentence cannot disagree with the list above it again.
Not live until someone presses Deploy. The host serves the 2026-09-17 build, which has no such link. node site/deploy-freshness.mjs is the check that says so.
2026-09-17 (fifteenth entry) – The staging mirror is retired, and it had quietly become the only crawlable copy of the site
It was retired because it had no job left, and removed urgently because of what the measurement found. forgebrand.dev.aleris.ai existed for one reason: Forge matches an app's domain to a repository of the same name, so while the old Coolify application still held brand.dev.aleris.ai the Astro build could not be staged from aleris/brand at all. The cutover ended that on 2026-09-11. brand.dev.aleris.ai now builds from aleris/brand directly — verified 2026-09-17, last deployed 07:48 UTC, /health reporting pages: /app/site/dist and index: 9488 bytes — so the workaround was maintaining a second deploy target for a constraint that no longer applied.
The measurement is the part worth keeping. The staging host was still serving the 2026-09-10 build, one day older than card 155, and a release decision does not travel to a host nobody redeploys. So it carried noindex on none of its four surfaces — no meta tag, no X-Robots-Tag, no stamp, /build-state.json answering Not found — while brand.dev.aleris.ai carried all four. Its robots.txt was the pre-fix one, still naming aleris-brand-os.vercel.app for a sitemap that 404s. And every page on it set <link rel="canonical" href="https://brand.dev.aleris.ai/...">, so a crawler reaching the unguarded host was pointed at the guarded host's URLs. Both names resolve to the same public address under a Let's Encrypt certificate, which means a public DNS record and a completed public challenge: internet-facing, crawl-allowed, and holding the corpus the release gate exists to keep out of an index.
Two things generalise, and both are about a check's reach rather than its logic. The release gate is correct and was verified live on 2026-09-13 — on one host. A per-deploy decision silently exempts every deploy target that did not receive it, and nothing in this repo could have reported that, because the gate's own test asserts the built output and the verifier is pointed at a URL somebody supplies. The second is the happier half: /build-state.json answering Not found is what made the drift legible at a glance, and that endpoint exists because a check refused a non-builtin import in site/serve.mjs. A design forced by a container constraint turned out to be the thing that could answer which build is this.
What was done here, and what is not. scripts/push-staging.sh is deleted and the ~/Dev/forgebrand clone is gone. Deleting the Forge app, releasing the domain and removing the aleris/forgebrand repository is console work and is card 158, Torfinn's — the domain first, because that is what stops the exposure. The mirror held nothing original: it was byte-identical to its own origin with no extra branches and no untracked files, and its only deliberate differences were its console-generated FORGE.md and a do-not-commit banner.
A side effect worth naming. The clone was one of three working copies of this repository on the machine, and the TAKS collaboration harness had already recorded (finding S28) that they reset every file's modification date into its 7-day window, so five documents were being reported as thirteen findings. Removing this clone removes a third of that duplication. brand-os-provenance-ikon is still a second working copy of the same remote.
Corrected in place rather than rewritten: _state.md's milestone line still named the domain move as next and listed the redeploy as outstanding, both of which had landed on 2026-09-11; the cutover plan's status table still described the staging host as provisioned, not deployed, true when written on 2026-09-10 and false a few hours later. The 2026-09-10 measurements are left standing as measurements, with the corrections dated beside them.
2026-09-11 (fourteenth entry) – The site is hidden until release, and the way it is hidden is the decision
Torfinn, once the site was live: "noindex the site until we are ready for release". One flag, INDEXABLE in site/release-state.mjs, driving four surfaces. Board card 155 exists to be closed at release rather than worked.
The substantive choice is what was NOT done. The obvious way to hide a site is robots.txt with Disallow: /, and it is the most common way a site stays in an index while its owner believes it is hidden. Disallow asks a crawler not to fetch the page; a crawler that never fetches the page never reads the noindex on it. A URL discovered anywhere else — a link, someone else's sitemap, a mail scanner — stays listed as a bare URL with no description, and cannot be cleared until crawling is allowed again. So the site now does the opposite of hiding: it invites crawling and answers every request with noindex. Allowing the crawl is what makes the refusal readable, and the test asserts the absence of a Disallow line with that reasoning beside it, because someone will eventually try to "fix" it.
Four surfaces, because a meta tag only exists in HTML. This corpus deliberately serves raw Markdown at /baseline/raw/* and /foundation/raw/* and a machine index at /llms.txt — machine readers are a first-class audience here, not an afterthought — and none of them has a <head>. An X-Robots-Tag header from the static server carries the same instruction where a tag cannot go.
The design was corrected by a check, not by thinking. The server first read the flag with an ordinary import, and lib/static-server.test.ts failed on the spot: site/serve.mjs imports nothing but node builtins, because the runtime image holds only site/dist and that one file, so a non-builtin import breaks the container at startup, in production. The decision now travels as data — the build stamps build-state.json, the server reads it. That turned out better than the import for a reason nobody had in mind: the server reports what was built rather than what the source currently says, and those two differ the moment someone edits the flag without rebuilding.
It fails closed, and the asymmetry is the whole argument. A missing, unreadable or malformed stamp means not indexable — proved by deleting the file and watching the header persist. Hiding a released site is visible the day someone searches for it; leaking an unreleased one into an index is not visible at all, and is the expensive half to undo.
One near-miss worth recording, because it is the same defect this project keeps meeting. The stamp was first written as _index-state.json, and Astro excludes _-prefixed files in src/pages from routing — so it emitted nothing, the server fell back to its default, and the output was correct for the wrong reason with no symptom. It was caught by looking for the file rather than by any check, and the trap is now recorded in that endpoint's own header.
The whole thing is reversible in one edit, and that was verified rather than asserted: flipped to true, rebuilt, confirmed the meta tag gone from all 54 pages, the sitemap line back in robots, the header absent and the stamp flipped — then flipped back. The suite is green in both states, and asserts the state the flag implies rather than hard-coding the pre-release one, so it does not go quiet after release. A test that only knew today's answer would pass forever while checking nothing, which is the failure the thirteenth entry found four instances of.
Not live until this branch merges and redeploys. The deployed site is still the build from before the icon, sitemap and robots fixes.
2026-09-11 (thirteenth entry) – The cutover finished, and deleting the Next app found four things that had been quietly wrong
The site moved and is verified. brand.dev.aleris.ai serves the Astro build from a Forge container: 26/26 routes byte-identical to the local build, 35 checks, 0 failures, /health reporting pages: and index: 8751 bytes rather than the placeholder's bare ok. Both rulings held — /sv/… 404s, the picker paths 404. Card 113 closes after 87 days, and closes by removal rather than by answer: why the deploy stopped was never diagnosed, and now cannot be, since the application that held it is stopped and its successor is a different platform.
One thing worth keeping from how it got unblocked. Card 148 had held the domain move because the Qlik picker answered 200 and "answering 200 is not evidence that nobody uses it" — an objection nothing in this repo could resolve. It was resolved in one sentence by asking the people who would be using it. Three sessions of this project have treated the endpoint responds as the best available evidence about usage, and it never was.
Then step 5 deleted the Next app, and that is where the session earned its keep. app/, components/, hooks/ and seven root files, plus next, next-intl, react, react-dom, @types/react, jszip and both @supabase/* packages. Brand OS is Supabase-free — card 149's own done-when, reached. Suite 447 → 439 in 23 files, and the eight absent tests were checked rather than assumed: they are the picker's render tests, running in ikon.
Four defects surfaced, and every one of them was found by a check failing rather than by reading the diff.
1 · The site was serving the wrong favicon, and had been since the Astro port. The five 2026 icon assets lived at Next's magic paths. Base.astro linked only /favicon.ico, and the file it pointed at — public/favicon.ico — was the old provisional single-entry 31×32 ICO, while the correct 16/32/48 one sat unreferenced under app/. The site that publishes the Aleris brand's digital identity was serving precisely the defect that work existed to avoid. icon.svg, apple-icon.png and the manifest were not linked at all. The reason it stayed green is the interesting part: every assertion in lib/site-icons.test.ts checked that a file existed with the right properties, and none checked that the served site referenced it. A file that exists and a file that is served are different claims, and only the second reaches a reader. The test now asserts both.
2 · A check's reach went to zero without saying so. The token conformance scan walked app/ for application code, and walk() returns empty on a missing directory silently. Repointed to site/src, where it immediately found two hand-picked dark-mode values nothing had ever scanned.
3 · vitest.config.ts kept an app/** include — a glob that used to match and would have gone quiet. Replaced with site/**, which currently matches nothing, and the difference between those two states is written down beside it.
4 · BASELINE.md made five claims measured against app/. Corrected in place. The package mirror keeps the old citation until the next cut, and that lag is now declared — the first entry DECLARED_LAG has ever held. Declaring it found that the mechanism had never worked: the sibling check hard-asserted both counts to zero while its own message said a matching non-zero pair was fine, so the route the list exists to offer failed the moment anyone took it. Written, documented with four anti-rot halves, and unusable from the day it landed.
Two more, found at the very end and both live on the deployed site. /sitemap.xml returned 404 — the Astro port never carried the route, so app/sitemap.ts sat in the repo emitting a list nobody received, and when it was finally read it was emitting /sv/ URLs the cutover had just retired. And public/robots.txt pointed crawlers at https://aleris-brand-os.vercel.app/sitemap.xml — a Vercel host this project left, for a sitemap that 404s. Both are now endpoints that read Astro.site, so a staging deploy advertises staging; the sitemap is built from lib/route-manifest.json rather than a second walk of the corpus, because a sitemap generated from a different enumeration than the one the gate blesses is a third opinion about what the site contains. 87 URLs, every one verified to resolve in the build, and lib/sitemap-and-robots.test.ts now asserts that — plus that no /sv/ survives and that robots never names a host we have left.
Two cards for Torfinn, and they are the same shape. 154: Baseline has no status tint scale and no dark-mode surface scale; the Next app invented three values for the first and the Astro site invented two for the second. Both were recorded as known exceptions rather than as gaps in the system, and deleting the app made that visible — the exception expired with the file while the gap did not. The dark-mode half carries more weight, because card 1293 established that respecting the setting is a legal obligation rather than a preference, so the system requires a mode it supplies no tokens for. 153: the plain picker went dark with the Qlik one, the site still serves 3,776 icon files no built page references, and the 23 sub-14px declarations that had a standing check here now have neither a check nor a home.
Also 152 gained its missing context: #003942 was already evaluated and ruled out once, as a secondary hover under card 16. So the question is not whether to add a step but whether a value rejected for one role earns a place as a surface.
The pattern across all four defects, stated because it is the fourth time this project has met it: a check that cannot fail, a glob that matches nothing, a walk over a missing directory, and an assertion contradicting its own message are the same defect wearing four costumes. Each reported success while measuring nothing. None was visible by reading — every one surfaced when something was deleted and the check had to say what it actually covered.
2026-09-10 (twelfth entry) – The extracted picker's provenance record got a reader, and the cutover's last dependency was lifted by asking
Two things, and the second one is one sentence from Torfinn.
The record had no reader. Card 149 said the picker "becomes the fourth consumer of Brand OS and vendors both, through _packages/adapters/vendored-app/, which exists for exactly this: a provenance record with a sha256, a freshness check against the source's current version, and an expiry check". The record shipped and none of the three checks did — ikon held an ad-hoc brand/provenance.json, no checker, no test, no script, and Brand OS had no instance file, so the sender did not know it had a fourth consumer. ikon/CLAUDE.md stated the invariant in as many words with nothing asserting it. Two repos holding one allowlist and nothing comparing them, which is the failure the card cited the adapter as the answer to. Nothing had drifted yet: both files were still byte-identical to canonical, measured.
Adapter v0.3, cut because the third consumer did not fit. ikon is the first that vendors from the corpus rather than a cut package — the allowlist is a data-product no cut carries, the icon set is 3,772 SVGs read from disk to rasterise. So the record gained a second source root (corpus, with from_root per entry) and vendored_tree[], a directory verified as a count plus one digest over every file's hash keyed by its path under the tree root, sorted. The count is separate from the digest because four files were added and one of 3,776 changed are different findings.
The more useful half was two silent passes that had been in the wiring since v0.1. An unresolvable from was a silent continue — the corpus moves a file, the record keeps the old path, the comparison stops, the suite stays green; the same defect class as a line-number citation, sitting inside the apparatus built to catch it. And the entrypoint guard compared import.meta.url to a raw `file://${process.argv[1]}`, which never matches under a symlinked path, so main() did not run at all: no output, exit 0, the whole check reporting success. Both are pinned as regressions by running the pre-fix version out of main and asserting it passes. A third defect found by installing rather than reading: hex_scan.include: ["app"], the obvious guess, crashed with a raw TypeError from node:path.
And the check found something on its first run in ikon: #003942, a header colour sitting exactly where a petrol-600 would be — a step the ramp decided against on 2026-08-05. It was already annotated "promote candidate → Brand OS petrol scale" in that repo's CSS and had never reached the corpus, so this is a reporting gap rather than a discovery, and the scan is now the route. Card 152, Torfinn's, because substituting either neighbour changes what the header looks like.
The second thing: the cutover's last dependency was lifted by asking someone. Card 148 had held the domain move because the Qlik picker answered 200 on brand.dev.aleris.ai and "answering 200 is not evidence that nobody uses it" — an objection nothing in this repo could resolve. Torfinn resolved it the same day: "the tool might be live, but I got confirmation today that nobody is actively using it atm. It's thought to be in dev", and "we can kill it and then work on setting up the new instance and invite users to that when we're done". So step 4 no longer waits on step 3. The picker goes dark between the cut and ikon serving — an accepted gap, because there is no one to interrupt — and the brand site's cutover is now one action in the console, which also closes card 113 and its 86 days of a stale live site. ikon keeps its own blocker: ASK-IKON-001, the schema with no documented route to a post-onboarding database.
Worth keeping from this: the measurement that could not be made here was answerable in one conversation outside here. Three sessions of this project have now treated the endpoint responds as the best available evidence about usage, and it never was.
Verified: 440 → 447 tests green in 24 files here; ikon 7 → 9 tests in 2 files plus a clean hex scan, both wired into its npm test. Five failure modes proved and restored before the green run was believed.
2026-09-10 (eleventh entry) – The icon picker is its own app, and its RLS is real for the first time
code.aleris.ai/aleris/ikon at 56169d6. Card 149, step 3 of the cutover. Torfinn created the repo; the extraction, the port and the verification are done.
The move was the easy half. 22 files, ~2,105 lines, nothing importing the corpus — the measurement on the card held. The port off Supabase was the real work, and it was small because the data layer was: one table and four operations, now lib/db.ts over raw pg with forge's claim bridge, followed from FORGE.md rather than adapted. Auth reuses forge's own lib/oidc.mjs — only the wrapper is rewritten, because the scaffold wrapped it for a plain node:http server and this app is Next. A hand-rolled PKCE flow differing subtly from the one the broker expects fails in a way nobody can debug.
The substantive change is the RLS. The Supabase original had four policies, every one using (true), under a comment that admitted it: "internal tool behind a known URL with one writer. Anon read+write+update+delete acceptable for v1." That is RLS switched on and then opted out of, and it is precisely what forced RLS exists to stop. The new schema gates on the request claim, ENABLE + FORCE, single-tenant on controller — these rows are org-wide configuration, so an owner-per-row model would have hidden colleagues' work from each other for no reason.
A dependency the card's measurement missed, found on the first build: the CSS module references 71 Brand OS token variables, so the app is a token consumer as well as an allowlist consumer. Both are vendored with a sha256 in brand/provenance.json, along with the 3,772 SVGs and the two Museo Sans faces. The card had measured the two filesystem reads and not the stylesheet — worth noting, because the measurement was otherwise exact and this is the shape of what a static read of imports does not see.
Verified by building the image and running it with no database, which is the state that matters: /health returns 503 naming what is missing while reporting icons: 3772; /qlik renders the catalogue and degrades to no assignments exactly as before; /login returns 503 naming the five variables it needs rather than a 500. Runs as node, 456 MB, no node_modules in the runtime stage, and the app's seven render tests pass.
One bug found by running rather than reading. The fonts were mode 0600 — copied from a working copy where the licensed files are restricted — so the container's node user got a 500 on them while an SVG from the same tree served fine. Local-only, since git records 100644 and a Forge clone gets 0644, and fixed anyway so both paths agree.
Three console actions remain and all are Torfinn's: the schema through the broker, the OIDC credentials and SESSION_SECRET, the domain and deploy. aleris/brand keeps the picker source until step 5 — the plan deletes the Next app last so there is something to compare against, and the picker is still serving users on the old host until the domain moves.
2026-09-10 (tenth entry) – Staging is live and verified: 26/26 routes byte-identical
Torfinn deployed; the container has now run somewhere that is not a laptop. forgebrand.dev.aleris.ai answers /health with pages: /app/site/dist and index: 8751 bytes — the same numbers the local image reported, and the line that tells our server apart from Forge's placeholder, which returns a bare ok with the same 200.
scripts/verify-deploy.mjs is the check, and it is kept for the cutover rather than being a one-off, because the question it asks is the one card 113 needed asking for 86 days: is this host serving the corpus in this working copy, or merely returning 200?
It compares bytes, not status codes. A deploy that built the wrong commit, or a container that was never replaced, answers 200 on every route with entirely wrong content — which is exactly how the stale deploy hid. So every published page is fetched and compared against the local site/dist, with canonical tags and host names normalised out so that a staging run is readable rather than solid red on a difference nobody cares about.
Result: 26/26 routes byte-identical, 35 checks passed, 0 failures. Also checked: the raw-markdown and llms.txt content types against what the Next routes published, immutable hashed assets, revalidating HTML, a Museo Sans woff2, an icon SVG, and an unknown route returning 404 — that last one because if a catch-all returned 200, every other check in the file would be measuring nothing.
Proved in both directions. Pointed at the stale brand.dev.aleris.ai it fails on all 26 routes plus the health probe — the failure it exists to catch, and the most concrete form of the /sv/ finding yet: every current URL 404s on the live host today. And planting a 21-byte change in one local page surfaced as a content difference on that route alone, so the comparison arm does work rather than agreeing with whatever it finds.
Steps 1 and 2 of the cutover are done. Next is step 3 — the icon-picker extraction, card 149, which gates the domain move because the picker is live on the old host.
2026-09-10 (ninth entry) – Forge ties a domain to a repository name, so staging got a mirror
Torfinn: "i cannot actually point the domain at a repo with the tools I've got — it's a 1:1 match between repo name and domain name." That is the constraint the cutover plan had missed, and it had missed it by asserting a capability nobody checked: step 1 read "point the Forge app at aleris/brand and press Deploy", and there is no such field.
Worth naming how that happened. The step before it was written as a one-click action attributed to Torfinn, and then time was spent hunting for a deploy API to avoid handing him the click — when the useful thing would have been to say plainly which single action was outside Claude's reach and ask. He interrupted that, correctly.
aleris/forgebrand is now a mirror of this repository. It was a pure Forge scaffold — Initial commit, Mohan's FORGE.md, and the generic auth.mjs/server.mjs/db/ starter — so a force-push met the condition gcc's FORGE-PUSH-RUNBOOK sets: confirm there is nothing real to lose first.
Two files differ on purpose. Its own FORGE.md, because Forge generates one per app naming that app's schema, OIDC client and domain, and the runbook's rule is that the console-generated brief is the app's identity card rather than ours to overwrite. And a README.md banner saying not to commit there — a commit made in a mirror is destroyed at the next refresh with no conflict to notice it by, so the warning has to live where someone opening that repository reads it first.
scripts/push-staging.sh refreshes it in one command, and getting its guard right took two corrections.
The first guard could not fail. It compared the tree after git reset --hard brand/main, which is tautologically the preserved files — the reset had already erased whatever the check existed to find. Caught by planting a rogue commit and watching it pass. The eighth instance of card 143's defect in one day, and the second in a check written the same hour. Moved before the reset, where it compares origin/main against brand/main and names every file the refresh would destroy; proved by planting a commit, pushing it, and watching the script exit 1.
And the refusal message executed a command while printing itself. The line carried `git reset --hard` inside double quotes, which bash runs. Harmless here only because the reset happened to be a no-op — and not a thing to leave in a script whose entire purpose is not destroying someone's work. Found by running it for real rather than reading it.
Where that leaves step 1: the mirror is pushed and current at brand/main@b3fb98c. All that remains is pressing Deploy on the forgebrand app.
2026-09-10 (eighth entry) – Both cutover questions ruled, and the sequence written down
Torfinn ruled the two questions card 148 raised, and created the staging domain the card recommended.
The icon-picker becomes its own Forge app. "I can set up the domain and we'll move it there." Card 149. It gates the domain cut, because the picker answers 200 on the live host today.
The /sv/ URLs die. "It was never operational with that feature." No redirect map. That reason is the evidence that was missing when the question was raised — the objection was that published links would break, and the answer is that there are none to break. Recorded rather than left implicit, because whoever meets the first 404 will ask again.
forgebrand.dev.aleris.ai is provisioned but not deployed. Measured: it serves a Forge placeholder — <title>hello</title>, /health a bare ok, /llms.txt as text/html. So the container has still never run outside a laptop, and step 1 is pressing Deploy there. The tell that it worked is /health returning ok followed by pages: and index: N bytes — the placeholder answers 200 with a bare ok, which is exactly the shape of green light that hides a deploy that never ran.
The extraction is measured rather than estimated. 22 files, 2,105 lines, plus lib/supabase-server.ts. External dependencies only; nothing imports the corpus. Two disk reads are the whole coupling: the 3,772 SVGs the renderer needs on disk, and data-products/iconography/allowlist.json — a corpus data-product, and the file card 58's review will change. So the picker becomes the fourth consumer and vendors both through _packages/adapters/vendored-app/, which already does provenance, sha256, freshness and expiry. Fetching the allowlist at runtime was considered and rejected: it trades a checkable copy for a cross-app dependency that fails silently mid-deploy.
The consequence worth having: with the picker gone, both @supabase/* packages and lib/supabase-server.ts leave. Brand OS becomes entirely Supabase-free — seven files on 2026-09-09, four after the annotation retirement, zero after this.
The whole sequence is now one document, workspace/plans/forge-cutover-sequence-2026-09-10.md: deploy to staging, verify it against the route manifest, extract the picker, move the domain, delete the Next app. Steps 2 and 3 run in parallel; step 4 waits on both — on 2 because nothing takes the live domain unverified, on 3 because the cut kills a live tool otherwise. Step 5 is last and deliberately so: until then the Next app is still there to compare against.
2026-09-10 (seventh entry) – The first Forge deploy was refused, and the refusal was doing us a favour
Torfinn pressed Deploy and Forge came back before building anything: coolify POST /applications/private-deploy-key -> 409: Domain conflicts detected. Use force_domain_override=true to proceed.
Not a build problem. The old Coolify application still owns brand.dev.aleris.ai, so Forge asked Coolify to register a second application on a domain that was taken. Verified by fetching the live host: _next/static/chunks/…, redirecting to /sv/about-the-brand/brand-in-brief — the Next app, frozen at the 2026-06-16 build card 113 has tracked for 86 days.
The error offers an override, and card 148 is the argument for not taking it yet. Two things are live on that domain and neither survives a static site replacing it.
The Qlik icon-picker answers 200 today. /tools/icon-picker/qlik, measured. It is the backend island card 145 deliberately left out of scope, and the last thing in this repo using lib/supabase-server.ts. A domain override switches it off silently, as a side effect of a deploy nobody framed that way.
And every content URL changes shape. /sv/about-the-brand/brand-in-brief returns 200 on the live host; /about-the-brand/brand-in-brief returns 404. The Astro build emits no /sv/ tree, so it is the other way round after the cut. Three shapes are in play — the live build's /sv/…, the repo's current Next app at /en/…, and Astro's prefix-free — and no two agree.
This is not the question Phase 0 answered. "Every URL unchanged" was a property of the corpus moves. Next → Astro is a different cut and nobody checked it. The locale prefix disappearing is the language exit finally reaching the renderer, which is correct — but it is a redirect job, not a no-op, and the awkward half is that the live /sv/ pages are a pre-Phase-0 content set, so some old paths have no current equivalent and need a destination chosen rather than computed.
Recommended route, on the card: deploy to a temporary domain first. The container has never run anywhere but a laptop. Verifying it in the real environment costs one console step and turns two discoveries into two decisions.
Nothing was built, nothing broke, and nothing in this repo changed except the record.
2026-09-10 (sixth entry) – The Font Awesome token is still dead, and the build no longer cares
Card 147, pulled on Torfinn's instruction and closed by removing the dependency rather than fixing the credential.
The first question turned out to be the wrong one. The card asked how to get a working token. The better question was whether the build needs one, and it does not. @awesome.me/kit-6f31575c9d was a devDependency that nothing in the repo imports — no .ts, .tsx, .astro, .mjs or .js file, and astro.config.mjs does not reference it. Its only consumer is scripts/build-icon-metadata.py, run by hand, and all 3,776 icons it extracts are committed under public/icon-picker/, shipping through Astro's publicDir. It is also the only private-scoped package in package.json.
So it moved to optionalDependencies, which is semantically what it already was: absent, the build proceeds; present, the regeneration script has a pinned version. And the Dockerfile now writes no .npmrc at all when the token ARG is empty — an empty _authToken is worse than none, because it authenticates as nobody and earns a 401 where an anonymous request earns a skippable 404.
Proved by building and running the full two-stage image with no token set. It served /health, the homepage, two content routes, llms.txt, a raw markdown file, a Museo Sans woff2 and an icon SVG; 3,772 icons present; no node_modules and no .npmrc in the image; running as node; 368 MB.
That corrects a claim made an hour earlier. The fifth entry above and card 145 both said the full image could not be built here. True of the Dockerfile as written, false about what the build requires — and the difference is exactly the kind of thing that gets inherited from a previous file and never re-examined. Both are struck rather than edited away.
lib/build-has-no-private-registry.test.ts, 5 tests, mutation-verified four ways. Moving the kit back to devDependencies fires two; deleting it entirely fires the one that guards against that fix, because solving this by removing the dependency would leave the regeneration script with no pinned version; restoring the unconditional .npmrc fires the Dockerfile assertion; a planted import fires the source scan. It also asserts the fact everything else rests on — the icons are committed, thousands of them, not a stub. A seventh instance of card 143's defect, in the check itself: its own PRIVATE_SCOPES literals made it report itself as an offender, which is how the test-file exclusion was found rather than reasoned to.
What is not fixed. The token is still dead: npm view @awesome.me/kit-6f31575c9d version returns E401. Nothing needs it today. Card 58's icon-allowlist review does — the icon set cannot be regenerated until it is re-issued — so that became card 58's declared prerequisite rather than an open card of its own. A dependency that blocks one specific piece of work is better recorded on that work than in a queue.
Suite 435 → 440 green in 23 files.
2026-09-10 (fifth entry) – The Forge container is built and verified, and the health probe can actually report unhealthy
Card 145, pulled and landed. Two of its three parts are done; the third is a button in a console this repo cannot reach.
The Dockerfile is two stages. The builder installs dependencies and runs npm run site:build. The runtime carries site/dist and site/serve.mjs and nothing else — no node_modules, no .npmrc, no token, no package.json. That is the security half of the Astro ruling rather than a side effect of it: a secret cannot leak from a layer that is not in the final image. It runs as the node user.
site/serve.mjs has no dependencies, on purpose. 54 pages and their assets need no runtime beyond a file server, and adding a package would re-import the supply chain the static cut just removed. It binds 0.0.0.0 on $PORT, resolves Astro's build.format: 'file' URLs, preserves the raw-markdown and llms.txt content types lifted verbatim from the Next routes it replaces — consumers fetch those URLs, so the contract is theirs — and revalidates HTML while freezing content-hashed assets, which is aimed squarely at card 113.
The health probe's subject is the built tree, not the process. FORGE-MIGRATION.md asks for "a real /health (not a catch-all 200)", and for a static site the honest question is not is node running but is there anything to serve: an image from a failed build starts perfectly and 404s everything. So it stats site/dist/index.html and answers 503 when that is missing, and the 503 is proved by running the real binary from a directory with no build beside it.
Verified in a real container rather than read off the file. The runtime stage was built and run; it served /health, /, /identity/colour, /documentation/site-map, /llms.txt and /foundation/raw/colour.md with the right statuses and content types, as user node, with no node_modules present, exiting 0 on SIGTERM. The full image could not be built here — stage 1 needs the Font Awesome token. Corrected within the hour by card 147, below: the build needs no token, and the full image was built and run. The claim was true of the Dockerfile as written and false about what the build actually requires.
Eleven tests, mutation-verified six ways — and two of the six were findings rather than confirmations.
The traversal test was a check that could not fail. Deleting the ROOT guard from safeJoin broke nothing, because new URL().pathname collapses .. before the server's own code runs. So that guard is a backstop unreachable through HTTP, and a test claiming to exercise it was asserting a mechanism it never reached. Rewritten to send raw request lines over a socket — a browser normalises, an attacker does not — and to assert the property rather than the mechanism: no bytes from outside the tree come back, whatever the path says. The unreachability is recorded in the test instead of being papered over, per the standing rule about invariants that cannot be asserted.
And the suite would have silently skipped eight of its eleven tests on any fresh clone. site/dist/ is gitignored, so describe.runIf(built) would have reported the file green having tested nothing — on CI, in a new worktree, in the Forge builder. Replaced with a build-if-missing, and proved by deleting site/dist and watching the suite stay green while rebuilding it. That is the fifth and sixth instance of card 143's defect, and both were in this session's own work — which is the argument for the entry being a cookbook line rather than a note.
FORGE-MIGRATION.md corrected in place rather than regenerated. The original wording is struck through and every correction is marked, because a generated document that has been silently edited is worse than one that is wrong: the next regenerate would restore the errors and nobody would know they had been found. Steps 2, 3 and 4 are struck as N/A with the reason, step 1 is checked off, and the Done when list is rewritten for a static site.
One thing found while trying to build the full image, and it stands in front of that Deploy — card 147. Torfinn said the Font Awesome token has been globally available since earlier this week, which matches the record of it being exported on 2026-09-08. Measured here today it returns code E401 — not 404, not a missing scope: the registry is reached and the credential is rejected. FA_PACKAGE_TOKEN is unset in this shell and neither ~/.zshrc nor ~/.zprofile mentions it. Stage 1 of the Dockerfile runs npm install before Astro ever builds, so if the Forge console's build variable holds the same value, the deploy fails at the first step with an error that reads like a platform fault. Named rather than worked around, per the rule about a stated premise conflicting with what can be observed.
What remains, and it is one action. Press Deploy in the Forge console. That closes card 145 and card 113 with it — 86 days of brand.dev.aleris.ai serving a 2026-06-16 build. The second remaining item is for whoever owns the scanner: teach it to read supabase/, or the next regenerate restores both errors and the next app onboarded is mis-scanned the same way.
Suite 424 → 435 green in 22 files.
2026-09-10 (fourth entry) – The annotation feature is retired, and two checks caught what reading did not
Torfinn: "retire it, no need to export anything." Card 146, ruled and executed the same day it opened.
Fifteen files deleted. components/comments/ (4), components/auth/ (3), hooks/useAuth.ts, lib/auth.ts, lib/supabase.ts, app/auth/callback/route.ts, app/api/notify-comment/route.ts, supabase/import-annotations-after-first-login.sql. CommentWrapper unwired from both layouts, LoginButton from the sidebar, resend dropped from package.json and the lockfile along with six transitive dependencies.
What stayed, deliberately. lib/supabase-server.ts and both @supabase/* packages: the Qlik icon-picker still uses them, and that island's future is card 145's explicit non-scope. The Supabase surface went from seven files to four, and all four are the island — which is the first time the boundary the corpus has described in prose is also the boundary in the code.
The annotations table stays in supabase/migrations/ and in the database. Rewriting a past migration falsifies the record, and nothing needs exporting, so the table is orphaned rather than dropped. Said here so it is a statement rather than an oversight.
Two things were caught by checks rather than by reading, and both are the mechanism working.
The out-of-set hex list held two KNOWN entries pointing into notify-comment/route.ts. Its anti-rot half asserts an exception is still present, so deleting the file failed the suite until the entries were removed too — the convention card 143 describes, firing on the exact case it was written for.
And hooks/useAuth.ts survived the sweep. The grep that cleared the tree searched app lib components site/src, and that file is in none of them, so the tree read as clean while a broken import sat in it. The Next build found it; the grep did not. That is the second instance in one session of a clean result being a fact about the query rather than about the world, after the branch-hygiene check passing on a clone whose remote had changed. Both belong in card 143's cookbook entry, which now has four instances rather than three.
Verified: 424 tests green in 21 files (426 → 424 with the two dead exceptions gone), npm run build clean, npm run site:build clean at 54 pages.
Card 145 is unblocked — it waited on this ruling only because route 3, rebuilding annotations against Forge, would have made it something other than a static file server.
2026-09-10 (third entry) – Astro wins: Phase C decided, and three of Forge's five migration steps struck
Torfinn: "Astro wins — scope 145 to that and drop the Next work." This settles Phase C, which documentation/site-map.md had carried as "re-platform off the Next.js app; Astro a candidate" since v3.
Measured before rescoping, against site/dist rather than assumed. The Astro build already emits all 26 content routes (54 HTML files), llms.txt, foundation/raw/*, baseline/raw/* and the 3,776 icon assets — the whole machine-readable surface the README advertises. site/src references Supabase, Resend and notify-comment nowhere. So the static site is not a partial replacement of the reader-facing app; it is the whole of it.
What that does to FORGE-MIGRATION.md: three of five steps go. No schema through the broker, no pg + withClaims seam, no Authentik OIDC, no RPC or table-read ports, no re-keying authz — because nothing static reads a row. Card 145 shrinks to step 1 plus a container: a Dockerfile that serves site/dist on $PORT with a real /health. It is a rewrite rather than an adjustment — the current file's own header lists four reasons it runs a full Node server, and three of them leave with the Next app.
The two errors in the generated scan are kept on the card even though the work they describe is struck. It reports zero Supabase call sites where there are seven files, and points at a schema directory holding one JSON seed while the four real migrations sit in supabase/. Both blind spots point at supabase/, and the next app Forge onboards inherits them.
Card 146 opened, for the consequence the ruling did not name. Astro wins was a decision about the renderer. It is also, silently, a decision to switch off in-page commenting. Every content page in the Next app is wrapped in CommentWrapper; a reader selects text, the panel writes to a Supabase annotations table over a realtime channel, /api/notify-comment emails Torfinn and Sanna, and there is a login flow and an import-annotations-after-first-login.sql behind it. The Astro site has none of it. At the cut, commenting stops and existing annotations become unreachable rather than deleted.
How load-bearing it is, left open rather than answered. It is wired into every page, which is the shape of a core feature; but "Resend sender-domain verification" has sat unfinished for weeks, so the notification half may never have worked in production. Neither fact is a measurement of use. The number that would settle it is the row count in the annotations table, and that is a query against a database this session did not read — stated rather than guessed at.
The order matters and is on the card. Route 3, rebuilding annotations against Forge, puts the whole migration checklist back on the table and card 145 is then not a static file server. Routes 1 and 2 leave 145 as scoped.
One correction inside this session. A first pass read lib/auth and /api/notify-comment as orphaned, on a grep that missed the @/ alias and the client-side fetch. They are neither — both are reached from components/. The finding that survives is the opposite of the first one: the feature is more wired in than it looked, which is why it earned its own card instead of a line in 145.
2026-09-10 (second entry) – The repo moved to Forge, and the day's rulings had to be merged across after the fact
What happened, in the order it happened. A session was part-way through recording ten rulings when Torfinn initiated the move to Forge. Forge forked aleris/brand on code.aleris.ai from GitHub at 2a1110d — PR #41, the v0.12 cut — and Mohan added FORGE.md and FORGE-MIGRATION.md on top. PR #42 landed on GitHub afterwards. So for a few hours there were two trunks.
The divergence and why it cost nothing. Two commits each way from a shared merge base, and zero file overlap — Forge's two commits create two new files at the root, the ruling pass touches 22 files, none named FORGE*. It merged clean at a91c7fd. The histories share real SHAs, which is the good case: a re-import would have rewritten every hash and left the two repos permanently uncomparable, discoverable only by noticing that git log on Forge recognised nothing.
Verified on the merged trunk before it was pushed: 426 tests green in 21 files, npm run build clean. Note the count went up by one against the GitHub checkout's 425-green-1-failed — the failing assertion was branch-hygiene flagging backup/auto-20260907, and that branch has since been deleted from GitHub and never existed on Forge. The pass is not evidence the finding was resolved by this work; the check reads its own remote, and its own remote changed. Same shape as the guard-with-a-hole finding in card 143.
The trunk is now Forge. origin on the local checkout points at code.aleris.ai/aleris/brand; GitHub is kept as github. README, CLAUDE.md § Where this repo lives, and _state.md all say so, because the one thing that makes this recur is a checkout quietly pointed at the wrong host.
Card 145 opened, and two findings in the generated scan go with it. FORGE-MIGRATION.md reports Supabase: YES — 0 call site(s) and prints (none detected) under all four "Replace Supabase" sub-steps, while seven files reference Supabase, four import it directly, both packages are in package.json, and the Dockerfile's own header names the Supabase server client as the reason the app is not a static export. It also reports an own schema at db/ · migrations/ · schema.sql, where db/ holds one JSON seed and migrations/ does not exist — the four real migrations are in supabase/migrations/. Both errors point at supabase/, which the scan does not appear to read. The consequence is asymmetric and worth naming: step 3 reads as finished while being the largest job on the list.
And the question that precedes the checklist is Torfinn's. Forge scanned and conformed the Next.js app, which is the one Phase C replaces. If Astro static wins, most of the checklist evaporates and what survives is the icon-picker, already scoped as the one backend island.
Two loose ends the move left. GitHub holds six branches Forge does not — one fully merged and deletable, five backup/auto-*, of which two differ from trunk; nothing lost, but they are stranded on a host that is no longer the trunk. And the pre-push harness did not travel: a fresh clone of the Forge repo has no hooks, so the gate that refused a push on 2026-09-09 does not exist there.
2026-09-10 – Ten rulings in one pass: six cards closed, the en-dash convention retired, and the starter kit gets a plan
Torfinn ruled on ten open cards in a single message. Six closed, four moved, three opened. Recorded here in the order he gave them.
Card 42 — closed on a premise that had already stopped being true. "ok." The English colour page has a publish path: principles/colour.md is accepted, lang: en, routed at /identity/colour. Route B was superseded on 2026-08-31 and the page moved in Phase 0 revised on 2026-09-07 — two changes made for other reasons. The shape is worth keeping: a card whose premise is a wiring fact can be closed by unrelated wiring work and nothing says so. Card 86 closed the same way eleven days earlier, which makes this the second instance rather than a one-off.
Card 108 — the coined words needed definitions before they could be accepted. "the words formrubrik and etikettrubrik needs proper definitions in order to be understandable." The 2026-08-31 wording pass fixed the pattern definitions and left the coinages themselves unexplained — a reader meeting formrubrik had a description of the pattern and no way into the word. Both now carry a The word line unpacking the compound: form + rubrik, a heading about the text's form; etikett (label) + rubrik, a heading that labels rather than tells, with etikett anchored in Digg's guideline 61. The rules file gains a term field for each and goes to 0.2.2. Drafted, awaiting-pass — the acceptance is still his.
Card 62 — gated means it needs his pass. The word was written on 2026-07-29 under the old hard rule, where gated and undraftable were the same thing. Rule 2's relaxation and the 2026-09-08 replacement of CLAUDE.md § The content boundary separated them, and the calibration four paragraphs below was left carrying the old sense for two days — so one file said Claude may draft pattern examples and also said pattern examples are "still gated, unchanged." Reconciled in place, with the old sense named rather than quietly overwritten. The card's remaining work is reframed rather than unblocked: seven of its eight entries become rendered components in phase 5 of the card 99 plan, and two stay his behind cards 64 and 65.
Card 119 — the v0.12 call stands; nothing new is cut until the pending drafts land. "after all these changes we need to update the draft before cutting a new version." Read as: the drafted version call is not revised to a point release, and the next cut waits for the material this pass put in flight — the two heading definitions, the Tailwind sentence, the comment-density rule. The three re-vendors are unruled and remain his, all three consumers being on the no-go list. (This reading is an interpretation of a short instruction; if the intent was that v0.12's own call needs redrafting before it is final, that is one edit in two files and this entry is wrong.)
Card 105 — pursue gcc. The profile has been filled with citations since 2026-08-28 and nothing in that repo has been touched. Pursuing does not unblock the work: card 104 gates it, because Gröna korset's five-state safety scale decides whether an adapter's checks exempt those colours or flag them, and adapter rule 2 forbids an adapter from answering it. Card 144 opened in Blocked so that pursuing is a state the board shows rather than an intention.
Card 25 — the latest plan is accepted and the former one is filed. The language decision was never the open half; B1 was accepted 2026-08-04 and constitutional/language-policy.md has been accepted since 2026-08-06. The open half was phase-0 accepted or re-scoped, and it stayed open while eleven files migrated under an unratified plan on 2026-09-07. phase-0-revised-2026-09-06.md is now accepted; phase-0-target-structure-and-migration.md (v0.1, proposed for 103 days) is superseded with a pointer and filed to workspace/archive/phase-0-2026-09-06/. Three live references repointed, three deliberately left — the left ones are dated records and two decision briefs citing the old plan as part of arguments they made at the time. language-exit-decision-brief.md's condition 2 is satisfied and is recorded here rather than written into his brief.
Card 136 — a depends_on identifier is a path. "it has to be able to link to the document so that the website can display links between documents, so path." The reader's requirement settles it: the site renders a dependency as a link, so the identifier must resolve. Recorded in documentation/site-map.md § Dependency model with the cost in the same breath — a path goes stale whenever a file moves, so DEPENDENCY_ALIASES becomes a migration device rather than a permanent layer, and a move now means a sweep. Scoped by measuring, not estimating: 67 files carry a stale identifier — 17 live corpus, 16 package mirror, 3 _markets/sv/ archive, 31 workspace/, 0 in en-draft/. The split is the interesting half: the mirror is a generated view and changes at the next cut, the archives are archives and stay stale on purpose, and most of the workspace hits are dated records rather than live citations. So the sweep is 17 files and a declaration covering the other 50. Card 142 executes it; card 137 loses half its blocker.
Card 93 — the en-dash convention is retired, not narrowed. "allow em dashes (let's stop spending tokens on searching for and swapping out dashes)." The 2026-07-28 rule is withdrawn in full: no sweep, no F5 home, and nothing normalised in either direction — the corpus keeps the dashes it has. The 2026-08-27 partial answer, which narrowed it to prose being written now, is superseded; using – remains his preference and is nobody's rule. The measurement earned the retirement: 9,079 em against 1,405 en, filters/ with not one en dash in 62 opportunities, and an accepted Foundation page gaining fourteen more on 2026-08-27 from a session that held the convention in its own context. One thing deliberately not retired, and it is a different rule: filters/ai-tells-and-filters.md lists em-dash as default punctuation as a Layer 1 AI-tell, measured by frequency per paragraph. That is on an accepted published page and nothing here touches it.
Card 95 — archive the Swedish originals and update their claim. All seven files in _markets/sv/foundation/ now carry status: superseded with a superseded_by: pointer. The seventh was not on the card: iconography.md was draft, so it never entered ORPHANED_ACCEPTED and the card never counted it — and draft is as wrong for an archived original as accepted is. The prose of all seven is untouched; a status field is the archive's label, not its content. Verified both ways — the suite passes at 12 tests, and reverting one file to accepted fires the orphan assertion naming that file.
Card 99 — an implementation plan, awaiting three rulings. workspace/plans/card-99-starter-kit-implementation-2026-09-10.md, awaiting-pass. Five phases: phase 0 (record the 2026-08-24 revision, name the fourth delivery surface, ship the Tailwind bridge) is worth doing even if the kit is ruled against, which is why it is first. Then the skeleton with one component to test the substrate, the lift of brand.css, the four honesty mechanisms, hosting, and the tail. The plan assumes the card's own answers on substrate, scope and hosting and says what changes if each is reversed. The render-testing half has to come from aleris-assistant — Sund vikt has the content and the structural enforcement, assistant has the browser testing and the deploy wiring, neither has both, and the kit needs both.
Card 124 — Sund vikt's four answers reviewed, every figure recomputed. workspace/evaluations/card-124-sundvikt-answers-reviewed-2026-09-10.md. All five measurements hold: 363 declarations exact, 67% comment density exact, no dynamic property reads, the dead Tailwind dependency confirmed, and 198 unreferenced tokens against the brief's 199 — off by one on reach, immaterial. Two things the brief does not have. §1's subsetting rule needs a fourth clause: subset against the union of every consumer of the vendored copy, not the app, or the starter kit — which references every token by design — breaks the first time it ships. And §4's flip condition has one real condition and three tasks wearing the same clothes; splitting them stops three of the four being able to postpone the decision. Also noted for card 119: the consumer is still on v0.11, so a subsetter written now would be written against a stale token set.
Three cards opened: 142 (the sweep), 143 (card 124's three Claude halves, on one card because two of them are single sentences and that is the size at which a card gets skipped), 144 (the gcc adapter, blocked on 104).
One environment note. The TAKS vault was not mounted, so TAKS/1-projects/brandguide/design-system-layering-analysis-260824.md § 7 — which the card 99 plan's phase 0a has to edit — is unverified, and metadata.md could not be read for the superseded_by convention. The in-repo convention was followed instead.
2026-09-09 (fifth entry) – agent-baseline v0.12: the first re-vendor since v0.6.3, and the card's own blocker was mis-diagnosed
Card 119, cut and on main; the consumer half deliberately not done. Sixteen of thirty mirrored files changed, fourteen byte-identical to v0.11, every destination re-read after copying.
A whole version, and not close. Every cut since v0.7 ended with the same sentence — no token file touched, so consumers only bump their pin. False for this one. Two tokens added, one retired, one value changed, plus substantial new Baseline content. The retirement is the strongest ground on its own: removing a declaration is the one change that can make a consumer's own stylesheet wrong rather than merely out of date, and no point release has done that. The call is drafted with its reasoning and is Torfinn's to ratify or revise.
The gap the card opened is closed. Nothing reported that the shipped mirror was behind canonical, which is how a token value sat un-shipped for five days. The new check fails when a mirrored file differs from canonical and nothing declares it — asserting that the lag is declared rather than that the two match, because a pinned snapshot is allowed to lag and the rule against hand-editing a package forbids syncing it. Run before the cut it reported sixteen undeclared files, both token files among them. Mutation-verified three ways.
The card's blocking prerequisite was mis-diagnosed, and measuring it is what found the real problem. It said re-vendoring would turn a print rule in patientguide into a dangling reference, silently changing a colour on patient-facing printed documents. It will not: that file declares the token itself and is a hand-maintained mirror rather than a byte copy, so it stays internally consistent. What actually happens is divergence — the byte copy beside it takes the new value while the mirror keeps the old one, so one patient-facing repo would hold two answers about the same token, and nothing compares them. That is a reconciliation in that repo, not a line change. Two further uses the card does not mention were found; one is a local alias declared once and used nowhere.
Not done, and not by omission. All three consumers are on the no-go list, so re-vendoring is prepared to the gate and left there: a per-consumer note carries the instructions, the grep that has to run before any copy, and the failure mode — a dangling reference inherits rather than erroring, so there is no build warning. The note also states a gap in itself: one consumer has not been surveyed for the retired token. And the count is honest — three consumers behind on v0.12, plus one that never took v0.7.
The manifest's source column was rebuilt from the generator map rather than retyped, because retyping is what let it go stale. Worth recording why nothing caught it: the pointer check reported those paths as resolving, correctly by its own dual-context rule — the manifest ships, so the package root is one of its read contexts, and the old paths exist inside the package. The rule is right, and it means a stale source column there is invisible to it.
426 tests green in 21 files.
2026-09-09 (fourth entry) – The consumer seam: four cards as one pass, and two that turned out to be blocked
Cards 129, 127, 128 and 125 — everything about the boundary between this repo and a consumer: what a consumer declares, what runs there, what runs here. Run as one branch rather than four, because each was small and every one of them ended "mutation-verified and on main", and four branches would have meant four gate cycles and four in-flight declarations against a sibling deadlock this repo has already hit.
Where each check runs is now declared and asserted, and the card's own inventory had drifted. It said eight check files in one folder and thirteen in total; there were thirteen and twenty. Five checks were added in the three days after the decision that placed them, and none changed a word in it — which is the argument for a list with an assertion behind it rather than a paragraph. The reason each check sits where it does is the valuable half and no machine can grade it, so the test enforces a proxy and says in its own failure message that passing the proxy is not evidence the reason is good.
A consumer had been recording real decisions in a format with no reader. The card named one undocumented field; six were measured, across both consumers, and the wiring read none of them. So the rule became: a field either has a documented meaning or it fails here. A consumer inventing a field is a signal that the format lacks something, and it should reach the sender rather than sitting unread in a file the sender generated.
One Brand OS rule now travels, and it defers to a consumer that already has it. Every other assertion is about a file the package ships and byte-verifies downstream; the out-of-set hex rule is about the consumer's own source, which this repo cannot see and never will. Unshipped it was unenforced everywhere but here. sundviktlakemedel already asserts it locally and its record says so — "which is what an adapter of this type should be spreading rather than reinventing" — so the shipped check reports skipped and names the covering file rather than running a second implementation beside it. Proved by driving it as a subprocess in a fixture repo with nothing installed, seven cases, including that a scan which read nothing is refused rather than reported clean.
One card was built deliberately differently from how it was written, and the departure is on the card rather than absorbed. It asked for the canonical paths each copy consumer took. That consumer had already closed the class two weeks earlier by repointing at the package path instead, with its reasoning recorded, so building to the card would have re-created the dependency it removed on purpose. What is live is the opposite risk — the package folder rename is a breaking cut and only two of four consumers had a machine-readable record of which package paths they use. The declaration now covers the rest, every row carries a verification note, and the rename's required migration note can be generated from data rather than recalled.
Two of the six cards in the cluster are blocked by their own text, and saying so is the finding. The rendered review of the two products needs a decision nobody has taken — where a review runs — and a target-size floor declared per viewport with its reason; both products are patient-facing, so neither is Claude's to pick, and the card is now owned accordingly with the two questions named. The component-gallery target waits on whether a component set exists at all, which is Torfinn's, and it had been filed as pullable since 2026-08-28 while its own body said it depends on that.
420 tests green in 20 files, from 398. Six mutations, all six fire.
2026-09-09 (third entry) – Board corrections: six mis-filed cards, two dissolved premises, and a decision that had no card
Found by a product-owner pass over the board, then spot-checked against the files rather than taken on trust. Every figure below was recomputed today.
A ruling of Torfinn's had nowhere on the board to live. He ruled hard rule 3 per section on 2026-09-08; the brief recording it sat in the drop zone and no card pointed at it. Now card 140, with the finding that matters: section has no definition anywhere in the corpus, so the ruling is undecidable at a glance and uncheckable, and card 23's adjacency invariant is the candidate definition. The order is load-bearing — ratifying the drafted wording first would put a decided-but-uncheckable rule into a Foundation page that is accepted v1.
Two cards rest on premises that have expired. Card 42 says the English colour page has no publish path; that page is accepted, English, and routed. Card 86 was blocked on constitutional/ not publishing; it has published since 2026-08-31. Both are marked for confirmation rather than closed, because closing a card is a judgement about whether anything survives it.
Four cards were filed against their own text. Card 50 sat as pullable while its own cell said explicitly paused — moved to Blocked. Card 34 sat as work in progress for 34 days while the three drafts it asks for had been finished the day it opened — it is a review waiting on him, and it is now filed as one. Card 121 was pullable while a measurement is owed first. Card 62's row described a boundary conflict that is now ambiguous rather than resolved, which is the correction worth making: CLAUDE.md permits Claude to draft pattern examples since 2026-09-08, while the untouched 2026-07-29 calibration four paragraphs below still lists them as "still gated, unchanged". Under the old rule gated meant do not draft; under the inherited rule it can only mean needs his pass. One word of his settles it.
Card 141 opened: an accepted constitutional page states that sixteen en-draft/ pages carry the English source; the directory holds two. The sentence is that page's own detection of violation, so a rule in force cannot be checked by someone who did not write it — the admission test the constitution sets for a rule existing at all.
Four stale figures named and left, per the rule about records — all four from an earlier session's measurement, correct on its date and never re-run: the title-case debt is 101 headings across 15 files rather than 106 across 16; the frozen deploy is 85 days and over 300 commits rather than 73 and 201, and the served-versus-canonical gap on the token file has widened rather than held; the unresolved-pointer debt is 87 rather than 94; the dash ratio is 10,971 em against 2,065 en, and new prose is following the rule while the corpus total still grows.
One consequence of the morning's card 109 fix, caught by running it in the main checkout. The snapshot test started failing as a timeout at 6.0s against vitest's 5s default, while its verdict was correct and printed. The cost scales with how many session-end snapshots are waiting to be cleared, and there are twelve refs. Timeout set explicitly with the measurement recorded. The check is also now reporting that one snapshot is finished with and deletable, which is Torfinn's call.
2026-09-09 (second entry) – The branch check was measuring against a ref that rots, and its symref filter never matched
Card 109. The check that says whether any work is sitting off the trunk was comparing everything to the local main. Sessions run in worktrees, so nothing updates the main checkout's copy of it — the stale state is the normal one. On 2026-08-31 that produced three branches reported as carrying unlanded work when all three were illusions, one of them merged weeks earlier. A check reporting three false findings out of three is on its way to being ignored, and an ignored assertion is the same as an absent one.
It now measures against the remote trunk, falling back to the local one when there is no remote ref — a fresh clone still gets a working check — and the report says which baseline it used, because a silent fallback is how the wrong reading was believed for a month.
The second defect was live, not latent as the card thought. A remote HEAD pointer is not a branch, and the filter excluding it matched a string git does not print: git renders that pointer as the bare name origin. So the filter had never excluded anything while its comment said it did — the same shape as an earlier check whose comment asserted its own misreading. It now asks git what a ref is rather than what it looks like, and the premise is asserted in its own test instead of trusted.
Proved against a deliberately stale trunk rather than against today's tree, which the card asked for and which is the point: this check was green for weeks with neither defect fixed, because someone had fast-forwarded the local ref by hand. Testing it meant separating the arithmetic from the live repository, so it now takes the repo and the trunk rather than reading them. The fixture holds both defects at once with real refs. Mutation-verified three ways. Suite 398 green.
2026-09-09 – The pre-push gate broke the repository it protects, and the damage identified the cause
Card 139. On 2026-09-08 at 18:11 this repo's git config acquired core.bare = true and a test fixture's commit identity. bare = true meant no working copy could run any git command at all — the main checkout and a worktree both dead — and the identity meant the next commit anyone made here would have been authored by "Fixture".
The cause was the gate. Git sets GIT_DIR in the environment of every hook it runs, and GIT_DIR beats cwd. The pre-push gate runs the test suite, and one test builds a throwaway repository with git init in a temp folder to drive the snapshot classifier. Under the hook those calls operated on the real repository instead. Reproduced in two throwaway directories before the fix, so the mechanism is measured rather than inferred — and it fires only from the hook, which is why the same suite run directly twice that session was harmless.
Two defects from one cause. The same inheritance would make the ordinary git helper read a parent checkout's git directory during a worktree push and report on a tree that is not the one being pushed. The gate's own header records fixing exactly that for the working tree; the environment was the half nobody looked at. A gate that measures another repository is worse than none, and this one could damage it as well.
Fixed by removing git's location variables from the environment of every git call in the file, written as a pure function because the alternative — a constant read at import — would produce a test that passes whatever the scrub does. Proved in both directions: one probe reproduces the incident, the next runs the same calls through the scrub and asserts the other repository is untouched. Mutation-verified two ways.
The durable half is owed and is not in this repo. The gate should clear those variables before running any suite, in the shared harness template, so every initiative is covered rather than the one that was bitten. Hand-editing the local hook is detected by design and would stop the fix reaching the others.
2026-09-08 – The drop zone joins the path check, by two rules rather than 38 exemptions
Card 138. lib/in-repo-references.test.ts asserts that every multi-segment path a rule surface cites resolves. It covered ten content folders and the package root — and not workspace/incoming/, the folder briefs arrive in specifically to be executed against, by an agent that will follow their paths. The one surface where a dead pointer is most likely to be acted on was the one surface nothing checked. It had been green on 84 pinned references while 47 sat one directory away.
Adding the folder was not the fix. A brief is a document about two repos, and 38 of those 47 references were correct — the consumer's own paths, package-relative shorthand, strings the regex invents. So the check asks a different question of a brief: does this path claim to be in this repo — is its first segment one of its own top-level folders, read from the tree so a new folder widens the check on its own. The rest go to a printed outOfScope bucket rather than a growing exemption list, because the rule's blind spot has to be inspectable. Nine instances remained, which was the whole signal.
A second rule fell out of doing the work. Repointing the four stale pointers produced four correction notes, and each note had to name the path it retired — reintroducing every reference the repoint had just removed. A path inside a ~~strikethrough~~ span is now read as a quotation rather than a citation, keyed on notation this repo already uses for retired wording. Measured before adopting it: eighteen strikethrough spans existed and not one wrapped a path.
A brief naming a file it wants created is a promise, not a defect. A new PROPOSED list holds foundation/print.md, and its anti-rot half runs the other way from an exemption — the entry fails when the path starts resolving, because at that moment the brief has been executed.
Four pointers repointed and a fifth thing found. foundation/typography.md → principles/typography.md (four uses; two carried line numbers that are one line off in the file as it now stands, so they are repointed by heading and quoted phrase), foundation/logo-identity.md → principles/logo-identity.md, baseline/setup.md → how-to/install-the-tokens.md, documentation/dependency-reciprocity.md → documentation/how-decisions-are-recorded.md § 10, verified to still carry the claim rather than assumed to. The fifth: the print brief still stated the pre-2026-08-31 publishing rule. One instance was deliberately left alone — the stale token path the assistant brief reports, where repointing would erase the finding.
KNOWN did not change: its twenty-one entries sum to 81 before and after, against 634 references scanned rather than 569. The raw unresolved total went 84 → 87, and all three new ones are declared rather than pinned — a quoted stale path, and two instances of one forward reference. Reach is asserted now instead of remembered, mutation-verified four ways. Suite 388 green. workspace/plans and workspace/briefs stay out with their numbers on the card — records, and a scope question for Torfinn. Cookbook 25.
2026-09-07 – Tier 1 ratified in full: five constitutional rules and the logo page move to accepted
Decided (Torfinn), on reading the post-migration status. The five proposed pages in constitutional/ — voice is the Present Expert, colour is the Aleris palette, typography is Museo Sans, a rule is applicable where it is read, a thinking tool stays a thinking tool — are accepted, as worded. So is principles/logo-identity.md, which publishes at /identity/logo, the URL lib/brand-nav.ts had reserved for it. Tier 1 now has nine rules and no proposals; tier 2 has eleven pages, all accepted.
What that settles. Card 89's open half — whether the rule sentence would be reworded before acceptance — is answered by acceptance. Cards 89 and 133 close. The two Claude-drafted rule sentences the state file carried as "two rule wordings" since 2026-08-27 are Torfinn's by ratification, and the (draft-xlat) and PROPOSED markers on the colour and typography pages now carry the ratification date rather than being removed, so the provenance stays readable.
What it does not settle, and is now more visible. The logo page is accepted with its author prompts and its Claude-translated rules still marked for checking against the 2025 source, so card 131's three questions sit on a published page. Card 134's remainder (two demoted typography rules with no home) and card 135 (the voice merge) are untouched.
Routes: 20 → 26. The colour rule needed a route override, because the loader strips aleris- from file names — a Baseline naming habit — and would otherwise have published it at colour-is-the-palette.
2026-09-07 – Phase 0 revised, the mechanical half: eleven files in their tiers, no URL moved, and the package still knows where everything is
Executed from workspace/briefs/brief-phase-0-execution-2026-09-06.md against workspace/plans/phase-0-revised-2026-09-06.md, one step per commit, suite green between steps. The Cowork handover of 2026-09-06 was committed first as its own commit, so a bisect separates yesterday's edits from today's moves.
What moved. Seven Foundation essays to principles/ (colour, typography, imagery, iconography, constant-and-contextual, emotional-modes, logo-identity), three Baseline reference pages to principles/ (grids-tables-dataviz, progressive-enhancement, motion — the last was aleris-baseline-animation.md), aleris-anti-patterns.md to patterns/, setup.md to a re-created how-to/ as install-the-tokens.md, and the three superseded documentation files to workspace/archive/phase-0-2026-09-06/. foundation/ now holds index.md, voice.md and en-draft/. Nothing was deleted.
What did not move, and this is the design. Every published URL: 20 routes before, 20 after, ten of them with a new source under an unchanged route (ROUTE_OVERRIDES and MOVED_PATHS carry the new locations). Every raw endpoint: /foundation/raw/* names are a consumer contract, so FOUNDATION_ITEMS gained a source per item. The package: Torfinn ruled 2026-09-06 that packages are generated views, not mirrors, and the map from package path to canonical source is now its own module, _packages/agent-baseline.map.mjs, read by the cut script and by lib/consumer-contract.test.ts — the package's folder names are the consumer contract and stay; the corpus moves behind them. Rendered before step 2 and after step 6 with the new --out mode, the package view differs in exactly the type:/layer: frontmatter lines the brief asked for (eleven files) and one repointed citation in design-governance; every other byte is identical. The brief asked for strict byte identity and for the frontmatter update in the same breath; the frontmatter won, and the diff is in PR #27 for the opposite call.
Two new instruments. lib/in-repo-references.test.ts (card 132): every multi-segment path a rule surface cites resolves, counts pinned per file, may fall and never rise, no clean file may join. The dual-context constraint is settled as a reference resolves in any context the file is actually read in — the repo root, its own folder, and the package root for files that ship there, looked up through the package map — so BASELINE.md's tokens/aleris-tokens.css is live and the same string in a constitutional page is dead. 555 references, 81 unresolved in 21 files at landing (card 132 counted 94 in 28 with a narrower scan); five left behind by the 2026-09-06 move were repointed before pinning. lib/layer-indexes.test.ts (step 6): every _index.md member list is rendered from its folder by scripts/regenerate-indexes.mjs, and six indexes went draft → accepted without gaining URLs — the loader skips index files, and they are declared unrouted.
What the moves answered. Cards 133 and 134 asked whether principles/ becomes the essay layer; the map that moved the files says yes, and the redirect notes on the two promoted constitutional pages now say the essay exists at that path. Their remainders stay open: two demoted typography rules with no home (134), and the moved essays were not written to principles/' question-led convention (the index says so).
What is Torfinn's, unchanged. The map's six ⚑ items. Plus one the moves surfaced: depends_on identifiers still read foundation/colour and the like in dozens of files, shipped patterns and untouched Baseline guides among them — the renderer resolves the old names through one alias table, and whether an identifier is a path or a name is a corpus decision (card 136). The package's frozen copies now point at four moved files; the next cut (card 119) fixes those and refreshes the manifest's source column.
Baseline fixes on the way in. Four checks were red when the handover was first run: two tombstones the retired-terms check met for the first time in constitutional/, one heading the title-case rule read wrongly (Arial is now a proper noun, which also retired a frozen false positive), and two stranded snapshot branches — one read and deleted, one that goes green when this lands. The feature branch was declared in branch-hygiene's IN_FLIGHT for the duration and removed in the landing commit.
2026-09-06 – Checks do not ship: the adapter is the unit, and a consumer's declared checks were unread
Decided (Torfinn). Brand OS ships no checks from agent-baseline. Enforcement reaches consumers three ways and every rule is enforced by exactly one of them, named: contributed into the gate the consumer already runs, pointed at from review/, or reviewer-applied and declared unenforceable. The unit that ships anything executable is the adapter, not the package — already true, now written down. Record at workspace/decisions/decision-record-shipping-checks-2026-09-06.md.
The property it turned on, and it makes the next placement computable: where the checked thing can be seen from. A subject the package ships is already byte-verified downstream by brand-provenance.mjs, so a consumer running that check re-verifies a verified artefact it may not edit — stays here. A subject that is the consumer's own source is invisible from here forever — must be contributed. A subject that exists only when a browser paints it can be read from neither file — pointed at. Eleven of thirteen checks stay, one is contributed, three are pointed at.
Three claims in triage-relook-2026-09-05.md did not survive checking, and its proposed rule is withdrawn. "agent-baseline ships no executable check" is true of the package and false of what ships: brand-provenance.mjs runs in the consumer's CI, needs nothing but node, and already implements the three-state reporting the relook proposed inventing. "Consumers are inventing the enforcement layer and nothing knows it exists" is false for sundviktlakemedel, which declared its local checks in an assertions block — but that field is undocumented in brand-provenance.mjs and nothing on either side reads it, which is the real hole and is much cheaper to close. And the position was already in the corpus: _packages/consumer-profile.md § Changes says a rule becomes enforced by "joining an enforcement point the consumer already runs and already believes in". One claim is unresolved — the relook cites src/brand/groundsUnused.test.ts, which the provenance record does not name and this working copy cannot read.
The cost is recorded rather than avoided. Each consumer's assertion file stays its own, so two consumers can assert the same rule differently and neither is wrong. Taken on the grounds that a declared difference is visible at intake while an imposed check that fires wrongly is deleted or pinned silently — and that uniformity was never the failure being fixed.
Four cards, 127–130, all [CC]: the adapter rule stated and assertions documented and read; the out-of-set hex scan contributed, dependency-free; every check declared shipped or repo-only with a property-shaped reason; and review/ targets for sundviktlakemedel and patientguide, which the rendered tier has never been pointed at.
Also today: the intake procedure written up at workspace/plans/intake-procedure-2026-09-06.md — the pass that turns material from other products into rules, patterns, tokens or nothing. Its finding is that the procedure already exists in fragments (the findings template, workspace/incoming/, observations.md, the board, and the 2026-09-05 triage as a worked instance) and what is missing is a defined pass, an output unit of rule + check + owner + disposition, and a read-only agent with a forced discard section. Consumer-side it is limited to helping write a good findings entry until cards 127–130 land.
2026-09-05 (second entry) – Eight loose files in workspace/incoming/ triaged; six cards opened, four mechanical fixes landed
Nothing in the eight was redundant, and three were already carded (90 print, 111 digital identity, 81 → the assistant's open route). Two had their easiest items shipped in v0.11 without the briefs saying so — the tabular-figures sentence and the extracted-glyph row. The triage is workspace/evaluations/incoming-triage-2026-09-05.md; its TAKS list is what Torfinn takes to Cowork, its DEV list is Claude's.
Four of the briefs' own claims did not survive re-checking. The serving rule's "and nothing else" already forbids the build-time subsetting one brief said was "not mentioned either way". The Tailwind bridge is fetchable by URL per setup.md step 1c, so the consumer's dead dependencies were its own. The onboarding conflict is inside aleris-design-governance.md, not between it and BASELINE.md. And the digital-identity brief's out-of-scope line — no redrawing the mark — was overtaken the next day by the 16px redraw Torfinn chose; the Q1 prose has to say so.
Landed today, none needing a ruling. The four generated icon files sitting untracked inside the package folder are removed, after hashing them against the parked copies and the site's own set (identical) and patientguide's (different on all four) — so Q3 is now a fork between two sets, not one offer. The icon allowlist's note said 79 icons and 55 ui-primitive entries while the array held 78 and 54; corrected, and allowlist.test.ts derives both counts from the entries, checks categories and families, and holds the 78 placeholder labels as a KNOWN figure so a fill moves a number — eight mutations, eight fire. Museo Sans has no arrow glyphs and no check mark, measured from the woff2 character maps (U+2192, U+2190, U+2264, U+2265, U+2713 absent at both weights); one sentence in BASELINE.md beside the tabular-figures one. --button-min-height-pointer now says it is a floor and that no pointer padding exists beside it.
Six cards, 121–126: controls on a branded ground and the gradient-blind checks; third parties on an Aleris surface plus "onboarding is not a mode"; the nine open Sund vikt library findings; the subsetting rule and the flip condition; the drift walk for copy consumers; and the voice-findings route. The axis paper moved to workspace/research/ as reading for cards 78 and 120 — it confirms that communicative/instrumental already is the two-stop dial it describes, and its one new piece, the frozen layer the dial may not move, joins the layout prose. Suite 283 → 290, green.
2026-09-05 – The column ladders are decided: communicative grows one column, instrumental is fluid
Torfinn picked both from baseline/conformance/layout/index.html, by looking — the same way the radius default was chosen. Communicative: A, grow the one column, clamp(40rem, 40rem + (100vw − 48rem) / 4, 48rem), promoted from sundviktlakemedel where he chose it on 2026-09-02. Instrumental: C, fluid with no breakpoints, repeat(auto-fit, minmax(14rem, 1fr)).
Two things were chosen with their cost in view, and the record says so. The communicative column holds 88 characters of 18px body at 768 and 105 at 1280 — past the 45–75 guideline from 640px up. A 548px measure-capped column was rendered beside it and not chosen; asked directly whether A should carry a cap, the answer was no. The token's @constraint states the departure with the numbers, and a test requires that statement to stay. The instrumental grid lands on five columns for four tiles from 1280 up; seen, chosen over the stepped candidates.
What superseded what. "Content spans 6–8 of 12 grid columns" in BASELINE.md § Surface temperature is struck: at 600–800px it held 82–109 characters and was never a measure proxy. Two tokens carry the decision — --grid-column-communicative, --grid-tile-min-instrumental — and tokens.test.ts holds the clamp's anchors to --breakpoint-sm/md (svl's test, promoted), renders the ladder at the seven decision widths, and asserts the tile counts, five included, so a change to either value reports itself with the old numbers in the failure.
The measure finding went back to sundviktlakemedel as a file in its _incoming/: its comment's 1ch = 10px and 64 / 77 characters per line are 9px and 87 / 105 against the real face. Read-only from here; theirs to correct.
Still Torfinn's: the Part 1 rewrite with a plain first line and an F6-shaped parent, the three phone-stacking patterns, and rem breakpoints with reasons — the decision settled the ladders, not the vocabulary around them.
2026-09-04 (fifth entry) – The layout decision surface exists
baseline/conformance/layout/index.html, the same shape as the radius comparison that decided card 45. Three candidate column ladders per surface, each rendered as a live specimen at exactly 320 / 390 / 640 / 768 / 1024 / 1280 / 1440 from the real token file and the licensed Museo Sans, with every frame reporting its own column width, characters per line at 18px body, columns in use and whether it scrolls sideways. Forty-two frames; none scrolls. Content is the accepted hero pattern, kit copy Torfinn authored, and svl's console rows — nothing written for the page.
The page found two defects in itself on its first look. The iframe's 1px borders sat inside the declared width, so every frame ran 2px narrow and the 640 frame — exactly --breakpoint-sm — reported one-up cards where the model says three. And the hidden surface was display:none, so its frames laid out at no width and the numbers table showed a column of zeros that looked like a result. Both fixed; both are the class of defect a decision surface must not carry, since a wrong number here becomes a wrong rule.
What the frames report is on card 120. Model A — svl's ladder — is past the 45–75 guideline from 640px up and reaches 105 characters at 1280. Model B holds 75 at every width from 640. Instrumental model C, fluid auto-fit, lands on five columns for four tiles at 1280 and 1440. The decision is now Torfinn's, by looking.
2026-09-04 (fourth entry) – Card 120's two prerequisites, and a measurement that moves a claim
A reflow check joins review/. scrollWidth ≤ clientWidth at every declared viewport, outermost overflowing elements named with their layout mode, a scrolling container treated as the remedy it is, an empty page a failure. Self-test discriminates. Declared against the corrected Claude Design kits with 320, 390 and 1280 viewports: 6 of 15 route-viewport pairs fail, and every offender is a nowrap flex row or a fixed-column grid — the two shapes the 2026-08-12 button entry already names. This is Baseline's own rule generalised, and it would have caught every kit by running rather than reading.
Museo Sans ch measured from the licensed woff2, Arial as control. 1ch is exactly 0.5em at both weights — 9px at 18px. Real Swedish body text averages 0.81 of a ch at 500 and 0.86 at 700, so a measure written in ch overstates by a quarter: 68ch holds 84 characters. Seventy-five characters of 18px body is about 548px, which means Baseline's communicative "6–8 of 12 columns" — 600–800px — holds 82–109 characters and was never a proxy for a readable measure. Any measure token has to be set from the average, not from ch.
The measurement contradicts a consumer's recorded figures. sundviktlakemedel's column comment says 1ch = 10px and 64 / 77 characters per line; measured, 9px and 87 / 105, so its "a cap would bite nothing" rests on the wrong numbers. Named in the brief and on card 120, not corrected there — a consumer's record is read-only to Brand OS.
tokens.test.ts now asserts the corpus's own breakpoint literals. Every width media query under site/ and app/ must equal a --breakpoint-* token, since nothing can var() a breakpoint and the tokens otherwise govern nothing. Generalised from svl's test; one literal today; mutated to 760px and it fired. 209 → 212 tests.
2026-09-04 (third entry) – Layout has numbers and no behaviour; research filed, card 120 opened
What Baseline says about responsiveness fits in a table of seven rows, and none of them is a behaviour. Twelve columns, two max-widths, four breakpoints that are Tailwind's defaults exactly and in px, one sentence saying "use container queries", and a heading — "component swap at breakpoints, not just scaling" — whose body is the z-index ladder. Nothing states what columns do when the screen narrows. --breakpoint-* has zero consumers in this repo. F6 does not cover space, so the surface-temperature layout rules derive from a principle that never mentions them.
Five builds gave five different answers, none from Baseline. sundviktlakemedel's is the only real one — a growing one-column measure anchored to the breakpoint tokens, rem for text-zoom, three phone stacking rules, all under test — and its central call was Torfinn's on 2026-09-02. It is in one product's CSS and nowhere upstream.
Seven reference systems were read at source — GOV.UK, NHS, USWDS, Carbon, Atlassian, Polaris, Material 3. They agree on five things Baseline lacks and one it has: column counts change with width; mobile is one column by default; reading width is stated in characters; classify by window not device; and two container widths for two content kinds — which is what 1200/1440 already is.
Brief at workspace/research/layout-and-responsiveness-2026-09-04.md. Card 120 opened, [T] — the column ladder for both surfaces is the decision, with two [CC] prerequisites that cost nothing: measure Museo Sans ch, and a 320px reflow check across every review/ target.
2026-09-04 (second entry) – Cards 115 and 118 ruled on, and both turned on a survey rather than a threshold
Both cards were [T] and both waited on evidence neither had. The surveys ran first — 849 files across patientguide, sundviktlakemedel, aleris-assistant, aleris-vibe-coding and this repo — and in both cases the evidence changed which option was cheapest.
Card 115: --text-tertiary is retired, not repointed. The survey found one text use of it anywhere: patientguide/web/src/app/globals.css:674, inside @media print, colouring the (url) suffix printed after an external link. Three further uses of the primitive in sundviktlakemedel are border-color, where 3.05:1 clears the 3:1 boundary floor and the AA text question does not arise. And no ::placeholder rule exists in any consumer, so --input-placeholder had been declared, aliased through --text-tertiary, and applied by nothing.
So the value was never the defect. gray-300 is right for what it is actually used for, and --text-disabled and --state-disabled-text already hold it. The name was the defect — a token reading as "the third level of body text" gets reached for as caption ink, and card 115's own evidence is that it was, three times in one hour while building the site. Renaming would have kept a fourth name for one value; removing closes the invitation at the source. --input-placeholder now points at the primitive, which is consistent with a position this suite already recorded: "WCAG does not require placeholder text to meet the AA text floor — it is not required content, and a field with a visible label (mandatory in this system) does not depend on the placeholder to be understood."
No caption tier was added, and that was a measurement rather than a preference. There is nothing between gray-300 and gray-500 (7.94/7.49/6.76). The lightest warm grey clearing 4.5:1 on all three grounds is around #6d6456 (5.82/5.49/4.96), about 1.4 ratio steps from gray-500 — close enough that it would not read as a separate tier. A caption takes --text-secondary.
One published page was prescribing the retired token. patterns/empty-state.md listed --text-tertiary as "Dämpad hjälptext" — muted helper text in an empty state, which is text someone is meant to read, and precisely the use that made the name a problem. Corrected to --text-secondary. That page publishes at /baseline/patterns/empty-state, so this was live guidance, not an internal note.
Card 118: the ground is ruled, the colour is untouched. --text-error measures 5.03:1 on white, 4.74:1 on sand-50 and 4.28:1 on sand-100. The survey found that no consumer renders error text on sand-100: only sundviktlakemedel renders error text at all, App.tsx:163 sets <Page mode="instrumental"> for its whole app, and the rendered ratio measured 4.74:1 — passing, from the product's own CSS and markup driven in a browser. brand-page--communicative has no user.
So the failing case is real in the token graph and absent from every product, which made the cheapest option the right one. BASELINE.md § Forms now says error text sits on a white card or an instrumental ground and never directly on the communicative one — where a form goes in a card anyway, by the surface-temperature rule. Darkening error-500 was rejected because foundation/colour.md cites #C14444 by hex in its colour-vision reasoning and both published ΔE figures would have needed recomputing. A separate darker error-text step was rejected as a primitive bought to solve a case no product has — though it is exactly what the Claude Design system invented unprompted (#8f2f2f), and § 4.1 of the audit still holds for status tints, which is a different question.
The 118 check is the part worth reading, because it was nearly a defective symptom. The card could have been closed by dropping sand-100 from the surfaces this suite measures error text against: a red test goes green, no product changes, nothing is fixed. So the narrowing is not in the test — the new block reads the permitted grounds and their stated ratios out of the rule in BASELINE.md and asserts the rule's own claims in both directions: every ground it says clears must clear, and the ground it says fails must fail.
And that block had a hole, found by mutation-testing it before trusting it. Editing the rule to say sand-100 also clears while leaving the prohibition in place changed nothing that fired — the ratio still matched, and the pass/fail branch was keyed on the surface's name rather than on what the rule claimed. The rule could contradict itself and the suite would stay green. The rule now states a verdict per ground and the verdict is checked against the measurement. Eight mutations were run in total and all eight now fire: reintroducing --text-tertiary, pointing an alias back at it, drifting a published ratio, deleting the prohibition, renaming the bullet, dropping a ground, widening the rule, and claiming white fails.
Suite 200 → 209 in the token file, 273 across 11 files. Browser-verified: 362 declarations register against the generator's 362, so retiring one token swallowed nothing else, and --input-placeholder resolves straight to #9e9281.
One consumer change is owed and deliberately not made here. patientguide holds a vendored copy that still declares --text-tertiary, so nothing is broken today — but re-vendoring turns its print rule into a dangling var(), which is invalid at computed-value time and silently inherits rather than erroring. The printed URL suffix would go from gray-300 to petrol-500 with no warning, on patient-facing documents. Added as a blocking prerequisite on card 119 rather than fixed from this repo: patientguide is on the no-go list and wants a code-reading human at its own gate.
2026-09-04 – Two Baseline defects the Claude Design system found, and the check card 115 asked for
A design system built in Claude Design from Brand OS and sundviktlakemedel was audited against canonical (workspace/evaluations/claude-design-system-audit-2026-09-04.md). All 34 primitive colour values matched byte for byte, which is real evidence the palette is right — the two derivations were independent. What the comparison surfaced was two defects in this repo, and both are fixed here.
--text-accent pointed at a colour that could not carry text at any size. It resolved to --color-orange-500, 2.38:1 on white — below the 4.5:1 normal-text floor and below the 3:1 large-text floor as well, so no size or weight would have rescued it. foundation/colour.md gives orange-500 as "Brand – accent, decorative, print" and never as text, and tokens.test.ts has classified it nontext in its own role table the whole time. Repointed to --color-petrol-400, which the same table documents as "Structure and accents. Large text only, from 18 pt or 14 pt bold" — 4.09:1 white, 3.86:1 sand-50, 3.49:1 sand-100. This was Baseline contradicting Foundation, not a decision either document had taken, so Foundation's precedence settled it rather than a new call. Grepped before changing: zero consumers, so nothing rendered was wrong. --brand-accent stays on orange-500, which is the role Foundation actually assigns it.
It was not a new finding, and that is the part worth keeping. board.md § "Decisions waiting on Torfinn" item 3, from the 2026-07-30 run: "--text-accent is orange-500 as a text colour. Roughly 1.6:1 on sand-100. Out of scope for the supersede, not covered by the fitness check, which tests buttons." Correctly identified, correctly scoped out, and then it sat for five weeks in a prose list, because nothing depended on anyone re-reading that list. The recorded ratio was also wrong — orange-500 on sand-100 measures 2.03:1, not 1.6:1 — which is the ordinary fate of a number written once in prose and never recomputed. Item 3 is now marked answered with the corrected figure. The check is what stops the next one waiting on a re-read.
The letter-spacing comments described a scheme the file does not implement. --letter-spacing-tight was commented "h2, h3" and --letter-spacing-tighter "h1". The --type-*-letter-spacing composites two hundred lines below have always assigned h1←tightest, h2←tighter, h3←tight. Both halves are in one file and they disagreed for months. The cost was not local: the audited system read the four comments instead of the composites, concluded Baseline used −0.01em for h2, and filed a correction against its own — correct — values. A stale comment produced a false finding in a document written to keep two systems aligned. Worse than a missing comment, because it looks authoritative and it fails silently.
The durable half of card 115 landed with them, and it is the reason both were findable. That card asked for exactly this: "every --text-* role must be measured against every page and card surface it can sit on, and a --text-* role resolving to a primitive documented nontext must fail." tokens.test.ts gained thirteen assertions — the semantic-alias join, and a check that makes each --letter-spacing-* comment agree with the composites that consume it, so the comment is now the assertion and cannot go stale silently again. Both were verified by re-introducing the original defect and confirming they fire, and only they fire — two failures, 198 passing. Suite 187 → 200 tests; the full repo run is 264 in 11 files, green.
Card 115's substantive fork stays open and is still Torfinn's. --text-tertiary (gray-300, 3.05/2.88/2.60) is recorded as a known failure asserted to still fail, per this file's convention — so fixing it without removing the entry breaks the suite. The card's question is unchanged: is the role for disabled and placeholder text, and misnamed, or is it a text level with the wrong value?
The check also re-surfaced --text-error, and calling that a new finding would have been wrong. It is 5.03:1 on white, 4.74:1 on sand-50 and 4.28:1 on sand-100 — the communicative page ground, where a field error most often sits, at the 14px regular --input-error-size sets. Card 18 already found this on 2026-08-05 and it has been carried in the suite as KNOWN_INPUT_FAILURES['error-text/sand-100'] ever since, one of eight failures that card surfaced and none of which it fixed. What this change adds is smaller and specific: the ratios now travel on the token itself, so baseline-tokens.json stops shipping --text-error to product teams as an unrestricted text colour. A known-failure entry is visible to whoever runs the suite; an @constraint is visible to whoever consumes the token. Card 118 gives it an owner, which is what it has lacked for a month, rather than reporting it as new.
Two honest costs. The token file gained 3,380 bytes, all comment, against a file BASELINE.md already flags as 67% comment and names as the largest single saving available on a page that inlines it — the documented comment-stripping build step is the mitigation, and the percentage moved 67 → 69. And _packages/agent-baseline/tokens/aleris-tokens.css is now behind canonical on a token value for the first time since v0.6.3, which by _packages/README.md's rule means consumers re-vendor rather than repin. Deliberately not synced here: a package cut carries a version call, and that is not mechanical. Card 119.
2026-09-02 – The backup hook was backing up the wrong directory, and is now tested
The session-end hook hard-coded its target repo and cd'd there, so every session running inside .claude/worktrees/<id>/ had the main checkout snapshotted instead of its own work. Reproduced in a throwaway repo before being changed: the snapshot held the main checkout's uncommitted file and none of the worktree's contents. It now reads cwd from the JSON hooks receive on stdin, snapshots that tree, and gives each worktree its own backup/auto-YYYYMMDD-<slug> branch so two sessions ending the same day cannot force-push over one another.
Two older defects surfaced while writing the test. Retention parsed the date as everything after auto-, so a suffixed branch would never have been pruned; and the "unchanged today" check exited above the retention block, so pruning was skipped on ordinary days. Both fixed.
.claude/ is no longer ignored wholesale. The hook and its test were untracked — machinery meant to prevent lost work, itself in no repository and no backup. The rule is now .claude/* with !.claude/hooks/, so settings.local.json and worktrees/ stay out.
Four assertions, and the assertions are mutation-checked. .claude/hooks/backup-push.test.sh builds a repo and a linked worktree under $TMPDIR. Each defect was reintroduced into the fixed script and the test confirmed to fail on it; the original could not be run directly, since it would have force-pushed to the real repository.
This partly answers card 116. Its five-for-five measurement — no snapshot ever holding content that was not already landed — was taken against a hook that, for worktree sessions, was snapshotting a tree that was never at risk. Whether the hook earns its cost is still Torfinn's call, but it can now be asked of a working one. Board card 117; cookbook entry 23.
2026-08-31 (fifth entry) – agent-baseline v0.11, cut by a script for the first time
Asked for as three title corrections; shipped as a whole version, and the reason is the interesting part. Three title: values in published Foundation pages were in title case — Emotional Modes, Brand in Brief, Constant / Contextual — which BASELINE hard rule 6 forbids without exception, and which render as each page's own H1. On their own those are textbook rule-6 point-release material: no new content, no new member, no token touched.
But a cut takes canonical as it is, and canonical had moved further. baseline/BASELINE.md gained real content on 2026-08-28 (fca537f) that v0.10, cut on the 27th, never carried. New Baseline content in the snapshot makes it a whole version under rule 6's own test. Stamping it v0.10.1 would have named the half that was asked for and hidden the half that was already waiting — so v0.11.
What the consumer gets that they did not have. The licensed Museo Sans has no tabular figures at either weight, measured by sundviktlakemedel on 2026-08-25: digit advance widths are identical with and without font-variant-numeric: tabular-nums, and the ten digits spread 8.72px at weight 500 against Arial's 0 — 8.2px between 111,1 and 000,0 at a 16px value size. Right-alignment is what aligns a numeric column; the property is a silent no-op. Also two reference rows: the Tailwind v4 bridge, and the fact that 3,772 FA Pro Regular glyphs are already extracted and fetchable without a kit token.
Both new rows point at corpus paths for files that are not package members. That is the class cookbook entry 21 measured at 80 references across 10 of this package's files. Recorded in the manifest rather than fixed, because fixing it is a question about what a path in a shipped file should mean.
How the title-case violation was found is worth keeping. The new static site reads title: from frontmatter to label its navigation. Three values that had sat in metadata nobody rendered became visible the moment something displayed them as labels. A fourth, documentation/site-map.md's Site Map, was corrected in the same pass and is not a package file. Language names and the Present Expert were deliberately left capitalised as proper nouns — lowercasing those would have been the same error in the other direction.
This is the first cut made by a script. _packages/cut-agent-baseline.mjs holds the file map, copies canonical into the package, re-reads every destination to verify, and reports any package file the map does not account for. It exists because cookbook entry 21, which landed the same day, measured the alternative: ten cuts between 2026-07-09 and 2026-08-27, every one by hand, with rule 2's byte-for-byte claim recorded as a sentence a consumer could not run. The count of thirty mirrored files stopped being a number in prose — the script derives it and fails on anything unaccounted for. It deliberately does not choose a version, write a manifest section, or edit the version table; those carry the rule-6 judgement, which is not mechanical. Rule 1 in _packages/README.md now records it.
All 30 mirrored files verified byte-for-byte after the copy rather than before. Four changed since v0.10, 26 identical. No token file touched — repin, not revendor, so no consumer needs to re-vendor anything.
Not done here: telling the consumers. Repinning patientguide, sundviktlakemedel and the aleris-vibe-coding mirror is a separate act in each of their own records, and the board tracks it.
2026-08-31 (fourth entry) – The site that publishes the brand's digital identity gets one of its own
Measured before anything was changed: brand.dev.aleris.ai returned 404 for /favicon.ico and 404 for /icon.svg. No app/icon.*, no favicon.ico, no icons: metadata anywhere in four segment-owned layouts. The site that publishes the Aleris brand's digital identity had none.
Nothing needed building. scripts/generate-logo-icons.mjs landed on 2026-08-31 with PR #14 and already produces exactly the 2026 minimal set — verified rather than assumed: the ICO carries three entries at 16/32/48 where production's carries one non-square 31×32; icon.svg uses fill="currentColor" with a prefers-color-scheme: dark rule and only the two permitted colours; the PNGs are 180, 192 and 512. So this was a decision and some wiring, not a build.
Wired via Next's file convention — app/favicon.ico, app/icon.svg, app/apple-icon.png, public/icon-192.png, public/icon-512.png, app/manifest.ts. The layout wrinkle was checked rather than assumed: this repo has four segment-owned layouts each rendering their own <html>, so the app-root convention might not have reached them. It does — all four links appear in /en, /baseline and /tools, and all six endpoints return 200 with the right content types. sizes="any" on the SVG is what makes it win over the ICO where it is supported.
Verified in a real browser, not by grep: the SVG renders petrol on a light scheme and white on a dark one.
The manifest names its tokens instead of restating them, and that is now checked. A manifest needs literal hex and cannot reference a custom property, but constitutional/tokens-are-canonical.md is unambiguous — "They name the token; they do not repeat its hex." So app/manifest.ts reads --color-petrol-500 and --color-sand-50 out of the token file at build time and throws if either is missing, rather than falling back to a default colour. lib/site-icons.test.ts asserts the file contains no hex literal at all. That is the first mechanical check of the tokens-are-canonical rule, which the constitutional page itself records as "not yet built"; it covers one file, and it was mutation-verified by planting #004851 and watching it fail. sand-50 rather than sand-100 because the token's own @usage reserves it for instrumental surfaces, which is what this site is and what its layout already renders.
Asserted by property, never by byte. lib/site-icons.test.ts deliberately does not check that the site's icons match the package's — they do not, and should not, while Q2 is open. Same argument review/SPEC-production-icon-surface.md makes about production: an equality assertion between two files that agree only by coincidence would fail correctly and become a nuisance somebody deletes. Six assertions, each independently worth keeping true, none pre-empting Q2.
Card 111 opened for Q1, and the reason it comes first is structural rather than a preference. The corpus does not govern favicons at all. Every rule in logo/ is written for a mark that includes the wordmark, and the 24px minimum exists because below it the letters fall under the 14px type floor — a tab icon is 16px, the one brand surface with no room for the wordmark. _packages/README.md rule 1 is that packages are generated from the corpus, so shipping an icon set before Foundation has a rule would make the package assert something the corpus has not said, across five consumer repos. Q2 cannot be answered correctly until Q1 is. What the icon looks like is already decided — the symbol-alone ruling of 2026-08-27, the 16px redraw of 2026-08-28, and a measured threshold asserted in both directions. What is open is where the rule lives and what it says.
The package is untouched. It still carries only the provisional 4158-byte ICO, byte-identical to what www.aleris.se serves. The generated set stays parked at workspace/drafts/parked-2026-08-31/.
2026-08-31 (third entry) – The main checkout catches up, and a four-line finding is stated correctly on the third attempt
~/Dev/aleris-brand-os had been ten commits behind origin/main since 2026-08-28 and could not be fast-forwarded: its working tree held six uncommitted files that collided with the commits it was missing. Sessions run in worktrees, so nothing ever updates that folder — the drift is structural, not an oversight.
Every file was compared against origin/main rather than guessed at, and that shrank the problem. lib/logo-minimum.test.ts, scripts/generate-logo-icons.mjs and workspace/evaluations/digital-identity-verification-2026-08-28.md were byte-identical to what PR #14 had already landed. review/SPEC-production-icon-surface.md was the older copy, missing the rescue provenance note that is on main. Five of six were duplicates or stale.
The favicon was the real hazard, and it was not what the card was opened on. The working copy held the generated 4258-byte ICO where main carries the provisional 4158-byte file — the one byte-identical to what www.aleris.se serves, and the one the digital-identity verification measured. Committing it would have answered Q2 of that verification by accident, the exact question review/SPEC-production-icon-surface.md's own rescue note says was deliberately left open. Restored; the generated set is parked with its reasoning at workspace/drafts/parked-2026-08-31/, inside the gitignored workspace/drafts/.
_state.md needed no reconciliation. None of the ten commits touch it, so the fast-forward left it alone. Its 2026-08-28 Progress entry and three Log entries remain live and uncommitted — including the correction owed in three files about emotional-modes.md being "the eighth and last Foundation page to flip" when foundation/ holds nine. Still owed.
Result: main at ffaa952, zero behind, and the full suite green — 243 tests across 10 files. Card 110 closed.
Card 109 corrected a second time, and the correction is the more useful record. The finding has now been stated three ways in one day: two of three findings false with a "stray local branch named origin" (wrong — that entry is refs/remotes/origin/HEAD, which git branch -a renders as a bare name); then three of three false with two active defects (right about the findings, wrong that both were active); and now the accurate version. The check is green, which fixed the symptom and neither defect. Defect 2 — a remote HEAD symref counted as a branch — is latent, not harmless: it only surfaces once the trunk reference is stale, so fixing defect 1 alone would hide it rather than remove it. The card now asks for the fix to be proven against a deliberately stale trunk rather than against a green tree, since reproducing today's failure is a two-line setup and waiting for main to rot again is not a test.
The pattern in all three wrong statements was the same: reporting what a tool printed rather than resolving what it meant. git branch -a lists a symref among the branches; a count against a stale ref reads as work ahead. Both were one command away from being checked. Adjacent to mechanism 5 in workspace/status/cookbook.md §22, from the other side — that entry is about a check nobody trusts, this is about trusting a listing too readily.
2026-08-31 (second entry) – Headings enter the voice filters, and the reason rules go unapplied gets measured
Prompted by a reading, not a plan. Torfinn read the Brand OS Explained artifact and named "What it is, in one paragraph" as an AI tell that was also redundant: the reader can see it is one paragraph, an introduction is short by its nature, and the heading is a placeholder wearing a heading's clothes.
All 24 headings in that artifact were extracted and sorted, and the split is clean. Every heading that works states a fact or names an action — "The published site is three months out of date", and the seven verb-first menu items. Every heading that fails describes the container — "What it is, in one paragraph", "Five things, described plainly", "The constituent parts", "What is there, and what is not", "The menu" — plus five siblings in identical grammar that say nothing when read as a list. Journalism already names the failure: a label headline is a noun phrase that identifies a topic without saying anything about it.
Three of the four failure modes were already named patterns on filters/ai-tells-and-filters.md. dygdesignalering (hard) covers "described plainly" — the text praising its own writing. formellabelloop covers the five-in-a-row. effektmakeri covers "Rules that can be checked, are". They never ran on headings because the page was written against paragraphs in June and its scope was never written down. Nobody excluded headings; there was no boundary to notice a gap in. The page now states scope explicitly, and says why.
Added: a Scope section (prose / headings and prose / headings only), heading examples on the three existing patterns, two new Layer 1 patterns — formrubrik (the heading describes the text's own form, hard) and etikettrubrik (label heading, soft) — and the heading test, three questions in Review methods. Question 1, read the headings alone with the body hidden, do they summarise the page, is the one that catches this class and the one no per-heading rule can see. All three trace to sources rather than preference: NN/g, Digg's guideline 61, ISOF klarspråk. Both new pattern definitions are marked as drafted and awaiting Torfinn's wording pass (card 108).
data-products/voice-filter-rules.yaml at 0.2.0. Every pattern now carries an explicit scope, the three heading-scoped ones carry heading_example, and a heading_test block records which questions are mechanical and which are not.
Built: lib/heading-tells.ts, lib/heading-tells.test.ts, scripts/heading-check.mjs (npm run headings <file>). The library reads its vocabulary from the rules file rather than restating it, because a rule with two homes drifts and this would have been the third surface. The CLI takes .html as well as .md, which is the point — the corpus measures clean on the mechanical rules while the defect lives in artifacts, decks and briefs. Run against the artifact it catches all three mechanical defects, including one nobody had noticed: the best heading in the document is at h4 directly under an h2.
The measurement improved the rule before it shipped. A first pass flagged fourteen sibling runs, of which twelve were legitimate enumerated sequences — Example 1/2/3, Job 1.1, Part 1, Step 1, twelve What changed in v0.x. An enumerator makes a run navigational rather than mechanical, so the rule now exempts runs where every heading carries a digit. Written without measuring first, the check would have fired on the changelog and the JTBD analysis and been switched off.
The finding: 106 title-case headings across 16 files, shipping to consumers. Sentence case has been decided for months — "no exceptions, not even table headers or nav labels". Nothing counted, so the rule was applied to new work and never to the seven files imported from ALERIS-DESIGN-WORKING, which ship inside agent-baseline. Frozen rather than failed: lib/heading-tells.baseline.json holds the known hits and the test fails on a new hit and on a stale row, so the debt cannot grow and cannot rot, and the cleanup stays a decision (card 106).
The second half of the session was Torfinn's question about the pattern itself — why the corpus keeps discovering that it already knew something. Answered with a measurement rather than an argument, in workspace/status/cookbook.md §22. 121 it() blocks exist, but 70 are in tokens.test.ts; route manifest and branch hygiene take 18 more, both about repository mechanics. Assertions checking a brand or content rule: about 21, against 39 bolded hard rules and 10 normative: true pages. What gets checked is what is numeric. Four mechanisms are named with dated instances — filed by subject but fires at a moment; scope never stated; more than one home; nothing asserts it — and a fifth was found the same day: branch-hygiene.test.ts reports three branches ahead of main, all three of them false (card 109). (First recorded as two of three, with a "stray local branch named origin" as the third. Corrected within hours: there is no such branch — the entry is refs/remotes/origin/HEAD, which git branch -a renders as a bare name and the check counts as a branch, and the second branch had already been merged in PR #13 and only looked ahead against the stale local ref. Two defects, not one.) The obvious remedy — fast-forward local main — turns out to be blocked: the main checkout holds uncommitted work that collides with the commits it is missing, and appears to be a second copy of the icon workstream PR #14 already landed (card 110).
The generalisation worth keeping: a rule the corpus already violates does not need a cleanup before it can have a check — it needs a frozen baseline. Fix 106 headings is a project; do not add a 107th is free. That removes the precondition that keeps correct rules unenforced.
The wording pass ran the same day, delegated. Card 108 opened as Torfinn's; he handed it back. Three things it changed beyond the prose. The page's own claim — "Named in Swedish because these are the established working terms" — was false for two of its fourteen patterns, since these two were coined here rather than inherited from the writing-style-profile, which has no vocabulary for headings; the claim is now qualified and both provenance lines say coined, not inherited. The two patterns overlapped: "The constituent parts" sat under formrubrik while being a noun phrase with no verb, which makes it etikettrubrik, so the boundary is now form-reporting versus topic-naming. And the page contradicted the rules file — "What is there, and what is not" was given as a matchable tell when no vocabulary list can hold it; both files now state that matchable is the subset a check can match rather than the pattern itself, so the gap is declared instead of latent. Rules file at 0.2.1. Left alone deliberately: dygdesignalering's second sentence is a kiaster on the page that rules kiaster hard, but it is inherited verbatim, so changing it is a change to the profile.
Then the kiaster in dygdesignalering, which the pass had flagged and left. Torfinn's instruction: fix it. Scanning for the shape rather than the instance found a second — anglicismdrift on filters/language-quality-sv.md carried "Kodväxling är inte djup, det är drift." Both are antithesis siblings on pages that rule kiaster hard, and both had been there since the patterns were transcribed in June 2026. Cut rather than rewritten, which is what the Kiaster entry itself prescribes when the substance is already in an earlier sentence, and in both cases it was. Two more went with them: a line of this session's own from two days earlier, and the tautology "Text is plain by being plain."
Four live copies corrected in one pass, including two outside this repo. Both filter pages, the counter_example in the rules file, and both source entries in TAKS/2-areas/collaboration/writing-style-profile.md — a cross-boundary edit made on purpose, since correcting the corpus and leaving the source it was transcribed from is mechanism 3 in workspace/status/cookbook.md §22, committed knowingly one day after documenting it. A change-log entry was filed in the profile under its own convention. One home could not be verified: the plain-english skill packaged from that profile is not on disk under ~/.claude and may still carry both sentences.
Not made into a check, and the numbers are why. A scan for antithesis shapes across the two filter pages, the rules file and the profile returned eleven matches: four real, one a deliberate quotation of the pattern being taught, six false positives on ordinary prose. Two more were left standing on judgment — "The split is by surface, not by content" and "It is the floor, not the gate" state a distinction that appears nowhere else, which the Kiaster entry's second clause explicitly allows. No regex separates a repeating antithesis from a load-bearing one, so this stays a review method. The reasoning is written into the page as a dated note so the next transcription does not re-import what was cut.
Two cards for Torfinn beyond the wording pass. WCAG 2.4.6 Headings and Labels is an AA criterion and appears nowhere in the corpus, while 1.4.1, 1.4.11 and 2.5.8 are cited by number and BASELINE commits to AA — card 107 asks whether it becomes a fourth constitutional non-negotiable, with the objection stated: the node's non-negotiables are defined by being cleanly checkable, and 2.4.6 is only half so.
2026-08-31 – The publishing rule opens to five folders, and the site rebuild starts
Torfinn's decision, and it revises a ratified rule. Route B1 of 2026-08-04 said foundation/ and baseline/ publish and nothing else. It now reads: foundation/, baseline/, constitutional/, filters/ and documentation/ publish, plus a named allowlist of three files from workspace/status/ that render into a gated section of the site. Full record with the reasoning and the edges at workspace/decisions/publishing-rule-revision-2026-08-31.md.
Only the first of the three conditions changed. A file still publishes only if it also carries status: accepted and a lang matching a served locale, so a draft or proposed page in constitutional/ stays unpublished exactly as one in foundation/ does. The gate is not weakened, and lib/route-manifest.json remains the diff a reviewer accepts on purpose.
What it closes. Card 86, blocked since 2026-08-27 — four accepted constitutional rules in force and unreadable by anyone the rules apply to. filters/, which holds the AI-tells filter every content-generating surface is meant to run against, with no route. documentation/, which is the reader-facing system documentation and which no reader could see. And the site introduction, which under the old rule could only publish by being filed in foundation/ as though an explanation of the system were brand content.
workspace/ does not become a publishing folder and the transit rule stands. Three named files publish; the folder stays in SKIP_DIRS. workspace/sources/ holds material received from other people, workspace/archive/ holds superseded history, workspace/drafts/ is gitignored, and none of them may ever appear.
A correction caught while writing the record. The first draft named principles/ as a sixth publishing folder. It already publishes — lib/baseline-nav.ts lists principles/design-tokens.md by name, and it reaches readers through resolveBaselineSource at its /baseline/... route. Adding it to CONTENT_DIRS would have put one file at two URLs, which is the failure this rule exists to prevent arriving from the opposite direction. patterns/, schemas/ and data-products/ are excluded for the same reason.
The decision has no visible effect until a frontmatter pass runs, and the measurement is the reason. Across the three newly-published layers: constitutional/ has 4 of 7 files at accepted, documentation/ 1 of 12, filters/ 2 of 5 — and no file in any of them carries a lang key at all. documentation/ also uses six statuses outside the ADR vocabulary — agreed-in-principle, inbox, synthesis, living, plus three files with no status. That set was ratified on 2026-06-16 and documentation/ never migrated, which went unnoticed for ten weeks because nothing read those files' frontmatter for anything.
Also decided this session, and scoped in workspace/plans/website-v3-plan.md: the site is rebuilt on Astro; the icon-picker is extracted to its own app and /fonts/* CORS moves to the server config; comments and global sign-in are dropped; the site is public with the workspace section gated at the edge; the Storybook is HTML and CSS with no framework, published at a subpath of the main site, which settles card 99's substrate question; and the current Next.js app is retired by tagging site-v2-final and deleting it.
One finding shapes the whole rebuild. app/globals.css line 52 records in its own comment that this app has never loaded data-products/tokens/aleris-tokens.css — it is a hand-synced duplicate token set under different names, --color-petrol against canonical's --color-petrol-500. So dogfooding the design system is the removal of a second, divergent set of values rather than a polish pass. The Tailwind v4 bridge cut on 2026-08-28 (card 97) is what makes the fix real rather than another hand-sync.
2026-08-28 (third entry) – The live site has been serving a June build for 73 days, and the website plan is written
Measured directly. Every published file was fetched from brand.dev.aleris.ai and matched by hash against the git history. The deploy is frozen at commit 23d4e7b, 2026-06-16; main has moved 201 commits since. aleris-tokens.css serves 36,804 bytes against canonical's 50,487 with a different sha256; BASELINE.md serves 233 lines against 292 and still teaches the confirm-green rule retired on 2026-07-31; baseline-tokens.json and aleris-fonts.css both differ; aleris-tailwind.css returns 404; and the site serves /sv/ while returning 404 on /en/, a locale the app has not defined since 2026-08-12.
The reason this needs fixing now is that a builder is told to fetch those URLs. baseline/setup.md line 22 tells them to curl the token file from that host and line 82 to fetch the Tailwind bridge, which does not exist there, so the documented install is broken as written. The aleris-design-system skill cut earlier the same day names five of those URLs as the canonical thing to fetch instead of building from prose; it holds no values on purpose and sends the reader to the source, so a stale source defeats its whole design. llms.txt is generated from the frozen build, so the machine-readable index advertises paths that moved on 2026-08-10.
What is not affected, so the exposure is not read as larger than it is. Both installed vendored-app consumers vendor from _packages/agent-baseline/ rather than from the URL, and brand-provenance.mjs hashes what they carry; sundviktlakemedel's token file was verified byte-identical to canonical the same day. The exposure is to anyone following the published setup path, and to agents.
Cards 113 and 114 opened, deliberately as two. 113 is the redeploy and the diagnosis, [T] because it needs Coolify access rather than a decision. 114 is the missing apparatus: this repo asserts what publishes, every token ratio, every promised consumer path and every unmerged branch, and nothing checks what the live host actually serves. Each of those checks was written after its failure had already happened once; this is the same shape, and it went unreported for 73 days. 114 can be written before 113 lands and will fail until the redeploy happens, which demonstrates the check works in a way a passing run cannot.
The website plan is written at workspace/plans/website-v3-plan.md, status proposed. It does not re-open the platform question — a static renderer was agreed in principle on 2026-05-30 and documentation/site-map.md names Astro. What it adds is that the site should answer three questions and today answers only the second: what Brand OS is, how it works, and what is missing from it. The third is new and is the one likely to be argued about. The argument for it is that readers fill silences with assumptions: where the corpus said nothing about components, the same person built a component layer twice, in two products, because there was nothing to point at. Five decisions are named as Torfinn's — the access model, whether the gap register publishes, whether constitutional/ publishes, whether the board publishes in any form, and the generator.
Also found: README.md states agent-baseline v0.7.1 (it is v0.10) and "215 tests across five files" (nine files, 118 assertions), and does not mention review/, the skill, the Tailwind bridge or observations.md. Not corrected in this pass — recorded here so it is not re-discovered.
2026-08-28 (second entry) – A build-time skill, and gcc profiled as a consumer candidate
A new skill, aleris-design-system, source at workspace/skills/aleris-design-system/. The existing aleris-brand skill is from February and is a communication skill — voice, storytelling, visual assessment. It carries nothing from Brand OS: no tokens, no hard rules, no accessibility floors, no packaging, and it still says "avoid turquoise" rather than the colour ruling that replaced it. Rather than stretch it, this is a second skill for the other job — someone building software.
It holds no values, and that is the design. Every canonical source it needs is already published and was verified reachable before being cited: BASELINE.md, the token CSS and JSON, setup.md and llms.txt all return 200 from brand.dev.aleris.ai. So the skill points rather than mirrors, which keeps it from becoming a fifth copy of the hard rules that drifts. The ten hard rules are restated by token name with the values removed, because constitutional/tokens-are-canonical.md forbids restating a value and a skill is exactly the kind of document that would do it without noticing. Two checks were run against the draft and both found something: one hex literal had crept in as an illustration — and it was a real token value, so it was copyable — and the Tailwind bridge URL 404s until the site next deploys, which is now said in the file rather than promised.
What it adds over "go read BASELINE.md" is the part a reference document does not carry: the install traps that have actually cost projects time (the font host's CORS pattern being a domain rule rather than an app register, and the resulting silent fall back to Arial on a platform's default hostname), the conformance checks with the reason they exist, and the boundary — what an agent may not decide, and where a finding goes when the system has a gap.
gcc is profiled as a consumer candidate — _packages/adapters/vendored-app/PROFILE-gcc.md. Filled by reading the repo with citations, per rule 1 of the adapters README. Nothing was installed and nothing in that repo was touched, which is also how review/ treated it.
It is structurally a vendored-app and nothing argues for a new type. It is filed separately from the two installed instances rather than as a third column, because it differs in three ways a shared row would average away: it consumes nothing today, its instruction layer is Swedish where theirs are English, and its release gate is one assertion reading expect(true).toBe(true) — so an adapter's checks would be creating a gate rather than joining one, the opposite of the condition vendored-app v0.2 was built for.
The finding with reach beyond gcc: it is the portfolio's first palette divergence. The palette is declared three times by hand — hex in src/index.css, the same values as HSL in the shadcn layer of the same file, and hex again in tailwind.config.ts — cited to "Aleris Brand Guidelines 2025", a document, rather than to a file. Three values disagree with canonical. Until now the token file has shipped byte-identical to five consumers without exception, so "vendored means identical" has been a true statement about the whole portfolio. It no longer is.
Card 62 gained its second piece of evidence, and it is the strongest kind. The environment badge was folded into that card from aleris-vibe-coding's written standard — a requirement stated but not yet built. gcc has built one, inventing three colours Brand OS has never seen. So the eighth candidate now has two independent instances that do not agree with each other, which is the shape none of the other seven have.
Two cards opened. 104 asks whether Gröna korset's five-state safety scale is a ratified clinical exception — it gates whether an adapter can be generated at all, since an adapter may not answer a brand question, and it is urgent rather than theoretical: the rendered review measured three of the five colours failing AA including green, and four of the five separate by hue alone, which for roughly one in twelve men merges risk of harm with harm occurred. 105 is the pursue-or-not decision on gcc, and records two blockers that are not ours — card 104, and that neither of gcc's instruction files is currently a safe place to install wiring, since the one holding the design guidance contradicts its own repo and the one the agent loads says nothing about UI.
The cheapest useful thing there needs no adapter and no decision: gcc runs a working findings loop pointed at the forge platform and has no Brand OS findings file. Adding one copies a pattern already working in that repo.
235 tests.
2026-08-28 (first entry) – Two measurements enter Baseline, and the Tailwind bridge stops being hand-written
Two sentences into BASELINE.md, both of which exist to stop a specific repeat. They come from workspace/incoming/brief-sundvikt-design-system-findings-2026-08-27.md, whose own text nominated each as "cheapest fix is one sentence in BASELINE.md", and both are utility text under CLAUDE.md's 2026-07-29 calibration rather than brand prose.
The licensed Museo Sans has no tabular figures, at either weight. Measured in a browser against Arial as a control: digit advance widths identical with and without font-variant-numeric: tabular-nums, the ten digits spreading 8.72px at weight 500 and 7.50px at 700 where Arial spreads 0, and 8.2px between "111,1" and "000,0" at the value size. So a consumer setting that property is asserting an alignment it does not get, silently. Stated under § Typography, with a pointer from § Tables' right-alignment bullet, which is where someone reaches for the property. Whether a cut carrying tnum exists under the licence is recorded as unknown rather than assumed either way.
The icon path data has been in this repo since May and nobody knew. Two independent design handoffs in the same week shipped hand-drawn placeholders and both said the swap waited on a licensed kit that npm.fontawesome.com 401s. It did not: public/icon-picker/svgs/regular/ holds 3,772 committed FA Pro Regular SVGs, and they are served publicly — verified this session, https://brand.dev.aleris.ai/icon-picker/svgs/regular/<name>.svg returns 200 with the name index at /icon-picker/metadata/icons.json. Only re-extraction on an FA upgrade needs a token. Stated as a § File reference row, in URL form first, because a consumer reading BASELINE.md out of a package does not have this repo. Deliberately not repeated: which names may be used, which stays with the allowlist.
Card 97 closed — the Tailwind v4 bridge is generated, and the design decision behind it is a measurement.
data-products/tokens/aleris-tailwind.css, produced by generate-tailwind-theme.js, 145 of 363 tokens bridged, every skipped family named in the file's own header with its reason — a bridge covering two thirds of a token file reads as covering all of it unless it says otherwise. Served at /baseline/raw/tokens/aleris-tailwind.css; both the raw endpoint and the browsable page verified prerendered and byte-identical to source.
The obvious bridge is unsafe, and only compiling it shows that. An identity map — --color-petrol-500: var(--color-petrol-500) — reads fine. Compiled against Tailwind 4.2.2, Tailwind emits that name into @layer theme, producing a self-referential custom property that is invalid at computed-value time. It survives today only because aleris-tokens.css is normally imported unlayered and unlayered declarations beat layered ones; a consumer who imports the tokens into a cascade layer would get empty values and no error. Prefixing every bridged name removes the collision, and Tailwind then inlines the mapping and emits no theme variable at all — so bg-aleris-petrol-500 compiles to background-color: var(--color-petrol-500), one hop to canonical, and the bridge adds nothing to :root. It also adds a review property worth keeping: bg-aleris-* in a diff is visibly a token where bg-[#004851] is visibly not. The cost is the longer class name, and it is stated in schemas/design-token.md along with the condition that would justify reversing it.
The compile test earned its place twice. On its first run it caught a silent generator bug: --color-goal-* and --goal-* — a pure 1:1 alias pair — both mapped to --color-aleris-goal-*, so four tokens quietly had no utility at all. The generator now throws on any two-to-one mapping rather than letting the later declaration win. And mutation-tested against a restored identity map, three of its six assertions fire. A check that has never been observed to fail has not been shown to measure anything.
A finding fell out that the card did not anticipate. schemas/design-token.md § "Tailwind CSS v4" already carried a hand-written bridge, and 7 of its 25 token references did not exist — --elevation-1/2/3 and --font-size-h1..h4, against a file declaring --shadow-e0..e3, --elevation-card/dropdown/flat/modal and --font-size-xs..2xl. A consumer copying that block got seven utilities resolving silently to nothing. This is _packages/README.md rule 5 in different clothes: a hand-maintained mapping goes stale and nothing tells you. Replaced with a pointer to the generated file, the measurement recorded, and the namespace change flagged — the old block replaced Tailwind's own scale (bg-petrol, p-4), the generated one adds alongside it, which is a real change in ergonomics and is reversible if the shorter form matters more than the safety.
Not done, and named rather than absorbed: BASELINE.md, setup.md and schemas/design-token.md all changed today, so _packages/agent-baseline is behind on three files. A cut has not been asked for and was not taken unilaterally.
Also this session, and separate from the above: workspace/evaluations/sundvikt-component-library-candidacy-2026-08-28.md evaluates the sundviktlakemedel component library as a starter-kit Storybook, and cards 98–103 are open on it. That work decides nothing; it is prep.
235 tests, npm run build clean.
2026-08-27 (sixth entry) – The four cuts reach their consumers, and a field that was doing two jobs
Card 91 closed. patientguide and sundviktlakemedel were pinned to agent-baseline v0.7.1 while canonical went v0.8 → v0.9 → v0.9.1 → v0.10 in a single day. Both now read v0.10.
The claim underneath the repin was verified rather than trusted. Every one of those cuts said repin, not revendor, which rests on no token file having changed. Checked by comparing both vendored aleris-tokens.css copies against canonical: byte-identical, same sha. So nothing was re-vendored, no artefact moved, and no build output changes.
Direction of travel observed. The adapter manifest records that "the consumer's copy is the one file a consumer edits, so the instance follows it rather than leading it." Both consumers' own brand-provenance.json were edited first and then copied into _packages/adapters/vendored-app/instances/, verified identical to the live files. The alternative – editing the copies here and hoping the repos catch up – is what makes a record lead reality.
Each note says what the four cuts contain, and each carries a repo-specific paragraph, because the general summary is not what a builder in either repo needs. sundviktlakemedel: injection technique is instructional imagery and is no longer governed by imagery.md's identity rules, which used to forbid it by implication, and the weekly check-in is a working surface – no hero image, though images may sit inside components that need one. patientguide: preparation steps reclassified the same way.
Both repos' own provenance checkers were run and are clean, each reporting pinned v0.10, source v0.10. Neither repo wires that check into npm test, so it only runs when someone runs it – worth knowing, not fixed here.
The adapter was deliberately not versioned, and finding out why fixed a real ambiguity. _packages/adapters/vendored-app/MANIFEST.md had a header field reading "Pins content: agent-baseline v0.7.1". That phrase means two different things: this wiring was written and verified against v0.7.1, and the consumers are on v0.7.1. They are the same number until content moves without the wiring needing to change – which is exactly what v0.8 through v0.10 did. This is the ambiguity that made the repin look like drift when it was not, and it is why an earlier pass in this session declined to move the pin without being able to say what moving it would mean.
Renamed Authored against in the adapter manifest and in adapters/README.md's table header, with the rule stated in both: an adapter needs a new version only when a content cut changes what the wiring has to do. What a consumer actually runs lives in that consumer's own record, which is the file the consumer edits. The adapters README row now also states that both instances are on content v0.10 while the adapter remains authored against v0.7.1, so the two numbers read as the different facts they are rather than as a mismatch.
One thing this did not do: ship to aleris-vibe-coding, which is a different adapter (vibe-coding-seed, authored against v0.6.5) with a mirror in Richard's repo, still four content versions further back. Not in scope for card 91, which named the vendored-app consumers, and it needs a Bitbucket token rather than a local edit.
229 tests, working tree clean in all three repos before committing.
2026-08-27 (fifth entry) – Imagery says what it governs, and stops dividing on the wrong thing
Cards 72, 84 and 96 closed in one sitting, and they were one sitting because they were the same question at three depths – scope, then axis, then granularity. Answering 84 first was load-bearing rather than tidy: once the page governs identity imagery only, instructional images leave it entirely and card 72's axis question narrows to identity imagery on working surfaces. Answering 72 first would have meant deciding an axis and then finding a whole class of image was never on it.
Card 72's decision had been taken two days earlier and had never reached the page. Decision 4, 2026-08-25, moved restraint's driver off instrumentality and onto whether the person's time on the surface is their job – and that decision record states, in as many words, that imagery.md was not edited by it at all. So this sitting was mostly prose, not decisions, which is a different shape of work from what the board implied.
§ "Imagery in professional and internal surfaces" is now § "When the surface is someone's work". The restraint rule kept its substance – function leads, often no hero image, decorative lifestyle images do not belong – and lost its driver:
Whose time it is decides this. A reader's profession does not.
That is neither candidate the original finding offered, and both would have been worse. Instrumental (candidate b) puts a patient's weekly check-in and a clinician's dashboard on the same side, which is the reading the finding itself said renders a consumer product as an admin tool. A third case bolted onto audience (candidate a) grows a taxonomy by exception. Replacing the driver keeps two sides and draws them in the right place.
Two consequences a consumer feels immediately. Professional readers on communicative surfaces – recruitment, partner material, material for referring physicians – may lead with an image, which the old section forbade by lumping every professional surface together. And a patient-facing surface can be a working surface: most of a treatment app is one by time spent, it is not held to restraint, and it is held to a new rule.
The image never holds the hero position on a working surface. The top of the screen belongs to the task.
That is the sentence that blocks the failure the consumer finding named – a lifestyle photograph above a weekly weight-submission screen.
Card 84: the page now says what it governs, and the omission had teeth. A scope line under the heading states that this page governs identity imagery and that images showing someone how to do something are not covered. Read literally, the old page forbade the image an injection instruction needs: its founding choice is "the person at the centre… images of equipment, premises, or processes serve as support, never as the main subject", and an instruction shows a device and a hand. sundviktlakemedel (injection technique) and patientguide (preparation steps) both need that image now.
Shape 3 with Torfinn's provision: a boundary rather than a home, and the owner is a conversation. "Instructional images require a conversation with marketing and are judged on a case by case basis" – deliberately not a named role, because which images need that judgement and who should make it depends on the case. That provision is what closed shape 3's weakness: a boundary naming no owner is out-of-scope with extra words. It is also honest about an organisation where imagery judgement is not held by one title.
Nothing was loosened to make room for it, which was the point of the sequencing. The "Not clinically documentary" prohibition is untouched. So is § "Why the images look the way they do". The instructional image sits outside the page's scope rather than inside it as an exception – the move Torfinn struck on 2026-08-25, when scoping the prohibition would have forced a founding choice to move with it.
The argument for it being a different kind of object rather than an uncovered category, and it is a measurement rather than a preference: an instruction needs even light so nothing important sits in shadow, and enough depth of field that the hand and the object are both sharp. Those are the opposite of two of the brand's three visual choices. Following the signature would make the image more recognisably Aleris and less able to do its job.
Card 96 was dissolved rather than answered, and that was the better outcome. Three shapes were offered, and all three assumed the question was how restraint relates to the four dispositional states. Torfinn's answer said the premise was false: "Professional can absolutely be communicative surfaces, for example material intended to be consumed by our industry peers or insurance partners, material intended for physicians, HR (recruitment)." "Professional" was never a category on the right axis. What separates those surfaces from a dashboard is whether the surface is someone's work, not the reader's stance – so card 96's content landed inside card 72's section and there is no per-state table in existence to maintain.
His constraint on shape is recorded because it will apply again: "What I don't want to end up with are long lists with states that need upkeep – I want two or three, max five, rules that afford direction. Guidelines or boundaries, not strict rules."
The quick test is reordered and gains a question, because the order was the defect. Is this surface someone's work? is now first and can stop the sequence; asking the category and reader-state questions first is exactly how an image ends up leading on a screen where it should not. The old third question – would a patient recognise what is shown – moves outside the sequence, because it tests the image rather than the choice and applies even when no image leads.
A deliberate silence, named as one. The page never points at the dispositional family. A reader who has just met four dispositional states on emotional-modes.md gets no imagery guidance per profession. That is judged correct rather than a gap – reader state governs which category leads where an image leads at all, which the mode table covers – but it is a silence and the record says so.
Deliberately not added: a balanced column in the mode table. emotional-modes.md gained a third patient mode in the third entry today and the obvious move was a third column here. It would have said no image leads, images may support inside components – restating the working-surface rule one level further down, where the two can drift apart when either is edited. The table now states that it covers two of the three modes and why the third is settled earlier. What stays genuinely open is narrower and better: where an image does sit inside a component on a working surface, which category it should be. A consumer choosing this week should expect Care or none rather than Life, and that expectation is not yet a rule.
One pointer removed rather than left wrong. The old section closed with "See Icons, the internal digital tools arena, for the same boundary on the icon page." The two pages no longer draw the same line: iconography.md divides on situation (dashboard versus presentation) while this page now divides on whose work the surface is. Removed until the icon page is written, and not carded – it belongs with that page's existing open prose rather than as a card of its own, which is a stated choice and not an omission.
Two copy-edits inside Torfinn's own edits, recorded because one is a factual pointer rather than grammar. "require a conversation with marketing and is judged" → "are judged". And "This is opposite of Aleris image guidelines" → "This runs against the visual signature described at the top of this page": the Aleris Group Image Guidelines govern production, rights, consent and storage, while the visual signature is this page's own, so the original sentence pointed at the wrong document.
agent-baseline cut to v0.10 – a whole version, since it adds a section and rewrites another. One file changed since v0.9.1, 29 identical, all 30 verified byte-for-byte. Repin, not revendor; the fan-out is still card 91.
Verified on a running dev server: /en/identity/imagery returns 200 carrying the scope line, the new section, the hero rule, the reordered quick test and the Instructional imagery section, with both stale strings gone. 28 em dashes normalised to en. 229 tests, npm run build clean.
2026-08-27 (fourth entry) – The rename reaches the two pages it left behind, and the query that found it found more
Card 94 closed. foundation/imagery.md and foundation/constant-contextual.md now call the first emotional mode uncertainty, agreeing with emotional-modes.md for the first time since the rename landed a few hours earlier. Three mode labels corrected: imagery's table column head, its "the table works from the patient's modes" line, and constant-contextual's Reader state row.
One of the five was not a substitution, and treating it as one would have left a broken claim. imagery.md's Avoid cell read "Life images as the main subject — they don't answer the worry." That sentence depends on there being a worry to answer; swapping the noun alone would have left the page asserting something about an emotion it no longer names. Rewritten as "they don't answer the uncertainty." Claude-drafted, under CLAUDE.md's utility-text calibration – a table cell is not voice-bearing prose, so it was drafted and marked rather than routed through a decision loop, which is what the calibration exists to prevent.
Nine ordinary-noun uses of worry deliberately survive, across all three pages: "clinical details that may increase worry", "they create a worry that isn't there", "no reason to worry", "possibly even worried or afraid", "often also worried". The mode is not called Worry, and worry is still something we can cause. That distinction is the rename's whole content, so a find-and-replace would have destroyed the reason for the change while appearing to implement it. Card 94 led with that warning and it held.
The honest limit, recorded because it is a gap and not a detail: this fix has no assertion behind it. Card 83's retired mode labels got three retired-terms rows, because "Mode A" is not ordinary English and a word-boundary match separates it cleanly from prose. Worry is ordinary English. What was retired is one sense of the word, and no substring or boundary match can tell the retired sense from the nine legitimate uses sitting beside it. So the thing preventing this drift from recurring is a reader, not a test – unlike every other invariant this week. What would assert it is a vocabulary-coherence check comparing the mode labels the definitional page defines against the labels the pages that use it actually print; that is real work and has not been done. Stated in the v0.9.1 manifest too, so a consumer wondering why the fix took two cuts has the answer.
Two findings the card did not have, both because the query was run wider than the card's five references. Card 83's lesson – an absence is a claim about a query – applied deliberately this time rather than in hindsight, searching the whole word family (worry / worried / worries / anxious / anxiety / oro / orolig) across everything that publishes and ships instead of the five known lines.
The first is now card 96, and it is larger than the sentence that revealed it. imagery.md:84 reads "Once the reader-state model for professional recipients (scrutiny, time-pressure) is settled, this page can point to it. Until then we keep the table at the patient modes." It was settled earlier the same day – the dispositional family publishes on emotional-modes.md with four named states. The pointer is one line, but the section below that sentence, Imagery in professional and internal surfaces, states restraint as a single rule for all professional surfaces, and it was written when professional readers had no model. The real question is whether restraint varies across those four states – whether an image for a decision-maker assessing risk does the same work as one for a practitioner under time-pressure. A brand call, not a cross-reference. It also collides with card 72, already open on the same page and about the patient half of the same split, so both should be read together.
The second is a smaller inconsistency, left alone with a reason. constant-contextual.md:30 says "the one who is anxious" of a patient the table now assigns to uncertainty. It is not a mode name – it describes one constructed example patient, and an example may specify its own premises – but anxious is stronger than the mode she is now assigned to, so prose and table disagree in register on the same example. Not changed: it is voice-bearing Foundation prose, and the objection that drove the rename applies to it more weakly than to a label. Flagged rather than carded, because a one-word edit does not need a card and a rewrite is Torfinn's.
And a third, noted in passing: the same sentence ends "Predictability eases worry" while emotional-modes.md's parallel sentence now reads "Structure eases uncertainty". Both are legitimate ordinary usage; they are simply no longer parallel.
agent-baseline cut to v0.9.1 – a point release that fits rule 6 without argument for once, unlike v0.8 and v0.9, which needed a third case invented for a page changing language. No new content, no new member, no token touched. Two files changed since v0.9, 28 identical, all 30 verified byte-for-byte. Repin, not revendor; the fan-out is still card 91.
229 tests, npm run build clean.
2026-08-27 (third entry) – The last Foundation page flips, and the model gains a third mode
Cards 73 and 53 closed together, which is why they were one sitting: both landed in the same table. foundation/emotional-modes.md is now English, status: accepted, lang: en. It is the eighth Foundation page to flip – foundation/en-draft/ holds no markdown at its root any more, and the Swedish original is archived at _markets/sv/foundation/emotional-modes.md, the seventh page there.
Corrected 2026-09-05. This read "the eighth and last Foundation page to flip" and "the English flip of Foundation is a closed track". Both were false when written:
foundation/holds nine files, and the ninth —logo-identity.md— was Swedish,status: draft, and the only Foundation page with nolangkey at all. The claim was true of what publishes, which is why an eight-route manifest made it look right, and false of the directory. Recorded as owed in_state.mdon 2026-08-28 and not applied then because a parallel session held the working copy. The ninth page flipped 2026-09-05 and the track is now genuinely closed — corrected rather than left to come true on its own, because a record that becomes accurate without anyone fixing it loses the reason it was wrong.
Card 73: balanced is written, and its own card's second constraint dissolved rather than being met. The row is a consequence of its own – lead with relevant, clear options; dial down general information; support understanding the consequences and choosing a next step – so decision 3's falsifier ("fails if the balanced row cannot be filled with a positive consequence") passes. The card also asked the page to say whether balanced describes the person or describes what the design assumes when nothing is pending, and to state that the assumption is falsifiable. Torfinn confirmed the first, which makes the second unnecessary rather than missing: balanced describes the person, the same kind of claim as uncertainty and curiosity, so the table mixes no kinds and there is no assumption to declare falsifiable.
The third constraint was struck, deliberately, and the reasoning is worth keeping. Decision 3 called "at her own pace" the load-bearing half and derived a rule from it: the pace is hers, so the surface does not push – no urgency, no streak, no nudge. Torfinn struck the slot and his own replacement note with it. The rule went because the wording it hung on went: once balanced is "partway through using one of our services and wants it to go smoothly", "the surface does not push" no longer follows from the definition. What constrains a builder instead is in the mode's needs list – friction only where it earns its place – which defends a confirmation step or a safety gate and refuses an engagement mechanic, without asserting anything about the reader's feelings. The decision record is amended underneath rather than edited, since a record is frozen at its date.
Card 53: next of kin gets its own family, which was the last of its three questions. Practical states – the person is helping someone else and the state is set by the job in front of them rather than by a feeling of their own. Option 3 of the three on the table; the corpus rejects the helplessness framing throughout, and that is what ruled out filing a helper under a feeling. The emotional family's definition trims to for patients. The umbrella is stated by structure – the section heading is Three families of reader state – rather than by a separate sentence.
A rename nobody had carded, with a reason that is not recoverable from the diff. The first mode is no longer worry; it is uncertainty. Torfinn: "I can have a lot of questions without being worried. The severity of 'worry' risks skewing our instructions to sound like all patients are worried prior to meeting with us, which is not true and too presumptuous." That is an argument about what the guidance implies, not a preference between two words, and it constrains the mode's wording going forward – nothing in the uncertainty row may imply distress by default. Recorded in the decision record for that reason.
The rename needed one fix on the page and opened a card for two others. English collapsed a distinction Swedish had kept: the mode was Oro and the thing the waiting room is full of was osäkerhet, two words. With the mode renamed Uncertainty, the sentence "in the recovery room, the uncertainty is of a different kind" read as the recovery room also being mode 1, when the table calls it disorientation – and the edit to "structure eases uncertainty" had removed the last instance of the distinguishing word. Now "what the person does not know has changed". Card 94 carries the five surviving uses of worry as a mode name in foundation/imagery.md and foundation/constant-contextual.md, plus a third word for the same state at constant-contextual.md:30 ("the one who is anxious", stronger than worry, so the objection hits it harder). Deliberately not bundled into the flip, and stated in the v0.9 manifest so a consumer meeting two names for one mode knows why.
Two of those five must not change, and a find-and-replace would destroy the reason for the rename while appearing to implement it. constant-contextual.md:38 and this page's own "clinical details that may increase worry" use worry as an ordinary noun, naming an effect to avoid. The mode is not called Worry; worry is still something we can cause. That distinction is the rename's whole point.
A structural finding fell out of adding a third column, and it was nobody's mistake. The page's table and its mode list were never the same taxonomy. The Swedish original's table row read Oro / Desorientering; its mode list read Oro / Nyfikenhet. So disorientation sat in the table and was never a mode, and curiosity was a mode and was never in the table – only oro appeared in both, and the mismatch was invisible because two entries and two columns matched by accident. Adding a third to each exposed it. Resolved by declaring the table what it always was – a worked example of one person across three occasions, with a line on the page saying so – rather than by expanding the model to four modes. The column headings are now all occasions (the form, the waiting room, the recovery room) and the table order matches the narrative above it, which it previously contradicted.
agent-baseline cut to v0.9, whole version on the precedent v0.8 set the same day. One file changed since v0.8, 29 identical, all 30 verified byte-for-byte. The package is now English throughout its foundation/ layer – what remains Swedish is voice/ and patterns/, which are a separately tracked question. No token file touched, so repin not revendor; the fan-out is still card 91.
The gates did their job twice, in the direction that matters. The route manifest failed on "route about-the-brand/emotional-modes changed shape" and the language allowlist failed on "is on KNOWN_LANG_MISMATCH but is no longer Swedish – remove it from the list". Neither was a nuisance: the second is the anti-rot half of an allowlist catching a stale entry the same day the entry went stale, for the second time in one day. KNOWN_LANG_MISMATCH is now empty in that direction.
A hole in this morning's own work, closed. AUDIT_DIRS (card 39) claimed to walk "every directory that can hold corpus markdown" and omitted _markets/, which holds seven Swedish originals. Added, and six of them entered ORPHANED_ACCEPTED – which raised card 95: each carries status: accepted while the English page that superseded it carries accepted too, so two files claim to be in force for the same content in two languages. Not fixed, and the reason it is Torfinn's rather than mechanical: _markets/ belongs to the market-package model, and the 2026-08-04 language decision recorded the destination as "a published English site with Swedish beside it, not instead of it". If Swedish returns as a served locale these are not superseded originals but the Swedish edition, and accepted is right. The frontmatter question and the market-package question are one question.
Card 93 partly answered, and the answer narrows the rule. Asked whether the en-dash convention is real or narrower than stated, Torfinn said he uses en dash himself and wants to keep doing so. That governs prose being written now – his and Claude's – and is not a mandate to rewrite 9,079 existing instances. The new page is 0 em / 30 en, which is what following the rule on a page you are already writing costs: nothing. The corpus sweep stays undecided, and should not happen by accumulation.
Also decided: the file is not renamed (2C) – emotional-modes.md names the family rather than the page, but the file name is abstract and the opening line does the work, which is CLAUDE.md's own 2026-08-25 convention. ROUTE_OVERRIDES, propagates_to and inbound links all unchanged.
Not done and stated: the professional-audience table publishes with its unvalidated status on the page ("analytical starting points, not researched") rather than being held back or validated first. It has sat in the en-draft since May; publishing it with the caveat visible is better than an accepted page implying it is settled.
Verified on a running dev server, not just a green build: /en/about-the-brand/emotional-modes returns 200 at lang="en", carrying the three families, the third mode, the worked-example line, and no Swedish or retired mode labels. 229 tests, npm run build clean.
2026-08-27 (second entry) – What publishes, what gets checked, and the difference between them
Card 39 was carded as a one-line fix and was not one, and the card itself said why. lib/content.ts and lib/route-manifest.test.ts both read const CONTENT_DIRS = ['foundation', 'physical'], against a publishing rule ratified 2026-08-04 that says foundation/ and baseline/ publish and nothing else. physical/ does not exist, which is the only reason the leftover had been harmless. But the card's second clause required that an accepted file outside a publishing folder must not publish and must not be silently ignored either – and the test's copy of that array is not the manifest's source, it feeds the orphan gate. So deleting the string from both would have narrowed what publishes and what gets checked in one move, and the suite would have gone green by looking at less.
Split into two constants doing two jobs. CONTENT_DIRS (lib/content.ts) is what publishes: ['foundation']. AUDIT_DIRS (lib/route-manifest.test.ts) is what gets audited: ten directories, every one that can hold corpus markdown. routed is now unioned across both the foundation manifest and the baseline manifest, because the seven pattern files, principles/design-tokens.md and schemas/design-token.md publish at /baseline/* routes through resolveBaselineSource and would otherwise have been reported as a wiring gap that does not exist.
ORPHANED_ACCEPTED went from empty to seven, and the emptiness had been read as a result. Its 2026-08-04 comment said so in as many words: "Empty as of the first run, and that is a real result rather than an unpopulated list." True of the two directories it walked, false about the repo, and it stayed false for three weeks while seven accepted pages sat unrouted one directory over – four constitutional/ rules in force and unreadable on the site, both filters/ files, and documentation/how-decisions-are-recorded.md. Same defect as card 83's grep, one layer up: an absence is a claim about a query, not about the world.
lib/route-manifest.json did not change. 8 routes, every one sourced from foundation/; physical/ never existed so fs.existsSync had always skipped it. Stated rather than glossed, because the card asked for a regeneration in the same commit and the honest report is that there was nothing to regenerate.
Card 86's blocker was pointing at the card that was going to make it worse. Its Waits-on read "unblocks when constitutional/ enters CONTENT_DIRS (card 39)". Card 39 does the opposite by ratified rule. Corrected: making constitutional/ publish is a change to the rule and therefore Torfinn's, and the four pages are now named in ORPHANED_ACCEPTED so the gap is asserted instead of remembered.
Card 83 – the two surviving A/B/C references, and the row that makes the third one impossible. baseline/governance/4-den-nara-experten-digital-behavior-v1.md:122 lost its (Mode C in the design working document) parenthetical; the sentence carries its own substance, and a dangling pointer to a retired vocabulary is a correction, which an accepted page allows. baseline/iconography/curation-session-b.html:535 now reads "Pröva mot ytor där patienten väntar på ett besked" – Claude-drafted utility text in an unpublished curation file, per CLAUDE.md's calibration, not gated prose. Verified on a running dev server, not just a green build: /baseline/governance/4-den-nara-experten-digital-behavior-v1 renders the corrected line and contains no "Mode C" anywhere.
Three rows added to lib/retired-terms.test.ts, one per label, because each has a different replacement: A was anxiety-driven, B aspiration-driven, C latent anxiety (mapping confirmed from workspace/archive/tone-construction-plan.md:214, not inferred). Mode C's replacement is not a term but the service property from decision 5 – can this service deliver news the person did not ask for? – which board card 77 is still open to record, so a reader meeting the old label is pointed at a decision that abolished it while keeping half its content. Card 83 does not close card 77.
One new field, and it was a measurement not a preference. The matcher was text.toLowerCase().includes(r.term). A word-boundary regex over the published surface finds exactly the two references card 83 named; plain substring matching on mode a/mode b/mode c finds 14 lines, twelve of them false – "dark mode as", "the mode and". Hence wholeWord?: true, defaulting off, since substring is right for a distinctive phrase like "the close expert". Both references mutation-verified: restored, seen failing with file and line, reverted.
agent-baseline cut to v0.8, canonical only – and the version rule needed a third case. The precedent in _packages/README.md sends a new package member to a whole version and a correction to a point release. This cut adds no member, so by the letter it is a point release; but foundation/constant-contextual.md changes language end to end, and a consumer reading a different document in a different language is not reading a correction. Recorded as a third case: a page changing language is a whole version.
The cut is four files and only one of them is the flip, which is the part a consumer would otherwise meet without warning. colour.md and iconography.md drop the communicative/instrumental vocabulary (card 76 – used in three Foundation pages and defined in none, so a reader had to guess which of their own surfaces was which; replaced with plain description rather than a definition being added), and emotional-modes.md withdraws its promise of a detailed digital model that nothing published carries (card 75). All 30 content files re-copied and verified byte-for-byte: 4 changed since v0.7.1, 26 identical. No token file moved, so this is a repin and not a revendor.
foundation/emotional-modes.md is now the last Swedish foundation/ page, and the manifest says so instead of implying a translation is pending. The row that carried it – "constant-contextual.md, emotional-modes.md | Swedish (unchanged)" – was wrong for one of its two files and has been split. Holding it is deliberate: cards 73 and 53 are open questions the brand owner writes first, and translating around them would freeze a model about to change.
The retired-terms check caught the cut's own manifest, which was the right outcome and needed a decision. The v0.8 section names both "the close expert" and "cream" while describing their correction, and _packages/agent-baseline/ is in the check's shipped scope. ALLOWED was the wrong instrument – its rows key to exact quoted text, and a manifest is rewritten at every cut, so each row would go stale one version later and fail the staleness assertion for no finding. Added to MAY_NAME instead, on the same reasoning workspace/ is excluded: a record must be able to quote what it retired, and the only unusual thing here is that this record ships. The cost is stated in the file – a genuinely live retired term in the manifest's provenance table would be missed; README.md and BASELINE.md, the files a consumer reads to use the system, stay in scope.
Three cards opened, and one of them was found by fixing a neighbouring sentence. Card 90 carries the print-surface brief, thirteen days unexecuted with nothing on the board – Torfinn's call to card it rather than archive or execute, because deliberately parked and forgotten look identical without one. Card 91 carries the v0.8 repin fan-out: three records still pin v0.7.1, and they are deliberately not updated here, because _packages/adapters/vendored-app/MANIFEST.md records the direction – "the consumer's copy is the one file a consumer edits, so the instance follows it rather than leading it" – and editing them would make this repo's records lead reality. Card 92 is "chrome", retired 2026-08-04 and still present 54 times in what publishes and ships, found because the one instance in _packages/agent-baseline/README.md's language section was inside a sentence already being rewritten for the v0.8 language split. It cannot be swept mechanically for the same reason it was retired – the word does two jobs, and most of the 54 are probably Chrome the browser – and it cannot be asserted yet either: retired-terms.test.ts requires an in-repo record, and the chrome retirement is recorded only in TAKS and Dev/CLAUDE.md, which the seventh constitutional rule forbids citing.
Also cleared: work.md deleted from the repo root – tracked, one byte of content (x), against CLAUDE.md's root-only rule.
Noted, not swept: both files in workspace/incoming/ carry status: inbox in frontmatter, which contradicts Dev/CLAUDE.md's 2026-08-24 correction that a brief sitting in a drop zone carries no status at all, because location is the record. Left alone deliberately – whether this repo's workspace/incoming/ is governed by the DEV drop-zone rule is a question, not a defect, and answering it by sweeping would be deciding it silently.
229 tests, npm run build clean.
2026-08-25 (fifth entry) — Two documents on one subject become one, and F6's position becomes a rule
The coherence pass on the three context documents (brief 2026-08-25) found the split they were moved under was not the split they had. context-architecture.md and dependency-reciprocity.md were labelled the model and the mechanism. They sat at the same altitude and stated one argument twice: the property-not-the-label claim, the target-size worked example with the same five candidate reasons in the same order, the same three evidence cases in the same ranking, the same scattering list, the same three capture changes. git log gives the direction — written 2026-08-24 in dependency-reciprocity.md, restated 2026-08-25 in context-architecture.md, under a header asserting that neither of the three restated the others. The drift shape this project has recorded five times, caught on consecutive days by the same person.
Merged into documentation/how-decisions-are-recorded.md — when we decide something, how do we record it so the next person can find it and knows what it covers? Two halves of one job: reach (what else does this apply to) and footprint (where has it been written down, and are those copies still right). Both old paths are pointer stubs carrying a section map. status: accepted, with an in-force map at the top: §1–4 and §8–10 in force, §7 (the language question, evidence tiers intact), §11 (the graph, unscoped), §13 and §14 carried open.
The alternative considered and rejected was ownership without merging — assign each argument to one file and have the other point at it. Torfinn took the merge. It makes the status question harder rather than easier, so card 74's move is applied inside the merged file: separate ratification from reasoning, rather than relabel a file to its weakest section.
Card 79 closes on both halves, and the second dissolved rather than being decided. It asked what status dependency-reciprocity.md should carry, offering mark-accepted / leave-proposed / split. None was needed: the file only held two levels of ratification while it held the ratified model, and once that moved, everything left was uniformly unratified.
constitutional/a-thinking-tool-stays-a-thinking-tool.md created, status: proposed. Guiding material teaches someone to decide a case the corpus has not met; a specification states what is correct in one it has; a page does one or the other, and a guiding page in the durable core never acquires a global enumerated set. Detection of violation is card 79's five-attempt table — three global attempts refused, two domain-local accepted and unargued — and Origin says outright that the rule exists so the fourth proposal meets something rather than arriving fresh. The rule sentence and Implication are Claude-drafted and marked in place; both argue, so both are Torfinn's before it moves to accepted. This is what F8's retirement was already an instance of.
Not made constitutional, deliberately: a decision records the property its answer turned on. It is in force on consumer findings and nowhere else — not on board cards, not on decision records, not on the record that proposed it. A rule nobody follows on the day it is written becomes decoration. Graduation trigger recorded: propose it once decisions inside the corpus carry the field, and once a property can be told from a label.
workspace/status/observations.md created — a TAKS/doab.md-shaped ledger for situations the brand has met, whose output is promotion or archival as noticed, not acted on. It gives F6's quick-test question 3 ("Hittar du ingen vägledning alls? … Flagga den.") a destination for the first time: that line was the only place F6 asks anything of the system, and the four recorded arrival routes did not include it. Started near-empty on purpose, with one Claude-proposed candidate awaiting Torfinn's wording (instructional imagery, card 84).
The question nobody had asked, and the answer that changed something. Does recording the property replace the judgement F6 asks for? No — five properties were true of the console at once, so choosing which the answer turned on is F6's "read the situation", and no table yields it. But two things follow that were not written down. The obligation attaches to F6's question 2, ordinary judgement inside existing guidance, not to question 3 — which changes what F6 asks of a reader, since question 2 currently ends at use your judgement and owes nothing back. And the failure mode is not an empty field but a filled one: "applies to the console" is a label in a property-shaped box and looks complete. A wrong list fails loudly; a lazily-filled box fails silently. That is a pre-declared slot where the corpus refused a pre-declared list, one level down.
So the obligation gained the detection statement it lacked — every constitutional rule has one and this did not. The test is doab.md's own, 2026-05-14, pointed at rather than restated: "if the principle could plausibly have been written before the project started, it is confabulated." Applied here: could someone who was not in the situation have written this property? sundviktlakemedel finding §7 passes; "the console" fails.
Two conventions written into CLAUDE.md. Every governing document opens with one line saying what it answers, in ordinary words — and it is an admission test (Torfinn): a concept has to be boiled down to that line before it is allowed into the corpus, and a document that cannot produce the line does not yet know what it is. Cheap where renaming is not, since the file name can stay abstract while the first line does the work. Second: cite by section heading plus a quoted phrase, never by line number. Two instances corrected — board card 54 and the coherence brief both cited "§169", which was a line number, and it had already moved. Claude's defect, fixed rather than carded.
Files: documentation/how-decisions-are-recorded.md (new, accepted) · documentation/context-architecture.md and documentation/dependency-reciprocity.md (pointer stubs, superseded) · workspace/status/observations.md (new) · constitutional/a-thinking-tool-stays-a-thinking-tool.md (new, proposed) · workspace/decisions/decision-record-context-documents-coherence-2026-08-25.md (the reasoning, the alternatives, and what would make it wrong) · CLAUDE.md, documentation/site-map.md, _packages/README.md, _state.md, workspace/status/board.md (pointers and conventions)
2026-08-27 — Brand OS is a product, so its rules have to be applicable from the repo
Torfinn asked whether Brand OS depends on anything that exists only on his machine. The sweep found five candidates and then corrected its own alarm: every reference in a published, shipped or accepted file is attribution or provenance, not instruction. The favicon named from TAKS/3-resources ships in the package. The detection test citing TAKS/doab.md is quoted in full and restated in applied form. The presentationsverktyg path is a downstream consumer — a dependency pointing at Brand OS. Nothing in foundation/, baseline/, or the shipped package references TAKS at all.
So the corpus was already compliant and had never said so. Same shape as card 79's finding two days earlier, and the reason both rules were cheap to adopt: a rule that describes what a corpus already does is the only kind that holds.
Torfinn's framing is what turned it into a rule: "I thought the collaboration was about how you and I work together, not about how Brand OS learns and collaborates. I think the Brand OS should be self-sufficient in this regard since it's a product and as such shouldn't have dependencies to my work stream."
Seventh constitutional rule, proposed: a rule is applicable where it is read. A rule stated in Brand OS must be applicable by a reader who has Brand OS and nothing else. A reference outside the repo may credit or date a rule; it may never be the only place the rule's content lives. Corollary: Brand OS may write outward, never require an outward read. Appending to a personal ledger is output; needing that ledger open to apply a rule is a dependency.
The line is between the maintenance method and the collaboration method, not between Brand OS and TAKS. Three things share one workspace and only one is the product: the product itself, the maintenance method (board, changelog, versioning and packaging rules — all in-repo), and the collaboration method (tenets, global context, /wrap, doab.md — correctly in TAKS). A different maintainer brings their own collaboration method and still needs the whole of the first two. The answer was never "move TAKS into Brand OS."
The rule this one had to amend, and it is our own. CLAUDE.md says never paraphrase a governing document — point at it. That assumes the target is reachable by whoever reads the pointer, which holds inside one repo and fails across a boundary. The new clause: inside the corpus, point; across the boundary, absorb and attribute. documentation/how-decisions-are-recorded.md reached accepted on 2026-08-25 citing an unversioned external ledger for its only stated detection test, and stayed applicable only because its author quoted the test rather than pointing at it — which was the right call made against the stated rule.
The check declares rather than judges. lib/external-references.test.ts cannot tell a load-bearing dependency from a credit, so every outside-repo path in a rule surface must be declared with a kind and a reason; an undeclared one fails, and so does a declaration whose reference has disappeared. Same discipline as KNOWN_LANG_MISMATCH and IN_FLIGHT, anti-rot half included. Scope is the rule-stating surfaces plus accepted documentation/ files plus everything shipped in agent-baseline; workspace/ and _packages/adapters/ are excluded and the exclusions are stated on the rule page so they cannot quietly widen.
Two exemptions worth naming. The rule page and the check quote the references they classify, so they trip themselves — exempted by exact path, two entries, so the exemption cannot grow into "documentation about the corpus". And the check catches resolvable-looking paths, not mentions of origin: filters/ai-tells-and-filters.md crediting a writing-style profile in TAKS offers nothing to follow, while ~/TAKS/3-resources/favicon.ico does.
Mutation-verified before being trusted. A new ~/TAKS path added to foundation/voice.md fails the undeclared check and names file and line; removing a declared reference fails the anti-rot check. Four assertions in total, 224/224 across the suite.
What is deliberately left outside the rule. _state.md defers to TAKS/2-areas/collaboration/metadata.md for its 2,500-character cap, and stays out of scope: _state.md is operational, classed with workspace/, carrying no reader badge. It is the clearest case of the boundary sitting in the right place — a different maintainer replaces that file rather than inheriting it.
The larger exposure this does not address: ~/TAKS/2-areas/collaboration/ is not under version control at all, and three files there govern how this repo is maintained. Out of scope, since none is load-bearing for a rule in the corpus. Recorded on the rule page under Open.
Also done: constitutional/_index.md now lists the seven rules. It had named none of them since 2026-05-30, so the layer's contents were discoverable only by listing the directory.
2026-08-27 — F6 flips to English, and the last thing blocking it was never a decision
foundation/constant-contextual.md is English canonical. The Swedish original — live and accepted since 2026-03-26 — is archived verbatim at _markets/sv/foundation/constant-contextual.md, the sixth page in that package, sixteen days after the other five went on 2026-08-11.
Card 54 blocked this for eleven weeks over a proposal that was never decided, and did not need to be. The 2026-05-31 four-axis proposal sat in the English draft as a bracketed author prompt, and the card's own done-when asked for it to be "confirmed, amended, or rejected". It was superseded instead: documentation/how-decisions-are-recorded.md settles that a pre-declared taxonomy of contexts is the wrong shape, so the proposal was removed rather than resolved. Strip it and the English page is structurally identical to the Swedish page that had been serving for five months — same three examples, same principle, same quick test, same closing line, seven headings each. The flip needed no new Foundation prose at all, which is the opposite of what the card had assumed for eleven weeks.
What the flip corrected, and neither correction reached the archived Swedish copy. The page said "the close expert" — a rendering retired 2026-06-16 in favour of the Present Expert, with the decision brief recording the question as "stale, not open". Typography and imagery were swept then; this page was missed because it never flipped, so it carried the last live instance of a retired brand term, on the page that teaches the brand's own constant/contextual split. Example 3 also stopped framing the colour rule by audience ("a landing page for patients… a clinical interface for staff"), which conflated surface with audience in the one place Baseline explicitly rules that out. The Quick test heading, which the translation had dropped, is restored — it matters because question 3 ("can't find any guidance at all? You've found a gap in the system. Flag it.") is the corpus's own gap-reporting intake, and flipping without the heading would have made it harder to find.
A dead pointer removed from the frontmatter. propagates_to: foundation/expression-calibration had named a page that has never existed since this page was written, in both language versions and in the shipped package. That is what led to retiring F8 on 2026-08-25.
Two gates were crossed on purpose rather than worked around, and one of them caught something. lib/route-manifest.json was hand-edited to record the route as lang: en — the deliberate acceptance card 27 built the gate for. And the flip failed the suite first: KNOWN_LANG_MISMATCH still listed this route, and its guard fired with "is no longer Swedish — remove it from the list." That list's comment had predicted the exact closing condition — "both close when their card closes and the English promotion lands" — and the entry is now removed with the reason recorded. An anti-rot list earning its place: it flagged its own stale entry before anything else noticed.
The route path is unchanged, so all 46 files referencing foundation/constant-contextual keep resolving. Path-preserving promotion is why this flip cost one session rather than the colour page's eleven weeks and counting.
The package mirror is deliberately left behind. _packages/agent-baseline/foundation/constant-contextual.md is still Swedish and its MANIFEST.md row still says so. Not patched — rule 1, packages are generated and never edited. Drift shows in the Package status tab; a cut for a language flip is worth making on its own rather than bundled into the next one.
One Foundation page remains unflipped: emotional-modes.md. Card 53's reader-state umbrella is confirmed (Torfinn, 2026-08-25) but two questions are still open on it — where next-of-kin belongs, and the balanced mode's row — and both are Foundation prose.
2026-08-25 (fourth entry) — Foundation stops borrowing a branch's vocabulary, and card 79 inverts its own premise
Card 79 asked for the positive statement of F6's position, on the grounds that three proposals had tried to give it structure and been refused each time. Checking the corpus corrected the premise, and the correction is the finding. Five attempts exist and they split by scope, not by outcome:
Three global attempts were declined or withdrawn — the four contextual axes (2026-05-31, still PROPOSED in the en-draft, still card 54), enumerating the constants (2026-08-24, declined on Torfinn's objection, "F6 stays a stance"), and the five context factors (2026-08-25, withdrawn the same day). Two domain-local attempts were accepted and nobody has argued with either: the physical domain's four factors, named in its own decision record as "the physical domain's instance of foundation/constant-contextual.md", and Baseline's two surface modes, whose § Surface temperature opens "Baseline applies F6…".
So the position is not that F6 resists structure. It is that F6 stays a stance and structure lives in instances that declare their own reach. The corpus settled this in practice and never wrote it down, which is why the same proposal keeps arriving. The physical instance shows a global list would have been wrong rather than merely unnecessary: its four factors overlap card 54's axes on two (patientgrupp is audience-shaped, land is market-shaped) and diverge on two no global list would ever carry — kliniktyp and byggnadens förutsättningar. A building's constraints decide an interior and mean nothing for a screen. Large enough to hold them the list is useless; small enough to be useful it excludes the factor that actually reorganised that domain's guide.
That reframing overturned card 76 and then closed it. Card 76 read Baseline defining communicative/instrumental, while F6 names neither term, as the defect — "Baseline is carrying a Foundation-level distinction on Foundation's behalf." Under the pattern above, that is not a defect at all: declaring a local instance is what a branch is for, and the physical domain does the same thing uncontested. The real defect was narrower and worse: two accepted Foundation pages were consuming Baseline's local vocabulary, so Foundation depended downward on a branch instance — undeclared, and in the opposite direction to the one the layer model rests on. That is the mechanism that let an accepted Foundation page contradict Baseline silently. Not that the term lived in the wrong place, but that Foundation borrowed a term it does not own and cannot see.
The fix was mechanical, which was not expected. Every borrowed use already explained itself by other means, so the branch term was carrying nothing. iconography.md read "In a dashboard they compete with actual data and risk becoming the sole carrier of meaning. This applies specifically to instrumental surfaces; in a presentation they are unproblematic" — the rule already names its discriminating property and its own contrast pair, so deleting the middle clause loses nothing and reads better. colour.md's cream note already gave the examples. Only two table cells needed new words, and role labels in a table are the utility text the CLAUDE.md calibration explicitly releases.
Six instances, all located before any were changed. Two were in _markets/sv/foundation/colour.md and would have been missed by reading foundation/ alone. sand-100 and sand-50 now read "Page background — pages someone reads" and "— surfaces someone works in", and the Swedish copy the same in its own words. Table cells and the cream note are Claude-drafted; the iconography change is a deletion. The verifying grep found 6 at HEAD and 0 after, so it was shown able to fail. 216/216 tests; no _packages/ path touched, so the mirrors go behind per rule 1.
Ten further uses were deliberately left alone, in principles/design-tokens.md, patterns/hero.md, patterns/chat-bubble.md and schemas/design-token.md. All four publish at Baseline routes and are branch documents by origin. Sweeping them would have destroyed the distinction the fix exists to protect — a branch using its own vocabulary is the layer model working, and BASELINE.md § Surface temperature stays exactly as written for the same reason.
Found and not fixed: the distinction is now defined twice inside the branch layer, at BASELINE.md:56 and principles/design-tokens.md:37, in near-identical words. A duplication inside Baseline rather than an inversion, and which one is canonical is a Baseline call.
Card 79 stays open on two decisions, both Torfinn's and neither a formality: where the F6 position gets written (F6 itself, a constitutional rule, or left as the decision record), and what status documentation/dependency-reciprocity.md carries — it is two documents under one status: field at different levels of ratification, an unscoped graph proposal from 07-28 and the learning model ratified on 08-25. Same shape as card 74's finding on logo-identity.md, where ratified rules sat inside a draft page and the badge described the weakest thing in it.
2026-08-25 (third entry) — The always-red check is fixed by changing the question it asks
lib/branch-hygiene.test.ts fails when any branch is ahead of main. .claude/hooks/backup-push.sh creates a branch ahead of main at the end of every session. So the check could not be green during ordinary work, and had not been since it was written on 2026-08-06.
Measured rather than inferred. Clearing the last four branches turned the suite green at 13:20 — first time ever. At 14:04 a session ended, the hook fired, backup/auto-20260825 came back with a tree byte-identical to main, and it went red again. One empty commit is enough.
The cost was never the red light. It was that being red stopped carrying information. The 2026-08-14 entry above already calls it "one pre-existing unrelated branch-hygiene flag on colour-swedish-port" — noted and stepped past, reasonably, because it was always red. That branch then sat two more weeks holding a verified ΔE number, two of Torfinn's decisions, and a promise to open a card, none of which existed anywhere else. The check was pointing straight at it the whole time.
Fixed by changing the question, not by adding an exemption. Torfinn's call: option (b) with an age condition. Branches matching backup/auto-YYYYMMDD — the hook's own naming, so deliberate snapshots like backup/brand-os-full-* stay under the ordinary rule — leave the ahead-of-main test and enter a new one: does this snapshot hold content that exists nowhere in main? Two days of grace, because ending a session with uncommitted work is normal and is what the hook is for.
The obvious implementation would not have worked, and this is the part worth keeping. Comparing a snapshot's tree to main flags every old snapshot forever, because main moves on and their trees necessarily diverge — it would have swapped one permanent red for another. The check compares content: it walks the 6,444 objects reachable from main (~70 ms) and flags only blobs found nowhere in that history. A snapshot whose content has since landed goes silent however old it is. Ahead of trunk and holds something trunk lacks look like the same question and are not.
Proven against five cases before being trusted, using the file's own BRANCH_HYGIENE_REPO and BRANCH_HYGIENE_TODAY injection points against a throwaway repo: an ordinary branch ahead of main still fails; a five-day-old snapshot holding a unique file fails and names the file; the same snapshot one day old passes; a five-day-old snapshot whose content has landed passes; and this repo passes today with a snapshot present. 216/216.
One fixture bug worth recording, because it is a real property of the check. The first failing-case attempt passed when it should have failed: ageDays comes from the snapshot's commit date, not the date in its branch name, and the fixture's commit was dated today. The hook makes the two agree in practice, but they are different fields, and reasoning about this check from the branch name alone will be wrong.
The hook is unchanged. It is careful, well-built work, and the fix belonged on the side that was asking the wrong question. Both mechanisms were good; only their combination was broken.
Same shape as GAPS.md §4 and card 82, one level out: a mechanism whose writing half works and whose noticing half is unowned. Here the noticing half existed and had been trained into silence.
2026-08-25 (second entry) — The last branch emptied and deleted, and a four-week-old number finally checked
Three backup/auto-* snapshot branches and colour-swedish-port were all ahead of main, which is what lib/branch-hygiene.test.ts had been failing on. All four are now gone and the check is green for the first time since it was written on 2026-08-06.
The backups were verified contained before deletion, not assumed. backup/auto-20260824 was byte-identical to main on its one file. backup/auto-20260815 differed only in package rows since superseded by v0.6.5, v0.7 and v0.7.1. backup/auto-20260825 was the one that mattered: it held the decision record and execution brief as first written, before that day's review pass, and both had been untracked until the amended versions were committed — so main never held the originals. Archived to workspace/archive/pre-review-2026-08-25/ before the branch went. The precedent is in that test's own docstring: four snapshot branches deleted in 2026-08 turned out to hold 705 lines that existed nowhere else.
colour-swedish-port could not be merged, and did not need to be. It ported the Swedish Foundation colour page while foundation/colour.md was still Swedish; ten days later 0ae89ff flipped six Foundation pages to English and moved the Swedish originals to _markets/sv/foundation/, so the branch was editing a file that had changed identity. Its own IN_FLIGHT declaration expired 2026-08-18, meaning merging it would have failed the suite on the expiry rule — that mechanism working exactly as intended.
And the branch was behind main, not ahead of it. All three mechanical fixes it is remembered for — the corrected brand-orange triplet, the 0–900 renumber that fixed a live "Sand 100" naming collision, the Teal legacy placement — were already in the market copy. Diffing the branch's page against _markets/sv/foundation/colour.md gave three lines, and all three were the branch holding older text: the petrol-400, petrol-300 and confirmation-green role claims that agent-baseline v0.6.4 corrected for documenting text uses below the AA contrast floor. Replaying the branch would have been a regression. Recorded because the branch's own changelog entry reads as though that work is outstanding, and it is not.
What was genuinely only on the branch was three records, none of them code, and that is what card 85 rescued.
The ΔE claim is verified, four weeks after it was written down. foundation/colour.md and three workspace documents assert that error red #C14444 and success green #4F866E sit ΔE 76.9 apart in normal vision and 14.6 apart under protanopia, off a Machado 2009 simulation. The model was never in question; the arithmetic on top of it was unverified, and plan-swedish-colour-port-session.md said so in as many words — which formula, which colour space, and whether the simulation ran before or after the difference was taken. It reproduces: 76.87 and 14.55. CIE76 in CIELAB, D65, standard sRGB → linear → XYZ → Lab, with Machado's protanopia matrix at severity 1.0 applied to linearized RGB before conversion to Lab — simulate, then measure.
Computed three times, and deliberately not copied the third time. Once on the branch on 2026-08-11, and again from scratch on 2026-08-25 without reference to that result, because recording someone else's verification without running it is the same error the rest of this week has been about. The check now lives at scripts/delta-e-check.py rather than as a number in prose, so the fourth person gets an answer instead of a claim. It states its method, carries two sanity pairs, fails if a documented figure drifts, and was mutation-verified against a deliberately wrong value before being trusted.
A finding from writing that check, worth keeping because it looks like a bug. Neither published matrix sums to exactly 1 at the precision it is given: the sRGB→XYZ luminance row sums to 1.0000001, and Machado's protanopia matrix has a row summing to 1.000001. So black against white lands at L* 100.000004 in normal vision and 100.000012 under the simulation, rather than exactly 100. Found because the sanity assertion was written at 1e-9 first and failed on both rows. The published constants are the published constants.
Two decisions from 2026-08-11 recorded on card 42, which had been reading as fully open while its central question was half answered on an unmerged branch. Route 3, Torfinn's: the English page points its colour-alone hard rule at [[constitutional/accessibility-is-foundational]], which is accepted but is not in CONTENT_DIRS, so porting the rule would put a dead link on an accepted published page — port everything except that rule until constitutional/ publishes. And the block-C confirmation: the Orange section's new frame is Torfinn's own writing, not lifted from a deck despite the register shift, so it ports as-is. Cheap to record, expensive to re-ask.
Card 86 opened for what route 3 defers — prose blocks D and E, the colour-alone rule's expanded reasoning and "Why colour cannot carry meaning on its own". Blocked on constitutional/ publishing rather than on writing time, and [T], since both blocks are Foundation prose that argues. That card was promised on 2026-08-11 and never opened, because the promise to open it lived in a changelog entry on the branch. A deferral whose only tracker is the branch it was deferred on is not a deferral.
The pattern across all four branches, and it is one pattern. Every loss risked here was a record rather than code: pre-review originals of two documents, a verified number, two decisions, and a promise to open a card. The code was fine everywhere — main was ahead of every branch. What sat on branches was the reasoning, and reasoning is the half this project keeps saying it exists to preserve.
2026-08-25 — Five decisions on the consumer finding, and a review pass that withdrew one of them
A working session took five decisions against cards 72, 73 and 75, recorded them, and wrote an execution brief splitting the work between Claude Code and Torfinn. Torfinn's review the same day moved two of the five. Both documents are amended in place rather than rewritten, because on this one the errors are the reusable part.
Decision 5 — A/B/C is retired as redundant, not relocated. The three-mode digital model (anxiety-driven, aspiration-driven, latent anxiety) is not published, not developed, and not given a home. Its own purpose statement is to "identify the patient's emotional starting point before making any design decisions", but it identifies each mode by product circumstance — has a condition, is choosing proactively, is being screened — so it infers a patient state from the service she bought, with no patient data behind it. Its own examples break the unit it claims ("Patientguide spans A and C"), and Mode C is a transition rather than a mode. Every doubt raised in the session was already written in the source document's own What needs testing, under its own Hypothesis label. This matters for reading the 2026-08-24 entry above: that entry frames placement as the open question, and a reader arriving cold would conclude card 75 was waiting on a destination. It is not. There is no destination.
Two things survive the retirement. The guard — "without naming the emotional mode, the tendency is to default to a generic 'calm and professional' register" — which is a real failure and survives as a principle needing no three labels. And one service property: can this service deliver news the person did not ask for? A property of the service rather than of a moment, and the only part of A/B/C that does not reduce to a flow state. Card 77, which starts from an existing sentence: baseline/governance/4-den-nara-experten-digital-behavior-v1.md:122 already carries it, filed as an undeveloped priority and labelled by the mode being retired.
Card 75 closed in full, and it turned out to be [CC] rather than [T]. It was opened as a placement decision the record had answered twice, differently; decision 5 removed the question. Torfinn took bucket 0 option (b) — do it now in full rather than folding the Swedish half into the eventual balanced-mode pass — knowingly accepting that foundation/emotional-modes.md gets edited twice in one cycle. The middle clause is gone from the Swedish sentence and from the en-draft's (verbatim) line, along with the bracketed pointer to a baseline/ page that does not exist. The package mirror was deliberately left behind, per _packages/README.md rules 1 and 6.
Decision 3 — balanced enters as a third emotional mode. Oro and nyfikenhet are both defined against a pending event, so a surface someone returns to weekly for months has neither. The alternative was to bound the model and route habitual surfaces elsewhere; that leaves it with no zero point, so the two existing modes can only be described against each other. A baseline gives the model an origin and closes card 73 by completing it. Three constraints, all open and all Torfinn's: its own row filled with a positive consequence rather than the midpoint of the other two; a stated kind, because if balanced describes what the design assumes rather than what the person feels then the page has to say so and say the assumption is falsifiable; and the rule that follows from "at her own pace" — the pace is hers, so the surface does not push. No urgency, no streak, no nudge as a default, testable against Sund vikt läkemedel now.
Decision 1 — withdrawn on review, and this is the substantive event of the day. It would have given F6 a named set of five factors, each identity element declaring which bind it, replacing "situation" as an unexaminable axis name. It collided with documentation/dependency-reciprocity.md, amended 2026-08-24: the one modified file in the working tree, and the only file in that tree the session's checks did not read. That record had already put a version of the same proposal to Torfinn and had it declined — F6's job is to let someone reason about a decision no branch or leaf covers yet, and a list answers covered cases and goes quiet on new ones, so "F6 stays a stance". Then the part that decides it: "adjacency is per-decision, not global. There is no single adjacency graph over contexts, and a pre-declared taxonomy of context relations will be wrong — each decision defines its own reach." Its evidence is the children's markers case, where the constraint that reorganised the whole design was that the summons letter names a lettered waiting area — nothing a pre-declared scheme would have contained. Torfinn: that addition is the correct one, and it is the learning model. Decision 2 is orphaned with decision 1, author prompt 2.1 is struck, and card 54's four axes now face an objection about shape rather than membership — which rules out the four axes and the five factors together.
What survives the withdrawal is the measurement, and it is better evidence for the learning model than for the list it was collected to justify: five rules in force each turn on a different property — colour.md's two page backgrounds on receiving-versus-doing, iconography.md's function breaches on whether real data is on screen, BASELINE.md's 44/24px target on input modality, emotional-modes.md on whether something is pending, card 73's gap on how often the person returns. Sund vikt läkemedel's weekly check-in reads five ways at once, only two of them the way the token name suggests.
Decision 4 — amended: the equipment permission is struck. The session permitted a patient-facing instrumental view to carry any image kind, equipment included, inside a component with its own job. Torfinn on review: a passing example, and wrong. foundation/imagery.md is not edited at all, and its refusal of the clinically documentary image stands unscoped. What remains of decision 4 is a relocation and a restriction, both of which the page absorbs without loosening: the restraint rule moves from § "Imagery in professional and internal surfaces" onto whether the person's time on the surface is their job, and the image never holds the hero position on a working surface. Recorded as a position rather than a finding, attributed and dated: a patient's instrumental view may be less efficient and more emotional than a clinician's view of the same kind.
And the exception he named turned out to be a gap. The refusal holds unless the image is clinically appropriate as instructional material. imagery.md governs identity imagery — Life, Care, Portrait, sorted by the story the image tells — and instructional imagery tells none of those stories. It appears nowhere in Foundation and in Baseline only as a compression setting. Read literally, the page forbids the image an injection instruction needs, which is live in sundviktlakemedel and patientguide today. Card 84, and it is a scope question about what imagery.md governs rather than a bullet to add.
A measurement behind two cards was false, in four places. Cards 73 and 75, the 2026-08-24 entry above and the 2026-08-25 brief all assert that nothing under foundation/, baseline/, patterns/ or principles/ mentions any of the three modes. True of the three descriptive names, which is what the greps searched. False of the labels: baseline/governance/4-den-nara-experten-digital-behavior-v1.md:122 (status: accepted) and baseline/iconography/curation-session-b.html:535 both reference the model. Neither publishes and neither ships, so every conclusion drawn from the claim survives — but the en-draft's "a hypothesis living in Baseline" was half right rather than fabricated, and the brief's stated reason for deleting it ("every factual claim in it is false") was overstated. The deletion stands on the reason that survives: decision 5 retired the model, so the pointer points at nothing. Corrected in all four places, and the two references now dangle — card 83.
workspace/status/file-manifest.md archived. The brief asked for an entry in it. It has carried its own banner since 2026-05-30 — "this manifest stays as the historical record of the pre-refactor state", classifications frozen — and its last refresh was 2026-05-07, with nothing from the three months since: not the physical-interior decision record, not the print-surface brief, not the consumer finding behind cards 72 and 73. Meanwhile CLAUDE.md listed it under Key files, under Documentation maintenance as "update when files move", and in the end-of-session ritual. Two documents disagreed for three months and the disagreement was only visible to someone holding both — which is what an execution list does by construction, and is how it surfaced. Moved to workspace/archive/file-manifest-superseded-2026-05-30.md, struck from CLAUDE.md and README.md and from live board card 15, and board.md added to Key files, which it had never been. Torfinn's call.
Nine cards opened, 76–84 — six of them from the review pass rather than the session. 76: communicative/instrumental is used in three Foundation pages and defined only in BASELINE.md § Surface temperature, which is the mechanism that let an accepted Foundation page contradict it silently, and whose "determined by the situation" sentence ships to every consumer. 77 and 78: the surviving service property, and the fact that moments have no home in the layer taxonomy. 79: the withdrawal, and the two things still open behind it — F6's position stated positively, and a decided status and home for dependency-reciprocity.md, currently proposed in a folder that does not publish. 80–82: the learning model's own capture changes, carded rather than executed on Torfinn's scope call — the fourth finding kind, the discriminating-property field, aleris-assistant's missing findings file, and the /wrap question that would make anyone own carrying a finding upstream. 83 and 84 as above.
Three method lessons, all about how this session got things wrong rather than what it decided. git status is part of reading the working tree — the record listed twelve files it had read and carried the caveat that it read the working tree rather than main; it named the right risk and missed the actual one, and the ground is newest exactly where a session is most confident. A measurement is only as wide as the string it searched for — an absence is a claim about a query, so record which strings were searched. A falsifier that can only find a better version of the same answer is not doing its job — decision 1 stated two falsifiers and both tested the membership of the factor list; neither tested whether there should be a list, and it failed on the untested axis.
2026-08-24 — Triaged the consumer finding behind cards 72 and 73, by testing the falsifiers it wrote for itself
The finding from Sund vikt läkemedel (2026-08-20) had sat in workspace/incoming/ since cards 72 and 73 were opened from it. Triage moved it to workspace/archive/, and the work of triage was not the move — it was running the two falsification conditions the document had stated in its own § "What would make each of these wrong". Both were cheap to test and both changed what the cards say.
Finding 1's falsifier: does a patient-facing instrumental rule exist outside the package? No — but the axis does, in the one page card 72 had listed as only "possibly" affected. foundation/iconography.md § "Function breaches" rules out number icons because "in a dashboard they compete with actual data… This applies specifically to instrumental surfaces; in a presentation they are unproblematic." That divides on situation, not audience, in accepted published content. So the corpus already uses both axes, in two pages that cross-reference each other, and the finding's candidate (b) — situation as the axis, with "professional and internal" as one instance of instrumental — has precedent rather than being the larger speculative change. Recorded on card 72.
Finding 2's falsifier: if the detailed digital emotional-mode model exists outside the package, this is a packaging gap rather than a corpus gap — the finding's own words, and the cheaper of the two problems. It is a corpus gap. The three-mode model (anxiety-driven, aspiration-driven, latent anxiety) appears nowhere that publishes: only in workspace/authoring/ALERIS-DESIGN-WORKING.md, five files under workspace/archive/, sources/baseline-web-view-research.md, research/brand-os-inventory.md and this changelog. Nothing in foundation/, baseline/, patterns/ or principles/ mentions any of the three.
(Corrected 2026-08-25: the struck sentence is false, and this entry is where it originates — it was copied from here into board cards 73 and 75 and into the 2026-08-25 brief before anyone re-ran the check. It holds for the three descriptive names, which is what the grep searched. Two files under baseline/ carry the labels: governance/4-den-nara-experten-digital-behavior-v1.md:122, which is status: accepted, and iconography/curation-session-b.html:535. Neither publishes and neither ships, so the conclusion this entry draws — corpus gap, not packaging gap — is unaffected and stands. The sentence is not. An absence is a claim about a query, not about the world, and this one named neither the strings it searched nor the fact that the corpus uses a second form of the name. Board card 83 carries the two dangling references.)
And the gap survives the richer model, which is the part worth keeping. Had the model been found, it would not have answered card 73: all three of its modes are also defined against a pending event — a condition or upcoming procedure, a proactive choice, an entry in the second that a result may shift to the first. None describes a surface someone returns to weekly for months with nothing pending. So card 73 is not "the missing model, missing"; it is a gap the detailed model shares.
Card 75 opened from the same check, and it is the cheap one. foundation/emotional-modes.md promises the detailed model in accepted, published Foundation content, and the en-draft version goes further — it names the three modes, calls them a hypothesis living in Baseline, and instructs "Reference it; don't restate it here. See baseline/ patterns." That pointer resolves to nothing. A published page has been making a forward reference to a page that does not exist, and the record has answered where the model belongs twice, differently (2026-03-26: deeper in the system, linked from F7; baseline-web-view-research.md: Patterns, not Foundations), which is why placement is Torfinn's and not mechanical.
Method note, since it generalises. A finding that states its own falsification conditions can be triaged by running them, and that is cheaper than re-deriving the analysis. Two greps and two file reads confirmed one gap, sharpened the second, and produced a third defect nobody had looked for — because the falsifier pointed at exactly the place where a promise and its referent could disagree.
workspace/incoming/ is not empty. brief-print-surface-scope-2026-08-14.md remains, unexecuted and with no board card. It stays in the zone deliberately: an unexecuted brief in a drop zone is what the zone is for, and scoping a print surface layer is not a triage decision.
2026-08-21 — agent-baseline v0.7: the logo, and the rules that had to be ratified first
Torfinn's test of the seed wiring found that the package carries no logo, so the instruction was to cut a version including it, usable directly by any project cloning Richard's repo. The first attempt was two SVG files in a folder. He stopped it mid-cut: "Logo without rules is trouble."
Reading foundation/logo-identity.md showed how right that was. The page is a draft skeleton. Its factual rules are quoted from Aleris Brand Guidelines 2025 and marked "ratificera eller revidera" — they existed without being in force. Shipping the asset alone would have handed every cloned project a mark with no clear space, no minimum size, and no prohibition list, and the first place a project puts a logo is a header, where all three matter.
Ratified 2026-08-21, unchanged from source: clear space at 50% of the logo's height (p. 6), the horizontal variant as primary with vertical reserved for space reasons (p. 5), the eight-item prohibition list verbatim (p. 8), and the sender rule from the ordlistan (2026-04-21). Recorded against each item in the page. What was not ratified is the reasoning — every Varför and Test in that page is still an author prompt, and a consumer placing a logo does not need the argument to follow the rule. That split is what let a package sheet exist before the Foundation page is finished.
One rule is new, and derived rather than invented: a 24px minimum height. No minimum size existed anywhere — foundation, baseline, principles, constitutional, the package — and the 2025 source does not state one either. It is also the single number a digital consumer needs most. Derived from the accepted 14px type floor: the wordmark's letter height is 0.4933 of the logo's total height, so a letter height of 10.1px (the cap height of 14px type) puts the measured floor at 20.4px, and 24px is the rounded rule with margin. If the 14px floor ever moves, this moves with it. Same shape as v0.6.3's target-size token.
Worth recording how it was measured, because the first method was wrong. Parsing the SVG's path data and taking min/max of the coordinate pairs produced bounding boxes taller than the 193-unit viewBox — arcs and relative commands break the x,y alternation. Rendered it instead with the @resvg/resvg-js already in this repo, at 1930px, and measured ink columns and rows. Measuring rendered output rather than parsed source is this project's own repeated lesson, and it applied to its own asset.
Three gaps are stated in logo/README.md rather than papered over. The vertical variant is named in the ratified rule and has no file, so the exception cannot be exercised. No symbol-only asset ships, and whether the symbol may ever stand alone is open in the page. Digital identity beyond the favicon — app icon, profile image, notification sender name — is absent from the 2025 source and unassigned between Foundation and Baseline.
The favicon is provisional and says so. From TAKS/3-resources on Torfinn's word that it serves for now. It is 31×32 rather than square, so it resamples imperfectly to 16px. Stated in the sheet so a consumer knows what it holds.
A whole version rather than a point release, per rule 6: it adds a package member. All 30 content files verified byte-for-byte against canonical, 30 identical, 0 differing.
Found while cutting, and it is the argument for the whole thing: sundviktlakemedel renders no logo and has no favicon link in index.html. A patient-facing Aleris product with no Aleris mark. Its provenance record now carries an available_not_vendored note pointing at logo/; adding it is a UI change and Torfinn's to ask for. The same record needed its pinned version bumped from v0.6.5, which the check caught rather than a person — the second time this week that cutting a version made a consumer's record stale and the check said so.
Files touched: _packages/agent-baseline/logo/ (new — two SVGs, favicon, README), _packages/agent-baseline/{BASELINE.md, README.md, MANIFEST.md}, _packages/README.md, foundation/logo-identity.md, baseline/BASELINE.md, _packages/adapters/vendored-app/instances/sundviktlakemedel.brand-provenance.json, workspace/status/board.md (card 74). Consumer commit eab2790.
2026-08-20 — The seed adapter is installed, and main turns out to have no design system at all
7156dda on branch add-brand-os, PR #1 updated. Twelve paths: the package v0.6.2 → v0.6.5, the wiring into docs/aleris-standard.md, the design-system section out of CLAUDE.md and AGENTS.md, four rows in the README table, and two new root scripts. Verifier green in that repo — seven checks. Merging stays Richard's, as it has for every version since v0.4.
The commit message is in Swedish, unlike Brand OS's three previous commits in that repo. It lands in his repo and he is the reader; his own commits there are Swedish. The audience rule, applied to a commit message.
One thing verified in a scratch copy rather than in the live repo, and it is the most useful finding of the install: main carries no docs/agent-baseline/ at all. PR #1 has never been merged, so every version of the package this project has shipped — v0.4, v0.5, v0.6, v0.6.2, and now v0.6.5 — exists only on that branch.
Which means the documented refresh, run in a real project today, clones main, finds no package, and the script's completeness guard aborts before writing anything. Confirmed by pointing a scratch project at a main-only clone: it printed "Inget har ändrats i projektet" and left the project's package and wiring intact. Good behaviour, and it was designed in for a different reason — but it means the wiring reaches child projects only once the PR lands. That is now the strongest argument for merging PR #1, and it is a better one than "there are corrections waiting": until it merges, the design system is not in the seed, so nothing cloned from the seed has it.
Worth stating plainly because it reframes eight days of "the mirror is behind": the mirror was never the thing children clone. The default branch was, and it has been empty of design system this whole time.
What the install did not do, on purpose: the four production-checklist.md lines are still an offer, not a commit — that gate is Richard's, and bundling a permission request with an architecture correction is how both get postponed.
Files touched: _packages/README.md (mirror row), _packages/adapters/README.md, _packages/adapters/vibe-coding-seed/MANIFEST.md, workspace/status/board.md (card 71 done). Consumer commit 7156dda.
2026-08-19 (seventh entry) — Richard's review, and it reversed the adapter's central decision
Torfinn supplied Richard's actual words, eight days after they were given and an hour after this session had rebuilt an answer to them from scratch:
Kolla en sväng till på strukturen. Claude.md och agents.md pekar på aleris-standard.md. Så jacka in där. Inte i Claude eller Agents.
Om du inte gjort det, kör några egna tester (eller låt Claude testa) och se att det hänger ihop också, tack.
He was right, and the reason is stronger than the one he gave. docs/aleris-standard.md:119 states that the refresh updates docs/ and not the project's own CLAUDE.md/AGENTS.md, so project-specific content is never overwritten. Wiring placed in those two files is therefore unrefreshable by that repo's own design — frozen at clone time in every child project, forever, with no route for a correction to reach it. Adapter v0.1 put the wiring exactly there, in the same cut that fixed the refresh command for not updating the package. His structural reason stands independently: README.md:32-33 calls both files tunn, and generated content in a per-project file collides with the one section its owner is meant to own.
v0.2 deletes both stanza files and replaces them with one section in docs/aleris-standard.md, carrying the @docs/agent-baseline/BASELINE.md import — transitive through CLAUDE.md's existing import of the standard — and the written-out imperative for Codex, which has no import mechanism. One place instead of two also ends a duplication that would have drifted: v0.1's two stanzas said the same thing twice in slightly different words.
Demonstrated in both directions rather than asserted, against a copy of the repo. Wiring in the thin files survives a refresh untouched and permanently stale. Wiring in the standard is reverted by a refresh from an unpatched seed — the same mechanism working in the useful direction the moment the seed carries it. Watching it revert is the whole argument for his placement.
The second ask became a script, not a test run. wiring/verify-aleris-setup.mjs: seven checks, Swedish output, no dependencies. Import chain, every path the standard names, dead links in the package, strict CSS parse of the token file, wiring in the refreshable file, readable package version, no font files in the repo. Each has broken for real — v0.4 shipped four dead links, a */ inside a comment failed a real build. Run against the repo as it stands it reports three failures, exactly his three complaints, without being told what to look for. A test run answers once; a script in the repo keeps answering, including in the children, which is where the wiring actually has to hold.
Two process findings, filed rather than only fixed.
His comment is now workspace/briefs/feedback-from-richard-pr1-2026-08-11.md. It reversed an adapter's central decision and had lived only in a Bitbucket PR for eight days — the same asymmetry adapters/vendored-app/GAPS.md item 4 names one layer out, and the reason a session an hour ago designed against a reconstruction of his words and got the placement backwards in the same way card 56 had.
adapters/README.md gained rule 4: wiring goes where the consumer's own update path can reach it. Find the refresh path before choosing a location. An unrefreshable instruction is worse than none, because it looks maintained. The profile had recorded the fact that decided this — question 5 already said the refresh skips those two files — and v0.1 read past it, which is the more uncomfortable half: the answer was in the document I wrote.
Files touched: _packages/adapters/vibe-coding-seed/ (v0.2), _packages/adapters/README.md, workspace/briefs/feedback-from-richard-pr1-2026-08-11.md (new), workspace/status/board.md (card 71).
2026-08-19 (sixth entry) — There was never a credential blocker, and the wiring question was answered narrowly in August
Torfinn, reading the draft email this session had just written asking Richard for Bitbucket access: he has had write access all along. It is how v0.4, v0.5 and v0.6 were pushed. The draft was deleted unsent.
The claim was inherited and repeated eight times without being checked. "Blocked on the Bitbucket credentials since card 52" went into _packages/README.md, the adapter's GAPS.md, INSTALL.md, MANIFEST.md, PROFILE.md, adapters/README.md, board cards 67 and 68, and three changelog entries — in a session whose entire subject was stale claims about other systems going unchecked. The board said otherwise in plain sight: card 52 records "Torfinn supplied an Atlassian API token; pushed to the existing open PR branch" and card 56 does the same for v0.6. Reading the card would have taken a minute. Nothing in this session's work depended on the claim being true, which is exactly why it survived — a false premise that blocks nothing is a false premise nobody trips over.
The real history, and it is more useful than the correction. Card 56, 2026-08-11, records Torfinn's brief in nearly today's words: "read Richard's repo first for instructions on how that repo works so that the package is cut in a way that wires into that software correctly." That pass read CLAUDE.md, AGENTS.md and docs/aleris-standard.md, and recorded: "Confirmed there's nothing to fix in Richard's own wiring." It then said so to Richard in the PR comment.
It had answered a narrower question than the brief asked — whether the language flip broke his wiring, which it hadn't — and filed the narrow answer as a general one. The two defects found today were in the three files that pass read: the refresh command does not refresh docs/agent-baseline/ at all, and BASELINE.md is called always-on in three places while nothing loads it. Richard's review said the package needed wiring to fit how his repo works. He was right, and he had been told in writing that it already did. That feedback is the origin of this session's whole ask, and card 56's claim is now struck on the board with the reasoning.
What this changes operationally: the vibe-coding-seed adapter is deliverable now, by the same route the three package versions took — a commit onto add-brand-os and an update to PR #1, which is still unmerged. Merging stays Richard's.
One gap this exposed and did not close: Richard's review comment is not recorded anywhere in Brand OS. It is the strongest wiring feedback the project has received, it is the reason an adapter exists, and it lives only in a Bitbucket PR and in Torfinn's memory of it. sundviktlakemedel's findings file produced four point releases because it was written down; this produced nothing filed for eight days. The asymmetry is the same one adapters/vendored-app/GAPS.md item 4 names, one layer further out: the writing-down is what makes feedback act.
Files touched: _packages/README.md, _packages/adapters/README.md, _packages/adapters/vibe-coding-seed/{GAPS.md, INSTALL.md, MANIFEST.md, PROFILE.md}, workspace/status/board.md (cards 56, 67).
2026-08-19 (fifth entry) — The font licence, read rather than paraphrased: the defect was ours
Torfinn: the Fontspring licence texts are public, and Museo Sans is fully licensed. Read from the primary text instead of from this repo's paraphrase of it, and the question inverts. The PDF-embedding case that had been sitting as an open licensing risk against patientguide is expressly permitted, and the two statements this repo made about the licence were both wrong and both stricter than the text.
What the licence says, with clause numbers so the next reader can check rather than inherit:
- §2e permits embedding the webfont in reports generated by the website, provided they are not sold for profit. A patient document an Aleris app generates and gives to the patient is exactly that.
- §2a prohibits linking the desktop font — the full CFF OpenType or TrueType built for desktop installation. Not font files in general.
patientguideembeds webfont-kit TTFs; their own name table readsMuseo Sans 500 Regular Webfont. - §2d limits use to websites the licensee owns or controls, with no domain count. Fontspring's Worry-Free terms state unlimited domains and no pageview tracking.
- §5 still requires separate licences for desktop use, applications and games, ebooks and epubs, and website templates.
And on the stricter reading — treat the PDF as a desktop-licence document rather than a generated report — Fontspring's own guidance permits a read-only PDF. Those PDFs carry no AcroForm fields; verified by inspecting one.
Both of our claims were wrong, and each in a way that cost something. baseline/setup.md § Step 1b said the EULA "licenses @font-face linking on Aleris-controlled domains only", which presented our own CORS configuration as a term of the licence — a self-imposed limit wearing legal authority. _packages/README.md rule 4 said the EULA grants "linking, not file distribution", which reads as forbidding precisely what §2e permits. The practical rule is unchanged and still right — packages ship no font files — but the reason is drift, not law, and stating it as law is what made a compliant pipeline look like a compliance question for weeks.
Cut as agent-baseline v0.6.5, per rule 6: the wrong claim shipped, and the mirror in aleris-vibe-coding still carries it at v0.6.2. No content file changed — all 28 verified byte-for-byte, 28 identical, 0 differing — only the package's own README.md § Fonts and MANIFEST.md. The shipped correction now also tells a consumer how to do it: embed the webfont-kit file, keep the document read-only, don't sell it.
The lesson is not about fonts, and it generalises. The rule paraphrased an external document nobody had on hand, and the paraphrase drifted stricter than the source. A rule that restates an external document should cite the clause. Worth a sweep of every other rule in the corpus that summarises something legal or contractual — the failure mode is silent, because a stricter-than-necessary rule never trips an alarm, it just quietly costs capability.
patientguide's prototype reconciled the same day, on Torfinn's word that the web app is a prototype and changes to it are fine. Eight token values corrected on top of the type-scale rebase, and one was a live conformance defect: --button-primary-bg resolved to orange-500 #f58c61, the value that repo's own CLAUDE.md says is never an interactive fill because white on it fails AA. Brand OS repointed it to orange-600 on 2026-07-29 and the app had no orange-600 declared at all — the same coral card 47 found on the live sites, reproduced in a prototype. Also corrected: confirm green #27ae60 (not an Aleris colour), the retired confirm variant, --radius-l 12px → 16px, --card-radius, --surface-page sand-50 → sand-100, --radius-full. Its self-hosted woff2 files are gone and it links the central host. 272 shared tokens now resolve identically.
Two deliberately left, and they became board card 70. --chart-4 and --chart-5 map to sequence positions 5 and 7 rather than 4 and 5, because the package's order puts blue next to blue-light and purple next to purple-light — a five-series chart drawn in package order contains two near-identical pairs. The consumer's skip is the more accessible choice, so aligning it would have been a regression wearing conformance's clothes. Whether the twelve-step list is an order to draw in or an inventory of hues is Torfinn's, and the light/dark pairs may simply belong to --chart-pair-N-a/-b instead.
Files touched: baseline/setup.md, _packages/README.md, _packages/agent-baseline/{README.md, MANIFEST.md}, _packages/adapters/vendored-app/{GAPS.md, wiring/font-loading.md, instances/patientguide.brand-provenance.json}, workspace/status/board.md (cards 69, 70). Consumer commit 542ea2a.
2026-08-19 (fourth entry) — Working the decisions the install produced, and one diagnosis that was wrong twice
Torfinn took the four open items in one pass. Three executed, one blocked on a working copy rather than on a decision.
The type scale was mis-based, not differently designed — and finding that out changed the decision he had already made. He first chose the conservative option: raise --font-size-xs to clear the 14px floor and leave the rest of patientguide's web scale alone, on my framing that the upper scale was a different aesthetic choice. Checking the arithmetic before touching it showed something else. Every one of the seven sizes in web/src/app/globals.css was its own commented pixel value divided by 18 — 14/18 = 0.78, 16/18 = 0.89, 60/18 = 3.33 — against a root the app never sets. Same 14/16/18/22/27/40/60 scale as the package, mis-based, everything rendering at 8/9 of the size stated beside it.
That made "adopt the package's numbers" not a design change but arithmetic: the package's values are that scale on a 16px basis. And it made the conservative option actively wrong — setting sm to its correct 1rem would have made it identical to base, also 1rem, collapsing labels into body text. A mis-based scale reconciles whole or not at all. Put back to him with the table; he chose the rebase. All internal-app text grew 12.5% and now lands on the px each comment already claimed.
The same claim was wrong in two places for weeks, in opposite directions. patientguide's CLAUDE.md said the 14px floor renders at 12.48px "because the type scale's 18px root is never set" and attributed it to this repo's package. The mechanism was an exact description — of that repo's own globals.css. Brand OS meanwhile carried the same claim about itself in BASELINE.md until v0.6.1 re-measured and retired it. So one file confessed to a defect it did not have while another accused it of one it did. The provenance check fired on the stale note on its first run, which is how the real address was found.
Target size ratified: console at AA, patient surfaces at AAA. sundviktlakemedel's console takes the 24×24 floor (2.5.8), every patient-facing and touch surface keeps 44px (2.5.5). Carried by a targets prop on Page rather than a wrapper div, because the console is five arms of one ternary. Both densities declare their own --target-min, so a nesting mistake fails towards the stricter value. Four assertions hold it, one mutation-verified. A side effect worth naming: the 36px compact table density is reachable for the first time — it never was, because every action row carries a button and a 44px control forced the row taller. The density level had been in the CSS, unusable, since it was written.
The card radius is ratified and did not land, and the reason is not the radius. Adopting 16px requires the token refresh rather than following it: v0.4's --radius-l still holds its retired 12px value, so repointing the alias against the vendored copy would have given 12px. And both templates/screen.css and templates/vendor/aleris-tokens.css are RENDERER_INPUTS, so touching either makes all eleven packages stale by patientguide's own freshness test and requires republishing every one — while another session was mid-edit on pdf-renderer.js, components.css, html-components.js, render-defaults.js, the schema and three served PDFs in the same working copy. Recorded in the provenance record with blocked_by: a clean working copy. Nothing else blocks it.
The font licence stays open by choice: Torfinn will find the Fontspring text and I will read the PDF-embedding case against it. There is no licence document anywhere in the portfolio, which is itself the finding — rule 4 asserts what the EULA grants without citing it.
Adapter bumped to v0.1.1, because a wiring change is a release including a message. The integrity check told a transcription "a vendored package file is never hand-edited" — right for a verbatim copy, wrong for a verbatim: false entry that exists because the file is a local restatement. It fired on a legitimate edit and pointed at reverting it. The two cases now get different guidance.
Drift in patientguide's transcription is down from 23 mismatched tokens to 16, and the remainder is four separate questions rather than one: the retired confirm variant (hard rule 5), the radius model (card 45), --surface-page sand-50 against sand-100 (hard rules 1/2, where card 47 already found the live sites to be the non-conformant party), and the grid/chart tokens, which differ by indirection rather than by value.
Consumer commits: fe7fd6c (sundviktlakemedel), 4f2a6ff and a1af807 (patientguide).
2026-08-19 (third entry) — Installing the adapter, and the 12.48px that turned out to be ours
Committed the v0.6.4 cut (which had been sitting uncommitted, and therefore invisible to the board's git-derived package status tab), committed the packaging split, then installed vendored-app in both consumers. 84de52b in sundviktlakemedel, 7fc2ed6 in patientguide. 245 and 351 tests green, both provenance checks clean, and each check proven able to fail by tampering with the vendored file and watching the suite go red.
sundviktlakemedel cleared all three expired entries. Token file refreshed v0.6 → v0.6.4 — only two changes in it, the comment fix and the new pointer-target token, no values, so nothing moved visually. cssMinify: false removed and the build is clean with lightningcss minification on for the first time. --target-min now falls back to var(--button-min-height-touch) instead of a literal 44px, which closes the missing-token half without lowering anything: whether that console uses the 24px AA floor is still Torfinn's ratification, and the record now says so in those terms rather than as a missing token.
patientguide stays at v0.4, and the reason is now measured rather than assumed. A token-by-token comparison found exactly two changed values between v0.4 and v0.6.4 — --radius-l and --card-radius — neither referenced from the forms pipeline. So the refresh is safe on values; what makes the lag a decision is that --card-radius now resolves to --radius-l (16px) per card 45, measured live on the three sites, while screen.css:17 still aliases --radius-card to --radius-s (4px). Adopting it changes every printed form and PDF, so it wants a render pass and a look. Review by 2026-09-15.
The install's largest finding was not on any list, and it points back at this repo's own history. patientguide/web/src/app/globals.css declares its own :root with 388 tokens, describing itself as "Source of truth — all values come from aleris-tokens.css". It is a hand transcription, not a copy, so nothing has ever compared it to the package. 271 tokens overlap and 23 hold different values: confirm green as #27ae60 (not an Aleris colour), the whole type scale one step small, --button-confirm-* still wired to the variant hard rule 5 retired, --radius-full at 9999px.
One of the 23 is a conformance defect rather than a preference. --font-size-xs: 0.78rem computes to 12.48px at that app's 16px root, its own comment on the same line reads "14px — accessibility floor", and it feeds --type-small-size, --input-helper-size, --input-error-size and --badge-font-size — helper text, validation errors and badges on a patient-facing surface, below the floor foundation/typography.md states for an audience of patients 40+ with reduced vision.
And this is where the 12.48px in patientguide's CLAUDE.md actually came from. That note said the floor renders at 12.48px "because the type scale's 18px root is never set" — blaming this repo's package. v0.6.1 re-measured and corrected that: the root is 16px and --font-size-xs is 0.875rem, exactly 14px. The number was real all along and the address was wrong, in both directions — Brand OS carried a stale defect note about itself for weeks, and the consumer carried a stale accusation. The provenance check fired on the note on its first run, which is how the real source got found.
Not repaired in place, deliberately: --font-size-sm is 0.89rem (14.24px), so raising xs alone collapses the bottom of the scale. Reconciling the scale is one visible decision across a whole patient-facing app, and it is Torfinn's. Full list in that repo's BRAND-OS-FINDINGS.md.
One earlier claim of this session corrected the same day. The vibe-coding-seed adapter's gap 2 said every child project needs a font allowlist entry and that nobody owns requesting one. Measurement says there is no such step: the font host echoes CORS for any subdomain of aleris.ai or aleris.se plus localhost, and denies everything else — nonexistent-app-xyz.aleris.ai is allowed, myapp.azurewebsites.net and a lookalike domain are not. It is a domain pattern, not a register. The risk is real but different, and sharper: that consumer's standard sends every app to Azure App Service, whose default hostname is *.azurewebsites.net, so a child shipping on its default hostname renders Arial with nothing failing. Corrected in the adapter's GAPS.md, PROFILE.md and INSTALL.md, and in sundviktlakemedel's CLAUDE.md, which had stated the narrower version.
What the next adapter version needs: a transcribed[] kind in the record — parse the target's declarations, compare the tokens it shares with the source, fail on any difference not in a stated allowlist. A transcription is what a project produces whenever the package's shape does not fit its framework, so patientguide will not be the last one. _packages/adapters/vendored-app/GAPS.md item 6.
Files touched: _packages/adapters/vendored-app/{GAPS.md, PROFILE.md, wiring/CLAUDE.stanza.md, wiring/font-loading.md, instances/*.json}, _packages/adapters/vibe-coding-seed/{GAPS.md, PROFILE.md, INSTALL.md}, _packages/adapters/README.md, workspace/status/board.md (card 68 done). In the consumers: the two commits above, plus a follow-up in each.
2026-08-19 (second entry) — The vendored-app adapter: four expired workarounds, found by asserting provenance
Torfinn: write the adapter for patientguide and sundviktlakemedel. Both profiled by reading the repos. They are one consumer type — an app on the forge that vendors package values into its own build — and what it needs is almost the opposite of what the seed repo needed.
The finding that shaped the adapter: for a seed repo, wiring is instruction; for an app with a build, wiring is assertion. Neither of these repos has the seed's problem of nothing loading the design system. The token file is what their build compiles, their component layers declare no literals and a test asserts it, noRawElements.test.ts stops a screen rendering a control that bypasses the component layer, and both suites cite Brand OS's own data-products/tokens/tokens.test.ts as the mechanism they borrowed. Handing these two consumers more prose would be handing the weakest available mechanism to the ones who need it least.
What they had no mechanism for is the package's own provenance, and the check written for it found four expired entries before installation. In sundviktlakemedel: cssMinify: false in vite.config.ts, whose own comment reads "revert this once the token file is fixed upstream" about the */ comment defect fixed in v0.6.2 seven days earlier; the foundation/colour.md read-from-canonical override, made unnecessary by v0.6.1; and var(--target-min, 44px), standing in for the token v0.6.3 added. In patientguide: the CLAUDE.md note stating the 14px floor renders at 12.48px, corrected upstream in v0.6.1 by measuring. A condition written in a code comment cannot fire. That is the argument for a machine-readable record with an expires_when per entry, and the reason an expired workaround is a failure rather than a warning: a dead workaround is a live false claim about another system, and it also makes a version lag look free while the workarounds justifying it sit unnoticed.
Both vendored copies are behind and both still carry the */ defect — patientguide at v0.4, sundviktlakemedel at v0.6, against v0.6.4. patientguide inlines that token file into every rendered page and PDF, where Chromium's leniency is the only reason nothing has failed. Worth noting against the seed consumer, whose updates are blocked on credentials: the two consumers that can be updated freely at any time are the two furthest behind. Access was never what stopped it.
The acknowledgement convention is borrowed from patientguide's own forms/tests/freshness.test.js: a known state is recorded rather than repaired, and the record asserts the state still holds, so fixing it without removing the entry fails too. A deliberate lag is legal here — refreshing patientguide re-renders every published form and PDF — but only with a reason and a review date.
Two decisions came out rather than being answered. Fonts split into two cases and only one is drift: measured against the font host on 2026-08-19, both patientguide.aleris.ai and sundviktlakemedel.aleris.ai are on the CORS allowlist and example.com is not, so patientguide's self-hosted woff2 on the screen path has no remaining reason — it predates the forge move that gave it an aleris.ai origin. The PDF path is different in kind: a PDF embeds a raw TrueType font program, and _packages/README.md rule 4 reads the EULA as granting linking, not file distribution. A document sent to a patient with the font embedded is a case the rule does not address, and closing it means reading the licence rather than reasoning from the web-font clause.
And an asymmetry worth recording. sundviktlakemedel/BRAND-OS-FINDINGS.md produced two filed briefs and four point releases. patientguide has no findings file, and its most consequential finding — the three colour roles documented below the AA contrast floor, which produced v0.6.4 — reached Brand OS with no artefact on either side; nothing in workspace/briefs/ records it. Shipping the file closes the writing half. The carrying half, someone noticing a findings file has content and filing it, is unowned.
Files touched: _packages/adapters/vendored-app/ (new — MANIFEST, PROFILE, INSTALL, GAPS, wiring/brand-provenance.mjs, brand-provenance.test.ts, CLAUDE.stanza.md, BRAND-OS-FINDINGS.md, font-loading.md, and two filled records under instances/), _packages/adapters/README.md, workspace/status/board.md (card 68).
2026-08-19 — Packaging splits in two: content core and per-consumer adapter
Torfinn's question: as the number of consumers grows, will they need different kinds of packages — including wiring that complies with each consumer's own way of working? Answered by comparing the three consumers that already exist rather than by reasoning about it. aleris-vibe-coding, patientguide and sundviktlakemedel all take near-identical content and wire it three different ways — a copy in docs/ with prose pointers, an absolute path reference plus a vendored token file, and a token file copied into src/styles/ with a build workaround for the v0.6 comment bug.
The decision: a package has two halves. The content core (agent-baseline/) is what a consumer needs to know and is the same for everyone. An adapter (adapters/<type>/) is how it reaches one kind of consumer's machinery. Content is one package; wiring is one adapter per consumer type, and the adapter is the half that grows as consumers do. consumer-profile.md is the template an adapter must be written against — ten questions, each stating what its answer changes, every answer citing a file and a line, and anything needing a design decision coming out as a gap rather than an invented answer.
Two wiring defects found by profiling aleris-vibe-coding, both invisible from inside Brand OS because neither is content. Its own documented refresh command, cp /tmp/aleris-std/docs/*.md docs/, does not refresh docs/agent-baseline/ — the glob does not recurse — so a project that follows the ritual faithfully keeps a frozen design system while believing it just updated, and two lines further down an agent is instructed to remind the developer to run it. And BASELINE.md is called "alltid-på" in three separate files while nothing loads it; the @ import that would is present and proven at CLAUDE.md:5 for a different document. Both fixed in adapters/vibe-coding-seed/ v0.1.
Also in the adapter: a Codex stanza that differs from the Claude Code one on purpose (no import mechanism exists there, so two numbered imperatives instead of a description), a refresh script that replaces rather than merges and aborts without touching the project on an incomplete fetch, a findings file that every child project inherits, and four lines offered for docs/production-checklist.md — the gate that actually blocks production in that consumer, and which currently has nothing on it about brand, tokens or accessibility.
The comparison worth keeping: patientguide and sundviktlakemedel each write findings into a file in their own repo, and between them produced every point release Brand OS has ever cut — v0.6.1 through v0.6.4, four releases from two builds. aleris-vibe-coding, the consumer with the widest downstream reach, has only a named person as its route home, and has produced none. The difference is the file, not the goodwill.
Not answered, on purpose: the environment badge that docs/environments.md:131 makes mandatory in every app that consumer seeds, and for which Brand OS has no pattern — folded into card 62 as an eighth candidate rather than drafted, because an adapter that invents brand content is a hand-edited package one layer out. Also open: an owner for the per-hostname font allowlist, and adapter-drift detection.
Installation is blocked on the same Bitbucket credentials as card 52, which now hold back two content point releases and an adapter.
Files touched: _packages/consumer-profile.md (new), _packages/adapters/README.md (new), _packages/adapters/vibe-coding-seed/ (new — MANIFEST, PROFILE, INSTALL, GAPS, wiring/ with six artefacts), _packages/README.md, CLAUDE.md, workspace/status/board.md (card 67, and card 62 gains an eighth candidate).
2026-08-14 — agent-baseline v0.6.4: three colour roles corrected, found by a consumer
(Entry written 2026-08-19 from _packages/agent-baseline/MANIFEST.md, which carries the full account. The cut itself was made on 2026-08-14 and sat uncommitted with no changelog entry until then — worth noting because the board's Package status tab computes drift from git log, so an uncommitted cut is invisible to the mechanism built to catch exactly this.)
Three colours were documented for text use at contrast ratios that do not support it. Found by patientguide and reported back — the return direction working as intended, since the defect was invisible from inside Brand OS.
- petrol-400
#4F868Ewas "Secondary text, labels" at 4.09:1 on white, 3.86:1 on sand-50, 3.49:1 on sand-100 — failing AA for normal text on every page surface, while the token file had said@constraint Not for body texton the same token all along. The two files in the same package contradicted each other and the token file was right. Now: large text only, from 18 pt or 14 pt bold, with the ratio stated. - petrol-300
#7FA9AEwas "Muted text, captions, tertiary" at 2.56:1 on white and 2.19:1 on sand-100 — failing even the 3:1 large-text threshold, while the accessibility table further down the same page already said "Decoration only". Now: borders, dividers, decoration, never text. - Confirmation green
#4F866Ewas "Completed, approved" at 4.23:1. Now large text or icon only, and never carrying the state by colour alone, which the palette's own deuteranopia analysis already required.
A conformance defect, not a preference: anyone who set body text in petrol-400 or petrol-300 on v0.6.3 or earlier has text below the AA floor, and petrol-500 or gray-500 clear it comfortably (10.27:1 and 7.94:1 on white).
Now asserted, so it cannot recur. data-products/tokens/tokens.test.ts gained two checks — every colour documented for text use measured against all three light page surfaces at the threshold its role claims, and no token annotated @constraint Not for body text may be documented for body text. The first carries a coverage assertion, so a colour added to the role table with no classification fails rather than passing silently. Both were mutation-verified by re-introducing the original defect.
Why the existing suite could not see it: petrol-400 has exactly one occurrence in the token file, its own declaration. Nothing resolves to it, so every contrast test in that suite — all of which walk the token graph through --button-*/--input-*/--text-* — was structurally unable to reach it. The claim that made it a defect lived only in prose.
Files touched: foundation/colour.md, _markets/sv/foundation/colour.md, _packages/agent-baseline/foundation/colour.md, _packages/agent-baseline/MANIFEST.md, data-products/tokens/tokens.test.ts, _packages/README.md. 210/211 tests passing, the one failure being the pre-existing branch-hygiene flag on undeclared branches.
2026-08-13 — agent-baseline v0.6.3: closing the drift the package status tab flagged
Torfinn: cut a v0.6.3 with the three mechanical fixes from sundviktlakemedel's second findings document (previous entry). The package status tab had flagged agent-baseline v0.6.2 as 3 files behind main the moment those fixes landed in canonical — this cut is the tab doing exactly what it exists for, closing real drift rather than one more file sitting stale until someone happens to notice.
Contents: --button-min-height-pointer (24px), the button text-zoom/EN 301 549 11.7 fix, and the pre-auth token-access documentation — the same three closed mechanically in the prior session. All 28 files verified byte-for-byte; 3 changed since v0.6.2 (BASELINE.md, both token files), 25 confirmed identical.
Deliberately not in this cut: --border-subtle (board card 64) and the non-colour status treatment (card 65). Both need a design decision Torfinn hasn't made yet — shipping ahead of that would be inventing scope, the same failure _packages/README.md rule 1 exists to prevent.
Not yet shipped to aleris-vibe-coding — Richard's repo still carries v0.6.2, pending the Bitbucket token.
Files touched: _packages/agent-baseline/BASELINE.md, _packages/agent-baseline/tokens/aleris-tokens.css, _packages/agent-baseline/tokens/baseline-tokens.json, _packages/agent-baseline/MANIFEST.md/README.md, _packages/README.md, workspace/status/board.md (card 66).
2026-08-12 (fifth entry) — sundviktlakemedel's second deliverable: two closed mechanically, two opened for Torfinn
A second, fuller findings document arrived from the same build ("this build's second deliverable" per its own framing), filed as workspace/briefs/findings-from-sundviktlakemedel-build-part2-2026-08-12.md. Seven findings: two (the token-file comment bug, the 14px floor) were already resolved by the same day's earlier work — cross-checked and confirmed rather than assumed redundant. Of the remaining five:
Closed mechanically, no new design decision required:
- Target-size token. OQ2 had already relaxed the 44px rule in prose (card 61); nothing represented the 24px AA half as a token. Added
--button-min-height-pointer: 24pxnext to the existing--button-min-height-touch, with a matchingtokens.test.tsassertion and aBASELINE.mdcross-reference. A completion of an already-decided rule, not a new one. - Button text-zoom failure. A normal-length label at 200% text size on a 320px viewport caused horizontal scroll — a real EN 301 549 11.7 conformance failure.
BASELINE.md§ Buttons now requiresmax-width: 100%and label wrapping on the button primitive, and names the identical row-level failure (flex-wrapon control rows). - Pre-auth pages and the token file. A login page rendered before the built stylesheet is reachable can't follow "never use raw values" literally, and that's exactly where a wrong colour hid (
#d9663d,#e6e1da— neither an Aleris value).BASELINE.md§ Token system now documents the one exception and names the four canonical values (page background, text, primary button, border) a pre-auth page needs, with a recommendation to pin them by test.
Opened as board cards, needing Torfinn's design call:
- Card 64 —
--border-subtle. A dense worklist wants a lighter row separator than--border-default(gray-100); no existing token covers it (sand-50would be functionally invisible as a border on its own page). Same shape as the corner-radius default (card 45): a new palette value needs a decision, not a mechanical wiring. - Card 65 — a non-colour status treatment. A genuine third status state ("could not assess", not degraded clear or flagged) has no non-colour token to build from — every existing status value is a colour, which is what the colour-alone rule forbids relying on. The consumer improvised
border-style: dashed; what the system-wide shape should be is a visual-language decision, the same character as the button state model.
Folded into card 62 rather than opened separately: a confirm-with-attestation control (Button variant="complete"), a direct, undecided-free application of hard rule 5 — the same shape as the six patterns already in that card's scope. The brief's other predicted pattern (a StatusChip three-state component) explicitly depends on card 65 being decided first, so it waits there rather than being written prematurely.
Files touched: data-products/tokens/aleris-tokens.css, data-products/tokens/tokens.test.ts, data-products/tokens/baseline-tokens.json (regenerated), baseline/BASELINE.md, workspace/status/board.md, workspace/briefs/findings-from-sundviktlakemedel-build-part2-2026-08-12.md (new). 187/188 tests passing (one new test added, one pre-existing unrelated flag), npm run build clean.
2026-08-12 (fourth entry) — v0.6.2 shipped to aleris-vibe-coding
Same PR, same branch — aleris-vibe-coding's add-brand-os was still open and unmerged from v0.6, so this pushed onto it rather than opening a second one, same pattern as every prior hand-off. Confirmed the branch's real state by git fetching directly rather than trusting a local clone that still showed v0.4. All 31 files (28 manifest + README/MANIFEST/FEEDBACK) verified byte-for-byte against the Brand OS source before pushing.
PR #1's title and a comment updated, the comment specifically flagging the lightningcss/Vite build-breaking fix as something a consumer on that toolchain needs to pull, not just read about — that distinction (a note versus an action) is what a version-bump comment is for. Default branch untouched; merge remains Richard's.
Files touched: docs/agent-baseline/* and PR #1's title/comment, both in aleris-vibe-coding (external repo). In Brand OS: _packages/README.md, _state.md.
2026-08-12 (third entry) — v0.6.2: a build-breaking comment bug in canonical, caught by a second real build
A second build's feedback, arriving mid-shipment of v0.6.1: data-products/tokens/aleris-tokens.css's Data Visualization comment read "Use chart-cool-*/chart-warm-* for intentional temperature grouping" — the */ inside chart-cool-*/chart-warm-* closes the CSS comment early. Browsers recover silently; lightningcss (Vite's default minifier) does not, and fails the build outright. The consumer worked around it with cssMinify: false rather than hand-editing the token file, and reported it back rather than living with the workaround.
Root cause was stale documentation, not just a stray character. The comment named tokens — chart-sequence, chart-cool-*, chart-warm-* — that were never implemented; the real tokens are --chart-1–--chart-12 (sequential) and --chart-pair-N-a/-b (paired). Rewrote the comment to name what actually exists, which fixes the syntax bug and the misleading documentation in one pass. Swept the whole file for a second occurrence — none found. No token value changed.
The same feedback independently confirmed v0.6.1's 14px-floor fix was correct: "resolved, by measuring rather than reading." Recorded as confirmation, not a new action item.
v0.6.2 cut rather than amending v0.6.1 in place, even though v0.6.1 hadn't reached any external consumer yet — each real defect gets its own dated, tracked version. All 28 package files re-verified byte-for-byte; 1 changed since v0.6.1 (tokens/aleris-tokens.css), 27 identical.
Files touched: data-products/tokens/aleris-tokens.css, _packages/agent-baseline/tokens/aleris-tokens.css, _packages/agent-baseline/MANIFEST.md/README.md, _packages/README.md, workspace/status/board.md (card 63).
2026-08-12 (second entry) — v0.6.1 point release: fixes from real consumer feedback, and the point-release model itself
A real build, Sund vikt läkemedel, used agent-baseline v0.6 the day after it shipped and sent back workspace/briefs/findings-from-sundviktlakemedel-build-2026-08-12.md: 6 defects, 7 gaps, 3 successes, 5 open questions. Torfinn: fix the three confirmed defects now, and answered the five open questions in the same breath (v0.6.1 as the versioning approach; relax the target-size rule; leave dark-mode-on-instrumental-surfaces explicitly TBD; reframe density as a note to builders; write the six missing pattern files now).
The imagery.md regression, root-caused before fixing. foundation/imagery.md had visibly lost content between v0.5 and v0.6 — a whole "Imagery in professional and internal surfaces" section, the Group Image Guidelines framing, the AI-generated-images and image-bank paragraphs, the market-variation and three-layers closing paragraphs. git log --follow traced it: the page flipped to English canonical back on 2026-06-17 ("Land F10 imagery"), an unrelated migration months before the language sweep. An intervening, unrelated commit (62b939d, branch-tail cleanup) accidentally un-tombstoned the superseded English draft. The 2026-08-11 sweep then treated that revived draft as live content still needing promotion, and overwrote the correct canonical page with it — while archiving the actual correct content into _markets/sv/foundation/imagery.md, mislabelled as "the Swedish original" it never was. Fixed at the source: canonical restored, the mis-filed copy removed, _markets/sv/MANIFEST.md corrected to five archived pages, not six.
Two BASELINE.md corrections, both self-contradictions or stale claims a build shouldn't have had to catch. The Buttons section still listed a Confirm (green) button variant with no note, directly contradicting hard rule 5's own claim to be "the rule of record" for confirm (retired 2026-07-31) — fixed with the same superseded-note pattern already used for the hover-direction rule. The accessibility conformance table called the 14px font floor "tested, and failing"; re-run, the test passes — --font-size-xs is 0.875rem, exactly 14.0px at the real 16px root, and nothing in app/ overrides root font-size. The stale claim assumed an 18px root that was never actually set anywhere. The unrelated, still-real qlik.module.css sub-14px-literals note stays.
Two policy answers from Torfinn, both now in BASELINE.md. Target size (OQ2, "relax it"): 44px stays the floor for patient-facing and touch contexts; dense instrumental surfaces on desktop pointer input now use 24×24 CSS px (WCAG AA 2.5.8), not the AAA-level 44×44 (2.5.5) the old blanket rule implemented everywhere. Density switcher (OQ4, "note to builders"): reframed from a stated requirement to an expectation to build toward — a first cut shipping only the default density is a known gap, not a violation.
Dark mode on instrumental surfaces: explicitly left TBD (OQ3). Torfinn: this needs a real design pass, not a quick mathematical or logical extrapolation from whatever the eventual communicative-surface dark-mode rule turns out to be. Recorded as an open scope question on board card 57, not answered by inference.
The point-release model, established rather than invented ad hoc (OQ1). _packages/README.md rule 6 amended: a point release (v0.6 → v0.6.1) is a full, byte-verified regeneration like any other — "point" describes the size and nature of what changed (corrections to something already shipped, not new decided content), not a lighter process. v0.6.1 also picked up the "No dark mode" retirement note that v0.6 had shipped without — closing a gap _packages/README.md had been flagging as outstanding drift since the day v0.6 went out.
Six tool-surface pattern files (OQ5): flagged, not written. Torfinn's instruction was to write them now. CLAUDE.md's content-creation boundary is explicit that Claude does not "produce pattern examples for the pattern library" — six new pattern entries read as exactly that, not the utility text the 2026-07-29 calibration freed up. Raised as board card 62 with three concrete options rather than either silently complying or silently sitting on the instruction.
Files touched: foundation/imagery.md, foundation/colour.md (package pickup only, no canonical change), _markets/sv/foundation/imagery.md (removed), _markets/sv/MANIFEST.md, baseline/BASELINE.md, _packages/README.md, _packages/agent-baseline/*, workspace/status/board.md. Full suite green (186/187 — the one flag is the pre-existing, unrelated colour-swedish-port branch-hygiene note) and production build clean. Not yet shipped to aleris-vibe-coding — Richard's repo still carries v0.6, pending the Atlassian token.
2026-08-12 — Package status tab: computed drift, board card 59
Torfinn: a status page on the board, under a separate tab, showing the state of active packages and what features they're behind main on — naming the exact example live at the time: agent-baseline v0.6 doesn't have the "No dark mode" retirement from the day before.
Why this isn't more hand-written prose. _packages/README.md already carried this kind of note by hand — the sRGB triplet parked behind a freeze, the radius default, and, as of yesterday, the dark-mode retirement. Every one of those required someone to remember to write it down and remember to keep it current. That's the same failure this corpus has caught itself in before, just the prose side of it rather than the measurement side (cookbook §13 named the measurable half winning by default; this is the reverse — the unmeasured half going stale because nothing checked it).
What it computes, and how. workspace/status/build-board-html.py gained a PACKAGES config: for agent-baseline, seven directory-prefix rules (not a 28-file list) saying which canonical directories the package mirrors. The generation commit for a package is derived, not recorded twice — it's the last commit to touch that package's own MANIFEST.md, which every regeneration this session has already updated in the same commit as the file copy, so the tool needs no separate date field to trust. Everything committed to a mirrored canonical path after that commit is drift, shown with its real hash and subject line — and because commit messages in this repo are already written to explain themselves, that line doubles as the human-readable "what changed," with nothing second to keep in sync.
Verified against its own reason for existing, not just built and trusted: agent-baseline v0.6 (generation commit 1681658) correctly reports foundation/colour.md as 1 of 28 files behind, citing commit 8169bdf — the "No dark mode" retirement, exactly the case the brief named.
Tabs are native radio+label, no JavaScript, matching this page's existing "readable without JS" pattern (cookbook pattern 1) — both panels stay in the DOM regardless of CSS support. Checked for real, not assumed: board.html isn't part of the Next.js app, so a bare file:// open renders as a static, non-interactive snapshot in the preview tool used to verify it; served locally instead and clicked between tabs.
_packages/README.md gained rule 7 pointing here: that file now records decisions, the board tab records state.
Files touched: workspace/status/build-board-html.py, workspace/status/board.md/.html, _packages/README.md.
2026-08-11 (third entry) — "No dark mode" retired: a legal requirement, not a brand preference
Torfinn: respecting a user's local/personal display settings — dark mode, high contrast, dynamic text size — is a legal accessibility requirement, the same class of obligation as the WCAG floors already in Foundation, not something Aleris interfaces can override with a brand choice. The full positive rule (what Aleris looks like in dark mode) is separate, real design work and comes later (board card 57); today's action is narrower — remove the prohibition itself, now, since it can't stand regardless of what replaces it.
Retired in foundation/colour.md (the canonical hard rule) and foundation/en-draft/constitutional/colour-is-the-aleris-palette.md (the still-draft constitutional page, which had already flagged uncertainty about this exact rule's status in its own Open section — resolved now, not just removed). documentation/site-spec-v2.md corrected too: it cited "no dark mode" as evidence the Brand OS site follows its own rules, which is backwards now — the site being light-only is an implementation gap, not proof of discipline.
Not touched, deliberately: _markets/sv/foundation/colour.md (archived Swedish snapshot — frozen by design) and _packages/agent-baseline/ (packages are regenerated, not hand-edited; flagged as behind canonical in _packages/README.md instead, picked up at the next cut).
New board cards: 57 (design the positive rule — real design work, not mechanical) and 58 (review the icon allowlist, raised the same session — too conservative relative to the cost-of-mistake principle foundation/iconography.md itself states).
Files touched: foundation/colour.md, foundation/en-draft/constitutional/colour-is-the-aleris-palette.md, documentation/site-spec-v2.md, _packages/README.md, workspace/status/board.md/.html.
2026-08-11 (second entry) — agent-baseline v0.6 shipped to Richard, reflecting the English flip
Board card 56, following directly from the same day's site flip (previous entry). Torfinn asked to read aleris-vibe-coding's own instructions before cutting the package, not just regenerate and push — checking whether the language change needed any wiring changes on Richard's end, not only a content refresh.
Read first, cut second. CLAUDE.md, AGENTS.md, docs/aleris-standard.md — all Swedish-language, all pointing agents at docs/agent-baseline/BASELINE.md and the token files by path. Nothing there depends on which language the content at those paths is in, only on the paths existing — and none of today's changes touched a path, only what's written at five of them. Confirmed there was nothing to fix, rather than assuming so; noted explicitly in the PR comment so Richard doesn't have to re-derive that himself.
The cut: index, colour, typography, voice, imagery now carry the reviewed English content instead of Swedish. foundation/iconography.md added — every prior manifest excluded it as draft, "ships once authored"; it's accepted now with Torfinn's own closing paragraph. constant-contextual.md/emotional-modes.md stay Swedish, matching canonical (cards 53/54). 28 files verified byte-for-byte against canonical before cutting; 31 (28 + README/MANIFEST/FEEDBACK) verified again after syncing into Richard's repo.
Shipped onto the still-open PR #1 (same pattern as v0.5, card 52) rather than a new PR. Title and a comment updated — the comment specifically flags that this version changes language, not just values, since that's a different kind of thing for a reviewer to notice than a token tweak.
Files touched: _packages/agent-baseline/*, _packages/README.md, workspace/status/board.md/.html. In aleris-vibe-coding (external repo): docs/agent-baseline/*, PR #1's title and a comment.
2026-08-11 — the site flips from Swedish to English, and a market-package pattern is born
Torfinn: "I need the english files to be the live and accepted content and the swedish to be put aside for now." What started as "move en-draft/ into foundation/" (the request itself) became a language sweep, a package-architecture question, and a real routing/UI change once the sweep found the request's premise didn't fully hold.
The sweep found a corpus-wide defect, not just missing translation. Of the eight top-level en-draft/ files, every one except colour.md carried its own "Draft — unverified translation... do not treat as canonical or stakeholder-facing" banner — and four of those (typography, voice, index, emotional-modes) claimed status: accepted in frontmatter at the same time, a direct self-contradiction that had sat unnoticed since the 2026-05-31 machine-translation batch that produced them. colour.md was clean because it was never a translation — it was authored fresh in English for the colour supersede and verified against ground truth this session (hex values against the token file, all 17 contrast claims recomputed and reproducing exactly).
Six files earned promotion after review, two didn't. colour, typography, index, imagery, iconography (closing paragraph written by Torfinn), voice (one item deferred) — reviewed, decided, no unresolved substance. constant-contextual and emotional-modes stay Swedish: their English drafts carry real unconfirmed framework proposals — a reader-state rename, a four-axis contextual model — not just untranslated prose. Board cards 53/54 track those; card 55 tracks the one deferred item in voice.md (the "For AI systems" idiom rule, self-referential now that its own page is the English source rather than a Swedish translation target).
Two more constitutional/principles drafts turned out to be further from done than "just needs promoting": constitutional/colour-is-the-aleris-palette.md and typography-is-museo-sans.md are explicitly marked (draft-xlat) throughout, "still needs Torfinn's voice pass" in their own words, despite a header claiming Status: decided. principles/voice.md is honestly status: draft with two full sections marked as needing Torfinn's voice specifically. None of the three moved — moving an unfinished draft into a live layer accomplishes nothing.
A new pattern: _markets/. Torfinn's framing, mid-conversation: think of country-specific Foundation content the way _packages/ already thinks about consumer packages — versioned, derived, self-documenting — but for a different purpose, since a market page needs an actual voice pass, not a mechanical copy. _markets/sv/foundation/ holds the six archived Swedish originals, each carrying whatever state it was actually in (colour.md's copy is the mechanically-ported-but-prose-incomplete state from board card 42, not a stale pre-port version and not a fabricated finished one). _markets/README.md documents the model and flags what's still open (no dependency wiring, no staleness check, no serving path decided).
The actual flip touched far more than content files. i18n/routing.ts and app/[locale]/layout.tsx's SUPPORTED_LOCALES moved from ['sv'] to ['en']; messages/en.json created (UI chrome — nav labels, page titles, footer, errors — translated, not brand prose); lib/translations.ts (a second, non-locale-aware helper) repointed from sv.json to en.json. Every hardcoded /sv/ path across Header, Footer, both sidebars, ConceptBreadcrumb, the root and auth-callback redirects, and the nav/baseline route tables needed repointing to /en/ by hand — none of this is covered by any test, so it was caught by walking the live preview end to end, not by npm test. Same for a second sweep of hardcoded Swedish strings in the auth and comment UI (login, logout, onboarding, reply, resolve, the comment-notification email) — utility text, not gated content, translated directly.
lib/content.ts gained a fix that had been flagged and never closed. en-draft/ was never in SKIP_DIRS — a gap the code's own 2026-07-30 comment named directly — so bumping iconography/imagery to accepted mid-session made them start publishing at stray auto-generated routes before the fix landed; caught by route-manifest.test.ts doing exactly its job. en-draft is now skipped outright, closing the leak for good rather than patching around today's instance of it.
lib/route-manifest.test.ts's language-mismatch check needed its direction reversed, not just its data updated. It was written assuming the site served sv and flagged English leaking under it; the moment SUPPORTED_LOCALES became ['en'] that premise inverted. Rewritten to flag non-English content under the en locale instead — constant-contextual and emotional-modes are now the (correctly) allowlisted exceptions.
One real defect fixed on the newly-canonical colour.md: two [[constitutional/accessibility-is-foundational]] wikilinks would have rendered as literal bracket text (no wikilink plugin exists in this codebase) on a now-live page — converted to plain file-path references. This is the identical dead-reference risk board card 42's Step 1 was designed to keep out of the Swedish page, resurfacing in English the moment English became the accepted one.
Six commits on main: 0ae89ff (content promotion + market package), 29ff084 (locale config), 7b62d95 (nav path fixes), 095d28c (manifest + test direction), bf95d0f (UI chrome translation), 3fac34e (board card 55). 186/187 tests passing throughout (the one failure is the expected, documented colour-swedish-port branch-hygiene flag — that branch is unrelated and still paused). npm run build clean at every commit. Verified live via preview, not just build output: homepage, /en/identity/colour, /en/foundation/iconography, breadcrumbs, sidebar, login/comment UI.
Files touched: too many to list individually — see the six commits above for the full, grouped diff.
2026-08-10 (sixth entry) – agent-baseline v0.5 shipped to aleris-vibe-coding, onto the still-open v0.4 PR
Torfinn supplied an Atlassian API token the same session card 52 was opened, closing the last piece of the day's package consolidation.
Checked before touching anything, and it changed the plan. The v0.4 hand-off (card 14, 2026-08-07) was never merged to aleris-vibe-coding's default branch — a GET on the Bitbucket REST API confirmed PR #1 (add-brand-os → main) was still OPEN. Opening a second PR with v0.5 would have split Richard's review across two open PRs carrying the same package at two versions; updating the existing branch instead keeps it one PR, one review.
Execution. Cloned add-brand-os, found docs/agent-baseline/FEEDBACK.md — a file outside the 27-file manifest, added in the same commit as v0.4 — byte-identical to the current source copy, so a full rsync --delete sync was safe rather than a selective patch. Replaced docs/agent-baseline/ wholesale with _packages/agent-baseline/ (30 files: 27 manifest + README/MANIFEST/FEEDBACK), which correctly dropped reference/aleris-design-tokens.md along with everything else unique to v0.4. Pushed to add-brand-os; PR #1's title updated v0.4 → v0.5 and a comment posted summarizing what changed, since Richard is mid-review on a PR that just moved under him. The API token was used transiently — a scratchpad file outside the repo, chmod 600, deleted at the end of the session — and never written into the cloned repo's git config, .git-credentials, or any committed file.
All 30 files verified byte-for-byte against _packages/agent-baseline/ both before and after the push. Merging PR #1 remains Richard's call.
Files touched: _packages/README.md (mirror row → v0.5), workspace/status/board.md / .html (card 52 closed), _state.md. In aleris-vibe-coding (external repo, not this one): docs/agent-baseline/*, PR #1's title and a comment.
2026-08-10 (fifth entry) – package system consolidated to one standard; the aleris-vibe-coding v0.5 hand-off opened, blocked on credentials
Torfinn: consolidate the package system, agent-baseline v0.5 as the single standard.
Retired, not deleted. klinfys-baseline/ (Klinfys redesign) and bhsv-baseline.zip (BHSV product team) both predated the manifest rule introduced with agent-baseline — neither had a recorded file list to regenerate against, so each had sat stale since at least 2026-08-05 with no path back to current. Both moved to a new _packages/_archive/, along with a stale pre-manifest agent-baseline.zip (2026-06-17) that was sitting next to the live agent-baseline/ folder under a confusing shared name. _packages/_archive/README.md records former consumer, last version, and retirement reason for each. _packages/README.md's current-packages table now carries one entry — agent-baseline — plus its aleris-vibe-coding downstream mirror, with a standing note that any future consumer, Klinfys or BHSV included, gets pointed at agent-baseline rather than a bespoke cut.
Deliberately not done: telling Klinfys or BHSV. This is file consolidation and documentation; consumer outreach is Torfinn's, not something this session can decide on his behalf.
Opened in the same pass: the aleris-vibe-coding mirror is still at v0.4 (card 14, 2026-08-07) while canonical agent-baseline has been at v0.5 since card 49. The hand-off needs the same kind of Bitbucket/Atlassian credential card 14 needed before Torfinn supplied it that session — this session holds none. New board card 52, blocked on Torfinn.
Board cards 51 (closed) and 52 (opened, blocked) added; board title and milestone line corrected to v0.5.
Files touched: _packages/README.md, _packages/_archive/README.md (new), _packages/_archive/* (moved), workspace/status/board.md, workspace/status/board.html (regenerated).
2026-08-10 (fourth entry) – agent-baseline v0.5 shipped; the full BASELINE.md retirement scoped, then paused
Torfinn asked whether schemas/design-token.md (new that day, card 48) was essential enough for an LLM to justify adding to the package. Tracing the answer back — BASELINE.md's own Token System section already restates most of what the schema file says, and constitutional/tokens-are-canonical.md already has an open item calling for that section to become a pointer instead — led to Phase B step 6, the one piece of the approved rebuild plan nobody had picked up: BASELINE.md splits and retires entirely.
Scoped in full: three new constitutional pages (status colour, motion, corner radius — all extraction of already-decided content, not new rule statements, matching the pattern tokens-are-canonical.md and language-policy.md already used), two new principles pages (buttons, surface temperature), two to three new patterns pages (forms, tables, loading states), a real extension to constitutional/accessibility-is-foundational.md (it's a v0.2 draft; BASELINE.md's own Accessibility section, with card 18's test-coverage audit, is more complete), a retirement stub rather than a deletion (the file is a published route and the named, self-contained entry point _packages/agent-baseline/README.md tells every downstream agent to load), and one real unsolved consequence flagged rather than invented: the package's whole "one file is the entry point" model breaks once that file is a stub, and nobody has designed its replacement yet.
Torfinn paused it there: "this is too big a scope right now. I just need to get v0.5 out." The plan is saved (~/.claude/plans/refactored-booping-parrot.md) rather than discarded, and a board card (50) holds its place in Ready now, explicitly not to be pulled piecemeal against a half-retired file.
What actually shipped instead — the narrow fix. agent-baseline regenerated to v0.5, closing the two things it had gone behind canonical on that same day: the radius default (card 45) and the Phase B step 3 file moves (card 48). reference/aleris-design-tokens.md dropped from the package rather than replaced with either half of its split — shipping the old file would mean shipping a document marked superseded at its own top; shipping nothing in its place is the honest interim state. BASELINE.md's File Reference table, which was still pointing at the pre-move token path and at the now-split file, was fixed in canonical first — not patched in the package copy, per rule 1. All 27 files re-copied and verified byte-for-byte. The schemas/design-token.md package question stays open, explicitly rescoped from "decide it now" to "decide it at the next cut" so this one could stay narrow.
187 tests, full suite green apart from the expected branch-hygiene flag, npm run build clean.
Files touched: baseline/BASELINE.md, _packages/agent-baseline/* (regenerated, including MANIFEST.md), _packages/README.md, workspace/status/board.md.
2026-08-10 (third entry) – Phase B step 3 executed: tokens and the reference file move into the layer taxonomy
Torfinn: "Execute step 3 now." Card 46 (what the reference layer is for) closes by being executed rather than further decided — the direction was already mapped twice over (the 2026-05-30 Phase 0 migration map and the 2026-08-05 Phase B plan), just never run.
Moved. baseline/tokens/{aleris-tokens.css, aleris-fonts.css, baseline-tokens.json, generate-tokens-json.js, tokens.test.ts} → data-products/tokens/. baseline/iconography/{allowlist.json, README.md} → data-products/iconography/allowlist.json and schemas/icon-allowlist-entry.md — the four curation HTML tools stayed in baseline/iconography/, per the migration map's own "TOOLING → renderer islands, not corpus" call; only index.html's one live fetch() and two footer-text paths needed fixing to keep pointing at the data that moved out from under them.
Split. baseline/reference/aleris-design-tokens.md (618 lines, three things wearing one name per card 46's own analysis) and baseline/reference/baseline-token-architecture.md became schemas/design-token.md — the shape of a token record, the three-layer architecture, framework bridges, Figma sync notes, all restated values dropped — and principles/design-tokens.md — 17 decision records, each reframed as the question it answers, the first real occupant of that layer's own documented convention (principles/_index.md, previously demonstrated only in an unlanded draft). aleris-token-governance-frameworks.md, the migration map's third merge target, turned out to already be dead — archived and superseded before this session — so it needed no work. Both live source files kept in workspace/archive/, marked superseded, not deleted.
What made this safe rather than just fast. patterns/'s 2026-08-05 move (card 32) kept its published routes for free, because dropping the baseline/ prefix from baseline/patterns/hero.md left the exact same relative path patterns/hero.md, resolvable from the repo root with no code change. Tokens and the reference file don't share that luck — tokens/aleris-tokens.css becoming data-products/tokens/aleris-tokens.css changes the leading segment, not just the prefix. lib/baseline-content.ts's resolveBaselineSource gained an explicit MOVED_PATHS map for exactly this case, so every route under /baseline/... that pointed at these files still resolves, unchanged, to their new locations. vitest.config.ts gained data-products/**/*.test.ts in its include list — the identical silent-collection failure the original baseline/**/*.test.ts entry was added to catch on 2026-07-30 (a moved test file that npm test quietly stops running), now with a new place to reintroduce it.
Test 14 retired by construction. The reference-drift check (tokens.test.ts, "the token reference does not contradict the token file") parsed value claims out of a restatement table and compared them to canonical. After the split, there is no restatement table anywhere in the corpus — schemas/design-token.md states no token's value, per constitutional/tokens-are-canonical.md's own rule. A check with nothing to check is not a weaker guarantee than a passing one; it is the same guarantee with the failure mode it was built to catch made structurally impossible instead of merely detected.
One real defect, caught by reading rendered content rather than trusting a 200. app/baseline/page.tsx's landing page carries a card: "Learn the system — understand the principles behind the design decisions," linking to /baseline/reference/aleris-design-tokens. That URL still resolved after the move (the whole point of the move-map) — but it now served schemas/design-token.md, the shape, not the principles. The mechanical check (does the route 200?) passed while the promise the card made to a reader was wrong. Repointed to /baseline/principles/design-tokens. Verified against a running server on an alternate port, since another session's dev server already held the usual one: every moved and new route checked by grepping the rendered body for content unique to the new file, not just a status code.
Left alone, on purpose. The four iconography curation HTML tools, per the migration map's own tooling disposition. agent-baseline's regeneration — now two structural changes behind v0.4 (the radius default, and this move), noted in _packages/README.md with the open question of whether schemas/design-token.md belongs in the next cut by the same execution-relevance test that already decided Figma notes don't.
187 tests, full suite green apart from the expected branch-hygiene flag on this unmerged branch, npm run build clean throughout.
Files touched: lib/baseline-content.ts, lib/baseline-nav.ts, vitest.config.ts, app/baseline/page.tsx, app/tools/icon-picker/qlik/{page.tsx, types.ts, QlikPicker.tsx, ChromeIcon.tsx}, baseline/iconography/{index.html, curation.html, curation-session-b.html}, CLAUDE.md, constitutional/{tokens-are-canonical.md, accessibility-is-foundational.md}, foundation/{colour.md, typography.md, iconography.md} + their en-draft/ twins, documentation/architecture-tools-and-site.md, lib/branch-hygiene.test.ts, _packages/README.md, plus every moved and new file named above.
2026-08-10 (second entry) – card 47 decided (rule stays), card 46 status checked (not decided)
Card 47: option A. The live sites (aleris.se/.no/.dk) shipping a white page background with sand inside cards, against Baseline hard rules 1 and 2 (sand page, white cards), are non-conformant — canonical stays as reasoned, nothing changes. The distinction from card 45 is the reason, not the shape of the question: the radius default was an unexamined Baseline role assignment with no cited evidence, so a clean production measurement was legitimate grounds to change it. The sand-page rule traces to reasoned Foundation content (foundation/colour.md: sand exists specifically so the page doesn't read clinical) — Foundation outranks Baseline when they conflict, so a live measurement doesn't carry the same weight here. A second tell pointed the same way before Torfinn even ruled: the same three sites also ship a primary button colour (#F58C61 coral) matching neither canonical orange-600 nor alerisgroup.com's own petrol — three divergent facts on one property reads as drift off a different system, not a considered "white works better for patients" call. No file changed. Those sites aren't tracked as a downstream consumer anywhere in _packages/README.md, so surfacing this to whoever owns them is outside what a board card here can do.
Card 46: checked, not decided. Torfinn recalled correctly that "split by kind" was already the direction — it is, twice over: the original Phase 0 migration map (workspace/plans/phase-0-target-structure-and-migration.md, row 90, dated 2026-05-30) targets baseline/reference/aleris-design-tokens.md at schemas/design-token.md + principles/, and the approved Phase B execution plan (~/.claude/plans/joyful-petting-hopcroft.md, written 2026-08-05) sequences the whole token layer's move to data-products/ as its step 3. Neither has executed: schemas/ and principles/ are still the empty June stubs, and Phase B has only completed step 1 (constitutional core — the four constitutional/* stubs, last one landing 2026-08-06) and step 4 (patterns reconciliation, card 32). Phase 0 itself has sat at status: proposed since 2026-05-30, over ten weeks, with a parked "Phase 0 sign-off" item in _state.md that nobody has picked up since colour shipped. What's actually open for card 46 is not the direction — it's whether to execute Phase B step 3 now (which resolves 46 as part of a larger move touching aleris-tokens.css's own path, a "consumer-contract — keep stable" file per the plan's own caution), scope a narrower version of just this one file, or leave both parked behind the sign-off that's been waiting since colour.
2026-08-10 – the radius contradiction closes: cards derive inward from 16px
Card 44: the 2026-08-07 sweep is finally verified. A brief that day found baseline/reference/aleris-design-tokens.md still asserting a 12px-card/pill-button radius model canonical had abandoned months earlier, in eight places — a partial correction on 2026-07-30 had fixed one row and left the rest. Torfinn decided the same day: the pill-button record was never in force, delete rather than supersede it; --radius-l is retired; the corner-radius rulespace is one problem, not case-by-case, with the base value and derivation direction left open. The sweep itself was committed 2026-08-09 (f60db76) but never run, made from an arm64 shell where vitest's native binding failed. Verified today: 163 tests pass, drift check proven by restoring the pre-sweep row and watching it fail with the exact contradiction named, then reverting.
Card 45: the corner default, decided — for cards. A second brief the same day reframed the open question with a finding: computed styles measured live on aleris.se/.no/.dk show every card shipping at 16px against Baseline's 4px, already nesting correctly by the concentric-corner convention. A four-model prototype (baseline/conformance/radius/index.html, unpublished) rendered the candidates side by side. Torfinn picked Derived inward from 16, cards only — panels, modals and tables were never measured live and stay --radius-s. --radius-l is reinstated to carry it, at 16px rather than its old, retired 12px.
Corrected the same day, on Torfinn's own question. The first implementation gave a flush top-of-card image its own --card-media-radius-top/-bottom tokens, hand-matching the image's corners to the card's. Torfinn asked why that was needed at all, rather than simply letting a card that clips its own contents (overflow: hidden) round a plain, radius-less image for free. It was: the token pair solved a problem clipping already solves, with a value to keep in sync that clipping doesn't need. Reverted everywhere it had landed — tokens, the JSON twin, BASELINE.md, the governance doc, the reference file, the prototype — replaced by one --card-media-overflow: hidden token, scoped to cards that actually contain flush media rather than every card, so a plain card stays free to let something overhang its edge later without reopening this decision.
tokens.test.ts rewritten across both passes: the image-in-card nesting inversion that had been asserted to still fail since 2026-08-07 is gone — clipping makes it structurally impossible to recur, not just correctly valued — replaced by an assertion that --card-media-overflow is hidden and a guard that the reverted token pair doesn't come back. 190 tests total (was 187 before today), full suite green apart from a branch-hygiene flag this branch's own unmerged state is expected to trip, npm run build clean throughout both passes.
Cards 46 (what the reference layer is for) and 47 (the white-vs-sand background divergence, found while measuring production for this) remain open. agent-baseline is not regenerated — the gate that was waiting on card 45 is clear, but a v0.5 cut hasn't been asked for.
Files touched: aleris-tokens.css, baseline-tokens.json, tokens.test.ts, BASELINE.md, aleris-design-governance.md, reference/aleris-design-tokens.md, conformance/radius/index.html (new), _packages/README.md, _state.md, workspace/status/board.md.
2026-08-07 (second entry) – agent-baseline v0.4 shipped to Richard; card 14 closed
The critical path this board opened around is done. Torfinn cloned aleris-vibe-coding via an Atlassian API token, once the brief's original app-password auth was confirmed dead rather than merely interactive — Bitbucket's brownout ran 2026-06-09 to 2026-07-28, so no password, app or account, was ever going to authenticate for anyone after that.
Executed per the brief's own scope. New branch add-brand-os. Package placed at docs/agent-baseline/ — alongside the repo's other context docs, where CLAUDE.md/AGENTS.md already declare them — 30 files copied verbatim, one macOS .DS_Store artifact caught and excluded before it shipped. CLAUDE.md and AGENTS.md both got a short pointer: load BASELINE.md as always-on design-system context for UI work, tokens always, the rest on demand — mirroring the package's own README.md rather than inventing new wording, since editing someone else's project instructions calls for restraint. It also closed a standing gap rather than opening a new one: docs/aleris-standard.md's "Look and feel" section had said only "Designriktlinjer tillhandahålls av Torfinn" since the repo's creation, with nothing behind it.
Opening the PR needed one more fix, found by trying rather than by reading documentation first. The same API token that authenticates git push over HTTPS (username x-bitbucket-api-token-auth) is rejected by Bitbucket's REST API with "API token must be used with an atlassian registered email" — the two protocols authenticate the same token under different usernames. Retried with the Atlassian account email as the basic-auth username; succeeded. pull-requests/1 opened against main, addressed to Richard, carrying the two confirmations the brief asked for (font-CORS allowlisting, the wiring wording) as open notes rather than blockers. Default branch untouched — merge is his.
Brand-os side closed out in the same pass: the brief archived (workspace/archive/2026-06-20-brand-os-into-vibe-coding-repo-brief.md, status: archived), _packages/README.md updated to list aleris-vibe-coding as a downstream consumer for the next regeneration check, board card 14 moved to Done.
2026-08-07 – agent-baseline regenerated to v0.4, and two Done cards actually landed
The question that started it: with a week of cleanup behind the corpus, is anything left gating a new package version — separate from card 14's Bitbucket blocker? It turned out yes, and the answer was worse than "stale."
v0.3 (2026-08-05) had gone wrong within hours of being cut, because canonical kept moving the same day it was regenerated. It shipped the pre-fix 14px accessibility-floor token values — card 18 found --font-size-xs rendering at 12.48px instead of 14px because no root font-size was ever set, and card 19 fixed aleris-tokens.css/baseline-tokens.json that same afternoon, after the package had already been cut. It also shipped four dead links: card 26, also that day, deleted foundation/en-draft/constitutional/_accessibility-shared-draft.md and moved the colour-alone accessibility rule to constitutional/accessibility-is-foundational.md, but BASELINE.md, governance/aleris-design-governance.md and both reference/*.md files in the package still pointed at the deleted file.
Checking for more found two dead links card 26's own sweep had missed — inside aleris-tokens.css's own comment blocks, in canonical, not the package copy. Fixed there directly and re-mirrored, per _packages/README.md rule 1: packages are generated, never hand-patched, so a defect found while regenerating gets fixed upstream, not papered over downstream.
Two things were also sitting uncommitted, and landed in the same pass rather than left for later: card 33's three constitutional rule statements (group-scope.md, language-policy.md, tokens-are-canonical.md — written 2026-08-06, marked Done on the board the same day, never actually on main) and the physical-domain colour.md notes from cards 35 and 38. Neither is in agent-baseline's scope, so neither blocked the package's content directly — but both were the exact gap card 41's "Done means landed" rule exists to catch, on the cards carrying this week's actual decisions. Card 41 itself had the same gap: its branch deletions landed that same morning (a1dce9c, 62b939d) but the board's own tables were never updated to say so — caught and closed in this pass too (new card 43 records all of it).
All 27 package files re-verified byte-for-byte, a full sweep rather than spot checks. 179 tests passing (was 174), npm run build clean. MANIFEST.md and _packages/README.md both updated. Committed locally; not pushed to origin/main. Pushing, and the Bitbucket clone for card 14, are both Torfinn's.
2026-08-06 (second entry) – "Done" gets a landing clause, and the tail gets a fitness function
The long tail was measured instead of estimated, and the cause turned out not to be forgetfulness.
The current work is fine. Phase B is committed straight to main and pushed; nothing in flight is unmerged. The tail is four old branches: origin/claude/thirsty-diffie-0cf507 (0 commits ahead — fully merged, safe to delete), basepath-test (1 commit, 2026-04-07, a reverse-proxy experiment), teal-legacy (4 commits, 2026-06-08, superseded in substance because teal-as-legacy reached main through the colour supersede), and feat/font-hosting (30 commits, 2026-06-16). Only the last carries content that exists nowhere else: Museo Sans hosting did land, but the Supabase brand-schema infra, the crossborder + vårdförsäkring DB migrations and one WIP refactor commit did not. Commits were matched to main by message subject, so the result is indicative rather than proof — the migrations want opening properly.
The cause, measured: of the 34 Done when: clauses on the board that day, zero mentioned merging, landing, pushing or main. Every one defined done as the change is correct — tests green, build clean, routes resolving. A card could therefore be legitimately, verifiably done while its work sat on a branch. That is one explanation covering all three recorded incidents (the colour supersede, Phase A on feat/route-manifest-gate, and the fourteen backup/* branches whose deletion cost 705 rescued lines). With no CI in the repo, nothing outside Torfinn's own attention was going to catch it. Not a memory problem — the step was never in the definition.
Both halves of the fix landed together, per Torfinn's call (hard fail, and add the landing clause).
One – Done means landed. board.md § How this works now states that every Done when: clause ends with and it is on main. Recorded in cookbook.md §20 with the measurement, because the transferable lesson is about where a discipline lives: one you have to remember is one you will keep missing, so it goes in the definition of done and in a check, never on a checklist.
Two – lib/branch-hygiene.test.ts, a fitness function that bites during ordinary work. Fails when any branch is ahead of main and undeclared. In-flight branches go in IN_FLIGHT with a reason, an added date and an expires date capped at 30 days. Three further assertions stop the list becoming a permanent exemption: an entry whose branch is no longer ahead fails (the anti-rot shape borrowed from KNOWN_BOUNDARY_FAILURES in tokens.test.ts, where known failures are asserted to still fail), an expired entry fails, and a malformed or over-wide window fails. A fifth assertion checks the check can actually see the repo, so a green run cannot mean "could not inspect" — the trap vitest.config.ts has already been bitten by twice. Collected by the existing lib/**/*.test.ts include. feat/font-hosting and teal-legacy are declared, expiring 2026-09-05.
Verified against a fixture, not against this repo. vitest cannot run through the device bridge — node_modules was installed on macOS and the Linux VM has no @rolldown/binding-linux-arm64-gnu, and the bridge has no network to install it. So the check was proven in the cloud container against a purpose-built git repo with known topology: an undeclared branch ahead (fails, naming branch, commits, age and refs), a branch and its origin/ twin (deduplicated to one logical row), a fully-merged branch (correctly ignored), a declared branch that has landed (anti-rot fires), an expired entry (fires), an over-wide window and a thin reason (both fire), and a clean repo with an empty allowlist (5 passed). It has not yet run against the real repo — that needs npm test on the Mac.
A scar, recorded because it cost something. Attempting to delete basepath-test through the bridge failed — unlink is denied under the mount — and the aborted ref write left .git/packed-refs.lock and .git/refs/heads/basepath-test.lock behind, neither removable from there. Reads kept working; ref-writing operations fail until they are cleared with rm from the Mac. Git ref deletion and anything needing git push is terminal-on-the-Mac work. Cookbook §20 carries it.
New card 41 [T] — close the four-branch tail, with the per-branch verdicts and the exact commands. It is [T] rather than [CC] for the reason above, not by preference.
2026-08-06 – The physical domain opens, from the domain owner's side
A guide arrived from Sanna, who owns physical, and the useful part of this entry is the mistake in the middle of it.
What arrived. Interiör och fysisk miljö.docx, roughly 1 100 words of finished prose plus seven heading-only skeletons. Converted verbatim to workspace/sources/interior-fysisk-miljo-v2-2026-08-06.md, triaged in workspace/evaluations/interior-v2-compliance-2026-08-06.md.
The first pass got the category wrong. It read the document as a candidate physical/interior.md and judged it against _TEMPLATE.md: no frontmatter, no depends_on, bullet-dominant, noun headings, no negative form, no Origin/Open/Related — and, structurally, a strict subset of the three archived interior drafts, so absorbing it would supersede about 1 700 lines with 1 100 words. All of that was accurate and almost all of it was beside the point. Torfinn's correction, two parts. It is a guide for humans — a derived rendering, in the same category as the downloadable kits — so the template tests do not apply to it at all. And it is written by the domain owner, so its substance is not draft prose awaiting correction but positions that carry weight. The evaluation was rewritten in place rather than left standing beside a corrected one.
The precedent was already in the corpus. workspace/archive/playbook-vardforsakringsdagen-2026-04-23.triage.md: Sanna's event playbook, evaluated, deliberately not absorbed, referenced as a worked example instead, and the gap it exposed became new scope. The same three moves apply to a guide from the same author two years later — don't absorb it, take what it decides, fix what it exposes. Worth noting that the corpus solved this once and did not recognise it the second time until told.
Five owner positions recorded in workspace/decisions/decision-record-physical-interior-2026-08-06.md, wording carried over unchanged: a fifth principle (Ansvar och säkerhet — hygiene zones, cleanability, integrated accessibility), the level model with content named per tier, the four variation factors, a form language with an explicit undvik list, and reception-as-welcome. All five are genuinely new against all three archived drafts. The level model closes v1's open question B1. proposed rather than accepted for two stated reasons: owner attribution needs Sanna's role rather than her name, per _TEMPLATE.md, and two of the five touch foundation/colour.md, which she does not own.
The palette boundary was resolved from an existing rule rather than negotiated, and that is the reusable part. foundation/constant-contextual.md already says "Paletten är konstant. Kompositionen är kontextuell." Applied to colour in a room it sorts cleanly: 70/20/10 is composition, and is the physical domain's by right — recorded as decided, no ratification needed. sage is palette membership, and is not — it appears nowhere in the corpus, and F1's hard rule is that every colour has a name, a value and a role, so it either enters F1 fully documented or the intent is expressed in existing names. varmvit and ljusgrå most likely resolve by rewording. One test now settles every future physical-domain colour question without another conversation.
Three obligations found running the other way — the corpus owing the domain. The guide asserts "miljön uppfyller gällande krav på fysisk, visuell och kognitiv tillgänglighet" with nothing to point at, and the reason is timing, not carelessness: the per-country floors were researched 2026-06-15, sat on a backup branch, and reached workspace/archive/ on 2026-08-03 — three days before the guide was written. She could not have cited them. Both June research documents git mv'd from workspace/archive/rescued-from-backup-branches-2026-08-03/_sources/ into workspace/sources/, and the floors extracted to workspace/sources/physical-accessibility-floors-2026-08-06.md with the two caveats promoted from the source's closing section to the front: these are web research, not primary sources and may not be cited in a published page or a supplier brief until checked against purchased standard text; and the Swedish ≥0,40 (NCS lightness difference) and Norwegian ≥0,8 (contrast value) figures are on different scales and must never be cited interchangeably. Also unaddressed: teal has no physical position at all, though F1 states its strongest remaining presence is Norwegian signage and interiors and that replace-on-touch will mostly be executed through physical updates — and F1's Ingen dark mode, which says explicitly that it covers fysiska rum, is not carried through.
The duplication question is closed, and the answer changes how the guide reads. interior-arrival-phygital-draft.md (27 KB, the most developed of the three, structured along the patient journey) opens by declaring itself a draft for Torfinn and Sanna to write together, with six [!utkast] slots marked skrivs av Torfinn/Sanna. Torfinn confirmed he shared it with her over Teams and she has seen it. So the guide's brevity is a choice, not an accident — and the corpus should stop carrying those six slots as pending work until that is said either way.
Three specifics are unsourced. sage, the 70/20/10 ratio and the supplier names (Input Interiör, RP Möbler, IKEA) appear in no earlier draft and in neither research source. The document is LLM-assisted, which makes the difference between decided and plausibly generated worth one explicit pass. The supplier list matters most: v1's open B3 asked "Finns det befintliga leverantörsavtal som påverkar möbel- och materialval?" and left it open — three names with no owner and no date is not an answer to it. There is also a face-value tension: IKEA against the guide's own undvik trendmaterial, its hög kvalitet, and its sustainability line möbler som håller länge.
Six new cards, 35–40. 35 [CC] closed the same day (this triage). 36 [T] ownership as a role plus the three confirmations. 37 [T] the sage decision and the two rewordings. 38 [T] teal's position in Norwegian built environments — specifically what "touching" means when the unit of change is a building rather than a file. 39 [CC] pullable now and independent: lib/content.ts:32 and lib/route-manifest.test.ts:28 still read CONTENT_DIRS = ['foundation', 'physical'] against the ratified publishing rule, so the first physical/*.md marked accepted would publish silently through a leftover B1 was written to close. 40 [CC] build the source layer and split wayfinding out, blocked on 36–38.
Nothing was absorbed into the corpus proper and no canonical file was touched. The output is one evaluation, one decision record, one reference extract, two git mvs within workspace/, and six board cards.
2026-08-05 – Cards 16, 17 and 19: the button state model decided and rebuilt
Decisions (Torfinn), on the critical path 16 and 17 → 19 → 13 → 14.
Card 16 – secondary/primary-inverse hover. Merge hover into the focus ring. Hover draws the outer ring only, no fill change; focus draws both rings. This was the board's own recommendation: it is the only live option that fixes a declared non-conformity (aleris.se's tillgänglighetsredogörelse names missing focus indicators) and simplifies at the same time, deleting every --button-*-hover-bg token and --state-hover. Cost accepted: a keyboard-and-mouse user loses a separate hover cue on a button their mouse rests over while focus sits elsewhere, mitigated by hover drawing only the outer ring rather than both.
Card 17 – the button state set. Adopted as proposed: per-variant active (press) fills, the dual focus ring (2px white inner, 2px petrol-500 outer, swapped on primary-inverse), and no disabled state (buttons do not disable). The blanket rule in BASELINE.md § Accessibility – "focus-visible with petrol ring for keyboard navigation" – is amended, because one ring colour cannot serve six variants on two surfaces.
The coupling the board flagged became real. Card 16's merge makes card 17's dual ring load-bearing for hover as well as focus, and it retires --color-petrol-600, which the 2026-08-01 design return had proposed as a hover-only step – no longer needed once hover stops being a fill. Only --color-petrol-700 (13.88:1) was added, for secondary and confirm's press fill.
Card 19, rebuilt rather than applied. Per planning/button-state-return-evaluation-2026-08-01.md's recommendation, the design return's reasoning was taken and its edits/ were not – they were a snapshot of the return's own turn 1, one commit behind card 20 and failing on the bundle's own tokens. Touched: baseline/tokens/aleris-tokens.css (state model rebuilt), baseline/tokens/tokens.test.ts (removed the now-empty KNOWN_TEXT_FAILURES, narrowed KNOWN_BOUNDARY_FAILURES to one entry, added three new describe blocks porting the return's Tests 4–6 onto this file's structure), baseline/buttons/index.html (hover/focus/active rendering rebuilt), baseline/BASELINE.md (§ Buttons rewritten, § Accessibility's blanket focus rule amended), baseline/reference/aleris-design-tokens.md and baseline/reference/aleris-baseline-animation.md (stale token references corrected).
Result: 131 tests passing (up from 128), npm run build clean. The visual surface went from 17 of 30 cells failing to 6: five are the disabled cell, now policy rather than a gap (buttons explain instead of disabling), and one is new – ghost/active at 1.13:1, the same defect that used to sit on ghost/hover before the merge retired that state. Not fixed here: giving ghost a border would close it, but changes what distinguishes ghost from outline, which is a design call and not made in this pass.
Confirm's token values land, closing the half of card 3's decision that card 24 deliberately left open: --button-confirm-* now aliases --button-secondary-* by reference, plus --button-confirm-icon: check (required). Card 23, the pairing rule, remains undecided and does not block this – it would unwind the alias, not this change.
Surfaced, not resolved, per card 19's own done-when: four disabled buttons exist in the live app (app/tools/icon-picker/qlik/CreateFlow.tsx:100, components/comments/CommentPanel.tsx:125, components/comments/SelectionPopover.tsx:120, components/auth/OnboardingForm.tsx:51) and are now non-conformant under "buttons do not disable." Three are the empty-form case, where the honest explain-on-click text would restate what the user can already see – whether that is a named exception is Torfinn's call, not decided here.
Card 13, same day: agent-baseline regenerated to v0.3. All 27 files in its MANIFEST.md re-copied from canonical and verified byte-for-byte. klinfys-baseline/ and bhsv-baseline.zip left unregenerated, explicitly: neither predates the manifest rule with a recorded file list, so regenerating them would mean inventing scope rather than executing a recorded one – the same failure the manifest rule exists to prevent. _packages/README.md now says so for both, with the fix (confirm the file list before the next regeneration).
Card 18, same day: the conformance surface extended beyond buttons. baseline/conformance/index.html (new) and tokens.test.ts Tests 7–13 give forms, status/goal colours, tables, page surfaces, the chart palette, the 14px floor and the 44px touch-target token the same treatment buttons got in card 19. 169 tests passing (was 131). Eight genuine failures surfaced, none fixed:
- Input error text, 4.28:1 on sand-100 (passes on white, 5.03:1).
- Disabled input boundary, 1.28:1 against sand-100 – legitimate this time, unlike the button case, because inputs do disable.
goal-borderline, 1.73:1 against white – fails even the 3:1 boundary its three siblings clear.- Table row hover and selected, 1.01:1 and 1.13:1 against sand-100 – both functionally invisible.
- All three
--chart-pair-*tokens fail 3:1 against each other (1.29/2.76/2.18:1) – built for two-series comparison charts that can't be told apart. - The 14px floor token itself renders at 12.48px. The type scale's own comment assumes an 18px root font-size; nothing in
app/ever sets it, so every size in the scale renders ~11% smaller than documented, not just the floor. One root cause, not seven – which fix (root font-size, rewritten rem values, or absolute units) is not decided here. - 23 literal sub-14px declarations in
app/tools/icon-picker/qlik/qlik.module.css, unrelated to the root-cause finding above.
The full seven-claim BASELINE.md § Accessibility audit this card was scoped to produce is written into that section directly, not duplicated here: three claims now have a real test, two are structural claims about components that don't exist in app/ yet and are correctly left unasserted, two are tested and found wanting (the two above).
Card 14 checked and confirmed genuinely blocked, not just next. Shipping agent-baseline v0.3 to Richard needs an interactive Bitbucket clone – the brief itself says to stop and hand that step to Torfinn if the session can't authenticate non-interactively, which this one can't. Everything upstream is done and ready to copy in verbatim.
2026-08-04 (second entry) – Route B accepted, and it splits in two
Decision (Torfinn). Route B on the language exit, with the published language left open. That deferral separated two things the brief had bundled, and the separation is the substance of this entry.
B1 – publication becomes explicit – accepted. A file publishes only if it sits in a published layer directory and carries status: accepted and declares a lang matching a served locale. Today the first condition is the only condition, and it is implicit in directory position. Working material leaves the published zone; the archive takes what is retired.
The transit rule, which is the half Torfinn asked for. A working file never publishes, whatever its status. Transit is a move into a published directory plus status: accepted plus lang. Because the move is a git mv and because it fails the route manifest gate until the manifest is regenerated, publication is always an event someone accepted rather than a side effect of creating a directory. Claude proposes; Torfinn moves. This is the existing content boundary expressed mechanically.
B2 – which language the published site serves – deferred to card 28. B1 needs no language decision and forecloses nothing; an explicit gate with a declared lang is what a two-locale site needs anyway. Two things recorded with the deferral. English is the group language and a Brand OS reachable only in Swedish is not a group asset, so the destination is a published English site with Swedish beside it rather than instead of it – which means the brief's "the site serves Swedish only" was one option inside B, not B itself. And the translation-lag worry looks inverted: the Swedish pages are the finished ones (six genuinely-Swedish accepted Foundation pages, voice-passed), while _translation-notes.md describes the entire English set as "unverified, machine-assisted translations – starting points for Torfinn to revise" with five voice-sensitive passages flagged for his pass. So the real unknown is how much English prose has had a voice pass versus was marked accepted by inheritance.
The finding that reframed the zone question. Of the eight layer directories the target architecture defines – constitutional, principles, patterns, qualities, schemas, data-products, how-to, tutorials – none publishes. CONTENT_DIRS is ['foundation','physical'] and physical/ does not exist. Meanwhile three internal planning documents do publish, at /baseline/planning/… under a nav section labelled Steward, beside three research documents under Source research. The layer the LLM-first decision named as the human-facing surface, how-to/, is the least reachable thing in the repo. The live site is the pre-migration shape plus leakage plus working material.
Two failures that look alike and are not. foundation/en-draft/'s sixteen routes publish because scanDir recurses and nobody chose it – that wants a gate. baseline/planning/ and baseline/source/'s six routes publish through hand-written nav entries with section labels and descriptions – someone chose that on purpose, and this entry reverses it. A gate fixes the first; only a decision fixes the second.
Written: planning/publication-and-workspace-decision-brief.md.
Two corrections Torfinn made to the first draft of that brief, both worth recording as method rather than detail.
The "zone" model was Claude's invention on top of a casual phrase. Torfinn had written "a clear separation of working files and live documents" and used the word zoning in passing; the draft turned it into a three-zone model with numbered zones and per-zone rules. His actual formulation is simpler and better: these folders publish, those don't. Rewritten to that. Cookbook candidate – building an apparatus out of a phrase the other person used loosely, then treating the apparatus as agreed.
The recommendation hedged on a number that had been estimated rather than counted. The draft recommended keeping planning/ flat and moving only the strays into it, justified by "roughly forty places" referencing planning/…. Counted properly: ~330 occurrences across 54 unique targets, but only four files need hand-editing – board.md (37, all in **Files:** lines), CLAUDE.md (13, already being rewritten by card 29), file-manifest.md (12, whose whole job is file paths), _state.md (7). board.html (71) regenerates for free, and changelog.md (80) must not be touched because its references are statements about the past. No code reads planning/ file paths. So the cost that justified the hedge was mostly not a cost, and the flat folder had already demonstrably failed – the evidence being that Torfinn had to ask where project material lives.
Decided: workspace/ as the single administrative root, with twelve subfolders typed by document kind – decisions/ plans/ briefs/ evaluations/ research/ authoring/ status/ sources/ incoming/ drafts/ archive/ skills/. And documentation/ as a new root-level folder, not a workspace subfolder (Torfinn): documentation is a consumable – CLAUDE.md already defines its job as letting an outsider understand what the site is and how it works – so it belongs beside the other consumables, not among the administrative material about the making. That call resolved the architecture/ split two drafts had declined. The boundary is not how the corpus is shaped versus how we get there, which is genuinely fuzzy; it is the same boundary as the root-level one – describes the system to a reader (→ documentation/: site-map.md, the three architecture notes, aleris-meta-alignment.md, versioning-rules.md, dependency-reciprocity.md, site-spec-v2.md, slash-commands.md) versus plans our route to it (→ workspace/plans/: phase-0, the refactor plan, the migration checklist). documentation/ is created not publishing, with recorded intent to publish – the architecture notes are currently written to each other rather than to an outsider. It is a different consumable from how-to/: how-to/ distils the brand for people using it, documentation/ explains the system to people maintaining or inheriting it. authoring/ stays inside workspace/, confirmed. Kind rather than topic because kind determines lifecycle: a decision is permanent, a brief is spent the moment it is executed, a status file is rewritten continuously, an evaluation is frozen at its date. Topic would file colour decisions, colour briefs and colour research together with no way to see which are still alive – which is why the 2026-07-30 observation that a third of planning/ is spent never went anywhere.
production/ was explicitly ruled out as the root's name: in software it denotes the live deployed thing, which is the opposite end of the pipeline from what the folder holds. Root level – repo and URL – is reserved for consumables and principals (_packages/, baseline/, the layer directories). That is enforceable only because publication is now declared rather than inherited from filesystem position, which is the same point B1 turns on: the repo root and the URL root are one namespace, and foundation/en-draft/ became sixteen URLs because a folder became a path.
tools/ was the wrong name for the Python helpers – app/tools/icon-picker is a live internal-facing Brand OS tool, while these are situational scripts used while making Brand OS. And the bucket mostly dissolves on inspection: four of the five are one-off audits, and baseline/tokens/tokens.test.ts:15 records that contrast-check.py's maths was ported into the suite, so the logic moved and the script stayed behind. Only build-board-html.py is live and load-bearing. It goes to workspace/status/ beside the board it generates; the audits go to workspace/archive/ if their logic is confirmed absorbed.
Side effect worth having: the leading underscores on _sources/, _incoming/, _drafts/, _archive/ and _tooling/ become redundant. They exist to mean not content, and inside workspace/ the root says it – one convention fewer to know.
The layer taxonomy goes from eight directories to seven, in two calls that are not the same kind of call.
tutorials/ is removed, and it is free. It has never held anything but a 157-byte _index.md, created 16 June and untouched, whose whole body is "Learning-oriented walkthroughs for newcomers (Diátaxis)." The split from how-to/ was Diátaxis imported wholesale with the Phase 1 skeleton rather than arrived at from a need here. It is the same failure as the empty layer directories, in its purest form: a directory claiming a category it never held, cited as live work in three planning documents – "the newcomer gap we already named", "NEW – learner walkthroughs (Torfinn authors)". Cookbook §12 one level up: not a document claiming an action it never performed, but a folder claiming a category. The thought is kept as a line in the surviving index; the scaffold is not.
how-to/ is renamed filters/, and removing it was rejected. Torfinn proposed removing it too and recreating later. Three grounds against, and the third is the decisive one. It holds real content – ai-tells-and-filters.md at 136 lines and language-quality-sv.md at 70, both accepted, plus Norwegian and Danish drafts awaiting native speakers. Three of the four declare depends_on: how-to/ai-tells-and-filters, so deletion breaks the dependency graph rather than the links. And data-products/voice-filter-rules.yaml names two of them in a machine-readable generated_from: field, under the rule "Generated from those pages, never edited directly – same discipline as design tokens" – so deletion would orphan a data product. These four are also the F5 pilot, which the LLM-first record calls "the first worked example" of a cluster spanning layers.
The rule agreed the same day forbids the move on its own terms. workspace/ never publishes; status: accepted means publishable and in force. Moving two accepted files there asserts both at once. Torfinn confirmed the two remain accepted, which settled it: the folder stays.
What was wrong was the name, not the layer. The index read "Task-oriented recipes for doers (Diátaxis)" – the framework talking, not the content. What the folder holds, in its data product's own words, is the answer to "does this text carry tells of machine generation or non-native production? It is the floor, not the gate." filters/ is the word the corpus already uses for that, in four places: planning/voice-filters-plan.md, voice-filter-rules.yaml, ai-tells-and-filters.md itself, and the LLM-first decision's "behind the filter floor". Source and rendering now share a name, which makes the cluster legible. Rename cost: three depends_on, four type: how-to/method, one layer:, four yaml references – about twelve edits in live files. Deliberately not named to accommodate the unwritten communication-genres.md, because naming a container for content nobody has written is the tutorials/ mistake repeating.
A find worth carrying. Those four files are the only ones in the corpus with an explicit language key – en, sv, nb, da. Everywhere else it is absent, which is exactly why the route manifest infers language from directory path. The convention the publishing rule's third clause needs already existed, in the folder proposed for deletion. It uses language: rather than lang:, so there is a reconciliation to make, and card 28 should start from this precedent rather than inventing one.
Board: new cards 28 [CC] the language and voice-pass inventory that unblocks B2, 29 [T] write the publishing rule and the transit rule into CLAUDE.md, 30 [CC] create workspace/, move the administrative material and take the eighteen routes out. Sequencing: 27 lands first, because 29 and 30 are both verified by its manifest. Card 30 does both halves in one pass – both are moves, and one manifest diff verifies both, so splitting them would mean updating the same references twice.
Also found: CLAUDE.md's repo-structure section has drifted – it names _inbox/ and arc - previous thinking/, neither of which exists. And _archive/ is doing its job: seven files spot-checked against their live descendants had all seven diverged, so it holds history rather than duplicating the present.
2026-08-04 – The language mish-mash measured, and given a costed exit
No card closed. Three cards opened, one rescoped, two reframed, and one decision brief written. No canonical file was touched – everything in this entry is planning/, _state.md and the board.
The measurement. Twenty of sixty published pages carry the wrong lang attribute, and the error runs in both directions. Seventeen of the twenty-six locale routes serve English prose under lang="sv" – sixteen of them the whole of foundation/en-draft/, live at auto-generated routes, plus the deliberate identity/colour-en. Three of the thirty-four /baseline/ pages serve Swedish under lang="en": the den-nara-experten voice guides, measured at 273, 231 and 182 Swedish function words against zero English. WCAG 3.1.1 is Level A, so this is the floor of the standard rather than a refinement.
The cause, and it is single. The language decision was taken on 2026-05-30 in the Phase 0 batch – English is the Aleris Group language and the source language for the whole corpus… renderer default locale en. Three things then failed to happen: it was never ratified (phase-0-target-structure-and-migration.md has been proposed for nine weeks), it was never given a home (constitutional/language-policy.md is six pending Torfinn markers), and the renderer never implemented it (i18n/routing.ts still reads locales: ['sv']). The corpus migrated page by page under a decision the renderer did not know about and no page records, so each page migrated differently: imagery completed the move, colour stopped halfway, voice moved upward into constitutional/ instead of sideways, and five files sit in a folder named en-draft while carrying status: accepted.
Three things are live that nobody would choose to publish: foundation/en-draft/imagery (status: superseded), foundation/en-draft/_translation-notes (an internal working note whose glossary still proposes "the close expert", a rendering retired on 2026-06-16 in favour of the Present Expert), and two Swedish status: draft pages, foundation/iconography and foundation/logo-identity.
What is not broken, stated because the mess looks larger than it is. The central term is settled. No accepted Swedish prose has been paraphrased. And the instrument to hold the line already exists and has already been run – lib/route-manifest.test.ts pins all twenty-six locale routes by route, source, status and language and asserts each mismatch still mismatches, so the allowlist cannot rot. It is uncommitted, and it does not cover the Baseline route group, which is where three of the twenty mismatches live.
Written: planning/language-exit-decision-brief.md – three costed routes (ratify English-canonical and build the en locale; make English unpublished source and serve Swedish only; govern the mixture with lang and publish: keys), recommending the second, on the grounds that the exposure is on the published site, that it is the first half of the first route rather than a detour from it, that it obeys the LLM-first decision's own guardrails, that it collapses the accessibility-home question at no extra cost, and that today's readers are Swedish-speaking.
Board: new cards 25 [T] the language exit, 26 [CC] reconcile the two accessibility homes (waits on 25), 27 [CC] commit the route manifest gate and extend it over the Baseline nav table. Card 18 rescoped from a visual test surface to a conformance surface – five of the seven claims in BASELINE.md § Accessibility (44px targets, focus-visible, prefers-reduced-motion, the aria pair, the 14px floor at component level) are asserted and unmeasured. Cards 16 and 17 reframed as remediation: aleris.se publishes a tillgänglighetsredogörelse declaring partial DOS-lagen compliance and naming missing focus indicators on interactive elements among its known defects, self-assessed August 2025 – and the measured evidence agrees, at 1.0:1 on secondary and primary-inverse. The recommendation recorded on card 16 is to confirm the hover-equals-focus merge with hover drawing one ring, as the only live option that closes the declared defect and simplifies at the same time.
Header corrected: the board still claimed the colour supersede had landed on a branch; it merged to main on 2026-08-03.
Evidence note. Digg is authoritative on DOS-lagen's reach – offentlig aktör extends to private actors running healthcare under HSL with public financing. The EAA specifics circulating in agency blogs (WCAG 2.2, fine ranges) were not verified against the statute and are not relied on here.
2026-08-01 (second entry) – Cards 21, 22 and 24: the control tightened, and the confirm retirement carried
Three decision-free cards pulled in one run. Suite at 86 passing, up from 59. Still on colour-supersede; nothing merged, nothing shipped, and no button values changed – every one of those waits on cards 16 and 23.
Card 22 – comments can no longer declare or erase a token. Comments are stripped before matching, and the file is parsed twice, raw and stripped, with the suite failing if the maps differ. Both guards were seen falling by name before being trusted, and that needed two injections rather than one: the swallow case fired at once, but the phantom case did not, because --state-active is still a real declaration on this branch, so a comment naming it produces no divergence. Only a comment naming a token that does not exist proves that half. A guard verified in one direction is a guard nobody has run.
Card 21 – the boundary test now measures every state, not just rest. The rule as landed: if a state paints a fill, either that fill or the variant's border must reach 3:1 against the surface. Data-driven rather than a per-variant exemption, which is why outline passes its 1.13:1 hover tint (petrol-500 border at 8.75:1) and ghost fails the identical tint (no border token). Three known boundary failures, not the two reported this morning – confirm/hover #7ba492 is 2.36:1 against sand-100, and I had missed it by reading its presence on the text allowlist as its being counted. All three land asserted-to-still-fail, verified in both directions.
The page was wrong in the same place, and that is the more interesting half. baseline/buttons/index.html applied the boundary floor to every variant except the transparent ones, on the reasoning that outline and ghost are identified by border and label rather than by fill. True of outline, false of ghost. Fifteen of thirty failing becomes seventeen of thirty, and both new cells read green before the question was asked correctly. What caught it was widening the test and finding it disagreed with the page. Neither control catches the other automatically — worth holding before card 18 builds more surfaces: the check gets a value wrong, a rendered surface gets a question wrong.
One divergence recorded rather than closed: the page counts ghost's active cell as failing too and the suite does not see it, because the suite measures declared tokens and the page renders what the button actually does, fallbacks included. Card 19 closes it by declaring the state.
Card 24 – the confirm retirement carried to six locations plus the token annotation. BASELINE.md hard rule 5 is inverted and is now the rule of record; governance:195, :199, reference:141, :148 and patterns/button-label.md reference it rather than restating it, the discipline card 7 applied to the colour-alone rule. The removed clause at governance:195 is preserved in a superseded block rather than deleted, because its tight scoping survives the retirement — those four verbs are still what a completion action is, they just no longer name a colour. The DDR at reference:148 is marked superseded on its interactive half only; its value, provenance and F1 link still hold.
A seventh location nobody had listed: aleris-tokens.css still carried @usage Interactive confirm actions on --color-confirm-500 — the source of truth contradicting every document that references it, which is the exact failure being repaired, in the file the repair is measured from. Corrected, and baseline-tokens.json regenerated. The --button-confirm-* values were deliberately left alone.
2026-08-01 – The design return on button states: the reasoning adopted, the files not
The return on planning/handoff-button-states-claude-design.md arrived as _incoming/Button design WCAG evaluation update.zip, unpacked to _incoming/button-state-return-2026-08-01/. Assessed in planning/button-state-return-evaluation-2026-08-01.md. All twenty-four of its contrast, ΔE76 and L* claims were recomputed and are exact, as was true of the brief before it. Its four replacement files were applied to the working tree in two configurations, the suite run against each, and both reverted. Nothing is applied to the branch. No decision was taken on cards 16, 17 or 23; this entry records what the return establishes and what it changes about the questions.
Card 16's option A is dead, on a number the board never took. Inverting secondary to a petrol-100 fill gives a 7.73:1 label, which is right – and petrol-100 on sand-100 is 1.13:1, so the button loses its boundary on hover. WCAG 1.4.11, the same clause the exercise is built on. The option traded a text failure for a figure-ground failure and only the first was counted. It survives with a permanent petrol border, which makes the collision with outline mandatory rather than incidental. The move works on primary-inverse because primary-inverse sits on petrol, where the same tint is 7.73:1 against its ground.
Card 16's question was a false dilemma. The ruling that petrol-500 is as dark as the brand goes constrains the palette; AA constrains contrast. Neither requires hover to be a fill change at all. The two meet only under the assumption that the fill is the hover channel, which was never examined. Drop it and both hold with no new hex. The live options are now 1b – a 2px petrol-500 ring at 3px offset, which is option C made buildable – and 1e, the hybrid the handoff asked to be costed: 1b for hover plus petrol-700 for press only, one new value instead of two.
A sixteenth failure, and a second blind spot in the fitness check. Test 2 measures figure-ground against --button-*-bg only, while Test 1 measures text against every state. So a fill that dissolves into the page passes CI as long as its label does not. Two live failures follow: secondary's hover, petrol-300, is 2.19:1 against sand-100 – the matrix counted it once as a text failure and it is two – and outline and ghost's hover fill, petrol-100, is 1.13:1, the same defect that killed option A, sitting in the token file already. Outline is rescued by its border; ghost is not. SURFACE_RULES covers neither, because both are transparent at rest and the loop skips them. New card 21, to land failing.
The return's own files would have reverted card 20 and shipped a failing test. edits/tokens.test.ts was built from the branch at 16:22 on 2026-07-31; card 20 landed at 16:25. Copying it over would have deleted splitButtonToken, the seventeen synthetic assertions and the hard failure on an unknown suffix – with no conflict and a green-looking suite. It is also unnecessary: the card-20 file already classifies every token the new CSS adds and measures all four active fills, giving 62 passed with only the three KNOWN_TEXT_FAILURES entries breaking, which is the allowlist working. Tests 4, 5 and 6 are real additions and get ported onto the current file instead. Separately, the bundle's own test fails on the bundle's own tokens, 2 of 57: a retirement comment written as --state-active: var(--color-sand-500) with no semicolon both registers the retired token as declared and swallows the real --state-focus-ring declaration twenty lines later. New card 22. Both became cookbook entries – §16, a complete replacement file carries a silent revert; §17, a comment that looks like a declaration.
The edits are one turn behind the return's own reasoning, which is the reason not to apply them beyond the two defects. Turn 2 says merging hover and focus makes every --button-*-hover-bg token redundant and may remove the need for petrol-600; the edits add petrol-600 as the secondary hover. Turn 2 says the flow ladder unwinds the confirm alias and puts the check glyph in question; the edits alias confirm to secondary and require the glyph, and assert both in a new test. Applying them would write three open questions into CI as invariants.
The pairing rule is new, and larger than either open card. Turn 2 produced it unasked: button form follows flow position, not visual rank. Filled orange instigates, petrol outline continues or is a sibling choice, filled petrol closes, a link is a sibling that is navigation. One invariant: never filled orange beside filled petrol. The measurement under it holds and is new – against sand-100 the primary's boundary is 3.85:1 and the secondary's 8.75:1, so the subordinate control is 2.3× crisper than the one it is subordinate to, and the two fills are 2.27:1 against each other, below the 3:1 that separates two adjacent components. The pair fails twice and neither failure was on any list. Adopting the rule makes it structurally impossible rather than discouraged. It costs: hard rule 3 changes from one primary CTA per screen to per section, the confirm alias unwinds, the check glyph reopens, and the in-flow escape – Tillbaka, Avbryt – is left with no form. New card 23.
Four answers are recorded in the return as Torfinn's; three check out against the corpus. petrol-200 is print as well as digital – Pantone 5513 C/U, C38 M12 Y21 K0 – so a press tint built on it cannot be marked digital-only. Ghost has zero instances in app/ and components/. Outline's ground is unwritten either way, which is a gap rather than a contradiction. The fourth, one primary CTA per section rather than per screen, contradicts hard rule 3 as written and needs a rewrite rather than a note.
Board: cards 16, 17 and 19 reframed; new cards 21 and 22, both [CC] and pullable now, both defects in the control found by running the return rather than reading it; new card 23 for the pairing rule. Card 19 retitled from "apply" to "rebuild", four corrections to seven.
2026-07-31 – The confirm button is retired; the button state brief evaluated; the active-state question sent out for design
The confirm variant is retired (Torfinn, card 3, option A). The reason given is simplification of the button set, and the accessibility numbers agree: white on confirm-500 #4f866e is 4.23:1, so the green failed AA at rest, before any interaction, and fixing it meant inventing two new greens whose only job was passing a contrast check. Instead confirm becomes the secondary cluster plus a required check glyph – "I'm done" carried by the glyph and the verb, which is what the colour-alone rule already demands of success states. --color-confirm-500 keeps its value unchanged and narrows to indicator use: status dots, badges, chips. The button set goes from six variants to five.
The consequence to hold onto: secondary now carries two jobs, "go here" and "I'm done". That raises the cost of anything on card 16 that would collapse secondary into the outline variant, because the hierarchy level being removed now has more traffic through it. It also means the confirm retirement is a rule of record in six places, only two of which the incoming brief patched – governance:195 and :197, reference/aleris-design-tokens.md:141 and :148, patterns/button-label.md, and hard rule 5 in BASELINE.md, which the brief ships while still asserting confirm-500 is interactive. Card 19 carries them.
A button state brief arrived in _incoming/ as a proposed diff of two live Baseline files rather than prose. Evaluated in planning/button-state-brief-evaluation-2026-07-31.md: every contrast number recomputed and found exact, the diff test-applied on a scratch copy and reverted. Nothing is applied. It closes all fifteen failures the button matrix found, and its dual focus ring is the strongest part – two rings always drawn rather than one chosen per surface, because a surface-aware ring fails silently when a button lands on an unexpected ground. Verified across seven configurations, including two the brief never mentions.
It also reverses card 16's ruling without saying so, and that stays open. It does option D – darkening petrol – and petrol-600 #003c44 sits at ΔE76 5.4 from petrol-500 against the previously rejected #003A42's 6.3. The hover it picks is weaker than the one that was turned down. Its implicit counter-argument is coherent: hover is pointer-only and matters least of the three touch states. But the decision was explicit and is Torfinn's, so the card stays open rather than being quietly overwritten.
The question Torfinn wants investigated is the active state, and it is not a conformance question. planning/handoff-button-states-claude-design.md is a self-contained brief for a design session with no repo access. Three gaps, none of which any existing rule will catch: outline and ghost may have no press feedback at all – they are transparent so a fill change is their only colour channel, they have no active fill, the generic fallback is retired, and the brief never says whether the inset shade and translate reach them; primary and primary-inverse have no fill change on press on a pointer device, because their active fill is their hover fill, leaving the press to two channels the brief itself calls "not load-bearing for conformance"; and the affordance therefore differs by input device, with the thinnest case being a reduced-motion mouse user, for whom fill is unchanged, the translate is dropped, and only an inset shade remains. Outline and ghost are also the variants that live in dense instrumental screens and toolbars – where targets sit close together and "which one did I hit" is the pressing question.
Board: card 3 closed, card 12 superseded by new card 19 (apply the brief with four corrections), new card 20 (fix the fitness check's per-variant state blind spot, pullable now and ahead of 19). Cards 16 and 17 wait for the design return.
2026-07-30 – Eight cards executed: the colour supersede lands on a branch, with a contrast check that runs
Everything on the board that did not need a decision from Torfinn was executed in one run. Cards 4, 5, 6, 7, 8, 9, 10 and 11 are closed; the board is at ten done, two ready, four blocked. Work is on branch colour-supersede, tagged accepted-corpus-2026-07-30 with recovery verified against 4161 files. Nothing is merged and nothing is shipped.
The gate for sharing is now two decisions wide, and both are Torfinn's. Cards 3 (confirm button values) and 16 (secondary button hover) are the only things between the board and Richard's repo. Card 12 needs them, 13 needs 12, and 13 is the actual gate. Neither card needs further preparation.
Contrast is now machine-checked. baseline/tokens/tokens.test.ts, 42 assertions, maths ported from planning/contrast-check.py. Button variants are discovered from token names rather than listed, so a new variant is covered when it arrives. Three known AA failures land documented and asserted to still fail — secondary hover 2.56:1, confirm rest 4.23:1, confirm hover 2.78:1 — so that fixing a value without removing its entry also fails the suite. The allowlist cannot rot in either direction, and removing those three entries is what turns the suite green in card 12. The claim in colour-is-the-aleris-palette.md that a token audit was "caught automatically" had been false since 2026-05-31 and is now true.
The check found six token-conformance violations on its first run, where the card expected none: three status tints the app invented because Baseline has no status tint scale, two hand-picked colours in an email template, and a dark petrol in the Qlik tool already commented "promote candidate". Notably that value is #003942 — the same dark petrol card 16 evaluated and ruled out as a secondary hover, already in production as a header colour.
Two rules collided with the cards that were supposed to execute them, and the rules won. Card 5's freeze on the accepted Swedish page and _packages/README.md rule 6 (a shipped snapshot cannot be corrected in place) each blocked a correction the cards instructed. Rather than break either rule or drop the fixes, both moved to an explicit carry list in handoff §6: the sRGB triplet in the Swedish page and in the shipped package, and the two Swedish dedup rows. The wrong triplet has been propagating to package consumers, so losing it would have mattered.
Beyond the supersede, the run found and fixed drift the cards had not named. The reference doc claimed the primary button radius was radius-full (pill), contradicting both the token file and the no-full-pill hard rule. --surface-strong-warm was still described as "primary actions". The primitives table was missing orange-400 entirely. vitest.config.ts scoped test collection to app/**, so a test placed under baseline/ would have collected nothing and reported green. And the goal-status note still called the icon requirement a "Fixed accessibility rule" in a vocabulary retired on 2026-06-16.
Card 11 was built on a false premise, and the truth is worse. It said en-draft/ was not scanned. It is a subdirectory of foundation/, scanDir recurses, and SKIP_DIRS never excluded it — so all 17 files under foundation/en-draft/ have been publishing since they were created, at URLs exposing the folder path, and under /sv/ with lang="sv", because SUPPORTED_LOCALES = ['sv'] and the content map has no locale awareness at all. The English colour page now has an intentional route, identity/colour-en, verified rendering. It deliberately does not take identity/colour: the Swedish page holds that until it is superseded at the last step. The route is a placeholder, because the route it should really have depends on a language-architecture decision that does not exist yet.
The dash convention was being broken by the writer enforcing it. The scaffold's standing rule is en dash, never em dash, in brand-os output and in Claude's own prose. 56 lines of new prose across the corpus were normalised. Verbatim quotes of superseded wording keep their original dashes — altering a quote would falsify it — and one such quote was caught being changed and restored.
Two sections of authored work had no card. Handoff §8 (promote contrast to a bright-line) and authoring 2E (the constitutional role line) were both accepted and both unassigned; §8 landed with card 7, 2E with card 6. A check for other unassigned handoff sections belongs in the next run.
Claude-drafted calls, all flagged in place for correction on sight, per the boundary recalibration: the accessibility node gained YAML frontmatter, since normative: true has to be machine-readable and that file was one of two Foundation pages created with inline metadata only; its status moved draft → proposed, because a draft node cannot be the canonical home for a binding rule that accepted pages declare depends_on; and two utility notes on the print table distinguish "no Pantone match exists" from the legacy teal's "match unconfirmed, awaiting press check", with orange-300's CMYK marked not yet built rather than left as a dash, and deliberately not computed.
Full amendment list and the four decisions this surfaced beyond cards 3 and 16 are on planning/board.md. Card 15's records pass still waits on card 13.
2026-07-29 (third) – Board built; the secondary button reopened as a design question; the content boundary recalibrated
Work moved onto a board. planning/board.md is the list of record for in-flight cards, with planning/board.html as a generated view of it (planning/build-board-html.py, re-run after every edit). _state.md points at the board and no longer repeats the card list. Sixteen cards to the milestone: colour supersede landed with the contrast check green, packages regenerated and verified, agent-baseline v0.3 in Richard's repo. Each card carries its file paths, the verified numbers, the traps and a done-when, so it can be pulled without a conversation first.
The secondary button is a design question, not a value pick (Torfinn). The AA card had recommended adding a petrol step below petrol-500 for the secondary hover. Wrong: petrol-500 is as dark as the brand goes, and nothing below it is distinguishable in any practical sense. Split into its own card 16. The numbers refine the reason rather than confirming it as stated – #003A42 measures ΔE 6.3 from petrol-500, so a meter can separate them, but ΔE76 overstates differences in the dark region and a 5.7-point L* drop on a small dark button with no side-by-side reference is a weak hover signal even when technically visible. Out because it signals poorly, not because it is invisible. Three live options, all measured: invert on hover to petrol-100 #D9E1E2 with petrol text at 7.73:1 and ΔE 63.8 from rest, which is the move the primary button already makes on petrol surfaces and reuses an existing token; make secondary outline-only, which works but collapses it into the outline variant and removes a level from the hierarchy; or keep the fill and move the hover signal off colour entirely, which makes the AA rule untouchable by construction but needs a spec. Card 4C's finding that the whole button set lacks state specs argues for deciding this across all variants at once.
Confirm stays a straightforward choice and keeps card 3. confirm-500 sits at L* 51.7 with range below it, so the recommendation stands: leave confirm-500 as the status colour since --status-confirm reaches beyond buttons, and add a darker interactive fill – rest #4B7F69 at 4.63:1, ΔE 2.9 so it still reads as the same green; hover #406D5A at 5.91:1, ΔE 10.1 from rest, matching the primary button's hover level.
The content-creation boundary was applied too widely, and is recalibrated (CLAUDE.md). A one-sentence footnote about empty Pantone cells and two role labels in a ramp table were treated as gated brand prose, producing rounds of briefing, a rewrite brief and a board card each for text nobody would defend as voice-bearing. Torfinn: "just fix it and let's move on. If it's wrong or confusing, someone will raise their voice about it." The boundary now states explicitly that table cells, role labels, column headers and utility footnotes are not brand content – draft them, mark them Claude-drafted, move on. The test is whether the wording does rhetorical work in Aleris's voice, not whether the file is a Foundation page. Foundation prose that argues or explains, pattern examples and stakeholder-facing communication remain gated.
Cards 1 and 2 closed accordingly. 2G rewritten and landed in the authoring doc; role text for orange-400 and orange-200 landed in the scaffold's ramp table. Both marked as Claude-drafted with an instruction to correct on sight rather than approve. One dependency recorded: 2G is deliberately silent on CMYK, because orange-300 still lacks a CMYK build and that value has to be produced (card 9).
2026-07-29 (later) – The orange-400 / orange-200 retirement reversed; the Pantone collapse cancelled
Late in the same session, reading the three secondary-orange swatch cards, Torfinn caught that the palette was being reasoned about as if it were digital only.
The retirement of orange-400 and orange-200 is wrong and is reversed. Yesterday's decision read "both steps lost their only role" and listed those roles as hover-on-CTA (orange-400) and lightest-tone (orange-200). Both are digital roles. Orange 80 carries Pantone 2022 C/U and C0 M38 Y40 K0; Orange 40 carries C0 M24 Y23 K0. They had print jobs throughout. Torfinn's statement of the rule the palette actually follows: the brand orange is used in steps in print as well as digital, and the only oranges absent from print are the interactive steps, because interaction is a screen event. Both steps are reinstated as brand tints, print and digital – satisfying the reinstatement condition set yesterday, which required a role to be stated at the same time.
The sharpest form of the mistake, kept as the record. Of the three secondary steps, the two retired for having no role are the two with print specification, and the one kept – orange-300 – is the only one with neither a Pantone nor a CMYK value. The digital reading came out backwards from the print reality.
Consequences, all applied: the ramp goes back to seven steps, with the Role column now carrying two distinctions rather than one – brand versus interactive, and print-and-digital versus digital-only; the collapse of the secondary-orange Pantone/CMYK table, decided earlier the same day, is cancelled, since it rested entirely on the retirement; --color-orange-400 stays in the token file and --color-orange-200 is added; authoring 2G keeps its home and roughly its original job, with orange-600 and orange-700 as its only subject, so the rewrite brief written earlier today is void; and the app/globals.css fix narrows from "remove the retired custom properties" to "repoint the hover usage, keep the tints." Handoff §1, §4 and §4b and the scaffold's ramp, retirement and Pantone sections are all updated, with the reversed instructions struck rather than deleted.
One constraint this creates. --color-orange-400 returns as the exact value the retired hover pointed at, so it needs an explicit ban on interactive use or the bug walks back in. orange-300 already carries the wording to copy: "Decorative/background only, not for interactive states."
Two real gaps, now visible rather than hidden by the retirement. orange-300 has neither Pantone nor CMYK; orange-200 has CMYK but no Pantone. Either source the values or mark them for matching at press, the way the legacy teal is marked. Torfinn's call; the handoff reports and does not fill.
Why it was missed, and what it implies. Yesterday's audit ran on aleris-tokens.css and contrast maths – the digital half, the half that is machine-checkable. The print half has no test and was not in view, so two steps could be declared roleless while holding Pantone numbers. This is exactly the failure planning/dependency-reciprocity.md predicts: verification method has to be a property of each node, or effort drifts toward what is easy to test and the untested half silently loses. It strengthens the case for scoping that document sooner. Recorded as planning/cookbook.md §13.
Also noted: the en-dash convention set 2026-07-28 (never em dash, in brand-os output and in Claude's prose) had been broken by Claude throughout this session, including in the earlier entry below. Swept.
2026-07-29 – Handoff reviewed; prose gate opened; AA failures pulled into scope; Pantone table collapsed (the collapse is cancelled – see the entry above)
Review session on the corrections brief Claude Code returned against the colour handoff. Four decisions closed, one open.
The 2B / 2C / 2D copy-edits accepted (Torfinn, all ten). These blocks were marked authored, copy-edited while 1A / 1A-long / 1B carried explicit approval, so edits to Torfinn's brand prose sat in the handoff without a sign-off marker. Seven were punctuation or a typo. Three changed something and were approved individually: softer hues → softer tints in 2B, because hue moves only 3.4° across the ramp while lightness moves 0.38 → 0.93, and "hues" would contradict the two-roles-in-one-ramp argument two paragraphs above; such as primary buttons and controls → on primary buttons and controls in 2C, since the list had attached to "actions" while buttons are elements; that will force → that forces in 2D, a rule stating what is the case rather than what would happen. The gate is now explicit – the handoff may carry 2B/2C/2D as edited.
Colour pulled ahead of the F5 pilot, consciously (Torfinn). Colour becomes the first concept fully migrated into the cluster model, ahead of the planned F5 pilot. The scaffold had flagged this as needing a conscious okay and the handoff's open gate had treated it as settled; it was recorded nowhere. Now decided. F5 remains the pilot for the pattern; colour is the first instance.
The secondary-orange Pantone/CMYK table is collapsed, not patched (Torfinn). With orange-400 and orange-200 retired, every remaining step in that table is digital-only or –, so dropping the two rows would leave a four-row table with no print data at all. The table is removed and replaced by one line stating that no secondary orange step has a print specification; the digital-only marker already lives in the ramp's Role column. Scope is that table only – the primary table keeps the brand orange's real values (Pantone 2024 C/U, C0 M56 Y62 K0), and the petrol and sand secondary tables carry real Pantone throughout. Knock-on: authoring 2G loses its home, since it was written to sit under the removed table. It needs to become the replacement line or be rewritten – Torfinn's, not mechanical. The scaffold's Pantone section is marked SUPERSEDED so it cannot be dropped in.
Brief for the 2G rewrite. The old line – "Some colour steps are digital only and lack physical colour descriptions" – was a footnote explaining blanks in a table's Pantone and CMYK columns. With the table gone there are no blanks, so the line has to do the table's job instead, and it has three things to carry rather than one: that the secondary orange steps have no print specification; that this is by design, because the interactive steps exist to solve an on-screen contrast problem and have no print existence at all; and where orange's print values do live – the primary table above, brand orange, Pantone 2024 C/U. Constraints: the reader in this section is looking for a value to hand a printer, so it must not read as forgotten or as coming later; petrol and sand keep their secondary tables, so orange will be the only family without one and the asymmetry has to be explained rather than left to look like an omission; and the section's register is plain statements about print values, matching its opening line "The print values are constant – the same values regardless of printer or substrate." Two surfaces, English canonical: foundation/en-draft/colour.md and the SV twin foundation/colour.md.
The three AA failures move into scope (Torfinn): fixed before Brand OS is shared again. Reverses the 2026-07-28 scoping-out. "Shared" means the agent-baseline shipment to Richard, so the package regeneration is the gate, not the supersede. One of the three – this repo's own accent-80 hover – was already in handoff §4b, so two decisions are added. Both are the same move orange just made: the brand or status step stays, a darker interactive step is added below it.
- Secondary. Rest is petrol-500 #004851 at 10.27:1 and passes; the hover fill petrol-300 fails at 2.56:1. petrol-500 is already the darkest petrol, so darkening requires a new step.
#003A42gives 12.47:1,#00323Agives 13.84:1. Cost to weigh: petrol-500 is also--text-primaryand--brand-primary, so this gives petrol the same brand/interactive split orange has – a Foundation statement about the palette, not a token addition. - Confirm. Fails at rest, 4.23:1, before any hover. Darkening confirm-500 itself ripples through every use of
--status-confirm, which reaches beyond buttons. Cheaper and consistent: keep confirm-500 as the status colour, add a darker interactive fill below it –#4B7F69clears AA at 4.63:1,#406D5Aputs hover at 5.91:1, the same level as primary's hover. One check outstanding:--color-goal-achieved#2E8540 sits at 4.62:1, close in luminance, and the token constraint requires confirm to stay distinct from it.
Numbers reproducible from planning/contrast-check.py and planning/button-audit.py.
Decided: split, not fold (Torfinn). The AA fixes run as a second pass after the colour supersede, not inside it. The supersede already carries four patches, twelve dedup locations, five Anchor sites, a new test suite and loader wiring; and the order buys something – the contrast test lands first, failing on secondary and confirm, then the fix turns it green, which makes the fix provable rather than asserted. The constraint is satisfied either way because the package regeneration is the gate.
The sRGB row resolved: the hex is right, the triplet is wrong (Torfinn + measurement). foundation/colour.md:77 records sRGB 248, 124, 86 beside #F58C61, which is 245, 140, 97. Yesterday's note left open which value was the record, since the neighbouring C0 M56 Y62 K0 is arguably closer to the triplet. Settled by measuring the swatch Torfinn confirmed as the correct brand orange: the image is Display P3, and converted to sRGB it is exactly (245, 140, 97) – zero distance to the hex, 19.6 away from the triplet. Fix the triplet to 245, 140, 97 in foundation/colour.md:77, foundation/en-draft/colour.md:79 and the shipped _packages/agent-baseline/foundation/colour.md:77. Noted on the evidence: the swatch renders from the hex field, so the measurement alone would be circular – it is Torfinn's confirmation that the rendered colour is correct that makes it decisive. The CMYK is a separate matter and not a data error; whether C0 M56 Y62 K0 reproduces #F58C61 at press is print validation, and the page already names the Norwegian profile as the one to validate against.
§4D reframed: no Anchor-retirement procedure is needed. The 2026-06-16 ADR migration retired Fixed/Anchor/Hypothesis as a vocabulary and migrated 34 files' frontmatter. So "the first Anchor retirement" asks for a procedure for a concept that no longer exists – the ADR set already answers it: the hover rule is superseded, with a pointer to its replacement, recorded where it sat. What is genuinely open is that the migration only touched frontmatter: aleris-design-governance.md still carries the old vocabulary on ~18 rule headings, defines it at :27–28, references all three levels at :318, and has badge-fixed / badge-anchor CSS in baseline/index.html; ALERIS-DESIGN-WORKING.md is organised by it; the same 20 mentions are mirrored into agent-baseline. The labels carry per-rule bindingness that the ADR set expresses only as page-level normative: true, and ### Forms and inputs (Fixed / Anchor) splits within one section – so removing them removes a distinction, not a word. Not scheduled as its own brief: phase-0-target-structure-and-migration.md already prescribes the disposition – aleris-design-governance.md SPLITs into constitutional/ for the rules and planning/ for the record, which is the same answer. Migrating the labels standalone would be work discarded when the split runs.
Two corrections to the corrections brief, verified against the files. Its Patch 2 blamed both the authoring doc and the scaffold for claiming they had updated the handoff; only the authoring doc did. The scaffold's line 227 corrected its own table in place, dated, and claimed nothing about the handoff – its method is the one to copy. And its §4b framing of the sRGB fix as settled is not: #F58C61 is 245,140,97 while the row records 248,124,86, but that triplet sits beside C0 M56 Y62 K0, which is at least as consistent with the triplet as with the hex. Two of three print values agree against the hex. Which is the record is a decision, not a typo fix, and the row has been shipping to package consumers.
The cheap fix applied, and recorded as an anti-pattern. The false claim in colour-authoring-2026-07-28.md §1B ("Eight locations, not six. Added to the handoff.") is rewritten to state the finding and name handoff-colour-supersede-claude-code.md §3 as the single list of record. planning/cookbook.md gains §12 – planning documents that claim an action they never performed: a list has one home; the document that will be executed is the list of record, others point at it; never write that a change is made in another document without making it in the same pass.
Standing observation (PO). One colour decision has produced four planning documents plus a fifth to correct them, while phase-0 – the document that would collapse much of this – has sat at proposed since 30 May. CLAUDE.md says planning artifacts should get shorter over time; they are getting longer. Phase 0 sign-off is proposed as the next decision after the colour track ships.
2026-07-28 (later) – Architecture settled; contrast made a bright-line; a package shipment blocked
Second half of the same session. The earlier entry below covers the authoring and the audit; this covers what came after.
Architecture question answered (Torfinn). Brand OS is both a document set and a system with tests, and the representations are interdependent. At its core a design system with tests; documents are derived from it to explain and use it. Scope is every medium the brand appears in – digital, print, interior design, clothing, signage, voice, motion, lighting, furniture, vehicles – with Baseline and the token system as the specific, detailed digital design system, one part of the whole, versionable and packageable including as an LLM reference in another repo. The end state is a service supporting anyone who needs to work with the brand, plus a library of visual examples and motion graphics. Recorded in _state.md on both sides and in planning/dependency-reciprocity.md.
WCAG-AA contrast promoted to a bright-line. No longer a "calibrated default". The old framing rested on "one documented, mitigated exception (white-on-orange button)", and orange-600 removes that exception, so the default framing had no evidence left. Two edits: the calibrated-default clause comes out of _accessibility-shared-draft.md:18, and the Detection-of-violation note in constitutional/colour-is-the-aleris-palette.md:43 comes out. This also resolves that node's own Open #1 – the accessibility rules get both homes: the bright-line is constitutional, the measurable floor is the fitness check in handoff §4c. Accepted knowingly: a bright-line with two known breaches (secondary and confirm), which is the honest state and what makes those failures a resolution path rather than a backlog item.
All prose accepted; handoff gate fully open. 1A, 1A-long and 1B accepted as drafted, including "meaning" in place of "information" in the constitutional bullet. planning/colour-authoring-2026-07-28.md is complete; nothing waits on Torfinn. Note that 1A-long is an addition, not a replacement – the short entry stays in Hard rules, the fuller treatment goes in Accessibility.
Three of Claude's own claims found wrong by verification, corrected:
- The 1B premise. Claimed the existing rule "only required an icon on success". Wrong – error already carries the requirement (
aleris-design-tokens.md:76), and a general "colour never carries meaning alone" sits in bothBASELINE.md:193andaleris-design-governance.md:87. The real extension is distinguishability: two states each paired with an icon can still be indistinguishable from each other once colour is gone, and that is what the ΔE 14.6 convergence evidences. The rule text was reworded. Consequence: the de-duplication reconcile table goes from six locations to ten. - The hue claim. Wrote "every orange step sits at roughly 17°, verified" having measured two of seven. Actual spread across the ramp is 16.0°–19.4°, range 3.4°, with lightness 0.38–0.93. Still supports "two roles inside one ramp", but not as stated.
- The Anchor's footprint. Said it sat in two places. Five live locations, including a token comment on
--button-confirm-hover-bgciting the rule by name and date to justify a value that fails AA, plus three_packages/mirrors.
A package shipment blocked, with a deadline. _incoming/2026-06-20-brand-os-into-vibe-coding-repo-brief.md would ship agent-baseline/ v0.2 to Richard's Bitbucket repo – which exists to be read by LLMs as current. That package carries the hover Anchor retired today in three files, the wrong brand-orange sRGB triplet, and the retired orange-400 token with its button-hover claim intact. The brief predates the colour decision by five weeks and carried no dependency on it. Blocked at the brief, flagged in the packages table, gated on the package regeneration in handoff §5.
All three packages are downstream. agent-baseline/ v0.2 marked STALE – do not send; klinfys-baseline/ already flagged as drifted; bhsv-baseline.zip predates the manifest rule with no regeneration path recorded. _packages/README.md gains rule 6: a shipped snapshot cannot be corrected in place, only regenerated and re-sent, so the downstream check in rule 5 happens before sending. Rule 5 was already written and nothing performed it – the same shape as the governance doc's imaginary token-conformance check. Also found: agent-baseline/MANIFEST.md records its own provenance in the decided/fixed/anchor vocabulary the June ADR migration retired.
planning/dependency-reciprocity.md created, then rewritten once the architecture answer landed. Its job is not "which pages cite which" but given a decision, list every representation of it – the hover rule had six and nothing connected them. Two requirements follow: verification method as a property of each node, because contrast is machine-checkable and a chair is not, and without it the physical branches read as coverage gaps and effort drifts toward what is easy to test; and packages as dependents that have left the building. Needs scoping, not a decision. Best done after the colour supersede lands.
doab (TAKS) gained four entries this session: re-dating a decision record as the chronology form of paraphrase; sessions ending at the decision rather than the checkpoint; a rule stating a direction can silently violate the constraint it was meant to serve; and brand-system representations as dependents, with shipped snapshots as dependents that have left the building.
2026-07-28 – Colour supersede authored; hover Anchor retired; three AA failures found
Long session. Colour prose authored through, one governance Anchor retired, and a button contrast audit that found more than it was looking for. Working tree also committed and cleaned after five weeks of accumulation.
Orange resolved as two roles inside one ramp, not two colours. The orange-100 … orange-700 ramp stays continuous; the page gains a family-level intro over two role headings, Brand orange (recognition, accent, decoration, print) and Interactive orange (primary buttons, active states, digital only). The framing is accurate rather than a compromise: #F58C61 and #D14811 sit at the same hue (17.4° vs 17.2°) with near-identical saturation, and only lightness moves, 0.67 → 0.44. One hue at two lightnesses.
orange-400 (#FAAA8D) and orange-200 (#FBD1C0) retired, reinstatable. Both lost their only role to this supersede – orange-400's hover job moved to orange-700, orange-200's "lightest tone" job moved to orange-100 when Foundation adopted it. Rather than carry them roleless against the constitutional rule that a named palette colour has a purpose, they are removed with values and reasons recorded in planning/colour-supersede-scaffold.md. Reinstating either requires stating a role at the same time.
Constitutional §1 updated: "Petrol carries structure and trust. Orange drives emotion, attention and action." The first pass said "emotion and attention", which left the interactive role – the reason orange-600 exists – with no constitutional statement while the Foundation page claimed it. Adding "action" closed the disagreement.
The inverted button's hover: petrol 20 / petrol-100 (#D9E1E2), 7.73:1. Torfinn caught a false claim in the 10 July scaffold – "the dark-surface inverted button lightens on hover" – which cannot happen: a white fill is at the lightness ceiling, petrol is the darkest text, and petrol-on-white (10.27:1) is the palette's highest-contrast pair, so every fill change reduces contrast. Petrol 20 chosen over sand (8.75:1) for consistency, and the consistency turned out exact: same hex as --color-petrol-100, which --button-outline-hover-bg already uses, so the tint is reused rather than added.
Hover Anchor retired. "Hover lightens, never darkens" (aleris-design-governance.md, Anchor decided 2026-06-12; stated in BASELINE.md) is retired. It stated a direction while leaving the constraint implicit, which is how a direction could silently break AA. Replacement separates them: contrast is the constraint – no interaction state may take a component's text below the AA floor against its own fill; darkening is the convention. The clean inverse of the retired rule – the token set was already moving that way and only the rule pointed the other direction. First Anchor to be retired; no retirement procedure is documented, so this sets the precedent.
Three live AA failures found, scoped out deliberately. Computed from the token file, reproducible via planning/button-audit.py:
- Secondary – white text on the petrol-300 hover fill = 2.56:1. Worse than the 2.38:1 white-on-orange failure this supersede exists to fix. Unfixable by picking a better lighter step: petrol-400 gives 4.09:1, petrol-100 gives 1.33:1, and no petrol darker than petrol-500 exists.
- Confirm – white on #4F866E = 4.23:1 at rest, before any hover; 2.78:1 hovered. The hover value's own token comment admitted it: "Derived value — validate when a confirm scale is added to F1."
- This repo's own site –
app/globals.cssstill hovers primary buttons to the retired orange-400, so brand-os fails the rule it publishes.
Three of four white-text buttons fail; only outline and ghost escaped, because their text is petrol. Fixing them means a new confirm colour (a Foundation status-colour change), possibly a new petrol step, and a component spec – three decisions, none of them colour-supersede decisions. Explicitly out of scope; the handoff reports and leaves them.
The corpus already held the confirm number and applied it to the wrong use. Foundation's accessibility table lists "Confirmation (#4F866E) on white | 4.23:1 | ❌". Contrast is symmetric, so a pair failing as "confirm text on white" fails identically as "white text on confirm". Recorded against one use, never carried to the other. The rest of that table wants the same check.
The governance doc claims an automated check that does not exist. aleris-design-governance.md states under Detection of violation: "In a token audit. Any hex not in the token set is a violation, caught automatically (token-conformance check)." There is no such check – no CI, no .github, and the single test file belongs to the icon picker. Same failure shape as the rest of the session: a rule recorded as if in force with nothing verifying it. A contrast fitness check is now specced in the handoff; it would have caught all three failures the day they were introduced.
Four pre-existing data errors found and routed to the handoff: the sRGB triplet for the brand orange is wrong (#F58C61 is 245, 140, 97; the page says 248, 124, 86 – and it has been shipping to package consumers); aleris-design-tokens.md records the primary hover as orange-300 while the token file says orange-400, a mismatch dating to March; --state-hover still points at orange-500, which is now brand-only; and four historical decision dates in the voice files had been bulk-changed from 2026-06-09 to 2026-06-17 (reverted – the calls were made on the 9th).
Conventions decided. En dash (–) throughout, never em dash (—), in brand-os output; candidate durable home is F5 Writing Conventions. One changelog only, at planning/changelog.md; baseline/planning/baseline-changelog.md merged in and superseded.
Boundary held. Claude authored no brand prose except the §8 interactive-orange section, drafted at Torfinn's explicit request and marked PROPOSED pending his voice pass. Nothing was written into the corpus pages – the handoff's branch gate holds. All prose lives in planning/colour-authoring-2026-07-28.md, the single authoring surface created this session.
Also: working tree committed in four groups after five weeks uncommitted (colour prep, voice-filters slice, archive housekeeping, voice-essay pass); both _state.md files refreshed; contrast-check.py, hover-check.py and button-audit.py committed so every number above is reproducible rather than asserted.
2026-07-10 — Orange interaction colour: physical-execution handoff written; boundary held on authoring
Torfinn okayed pulling colour ahead of the F5 pilot as the first fully-migrated concept, authored natively in English. Boundary held: Claude did not author the Foundation/constitutional page prose or write the live pages — that's Torfinn's voice authoring. Prep taken to turnkey instead: planning/handoff-colour-supersede-claude-code.md written (branch/snapshot, en-draft authoring surface, 0–900 renumber, constitutional single-source + de-dup, token ripple, package sync, loader + integrity, SV supersede). Two owned steps remain: Torfinn writes through the voice slots; Claude Code executes the handoff on a branch.
2026-07-10 — Orange interaction colour: edit surface resolved to English source; sequencing risk flagged
Finding: site-map.md v3 confirms English is the Aleris Group source language (changed from v2 Swedish default). So the colour supersede is authored natively in English in the foundation/en-draft/ tree; SV becomes a localisation. F5's "English a peer, not the reference language" reconciles — that's about language-specific conventions staying native, not about identity content like colour. Torfinn's guess confirmed.
Sequencing risk flagged (for Torfinn's conscious okay): three operations collide on the colour page — content supersede (a true rewrite, the only non-additive one), SV→EN language conversion, and flat-page→cluster structural migration. The LLM-first migration checklist is additive/verbatim/one-concept-behind-the-F5-pilot; a content rewrite doesn't fit that model, and doing it in English makes colour the first accepted concept fully migrated ahead of the F5 pilot. Recommendation recorded in scaffold: author natively in EN, populate the already-drafted en-draft/constitutional/ colour + accessibility nodes (resolves sorting-test Open #3), SV outgoing; Torfinn okays the deviation. Loader wiring (en-draft/ not currently scanned) is a Claude Code physical-move task.
2026-07-10 — Orange interaction colour: scope B locked, semantic-rule consolidation mapped
Decisions (Torfinn): migration scope B (whole palette to 0–900); naming merges blessed (Slate → gray-100, Sand dark → sand-500); orange-100 (#FDE8DF) adopted into Foundation (sync with Baseline); hover step orange-700 = #B23C0E (Claude-suggested, white text 5,92:1). Scaffold tables updated to final.
Consolidation finding (answers Torfinn's DRY question): the "colour alone" rule is restated in ~6 canonical places, and the en-draft architecture already began consolidating it — en-draft/constitutional/_accessibility-shared-draft.md holds it, and en-draft/constitutional/colour-is-the-aleris-palette.md states the rules are "referenced, not restated … so they live once," with sorting-test Open #3 to confirm that shared node as home. So the constitutional routing = finish that consolidation, not add a page. Reconcile-each-location table added to the scaffold. Distinction held: one rule statement (constitutional); domain implementations (charts, goal-status, tokens) stay at their layers but reference it.
Coordination flag: the supersede intersects the in-flight LLM-first / sorting-test migration; resolve whether SV foundation/colour.md or the en-draft/constitutional/ tree is the edit surface before writing prose.
2026-07-10 — Orange interaction colour: supersede scaffold drafted (prep)
What happened: Read foundation/colour.md in full and built planning/colour-supersede-scaffold.md — a block-by-block KEEP/REPLACE/ADD map with all factual content worked out (renumbered ramp, orange-600 rows, Pantone note, updated contrast tables) and voice prose left as [SLOT — Torfinn] markers. No brand prose authored.
Two findings surfaced from the source: (1) the "semantic colour never stands alone" rule already exists in colour.md (hard rule, line 149) — so the constitutional routing is an elevation of an existing rule, seed text in hand, not a new addition. (2) Baseline already runs the whole palette on 0–900, so migrating orange only leaves Foundation internally mixed and still mismatched on petrol/sand/gray — full-palette migration (option B) recommended, full mapping in the scaffold. Every stop already exists in Baseline; only orange-600 is a genuinely new colour.
Open for Torfinn (in scaffold): migration scope A (orange only) vs B (whole palette, recommended); adopt orange-100 into Foundation?; naming merges under B (Slate → gray-100, Sand dark → sand-500); the orange-700 hover hex (darker terracotta, safe by construction).
2026-07-10 — Orange interaction colour: scale → 0–900, semantic rule → constitutional
Decisions (Torfinn): (1) migrate Foundation's orange ramp to the 0–900 scale; (2) route the semantic-colour rule as a constitutional hard rule.
Key finding: baseline/tokens/aleris-tokens.css already uses 0–900 and its stops match Foundation's tint colours exactly (orange-300 #FFBE9F, orange-400 #FAAA8D, orange-500 #F58C61). So the migration is Foundation catching up to Baseline, not a two-sided renumber. Mapping table added to the brief. Only orange-200 (#FBD1C0) is a new assignment to confirm; orange-100 (#FDE8DF, Baseline-only) is an open call for Foundation to adopt or not. orange-600 = #D14811.
Semantic rule routing: hard rule → constitutional/ (new page, normative: true; folder + precedent already exist via constitutional/voice-is-the-present-expert.md); justification → foundation/colour.md (CVD collapse); implementation → Baseline status pattern (icon + label, error and success).
Ripple mapped (prep): cross-file handoff list added to the brief — en-draft/colour.md, baseline tokens (add orange-600/200, rewrite orange-500/400 roles, repoint button hover, fix pre-existing token violation), BASELINE.md hover rule, the two _packages/ copies, new constitutional page, surface-aware + H2 button patterns. Write sequence recorded: colour.md + constitutional page (Torfinn + Claude) → Claude Code propagates tokens/packages → index/changelog/manifest/versioning.
Boundary: no brand content authored. Mapping + ripple are structural prep; replacement colour.md and constitutional prose remain Torfinn's, written with Claude.
2026-07-10 — Orange interaction colour: remaining slots decided (name, hover, home, rules)
Decisions (Torfinn): (1) name Orange 600 / --color-orange-600 for #D14811; (2) hover H2 — rule reframed as "hover moves toward higher contrast against its own text" (supersedes BASELINE.md "hover lightens, never darkens"); (3) home Foundation (named entry, not a baseline-only digital token); (4) supersede scope — Torfinn + Claude write the colour.md changes together; (5) "semantic colour never stands alone" approved; (6) surface-aware CTA approved (Orange 600 fill on light surfaces, inverted light button on dark).
Challenges raised back (in brief): (a) "Orange 600" imports a 0–900 scale Foundation doesn't use — its orange ramp is tint-named (Orange 80/60/40, higher = lighter, no darker-than-base slot). Recommend adopting the 0–900 scale in Foundation as part of the supersede so Foundation + Baseline share one orange scale. (b) Orange 600 is digital-only (no Pantone/CMYK) — Foundation must say so explicitly. (c) Placement of the semantic-colour rule — likely resolves as a constitutional hard rule (normative: true) justified from the colour page and implemented as a Baseline status pattern, rather than a single Foundation rule (mirrors the F5 cross-layer pattern).
Prep added: colour.md supersede change-map drafted in the brief (sections affected, versioning note — likely a Foundation major bump). Replacement prose remains Torfinn's; we write together. No brand content authored this session.
Also: Storybook logged as the candidate Phase 3 visual-reference trigger (baseline/planning/baseline-current-state.md).
2026-07-10 — Orange interaction colour: Resolution A decided; hover + CVD rule opened
Decision (Torfinn): Resolution A adopted — #D14811 (terracotta, BK3) becomes the single constant interactive-surface tone; warm coral #F58C61 is retained for brand/decorative and non-digital use (print, environmental, campaign), and no longer carries white text. Rationale: keeps the coral intact for purposes beyond digital while giving the digital CTA an unconditional-AA fill (4.52:1 white-on-#D14811). Recorded in planning/decision-brief-orange-interaction-colour.md (Decision recorded section).
Colour-blindness check added (evidence): independent Machado-2009 simulation (deuteranopia + protanopia). Interactive vs coral stays separable on lightness (ΔE 24–26) → the darker-CTA decision is clean for CVD. Structural finding: all warm colours collapse to one yellow-khaki band under red-green CVD, so colour alone can't carry meaning. Acute pre-existing issue: error #C14444 and success #4F866E converge under protanopia (ΔE 14.6) and success loses its "green" read. → new proposed rule, "semantic colour never stands alone" (extend the existing success-icon requirement to error; no error/success by colour alone).
Hover escalated to a rule conflict: with #D14811 as base, BASELINE.md's "hover lightens, never darkens" breaks — a lighter hover moves back toward coral and drops white text below AA. Three options framed (H1 darken / H2 reframe rule as contrast-direction / H3 non-colour); H2 recommended. Baseline-rule change → Torfinn's call.
Open (Torfinn): palette name for #D14811; hover H1/H2/H3; architectural home (foundation entry vs baseline digital token); colour.md supersede author; approve the semantic-colour rule + its home. No brand content authored — replacement colour.md text and hover hex are Torfinn's.
2026-07-10 — Orange interaction colour: underlay consolidated into a decision brief (prep)
What happened: Triaged the Sund vikt digital underlay from _inbox/ and consolidated it into planning/decision-brief-orange-interaction-colour.md (prep) — a decision record structured against Torfinn's three criteria (ease of use across channels, compliance, brand coherence). Contrast math re-computed independently; all WCAG 2.1 values in the underlay verified. No brand authoring, no palette decision, no edit to foundation/colour.md.
Framing surfaced (prep, for Torfinn's call): the three candidates don't rank the same on the three criteria — BK1 (darker coral + size rule) is strongest on coherence but its size rule is unenforceable outside controlled UI (the cross-channel weak point); BK3 (terracotta, full AA) is strongest on channels + compliance but departs from coral. Resolves to an F6 constant/contextual choice: one constant tone (BK3) vs contextual-by-channel (BK1+BK3, at a two-tone coherence cost).
Flags raised (either resolution): (1) BK2 changes foundation/colour.md (status accepted) → supersede with pointer, not silent edit; (2) baseline/tokens/aleris-tokens.css already violates the current size-rule exception (white/regular/base on orange-500) — fix required regardless; (3) darker base implies darker hover, inverting the lighter orange-80 hover step; (4) digital-only problem → argues for a baseline digital token with a named palette entry (F6). Legal-applicability of the accessibility directive flagged as a legal question, not a design-brief assertion.
Triage: _inbox/ emptied. orange-utforskning.html → _sources/orange-utforskning-2026-07-09.html (reference, indexed). Original brief absorbed into the planning brief; kept in _archive/.
Open (Torfinn): resolution A/B; tone + palette name; hover direction; architectural home; confirm supersede scope + author of the colour.md replacement.
2026-06-17 — F5 Writing Conventions: architecture worked out, recorded as a cross-layer cluster
What happened: Built a first-version F5 scaffold as prep (planning/f5-writing-conventions-draft-v0.md), provenance-marked, consolidating decided Swedish conventions verbatim from aleris skrivregler.docx and the ordlistan, plus the sentence-tells inherited from the voice principles. No brand authoring — decided material carried, gaps and decisions left as marked slots. Reshaped twice in dialogue: first into a language-agnostic Foundation layer + per-language guides, then resolved as a cluster across layers, not a single page.
Load-bearing decision — the corpus is LLM-first. Humans consume the brand mainly through synthesised distillations (the how-to/ layer) and runtime answers, not by reading the source. The content boundary relocates across three surfaces rather than weakening: source is machine-structured; how-to/ distillations are authored and signed off (the translation-layer pass built into authoring); runtime distillation has no human pass and leans on the voice-filters floor. Because the corpus needn't be human-navigable, splitting a concept across layers carries no findability cost.
F5 as a cluster: cross-language policies → constitutional//principles; the per-market "contract" (the slots every language guide fills) → schemas/; the language guides (SV / NO / DK / EN, English a fourth peer not the chrome) → how-to/; wired by depends_on.
Decisions resolved (eight): (1) language-agnostic Foundation + per-language guides, English a peer not the reference language; (2) integritetsregel → constitutional/ hard rule (normative: true), F5 cites it; (3) avsändarregel → F3 (handoff planning/f3-sender-identity-handoff.md) — with the nuance that the approved "Aleris [specialty] [place]" unit-naming pattern must not be gated; the ordlistan's flat "aldrig en enhet" was written for the notification/privacy context; (4) Inera → adopt as baseline, document deviations; (5) skrivregler opener stance-lines → F5 keeps them, cross-referencing voice; (6) formality-creep → F5 defines, filters detect (widen the voice-filters uppblåsta ord pattern to human writing); (7) web-specific rules (hyperlinks, file/image naming) → Baseline, not F5; (8) F5's home → cluster, per above.
Flag raised: foundation/en-draft/principles/voice.md still carries status: draft and sits in en-draft/, contradicting "voice principles accepted on main". Needs a status flip + move — Claude Code.
Handoffs (Claude Code): split the F5 draft into its destination layers; fix the voice en-draft status/location.
Boundary note: the whole session was consolidation + architecture (prep), provenance-marked; no Foundation-page authoring. The scaffold is not the F5 page until Torfinn writes through it.
2026-06-17 — F11 Iconography: six open decisions resolved
What happened: Torfinn closed the open F11 decisions; recorded as a dated decision block in foundation/iconography.md (the SV canonical, where his week's work lives — EN draft is stale and was not touched). Decisions: (1) retire the green/yellow/red tier model — strictness is carried by the four-arena governance mechanisms (curated list / generous list / character-test+blocklist / orientation convention); the contextual "tre nivåer" bullet was replaced accordingly; (2) outline weight = Font Awesome Pro Regular (a11y: Light degrades at small sizes); (3) patient-facing allowlist editorial owner = Head of Design; (4) icon size 1.25× cap height — confirmed; (5) signage governance confirmed as a different logic (external wayfinding standards ISO 7001/7010, defined with the Physical chapter, not a JSON list) — closes the two 2026-06-15 open points; (6) the draft's proposed constants confirmed (outline-not-filled, single stroke weight, single colour, label-always with utility exception, allowlist-only).
Edits were additive and surgical to protect Torfinn's recent authoring — a decision block + the one contradictory bullet. Prose and author-prompts left for his writing session.
Downstream implications (not done here): retiring green/yellow/red may affect baseline/iconography/allowlist.json and the Qlik picker if they encode tiers — Claude Code call. The colour constant has a wording mismatch to reconcile (SV "färg ur paletten" vs the draft's "inherits the text's colour"). EN en-draft/iconography.md is stale; any F11 localisation switch must re-sync from the SV, not the old EN.
Terminology (2026-06-17): the model's core term "arena" renamed to "kontext" throughout foundation/iconography.md — clearer, and avoids collision with "specialistområde". Verified word-form swap (all variants), "väljaren"/picker untouched. Anglicism "kurerad"/"curation" removed from the page (Swedish kurera = to cure) — swapped to "granskad"/"granskning" (table, decided block, contextual bullet, author-prompt, v1-principles heading). The briefing planning/f11-briefing-iconography.md and the baseline doc baseline/iconography/README.md updated: "arena" → "context" (English docs; "curated/curation" left as correct English; "parent" protected), with a dated note in the briefing §12. Changelog history not rewritten.
2026-06-17 — F10 Imagery accepted + promoted to English canonical
What happened: Resolved F10's five open [!decision] callouts and ratified the page. Status proposed → accepted. The five resolutions: (1) the emotional-mode table is scoped to patient-facing surfaces, with a new section "Imagery in professional and internal surfaces" added for internal tools (imagery recedes, function leads) — links to F11's internal-digital-tools arena; (2) AI imagery follows the Aleris Group Image Guidelines (symbolic/illustrative only, never replacing real people/environments in patient-facing content, labelled externally, never staff portraits) — the page adds only the character consequence; (3) "real images" is stated as the standard, with the shared image bank (Mediaflow in Sweden) named as an aspiration, not a present guarantee; (4) market variation: the Group guidelines are the overcoat (VP of Communications / Sofia), the page is concrete for Sweden, NO/DK pointed at as future; (5) a three-layer boundary — identity (this page) / governance (Group guidelines) / technical implementation (baseline/reference/aleris-baseline-images.md).
Localisation switch: English promoted to canonical. The cleaned English now lives at foundation/imagery.md (first Foundation page where EN is canonical). The Swedish original is archived at _archive/imagery-sv-superseded-2026-06-17.md; foundation/en-draft/imagery.md is tombstoned (status: superseded) and can be removed by Claude Code. Path unchanged, so inbound depends_on (F11 iconography etc.) is intact. Voice term confirmed: the Present Expert (was "close expert" in the draft), aligning with voice.md and the Group doc.
Source filed: _sources/Aleris Group image guidelines.docx — Group-level governing document (production categories, AI rules, rights/consent, Mediaflow storage). Now the cited governance layer for F10.
Backlog: the commissioning checklist / asset specification (on-brand motive criteria at capture time) was decided to be a standalone asset, home deferred, production on the backlog (logged in brand-os-inventory.md, F10 and B3 entries).
Caveat: the English prose originated as Claude drafts that Torfinn accepted with light language clean-up still in progress; not a from-scratch authoring pass.
Boundary note: content drafting was done as in-document [!proposal] blocks (the F11 author-prompt pattern) for Torfinn to react to and accept — not unilateral brand authoring.
2026-06-17 — Voice filters Phase A: brand-OS content first (reorder)
What happened: Built the brand-OS-native slice of the voice-filters track (planning/voice-filters-plan.md). Five files, all status: draft for Torfinn's pass: how-to/ai-tells-and-filters.md (Layer 1 generic AI-tells, in English — the layer model, the patterns with example/counter-example/severity, the review methods, the boundary statement), how-to/language-quality-sv.md (Layer 2 Swedish, in Swedish — calque check, word swaps, svengelska verbs, question-syntax pass), how-to/language-quality-no.md and -dk.md (scaffolds, status open — needs native speaker, no borrowed ruleset), and data-products/voice-filter-rules.yaml (machine-readable rendering with per-genre severity overrides).
Reorder from the plan: the plan's decision 3 sequenced "working skill first, corpus pages after it proves itself." Torfinn redirected (2026-06-17): the filters should live in the brand OS, available to humans and LLMs, not primarily as a Claude skill. So Phase A produced the corpus pages + data product first; any _tooling/ skill becomes a thin secondary layer that reads from the data product. Decisions 1, 2, 4, 5 in the plan are unchanged.
Provenance: every pattern carries a source tag (writing-style-profile, TAKS calque check, memory/feedback, dated doab/decisions). Content is curated/structured from existing decided material, not new brand-voice authoring. The viewport-overlap resolution and depends_on wiring (plan Phase B) are not yet done.
Next: Torfinn's pass on the drafts; then decide whether the NO/DK review owners get pinged and whether a thin skill/linting consumer is built on the YAML.
Update (2026-06-17): how-to/ai-tells-and-filters.md accepted — Layer 1 (twelve cross-market patterns) is in force, including the soft severities for em-dash, töntigt, formellabelloop ratified by approval. kodnamn-i-prosa removed before approval (personal working rule, stays in TAKS). Then how-to/language-quality-sv.md accepted — Layer 2 Swedish in force, including the patient-guide hard/soft genre overrides. YAML layer_status: layer_1 + layer_2_sv accepted; NO/DK open.
Phase B cleanup (2026-06-17): Resolved the viewport overlap per the plan's one-source rule. foundation/en-draft/viewports/voice-for-ai-systems.md now cites the filter patterns instead of restating them — a marked, dated addition maps "no empty intensifiers" → uppblasta-ord and the translation-artefact rule → direktoversatta-fraser, with the filter pages added to Related. Torfinn's *(verbatim)* prose left untouched (governing-doc rule: add marked layers, never paraphrase the source). The remaining viewport items are flagged as F5 production conventions, not filters. The baseline/governance/aleris-anti-patterns.md distinction (behavioural guardrails vs generation tells) is stated in the filter page; no merge.
2026-06-16 — Icon-picker fix, Coolify deploy model documented, /new-project retired
What happened: The Qlik icon picker (/tools/icon-picker/qlik) rendered empty in production. Root cause: the Supabase consolidation moved every Brand OS table into the brand schema, but the clients queried the default public schema and silently got nothing. The db: { schema: 'brand' } fix was already in main (PR #7); the page was empty because the Coolify build was stale.
Deploy chain fixed: Coolify deploy is manual (push to main does not deploy). The redeploy then failed at npm install with E401 — package.json had gained the Font Awesome Pro kit (@awesome.me/kit-*, private registry npm.fontawesome.com), but (a) the Dockerfile .dockerignored the .npmrc holding the token, and (b) the committed .npmrc held only a placeholder token (~19 chars, not the 36-char FA UUID) — the real token lived only in the developer's global ~/.npmrc. Final fix: token supplied as a FONTAWESOME_NPM_TOKEN build arg (Coolify build variable), written to a throwaway .npmrc inside the install RUN and deleted in the same layer; .npmrc untracked, gitignored, and dockerignored so no real token enters git or the image. Requires the FONTAWESOME_NPM_TOKEN build variable to be set in Coolify before the build will pass.
Docs: planning/cookbook.md refactored for the Coolify environment — Vercel → Coolify throughout; §5 documents the brand-schema requirement + the schema-fälla incident; new §11 covers the manual-deploy model, the stale-deploy checklist, and the Dockerfile-drifts-from-package.json gotcha.
Retired: the /new-project skill (~/.claude/skills/new-project/) — a Vite + Vercel-era scaffolder that never followed the Coolify migration; the slash command was already dropped from the DEV portfolio 2026-04-03, so the skill file was a leftover. Its robustness principles live on as cookbook §0–§3. Removed from the skill dir and from planning/slash-commands.md; cookbook §9 now marks it retired.
2026-06-16 — Status taxonomy migration executed (ADR set live)
What happened: Implemented the ADR-style taxonomy decided 2026-06-15. One status: field across the corpus: draft | proposed | accepted | superseded | deprecated.
Migration: content-corpus frontmatter migrated — decided/anchor/fixed → accepted (34 files), considered → proposed (imagery + its en-draft twin). normative: true added to the hard-rule pages (anti-patterns, voice kärnguide + dokumenttyper, progressive-enhancement) — final normative list pending Torfinn's confirmation. Operational/working docs (planning/, baseline/planning/) keep their ad-hoc statuses and carry no reader badge.
Render layer: components/ui/StatusBadge.tsx rewritten to the four ADR badge values (proposed/accepted/superseded/deprecated; draft unbadged) with English labels rendered directly; the Swedish badge strings (Beslutad/Etablerad/Föreslagen/Under granskning) removed from messages/sv.json. This revises the 2026-04 decision that badges render in Swedish at build time — badges are now English in all locales; localisation, if a market needs it, re-enters one layer down, never stored in frontmatter.
Docs: CLAUDE.md, planning/site-map.md, planning/site-spec-v2.md §5, and _TEMPLATE.md updated to the ADR set; the ADW anchor/fixed/living certainty-level reconciliation item in brand-os-inventory.md is closed (this migration resolves it). The interim decided/considered/suggested set is retired.
2026-06-15 — Status taxonomy moved to an ADR model; agent-baseline v0.2 scoped
What happened: A package-readiness review for Richard's vibe-coding handoff surfaced two infrastructure problems, both now scoped as briefs in _incoming/ for one Claude Code session. Also a small docs consolidation and the imagery page given its open-question callouts.
Decisions made:
- Status taxonomy → ADR model. Replaces the three competing vocabularies in use (
decided/considered/suggestedbadge set; the ADW certainty levelsanchor/fixed/living/stable; and the deadconstant/contextual/hypothesis/draftstill in some docs). New single set:draft → proposed → accepted → superseded(+deprecated).draft= divergent/creative stage (competing options live in-page, not as parallel badges);proposed= one direction advocated, not in force;accepted= decided and in force, immutable except corrections;superseded/deprecated= the retirement state the corpus previously lacked. Chosen because the corpus is decision-record-shaped. Migration mapping + scope in_incoming/2026-06-15-status-taxonomy-adr-migration-brief.md. normative: trueflag adopted. Carries the hard-rule-vs-guidance distinction (the droppedconsidered/decidedsplit) as a machine-readable flag for the LLM, independent of status. Applied to anti-patterns, the package Hard rules, and the formerfixedvoice/reference set.- Badge labels are the English base; translation sits in the localisation layer. Revises the 2026-04 decision that on-site badges render in Swedish at build time. Badges now render the English status value directly; badge localisation re-enters downstream at the localisation layer if a market needs it, never in frontmatter.
foundation/imagery.md→proposedunder the new model (five open[!decision]callouts; not converged).- Operational docs exempt from the badge ladder:
baseline/planning/*andbaseline/governance/baseline-update-protocol.md, treated like_state.md.
Also done this session:
- Imagery callouts placed. Five
[!decision]callouts added tofoundation/imagery.mdper Option C, surfacing why it's not yet converged: emotional-mode table covers only patient modes; AI-imagery policy half-drawn; real-photography asset precondition; market-variation governance; F10↔Baseline implementation boundary. No brand prose authored — flags only. - Docs consolidated. CLAUDE.md and
planning/site-map.mdimagery/iconography status lines made accurate (imageryconsidered/proposed, iconographydraft); both will be rewritten again to the ADR set by the migration. Typo fix infoundation/index.md+ package copy ("Denna skillnaden" → "Denna skillnad"). - agent-baseline v0.2 scoped. v0.1 had ~13 dangling file references in
BASELINE.md. Brief at_incoming/2026-06-15-agent-baseline-v0.2-completion-brief.mdmakes the package mirror the canonical subtree (self-contained for an LLM), adds a version registry +FEEDBACK.md. Sequence: taxonomy migration first, then regenerate the package so its MANIFEST shows the new statuses.
2026-06-12 — Database moved to self-hosted Aleris Supabase
Decision (Torfinn): the app now runs against the self-hosted Aleris Supabase (api.dev.aleris.ai, Falkenstein — all data stays in the EU) instead of the cloud project (tqfipvvvxhqjuquqroik). Per the platform setup, each app gets its own schema; ours is brand.
Applied: all Supabase clients (browser, server, middleware, auth callback) now specify db: { schema: 'brand' }; realtime subscription in CommentPanel moved from public to brand; .env.local points to dev URL + anon key, cloud service-role key removed. New migration 20260612000000_selfhosted_brand_schema.sql recreates the full schema (tables, RLS, realtime publication, grants) in brand and imports cloud data with no auth dependencies (1 allowed_user, 19 icon_assignments). supabase/import-annotations-after-first-login.sql imports the 4 annotations with author remap once the first dev login has created a profile — auth users don't transfer between environments.
To run (Torfinn): migration + annotation import in Studio (sb.dev.aleris.ai); npx supabase unlink (or delete supabase/.temp/) to drop the CLI link to the cloud project; pause the cloud project after verifying. Earlier migrations in supabase/migrations/ are cloud-era history, kept as-is. Open: no /api/auth/send-email-hook exists yet — self-hosted email sending uses a webhook (see klinfys in Coolify), so magic-link login emails won't send until it's added. Prod is not provisioned (Mohan).
2026-06-12 — F11 revision: tiers → four arenas; first prose placed
Same-day revision of the tier pivot (Torfinn): the sorting key is the arena — the use situation where the icon is met — not consequence level; consequence becomes the rationale for each arena's strictness. Four arenas: patientmötande (digital services + patient material regardless of medium), interna digitala verktyg (Qlik picker = reference), presentationer & internt material (solid/duotone/illustrative permitted), skyltning & vägfinning (new — strictest, defined with Physical). Extend beyond four only if a new use situation demands it. New principle: "specificitet simmar inte uppåt" — locally assigned icon meanings (Qlik app icons) create no rule outside their tool.
Physical chapter stub created: physical/README.md — placeholder that registers all known inbound dependencies on the unwritten Physical chapter (F11 skyltning arena, F1 Pantone/material specification, teal replace-on-touch in Norwegian environments, no-dark-mode in physical rooms, F10 interior imagery) plus first open questions. Grows by appending rows; becomes the requirements inventory when Physical is planned.
Prose placed (Torfinn, via dictation flow): the character test — "Matchar ikonen den nära expertens uttryck när den används så här?" — plus supporting paragraphs; and the intro ("Varför ikoner ser ut som de gör"), which carries a revision prompt (three-ways text needs four-arena pointers; patient print material must point to patientmötande, not presentations). Scaffold, baseline README (arena-scoped), and briefing §12 updated accordingly.
2026-06-12 — F11 pivot: three application tiers, character test as the constant
Trigger: presentations and the new Qlik icon picker broke the single-tier model — in decks, icons legitimately do illustration work (solid/duotone, decorative), and the picker needs near-full FA search. Torfinn's framing: restrictions must serve as boundary for creative needs, not rule.
Decisions (Torfinn): (1) three tiers calibrated by consequence, not medium — patient-facing & product UI / internal tools / internal & informal communication; a patient brochure in PPT is tier 1. (2) The constant is the character test ("behaves as if den nära experten were in the room"), judged within its tier, with categorical exclusions everywhere: weapons, logos, religious symbols, aggressive/alarmist motifs. The "calmest Aleris surface" test demoted to tier 1's local gate. (3) Tier 2 (Qlik picker = reference implementation) gets a generous allowlist with two distinct exclusion types: character violations (generalise to all tiers) and function violations (number icons — instrumental surfaces only). (4) Style (outline/solid/duotone) and role (functional/illustrative) become contextual; FA Pro as single source, palette-bound colour, and meaning-never-alone remain constant.
Applied: foundation/iconography.md scaffold restructured (tier table, character test section, updated author prompts — prose remains Torfinn's); baseline/iconography/README.md scoped to tier 1; briefing §11 added. Follow-up decision (same day): duotone binds to two petrol steps — never cross-family pairs; exact steps specified in the tier 3 document. F11 prose session remains the next authoring step.
2026-06-12 — Font hosting live + package updated
Claude Code implemented the font-hosting brief: Museo Sans woff2 + derived aleris-fonts.css (absolute URLs, font-display: swap) served from the brand site at /fonts/, CORS-allowlisted to Aleris domains via middleware, setup.md carries the link snippet. Agent-baseline package updated: README §Fonts with the two-line loading snippet, MANIFEST exclusion note now points to the hosted URL. EULA verification recorded in _packages/README.md rule 4 (link-only license; Receipt still to be located for Licensee name + pageview cap).
Found during verification, not yet fixed: llms.txt serves https://localhost:3000/... URLs in production (base-URL env not set in deploy); llms.txt has no fonts entry; llms.txt closing note still lists "photography" as missing although F10 imagery is in the index above it.
2026-06-12 — Corpus audit: two decisions + consistency cleanup
Decisions (Torfinn):
- Confirm green = #4f866e (Foundation F1 wins over Baseline's #27ae60). Also an a11y win: white on #4f866e = 4.23:1 (passes AA large/UI); #27ae60 failed at 2.87:1. Updated:
aleris-tokens.css(+ regenerated json),BASELINE.md,aleris-design-governance.md,aleris-design-tokens.md(old "No success colors" DDR replaced),baseline-current-state.md. Hover--button-confirm-hover-bgset to derived #43725e with OPEN flag — Torfinn confirms. - Table headers = sentence case (no-all-caps rule wins over grids-tables' uppercase decision). Updated:
aleris-grids-tables-dataviz.md(token + principle + DDR),aleris-design-governance.md(×2),BASELINE.md, open-questions RQ-11. "Wider letter-spacing" removed — no such token exists.
Mechanical fixes from the audit: BASELINE.md surface-mode example corrected (was scrambled; now matches governance), all-caps rule aligned with Foundation's abbreviation exception, stale version stamp replaced, dead OQ-11 removed from governance, hardcoded OQ counts removed, setup.md "Photography direction" gap removed (F10 exists), animation heading "Three Levels"→"Four durations", anti-patterns' stale "Relationship to Other Documents" section removed, voice guide renumbered as 4 parts (part 4 in governance/), historical-source caveat added to typography-spacing-scale (font-weight 400). Agent-baseline package re-synced.
Open decisions surfaced by the audit — resolved same day (Torfinn): (a) booking flow = instrumental throughout — BASELINE.md and setup.md updated to match governance; (b) "pill shape" deleted from foundation/colour.md by Torfinn; (c) fasting example: voice/3's wording is the single version — chat-response-simple aligned. New decision: hover lightens, never darkens — interaction states move one step lighter ("choices light up"). Token set already followed this; rule now explicit in BASELINE.md §Buttons and governance §4. --button-confirm-hover-bg set to #7ba492 (derived lighter step — validate when a confirm scale is added to F1).
Still open: (d) digital-behavior-v1 frontmatter anchor vs body "draft" — Torfinn reviewing the document; (e) baseline/index.html and the Brand OS presentation layer use uppercase + 10px chrome, violating the system they present — rewrite planned after cleanup, not yet briefed.
Queued for Claude Code: brief placed in _incoming/2026-06-12-arc-moves-brief.md — move reference/aleris-token-governance-frameworks.md and governance/baseline-update-protocol.md to arc, update manifest, repoint references.
Flag review (Torfinn, same day): all six ⚑ UTKAST flags approved and removed — the four clinical-example replacements (chat-response-simple, voice/3, foundation/voice.md ×2) and the two older teal/legacy flags in foundation/colour.md (Swedish mirror of approved en-draft, pending since 2026-05-31). foundation/colour.md and foundation/voice.md are now flag-free canon.
Follow-on decision (Torfinn, same day): no clinical content in chat/AI examples. Automated chat carries clinical content only under explicitly controlled, clinically approved configurations; examples with clinical content require a clearly licensed clinical sender. Tightens digital-behavior's medical boundary (preparation no longer generally in scope for chat). Applied: fasting examples replaced with provsvar logistics (verbatim from chat-bubble) in chat-response-simple.md (×3) and voice/3 (×2); foundation/voice.md AI-section example replaced + new "aldrig klinisk rådgivning" bullet — all voice-canon edits carry ⚑ UTKAST flags pending Torfinn's approval. Rule documented in 4-den-nara-experten-digital-behavior-v1.md §AI chat guardrails and as example convention in CLAUDE.md. Patient-guide contexts (foundation/voice principles, voice/2) keep clinical examples — licensed sender. Package re-synced.
2026-06-12 — Agent baseline package v0.1 + package governance
Trigger: Richard (AI-ansvarig Sverige) requested branding/UI/UX/accessibility material for an agent vibe coding instruction set — the first external pull on Brand OS as an LLM-consumable source.
Created:
_packages/agent-baseline/v0.1 — BASELINE.md, tokens, brand-in-brief, anti-patterns, 7 patterns, 2 voice docs. README is draft pending Torfinn's review before anything goes to Richard._packages/README.md— package governance: packages are generated snapshots, never hand-edited; every package carries a MANIFEST.md (version, date, per-file provenance); consumers are listed.
Findings:
- Klinfys package BASELINE.md has drifted from canonical (canonical evolved after snapshot, no version stamp to detect it). Regenerate on next Klinfys contact.
- Museo Sans woff2 redistribution rights unverified — fonts excluded from packages until the webfont license is checked.
- Accessibility coverage adequate for v0.1 via BASELINE.md §Accessibility; placement of the shared a11y node (
en-draft/constitutional/_accessibility-shared-draft.md) remains open and does not block.
Open: delivery terms with Richard (versioned snapshot replaced on release, not a frozen embed); package language migration to English canonical once en-draft lands.
2026-06-11 — Voice filters track opened (anti-AI-tell tooling)
Decision (Torfinn, planning session): the accumulated anti-AI-tell work (writing-style-profile anti-patterns, TAKS calque check, svengelska rules) becomes Brand OS voice tooling — a filter layer under F4, usable in every content-generating process (vibe-coding, presentations, documents, brand assistant). Four scoping decisions made:
- Curated transfer, not wholesale. Only brand-transferable patterns from the personal writing-style-profile enter Brand OS; personal-voice preferences stay in TAKS. Every pattern carries provenance.
- Three-part placement. Canonical corpus page (
how-to/, sibling to F5), machine-readable rules indata-products/, applying skill in_tooling/. Single source, multiple renderings. - Skill first. Working
_tooling/filter skill ships before the corpus page; the page is authored from what use proves. - Markets: generic (cross-market) layer + Swedish language layer now; NO/DK scaffolded as
open — needs native speakerper the no-fallback rule.
Architecture note: three-layer model keeps provenances separate — Layer 1 generic AI-tells (language-independent), Layer 2 language quality per market (SV mature, NO/DK open), Layer 3 brand voice = F4 (already exists; filters sit under it, not beside it). The viewports/voice-for-ai-systems.md prescriptions will cite the new canonical pattern page rather than restate (one-source rule). F5 owns production mechanics; filters own detection — siblings, not parent/child. Nothing changes the pending ordlistan placement decisions.
Boundary restated: filters reduce tells; they don't replace the translation layer. Filtered output is still AI output — human ownership remains the gate for stakeholder-facing content.
Artifact: planning/voice-filters-plan.md — full plan with curation table (provisional sort of all 14 + calque-check patterns, pending Torfinn's confirmation), phasing A/B/C, and five open decisions.
Same day — all five open decisions resolved (Torfinn): curation table confirmed (smarta metaforer and two-sentence rhythm both soft); names confirmed (voice filters / ai-tells-and-filters); per-market language layers as sibling pages (language-quality-sv/-no/-dk); auto-rewrite for hard hits (purpose: catch tells before they reach a reader — runtime surfaces have no human pass; soft hits stay proposal-only; rewrites logged for audit; boundary unchanged, see plan §9); NO/DK native review likely owned by each market's marketing manager, confirm when those layers open.
Next: Phase A — build the _tooling/voice-filter skill from the confirmed curation table.
2026-06-17 — Voice keystone resolved: perspective→spine, P1→agency
Decision (Torfinn, confirmed): "Adopt the audience's perspective" becomes the root stance (the spine), not Principle 1. Principle 1 re-scopes to its distinct facet — agency (the patient as the one who acts, not the one acted upon). Live-frontier #1 closed.
Why it holds (the reasoning, so it survives): perspective and agency are separable knobs — you can write from the patient's vantage yet leave them passive ("the process will carry you through"), and you can grant agency from the org's POV ("submit your referral to us"). And because all five principles are facets of perspective-taking, perspective is the genus and the five are species — a genus can't also sit as one of its species. So keeping perspective as P1 would put it at a higher altitude than P2–P5, breaking the "one altitude" goal of the whole restatement. Dissolving P1 entirely was rejected because agency is the facet most tied to the sentence-level craft (active voice, the summons example) and would orphan that evidence. Five principles; perspective on the spine. This is the third instance of the recurring pattern named in voice-editorial-overview.md — a candidate root swallowing a principle (allegiance→P5, now perspective→P1); resolution each time: root goes to the spine, principle re-scopes to its narrower facet.
Plain-language guard recorded: "agency" is the label for the facet, not the wording. The principle's title and prose stay at "the one who acts / the one things happen to" — the abstraction (EN agency, SV agens) stays out of the shipped line.
Drafted (PROPOSED, for Torfinn's fold-in): foundation/en-draft/principles/_principles-restated.md — added a spine block above the five (perspective + allegiance + the override; home flagged as constitutional/voice-is-the-present-expert.md); P1 rewritten to agency-only with the perspective "swallow" sentence removed and the anti-pattern's perspective tells ("the clinic offers…") moved up to the spine; Notes updated to record that the spine now carries two things (perspective + allegiance) and to surface the next domino.
Spine resolved in the same session (Torfinn, confirmed): three further decisions settled the spine itself —
- Medium-neutral at the spine (frontier #3, partial). The spine forced #3 forward: it's the one place the medium-agnostic claim lives or dies, since everything below inherits its language. Spine reworded off "write/reader" onto "wherever Aleris shows up — a letter, a screen, a sign, a spoken word." The five principles' bodies still say "reader/text" — the rest of the #3 sweep is still pending.
- Audience-general; patient as primary instance (revises a prior decision). The spine names the reader/audience in general (patient, next of kin, partner, public servant, investor); patient is the hardest case and gets the most care, but is no longer the definition of the spine. This revises the 2026-05-31 "patient stays the spine, not generalised to 'reader'" decision — Torfinn's reasoning: patients are peer to the other audiences in deserving respect, primary only in amount of care.
- Override dropped → reach (resolves a contradiction I introduced). The "divergence must be named and justified (the override)" line clashed head-on with the constitutional file's existing "There is no override procedure / non-negotiables are not waivable," and by the project's own constitutional-core-test an override would demote perspective from constitutional to a calibrated default — undoing the keystone. Replaced with a reach statement (what counts as Aleris meeting a person; a contract/regulatory notice is a different genre, not an exception). Reach bounds the rule without waiving it, honouring Torfinn's "few absolutes in human communication" without building a waiver into the constitution.
- "Meet" carries the bemötande fusion (frontier #6, confirmed on purpose). The spine verb is "meet" — deliberately folding the bemötande (spoken-encounter) work into the voice structure, the authority claim flagged in the editorial overview. Confirmed knowingly, not by accident of word choice.
Resolved spine (PROPOSED, in _principles-restated.md): "We take the perspective of the person we're meeting, not our own. We see the situation from where they are — patient, next of kin, partner, public servant, investor — not from inside the clinic or the company. We serve their purpose, with no agenda riding underneath. … This governs every place Aleris meets a person as Aleris; a document of a different kind — a legal contract, a regulatory notice — isn't an exception to it; it's a different genre, under its own rules."
Constitutional file finalised to v1.0 (Torfinn authored, same session). The pass landed: §2 "The stance" carries the spine (perspective + service + Reach); §4 non-negotiables generalised to "the person" with the sales line kept clinical-scoped; the detection §-references fixed after the renumber (test §3, non-negotiables §4); status bumped decided/v1.0-draft → accepted/v1.0, scope line widened to the full audience set; all PROPOSED markers cleared.
Detection / governance section reworked (Torfinn's challenge, resolved). Torfinn pushed back that the old "Detection of violation" described unbuilt machinery (logging, automated eval, feedback loop) and that "there is no override procedure" read as foreclosing all future revision. Both right. Resolved into two short sections: Detection and enforcement — honest to what exists (human review against the test and non-negotiables); automated eval/logging deferred to a future qualities/ fitness-function page; a real failure sharpens the examples in principles/voice.md, not the non-negotiables, which harden only by passing the constitutional-core test (the guard against the voice over-fitting to past mistakes). Governance and revision — governed jointly by Head of Design + VP of Market and Communication; the key distinction drawn between override (a silent per-text waiver — forbidden) and amendment (a deliberate, governed revision via a version bump — the proper channel for a legitimate future failure), with the amendment path referenced to planning/versioning-rules.md, not restated (inheritance-by-reference, per the way of working). Owner field updated to the two roles. File now fully clean — no PROPOSED markers remain. (Follow-up flagged: versioning-rules.md still speaks the old trunk/branch/leaf model and wants a light reconciliation to the layer taxonomy.)
Promotion queued: _incoming/promote-voice-constitutional-v1.md — a Claude Code brief to move the authored v1.0 from foundation/en-draft/constitutional/ into canonical constitutional/voice-is-the-present-expert.md, replacing the stub. Gated on (1) Phase 1 committed/verified and (2) the Detection sign-off cleared. English is the v1 base; SV localisation is a separate later step (flagged as an assumption in the brief). Verbatim move — Claude Code authors/edits nothing.
Frontier #2 resolved (Torfinn): the overarching test stays "genuine and attentive" — no hidden agenda is not added. Reasoning: "hidden agenda" connotes a sinister motive, while "genuine and attentive" is positively framed and already carries the allegiance. So allegiance lives on the spine as a positive statement ("we serve the purpose they came for, not a second one of our own"), and the test stays warm rather than defensive.
Constitutional file pass (PROPOSED, staged for Torfinn's editorial pass): foundation/en-draft/constitutional/voice-is-the-present-expert.md updated to match the resolved spine —
- New §2 "The stance: we meet people from their perspective" inserted (the spine): perspective + the positive allegiance line + a Reach paragraph (governs every place Aleris meets a person as Aleris; a contract/regulatory notice is a different genre, not an exception; absolute within its reach). Medium-neutral wording. Old §2/§3 renumbered to §3/§4.
- §4 non-negotiables breadth pass: "patient" generalised to "the person" where the rule holds for every audience; the sales-in-clinical line kept clinical-scoped on purpose (partner/public value-cases stay legitimate). Marked PROPOSED for Torfinn's confirm.
- Detection-of-violation "no override procedure" line kept (now consistent — the override was dropped) and re-pointed at Reach in §2 instead of the old "guide's reach" cross-ref.
- His existing §1 identity prose and §3 perspective-test left verbatim.
Allegiance-wording flag for Torfinn: the spine carries allegiance as "we serve the purpose they came for, not a second one of our own" — chosen to avoid the same "hidden agenda" sinister connotation he flagged on the test, and to tie to P5 ("one honest job"). His to confirm or reword.
Essay deltas staged (en-draft/principles/voice.md). Started folding the session's decisions into the explanation essay (the June-7 Pass-1 draft), in "stage deltas" mode — targeted, low-risk only; the text-first prose left for the separate medium-neutral pass (frontier #3). Done: Principle 1 re-scoped to agency (title → "Is the patient the one acting…", the perspective-swallow paragraph cut and replaced with a pointer up to the spine, the "the clinic offers…" perspective tell removed from the anti-pattern), marked PROPOSED; duplicate Principle 5 removed (copy-paste artifact — two ## 5 sections); scope line widened to the full audience set (frontmatter + status line); the test marker cleared (frontier #2 locked the wording). Left deliberately: P5's (PROPOSED) on its test — only P5's structure was settled, the wording is still Torfinn's pass; the title "how the Present Expert is written" and all text-first language — that's the frontier #3 sweep, a separate deliberate pass. Essay stays status: draft.
Naming decision — "the stance" (Torfinn). "Spine" was internal shorthand that had crept from the planning docs toward the material; it was never a deliberate term. Torfinn flagged it (per the plain-language rule: use words that exist, don't coin). Chosen term for the root that the five principles are facets of: "the stance" — already the word the constitutional file's §2 uses, so the two documents now agree. "Spine" replaced in the material (the two Principle-1 spots in voice.md); it remains in the planning docs as internal shorthand, to be aligned in cleanup (low priority, chrome). Whatever the term, it must be defined where the root is introduced — the constitutional file owns that.
Hook + proposals plained (Torfinn's steer). The PROPOSED hook was rewritten plainer (dropped the "logotype is seen, colours are seen, but the voice is felt" parallel and the "the question transfers, the answer is its application" aphorism; the closing line now states the purpose flat — each principle leads with a question so you can reason through a situation the guide never anticipated). Same make-over applied to the P1 pointer and the two flowery lines in the "patient information → patient guide" proposal. Torfinn's instruction stands for the rest: the AI-drafted PROPOSED prose should match the plain register. The "Across audiences" framing is left as scaffold he'll author himself.
2026-06-03 — Norwegian widget voice: 3 Open decisions resolved, kit registered
What happened: Resolved the three Open decisions on the provisional widget-voice kit and wired it into Brand OS tracking.
Decisions made:
- Målform — Bokmål. National private-healthcare assistant; widest reach.
- Artifact location — the widget repo (
Dev/aleris-assistant/), not a_packages/entry. The kit itself is the Brand OS-side pointer; no separate stub file. - Reconciliation trigger — canonical Norwegian voice reaching
decided. Diff provisional against canonical and fold learnings both ways.
Built:
planning/no-widget-voice-source-kit.mdbumped to v0.2; "Open" section converted to "Resolved"; §4 records Bokmål.- Provisional artifact scaffold staged for Torfinn to move into
Dev/aleris-assistant/— structure-only (status line, section headers,[[ render ]]placeholders), no Norwegian prose (boundary held; Torfinn authors the Norwegian fromfoundation/voice.md). - Kit registered in
planning/file-manifest.md.
Source-priority revision (supersedes the 2026-05-31 "render Swedish→Norwegian" decision): Torfinn pushed back on Swedish-primary. After reading both source sets side by side, revised the kit's §2: structure/reasoning now leads from the refactored English (en-draft/principles/voice.md + constitutional/voice-is-the-present-expert.md) — the question-first principle framing is the better reasoning shape for an LLM widget, plus anti-patterns, the perspective gate, and the constitutional/principles split. Character stays anchored on den nære eksperten (Norwegian-native cognate of the SE term) — the one thing not inherited from English is "Present Expert," which shifts closeness→presence. Swedish demoted to clean-prose reference. Rationale: the double-translation risk that justified Swedish-primary doesn't hold when the author is trilingual. §3 (stable/in-flux) updated to match; scaffold rebuilt to the question-first shape (v0.2-scaffold).
Open: kit is still untracked in git (along with ~15 other planning files) — git add/commit to be handled separately, not from Cowork.
2026-05-31 — Norwegian widget voice: provisional source kit (not canonical)
What happened: The aleris.no AI-assistant widget needs a Norwegian voice and Brand OS has none. Decision: do not author a canonical Norwegian Foundation voice now (the voice is mid-refactor — a moving target). Instead, give the widget a provisional, non-canonical Norwegian voice source, rendered Swedish→Norwegian (closest hop; dissolves the "Present Expert" problem — Norwegian carries den nære eksperten natively), patient-scoped, authored by Torfinn (Norwegian native — resolves the local-validation requirement the market axis demands).
Built: planning/no-widget-voice-source-kit.md — source priority (render from Swedish canonical foundation/voice.md, esp. its "För AI-system" section; AI-adapter shape from the viewport; English refactor for structure only), a stable-vs-in-flux map (character/test/five principles/non-negotiables are safe; question-leads, audience/market, reader-state naming are not needed for v1), SE→NO translation discipline (den nære eksperten; no calqued idioms; Bokmål/Nynorsk decision), the AI-widget rule layer, and governance (provisional label, ownership split widget-vs-BrandOS, reconciliation checkpoint). No Norwegian prose authored by Claude — Torfinn renders it.
Reassurance recorded: the voice's substance is stable; the fragmentation is in form/wording, not the core definition — enough for a widget that needs character + principles.
2026-05-31 — Reader state introduced as the umbrella over emotional modes
What happened: Extending emotional-modes.md to hold across audiences (the dependency voice's Across audiences/markets created). Torfinn chose "reader state" as the concept — more correct, since professional scrutiny isn't an emotion, and better received at Aleris.
Found the richer model: the detailed mode model — anxiety-driven, aspiration-driven, latent anxiety — is hypothesis status, lives in Baseline (not Foundation), is all patient depth, and is referenced by iconography (Mode A/B) and carried as machine-readable metadata on Baseline components.
Decision (CONFIRMED by Torfinn, 2026-05-31): umbrella, not rename. "Reader state" is the umbrella; emotional modes (worry/curiosity + the digital 3-mode hypothesis) stay valid as the patient/next-of-kin family; professional audiences get dispositional states (scrutiny, time-pressure). This is additive — emotional modes keep their meaning, so Baseline metadata and the hypothesis model don't break. A full rename would be a Foundation major-version change per versioning-rules.md, propagating to voice, imagery, iconography, Baseline metadata, the contextual-axis name, and the nav — left as Open #1.
Drafted (scaffold, PROPOSED): emotional-modes.md retitled Reader state as a design entry point (file/slug kept to preserve links); patient waiting-room/recovery-room example kept as the spine; two families (emotional modes / dispositional states); an Other audiences, other states table mirroring voice; Open items for the rename decision, digital-model reconciliation, market-is-not-a-state-axis, and validation of the professional rows.
2026-05-31 — Audience named as a contextual axis; voice breadth made visible
What happened: Torfinn flagged that the voice is patient-heavy though the audiences also include next of kin, private and public decision-makers, coworkers, and care practitioners. Analysis: the voice holds across all of them — the five principles are about perspective and structure, not patient content, and the "Present Expert, not a salesperson" character is arguably strongest in B2B/B2G. So it's nuance and vocabulary, not register. The real gap is visibility: the guide as written looks patient-only while its scope line already claims breadth.
Drafted (scaffolds, PROPOSED — Torfinn's voice pass):
principles/voice.md— new Across audiences section: framing line, a per-audience "what shifts" table, two edges that are more than vocabulary (internal audiences flip we/you; emotional mode is audience-specific), and one boundary clarification (the "no sales in care comms" non-negotiable is scoped to clinical contexts — partner/public value-cases are legitimate). Open #5 added.constant-contextual.md— names the contextual axes and, on Torfinn's push-back, sharpens the model: axes are domain-specific; each identity element declares which bind it. Channel/format, emotional mode, audience, and market/culture. Correction to the earlier blanket claim — market IS an axis for voice (different languages; different norms for how directness/warmth/formality are read) and for imagery (which faces/environments read as authentic), but not for colour (uniform palette, petrol primary everywhere; teal's market difference is rollout/legacy, not sanctioned expression). So "is X an axis?" is answered per element, not globally.principles/voice.md— added an Across markets (culture + language) subsection: language (native realisation, no calqued idioms) and culture (per-market calibration of the constant voice), with the caveat that specific calibrations are set with local teams/native speakers, not prescribed centrally.
Follow-on: emotional-modes.md is patient-shaped (worry/curiosity); extend with audience-analogous modes (next-of-kin helplessness; decision-maker scrutiny/accountability) or note each audience has its own. Patient stays the spine of voice.md — not generalised to "reader" — because it's the hardest case and the brand's centre.
2026-05-31 — Teal (ex-turquoise) revived as a legacy tier (political directive)
What happened: Torfinn: for political reasons teal (formerly "turquoise") must be revived as a legacy colour, not retired. Petrol is the primary in every market including Norway; teal is kept because Norway remains strongly attached to it, has been slow to transition, and still uses it off-profile (heavy physical-environment presence). Sweden/Denmark carry only faint traces. The earlier "turquoise is retired/not used" position is reversed.
Model adopted (default, confirm): the palette gains a legacy tier. Teal is a named palette colour at legacy tier — so the "only palette colours" non-negotiable still holds (teal is in the palette, not arbitrary). Teal's sanctioned status is uniform (legacy everywhere); what differs by market is real-world lingering presence and transition difficulty — an operational/rollout matter (Baseline), not a per-market role. (Earlier framing of "market as a new contextual axis" walked back — petrol is primary everywhere, so there is no per-market role difference in the sanctioned palette.)
Drafted (working copies, PROPOSED): foundation/en-draft/colour.md — replaced the turquoise hard-rule with a "Teal is legacy, not retired" line and added a Legacy colours section (per-market status table + a values table with HEX/Pantone/CMYK to supply). constitutional/colour-is-the-aleris-palette.md — bounded-instance note flipped from "turquoise is retired" to "teal is legacy, still primary in Norway."
Resolved (Torfinn, 2026-05-31): name = teal; HEX #00C6B2 (sRGB 0,198,178; CMYK ~C100 M0 Y10 K22 computed, validate at press; Pantone TBD); posture = replace on touch (replace when a surface is next updated, no removal campaign); update the token set. English drafts (en-draft/colour.md, constitutional stub) carry all of this.
Change-brief prepared: planning/teal-legacy-change-brief.md — exact edits for the Swedish canonical foundation/colour.md and Baseline tokens (teal as a legacy-tier token; conformance check treats it in-set, warns on new use). For Claude Code on a branch; Torfinn authors the Swedish legacy prose and signs off. Not applied from Cowork.
English accepted (Torfinn): en-draft/colour.md legacy content signed off (PROPOSED flags removed). Still pending: Swedish canonical foundation/colour.md (still on the old "Turquoise används inte" rule) and Baseline tokens — both covered by the brief.
Handoff queued: _incoming/teal-legacy-claude-code-task.md — executable task for Claude Code on a teal-legacy branch. Surfaced two repo specifics: (1) --color-chart-01-teal: #0f9081 already exists as an unrelated chart colour → legacy brand teal needs a distinct name (e.g. --color-legacy-teal); (2) aleris-tokens.css is the token source of truth, baseline-tokens.json is generated via generate-tokens-json.js. Swedish prose to be drafted for Torfinn's sign-off; branch + PR, no merge.
2026-05-31 — Colour + Typography constitutional cores sorted and drafted
What happened: Applied the constitutional-core test to two more pages. Drafts in foundation/en-draft/constitutional/:
colour-is-the-aleris-palette.md— core residue: only-palette-colours (turquoise/cream fold in as bounded instances), sand-not-white ground, no-dark-mode. Palette roles → identity/essay; one-orange-button → craft; accessibility → shared node.typography-is-museo-sans.md— core residue: only-Museo/Arial, sentence-case (abbreviation exception is a scoped boundary). Italics/rem/keep-it-simple/weights → essay+tokens; 14px floor → shared node._accessibility-shared-draft.md— new capture node holding the accessibility non-negotiables pulled from both pages, so they're referenced not restated. Placement (constitutional node vs qualities/ility vs both) open.
Reusable findings:
- Every identity-element core has one dominant non-negotiable of the form "use only the canonical set, never substitutes" — palette / typeface / (voice: write-to-the-person). The spine of each core.
- Scoped boundary vs override — folded into
constitutional-core-test.md§4. An exception that bounds a rule (all-caps except abbreviations) keeps it constitutional; an exception that overrides it (sub-AA white-on-orange button) demotes it to a calibrated default. This is why WCAG-AA contrast is not a bright-line. - Accessibility wants one shared home, not one per page — logged as Open #3 of the sorting test; mirrors how voice surfaced the values-layer gap.
Term note: typography draft updates "close expert" → the locked "Present Expert" (flagged for confirmation).
2026-05-31 — Constitutional-core sorting test + values layer seeded
What happened: Two reusable pieces came out of the voice §3 work, where Torfinn kept pulling important-feeling statements into the non-negotiables that didn't belong.
documentation/constitutional-core-test.md— a reusable two-step sort (bucket: identity / bright-line / contextual principle / craft principle / value → gate: detectable + constant + identity-defining) for deciding what is a non-negotiable on any Foundation page. The voice §3 sort is the worked example. To be applied to every page's core; candidate for the shared root across the three metas.planning/values-layer.md— seeds the missing values layer. Canonical values recorded as Simple · Responsible · Innovative (Torfinn's framing; official wording to confirm), noted as currently thin and to be unpacked. Mechanism: capture values-references from ongoing brand work (tagged to a value) rather than composing cold — same accretion pattern as_examples/. First capture: "we own our failures" (Responsible), routed out of the voice constitution. Placement (Foundation page vs aleris-meta vs voice-slices inprinciples/) left open.
Housekeeping: non-negotiable count de-hard-coded (Implication reads "these"); constitutional file Open #1 (anaemic stub?) and Open #2 (count) both closed.
2026-05-31 — Voice refactored into the meta file grammar (probe → drafts)
What happened: Tested the aleris-meta file grammar on one content-rich Foundation page. Produced planning/voice-meta-refactor-probe.md (file-level instantiation, one layer below the corpus map in aleris-meta-alignment.md). Torfinn accepted the cut and the four objects were drafted into foundation/en-draft/:
constitutional/voice-is-the-present-expert.md— the small non-negotiable core (identity, the test, the non-negotiables, detection-of-violation).principles/voice.md— the explanation essay; each principle now leads with the question it answers; per-principle test + anti-pattern; examples cited not embedded._examples/voice-examples.md— the evidence set (summons, fasting, Trombyl, quick-reference catalogue), tagged per principle.viewports/voice-for-ai-systems.md— the "For AI systems" block lifted out as an adapter (generated-from-source, not canonical).
Decisions: (1) the two-file split (constitutional + principles) is right for Voice — Open #1 closed; (2) evidence lives in _examples/ for easy accretion; (3) container-before-content — this structural cut precedes the next voice content pass so the consolidation pours into the new shape; (4) English is the working axis, grammar mirrored to Swedish canonical later.
Boundary held: Torfinn's existing voice copy was relocated verbatim; new brand-voice slots (the five question leads, the hooks) are marked PROPOSED for his voice pass, not finalised. Drafts are in en-draft/, non-canonical.
Next: Torfinn's voice pass on the PROPOSED leads; then apply the same grammar to a simpler page (colour or typography) to confirm it generalises; reconcile with the live voice workstream before content moves.
2026-05-31 — Voice §1 identity + §2 gate authored (constitutional core)
What happened: Torfinn took the voice pass on constitutional/voice-is-the-present-expert.md §1–§2, working from the beat-spec and gate analysis. Two decisions locked:
- The English identity carries a "closeness layer" the Swedish leaves to the label. Since no English word matches the warmth/proximity of nära, the explanation installs it explicitly: "a knowledgeable colleague who will stand beside you… not an authority, a salesperson or a parent, but a peer who wants to help." Rationale: nära's connotations are lost on non-native readers anyway, so making the dimension explicit is a transmissibility gain, not a loss. The constant is the character, not the label; "Present Expert" and den nära experten are two contextual renderings of it.
- The gate tests perspective, not honesty/agenda. Old test ("…has time for me without a hidden agenda?") was recalibrated — ulterior motive is a rare failure; writing from the institution's point of view is the common one. New gate: "Does this feel written for me, from where I am?" with the failure named below ("If it reads as about me, from where they sit, it isn't finished"). Section renamed Testing the perspective. The trust/commercial-creep concern the old gate guarded is left to §1's "not a salesperson."
Open #1 (anaemic stub?) — closed. With the closeness layer and the peer framing, §1 carries real weight on its own; the constitutional/principles split holds for Voice.
Boundary: all of the above authored by Torfinn; Claude's role was analysis (beat-spec, gate calibration, line-level critique) only.
2026-06-07 — site-map refreshed to v3
What happened: Updated site-map-v1.md to current architecture and bumped it to v3 (simple per-doc version scheme — there's no monolithic brand-os version to match). Kept the durable core (the inheritance/dependency model, no-fallback language stance, status badges, design philosophy); replaced the superseded specifics (trunk/branch/leaf naming → layer taxonomy; Baseline-as-branch; Swedish-default → English-default; Next.js stack → static re-platform; writing-conventions-in-Foundation → conventions layer). Dropped the old branch-model nav tree, 17-chapter mapping, and v1/v2 ship-scope (superseded by phase-0…); the doc now defers to the newer canonical docs for detail and is ~half the length. Frontmatter + status line added. Done same day: Torfinn confirmed Open #1 (trunk/branch/leaf retired as naming). Renamed site-map-v1.md → site-map.md (git mv, worked from Cowork — deletion was enabled for the folder earlier); fixed inbound depends_on in architecture-source-of-truth-and-assets.md and refactor-brief-structure-and-wow.md; updated CLAUDE.md's architecture line and key-files entry (trunk/branch/leaf → layer taxonomy; new filename). Legacy/archived references (changelog history, f6/f9 briefings being archived, file-manifest, worktree copies) left as-is.
2026-06-07 — Archive DECIDE items resolved
Torfinn's calls on the DECIDE list: keep f11 (curation ongoing) and storybook (live); archive the probe, the voice kickoff context, and the two qlik briefs (now in the Archive-now list). Probe's structural reasoning captured into voice-editorial-overview.md (Background) before archiving, so the "page does four jobs / answers don't generalise, questions do" reasoning survives. site-spec-v2.md kept as a Phase-3 salvage source (durable: users, kits, the feel, teaching mechanics; superseded: IA, tech, header/footer). site-map-v1.md to be updated + version-bumped — but there's no canonical brand-os version to match (CLAUDE.md: "building toward Foundation v1, nothing versioned yet"), so the version scheme is an open question for Torfinn.
2026-06-07 — Planning-layer archive sweep queued
What happened: Scanned planning/ (~42 docs) at the floor-setting moment; ~a third is spent. Queued a Claude Code task at _incoming/archive-planning-scaffolding.md to archive (not delete) the clearly-spent scaffolding: the Foundation extraction briefings whose pages are placed (f1, f2, f4, f6, f9, b1), the superseded build/web/baseline/commenting briefs, and voice-pass-1-merge-map (retired for draft-to-react). DECIDE items left as checkboxes for Torfinn (f11, site-spec-v2, site-map-v1, the probe, kickoff context, storybook research, qlik briefs). Fenced off: the content-layer supersession (foundation Swedish originals, baseline, en-draft) — those archive at Phase 2, not now. Archive ≠ delete; runs in Claude Code on a branch.
2026-06-07 — Medium-agnostic pivot + editorial overview
The pivot (Torfinn). The voice principles aren't about text — at this altitude they're about how Aleris meets people in any medium (spoken/bemötande, signage, video, text). "Adopt the audience's perspective" is the founding root/spine; divergence is allowed but must be named and justified (constitutional override pattern, = the general form of the existing "guide's reach" scope carve-out). Consequence: P1 re-scopes from "perspective in general" to agency (the audience is a subject who acts); the bemötande work formally folds into this structure; the conventions layer multiplies per-medium; examples need non-text instances. Patient stays the spine (only the medium generalises, not the audience).
Recurring pattern named: a candidate root that swallows a principle (allegiance→P5, now perspective→P1). Resolution each time: root to the spine (definition/constitutional/test), principle re-scopes to its facet.
Editorial overview created: planning/voice-editorial-overview.md — a living map (the emerging three-layer shape: spine → five facets → operationalisation; settled vs live-frontier decisions; the destinations table; the recurring pattern) so Torfinn keeps footing across the conceptual moves. Complements voice-workstream-state.md (status).
2026-06-07 — Voice principles restated at one altitude + F5 seam
Principle altitude resolved (thinking-partner session). The five voice principles read at uneven grain (some sentence-level, some text-level). Decision: state every principle as a disposition at one conceptual altitude; the sentence-level techniques (active voice, word-cutting, instruction+reason) move out of the principle bodies into Evidence, and their permanent home is the writing-conventions layer — not Foundation. Restatement drafted at foundation/en-draft/principles/_principles-restated.md (draft-to-react; Torfinn folds in). Principle 5 landed as "one honest job" (the (c) reframe); allegiance moved up to the spine/test, not a principle.
F5 seam captured. Created planning/f5-writing-conventions-briefing.md (placeholder) so two handoffs survive: (1) the sentence-level tells the principles shed, with back-references to the principle each operationalises; (2) the existing F5 source material (skrivregler, Inera klarspråk standard, ordlistan). Flagged the placement decision — in the new taxonomy writing conventions likely land in how-to/, not principles/.
2026-06-07 — Voice format conformance: frontmatter + question-leads convention
Admin fixes toward promotion of principles/voice.md (format only; voice text untouched):
- YAML frontmatter added (machine-readable
status,layer,owner,scope,version,depends_on,related,supersedes) so the assistant can readstatusrather than parse it from prose. Setstatus: draft(notconstant) — honest while PROPOSED markers remain; promotes toconstantonce Torfinn accepts them. - "Question-leads" convention blessed. Principle files may title each section with the question it answers (declarative claim as subheading) — an intentional inversion of the standard's declarative-title rule, justified by transfer-to-unnamed-cases (helps writers and the LLM). Written up in
principles/_index.mdas a design-meta local convention (pending June-3 shared-root); pointer added to_TEMPLATE.md.
Remaining promotion gates (not admin, so not done here): (1) Torfinn accepts/finalises the PROPOSED markers (the test wording, Principle 5) → then strip the provenance note and flip status: constant; (2) physical move foundation/en-draft/principles/voice.md → principles/voice.md — a Claude Code task (file move), to run after (1).
2026-06-07 — Voice Pass-1 draft refreshed + next-of-kin trope rejected
Voice draft refreshed (draft-to-react mode): foundation/en-draft/principles/voice.md updated to apply the Pass-1 decisions the probe hadn't — the single test (Dec 1), Principle 5 reframed around purpose (Dec 2), the relocated definition, the patient-guide shift; status set constant. Existing English reused; new prose marked PROPOSED. Torfinn now reacting/editing in place.
Decision (Torfinn, 2026-06-07): the next of kin is a helper with jobs, not "helplessness". The "next-of-kin = helplessness" framing is rejected as an unfounded edge-case trope. The next of kin is a capable helper doing practical jobs (digital service, travel to/from the clinic, translation, forms) — even when worried, the Present Expert serves them by making the job doable. It also contradicts Principle 1 (the reader as a subject who acts). Corrected in voice.md (the table cell was already cleaned by Torfinn; the edge bullet fixed) and emotional-modes.md (table row reframed + new Open §3 flagging where the next of kin sits in the reader-state families). The earlier follow-on note (2026-05-31) proposing "next-of-kin helplessness" as an emotional mode is superseded by this decision.
2026-06-07 — State reconciliation (project thread)
What happened: Reconciled the project state against the repo after ~a week of execution in other sessions. No new decisions — a status refresh.
Where things actually are:
- Phase 1 ("adopt the way of working"): done and committed. Layer skeleton,
_archive/_incomingrenames, constitutional stubs,_TEMPLATEall in. Four legacy voice docs archived; live sources intact. - English conversion: broad effort in
foundation/en-draft/— drafts of the whole Foundation set (voice, colour, typography, constant/contextual, emotional-modes, iconography, imagery) +_translation-notes.md. - Voice workstream: all prep done, authoring not yet started. Pass-1 merge map, Pass-2 rule prep, and a structural probe (
voice-meta-refactor-probe.md) with a reactable draft atfoundation/en-draft/principles/voice.mdare ready. Constitutional voice rule still a flagged stub.voice-workstream-state.mdrefreshed (was stale). - Parallel colour workstream "teal legacy" (teal revived as legacy tier, petrol primary, replace-on-touch): en-draft signed off, change-brief + Claude Code task queued in
_incoming/. Swedish canonical prose + tokens = Torfinn / Claude Code. - Provisional Norwegian widget voice kit drafted for the aleris.no assistant (non-canonical, waits on the voice refactor).
- Phase 2 (content relocation) not run —
foundation//baseline/still live homes; ~45 uncommitted changes on theteal-legacybranch (hygiene flag).
Recommended next effort (Torfinn): commit the in-flight branch, then push the voice authoring — react to the probe's structural cut, then write principles/voice.md from the merge map (keystone; unblocks digital voice + SV localisation and validates the layer grammar for every other page).
Update (same day): in-flight work committed (5736cec brand work + DB migrations). Structural cut approved. Working mode changed: Torfinn reacts to a finished consolidated draft, not merge-maps or recommendation lists applied by hand ("trust me to recognise when something is off"). Voice passes now produced as provenance-marked drafts (verbatim vs PROPOSED) for Torfinn to react to; he stays the editorial gate and nothing ships un-vetted. voice-workstream-state.md boundary + next action updated; merge-map approach retired.
2026-05-30 — Voice workstream kickoff context
What happened: Produced planning/voice-workstream-context.md — a portable kickoff packet so the voice consolidation + English authoring can run as its own managed piece in a separate conversation (PM mode). Self-contained: the boundary (PM preps/tracks, Torfinn authors), source material, the locked decisions, target outputs, a 7-pass work breakdown, dependencies, and how to run the separate conversation. Connects as the Phase 2 content dependency, parallel to structural scaffolding.
2026-05-30 — Phase 1 brief + conventions-first reframing
What happened: Torfinn gave the go-ahead, then sharpened two conventions: (1) build briefs land in _incoming/ (DEV convention; TAKS equivalent is _filedrop/) — brand-os is a DEV project, so _inbox/ → _incoming/ (corrected from the earlier _filedrop note); (2) do the way-of-working adoption first, on the project's own docs, before content moves. Build brief now at _incoming/build-brief-phase-1.md.
Phase 1 reframed → "adopt the way of working": branch; clean the rename leftover; arc - previous thinking/→_archive/; layer skeleton + _index stubs; provisional _TEMPLATE.md; constitutional stubs flagged for Torfinn's wording; retrofit durable planning docs to the conventions (status line + hook + frontmatter — lightweight, not the full literate/Origin form; transient briefs and legacy docs left to retire); update CLAUDE.md conventions. Excludes content moves (Phase 2), brand authoring (Torfinn), renderer (Phase 3).
_incoming/ set up by Cowork (renamed from _inbox/). Learning: direct file moves/deletes in the connected repo are guard-railed — file ops belong in Claude Code on a branch; the rename collision here is the cautionary example.
Boundary held: the brief authors no brand content; voice consolidation + English conversion remain Torfinn's parallel track.
2026-05-30 — Voice consolidation investigated + decided
What happened: Investigated the four voice docs (read three in full, mapped the fourth) and Torfinn ruled on the contradictions. Captured in planning/voice-consolidation-investigation.md.
Findings: two overlap axes — (1) foundation/voice.md and the kärnguide are the same document at two altitudes (collapse to one principles/voice.md); (2) digital-voice doc 3's micro-copy duplicates the existing patterns/ files (dedupe into the patterns; doc 3 keeps only the digital-voice principles).
Decisions: test → one version (drop the two AI-ish variants); principle 5 → reframed around purpose (legal matter ok only when it serves the patient, never as theatre/politics; privacy dimension re-examined via data-meta/Niclas); definition + "what it isn't" → consolidate; "för AI-system" → dissolve (generalises to a corpus rule: one source, rendered per audience, no audience-specific duplicate files); status → normalise.
Name / language cascade: the voice is "the present expert" in English source; "den nära experten" for Swedish localisation.
Status mapping (from the voice's own constant-character / contextual-register line): constitutional rule + principles/voice.md = constant; principles/digital-voice.md + how-to/communication-genres.md = contextual.
Worked through: digital voice → own doc (principles/digital-voice.md, additive, contextual); genres → one how-to/communication-genres.md for now.
OQ-06 surfaced: internal voice register ("den nära chefen") for instrumental tools that must require actions — reframed as a contextual register of the present expert, kept open as hypothesis, doesn't block consolidation; touches framework-meta.
2026-05-30 — Phase 0 decisions + group-level language policy
What happened: Torfinn ruled on the Phase 0 sign-off items and set two new governing conventions. Phase 0 doc updated.
Governing conventions (new):
- Aleris Group scope — the corpus is now group-level guidelines, not Aleris Sweden.
- English-default language — English is the Aleris Group language and the source language for the whole corpus; local languages (sv/no/dk) only for local examples. Affects naming, renderer default locale (
en), and the assistant. Content translation is Torfinn-owned authoring, not Claude's — Claude does naming/structure/i18n scaffolding and flags what needs translating. Voice name den nära experten (keep Swedish vs English handle) flagged as a brand decision. _inbox/→_filedrop/,arc - previous thinking/→_archive/(with do-not-edit README).- Language + scope become constitutional rules in Phase 1.
Sign-off rulings: (1) domain tag ✅ (2) components-in-patterns ✅ (3) voice consolidation → investigate together 🔍 (4) privacy → leaves brand corpus to data-meta/Niclas, referenced ✅ (5) principle-vs-schema → lazy extraction (keep token explanation in principles, carve schema only when tools need it) (6) one canonical explainer ✅ (7) retire + rename arc→_archive ✅.
Next: voice investigation (read the 4 voice docs, surface overlap/contradictions for Torfinn's editorial split).
2026-05-30 — Phase 0 drafted: target structure + migration map
What happened: Drafted the Phase 0 deliverable — target taxonomy for design-meta and a file-by-file migration map covering the full current corpus (grounded against a fresh inventory). Captured in planning/phase-0-target-structure-and-migration.md. Awaiting Torfinn sign-off; nothing moves yet.
Key structural proposals:
- Flat layer taxonomy (constitutional / principles / patterns / qualities / schemas / data-products / tutorials / how-to + _sources / _inbox / planning).
- Domain as a frontmatter tag (
digital | physical | cross), not a folder split — keeps the taxonomy flat while honouring physical brand (interior/signage/print/film). components/collision resolved by omission — UI elements (hero, chat-bubble, button-label) live inpatterns/.- Consumer contracts preserved —
aleris-tokens.cssmoves todata-products/tokensand stays stable;_packagesregenerate from data-products, never hand-edited. - Every file assigned a disposition (MOVE/SPLIT/MERGE/REGEN/RETIRE/TOOLING/KEEP); ⚑ flags the reconciliation calls reserved for Torfinn (voice consolidation, patient-journey elevation, ADW extraction) and ✚ the cross-meta privacy question.
Manifest: file-manifest.md "Brand OS location" column marked superseded; the Phase 0 map is now authoritative for the migration.
Files added: planning/phase-0-target-structure-and-migration.md.
Open for sign-off: domain model; components/ resolution; voice consolidation; privacy cross-meta (June 3); reference principle-vs-schema split; corpus front page; arc extractions.
2026-05-30 — Refactor plan (re-platform + Baseline dissolution)
What happened: Planned the brand-os + baseline refactor against the now-known destination. Two scope decisions taken: the re-platform off Next.js is in scope, and Baseline dissolves into one unified layer taxonomy. Captured in planning/brand-os-refactor-plan.md. Grounded against current code coupling first.
Decisions:
- Re-platform included — move off Next.js to a static generator (Astro candidate) as part of the refactor.
- Baseline dissolves — its tokens, patterns, voice, governance distribute into the design-meta layers; the Brand OS ↔ Baseline duplication is resolved in the process.
Plan shape: five phases — (0) freeze target + build file-by-file migration map; (1) corpus skeleton + file standard + constitutional; (2) migrate content + dissolve Baseline + reconcile contradictions; (3) static renderer + cutover; (4) net-new layers (diátaxis, qualities, asset registry/CDN). Operating principles: source taxonomy ≠ public IA; build alongside + cut over once; freeze content before changing renderer; preserve consumer contracts (_packages, aleris-tokens.css); essentials first.
Grounding facts that shaped it: content is path-coupled to the loaders but public IA is already decoupled (lib/content.ts); Swedish-only today (multi-locale latent); _packages + token CSS are live consumer contracts; physical/ already seeds the non-digital remit.
Files added: planning/brand-os-refactor-plan.md.
Open for Torfinn: Phase 0 sign-off (taxonomy + components/ naming + migration map); Phase 3 access posture (public vs gated static site); June 3 outcomes (shared root, cross-meta refs); content authoring (Phase 2 reconciliations, Phase 4 tutorials/how-to/qualities).
Immediate next step: Phase 0 — draft target-structure spec + extend file-manifest.md with a target-layer migration column.
2026-05-30 — Brand OS ↔ aleris-meta alignment
What happened: Read the aleris-meta corpus and reasoned about bidirectional influence, then examined adopting aleris-meta's structure/method/style for brand-os. Captured in planning/aleris-meta-alignment.md as prep for the June 3 working session. No content changed; direction proposed, Torfinn decides (and the merge/authority questions touch a second governance body).
Findings / direction:
- The two corpora are the same species. aleris-meta already contains brand-os (
constitutional/brand-os-is-canonical.md+patterns/canonical-component-as-peer.mdnames it "the prototype"). The relationship is implicit and should be made deliberate. - Ownership interface (consume, don't absorb). Brand owns voice, tokens, the full remit incl. physical brand, the status taxonomy, the reasoning tools — aleris-meta should reference these, not restate. Notably
brand-os-is-canonicalparaphrases the voice rules (an absorption that will drift) and scopes brand-os to digital only (understates the remit). aleris-meta owns the file standard, layer model, and parallel-surfaces discipline — brand-os inherits these. - Structure adoption resolves the Baseline duplication. Mapping brand-os onto the layer model lands tokens→data-products/schemas, anti-patterns→pattern negative forms, voice guides→principles. "Two operating systems" dissolves into one taxonomy. Diátaxis tutorial/how-to layers are the newcomer fix.
- Convergence is the proof of fit. Brand-os independently invented patterns+anti-patterns, tokens-with-@usage/@constraint, status-as-legibility, _sources-as-Origin.
- Verdict: adopt, with five conditions — meta style is for the source + agents (render warm for humans); don't fake fitness functions for unmeasurable qualities; right-size governance (file discipline, not the committee); adopt incrementally; adopt faithfully or not at all (a near-copy violates conceptual integrity).
Files added:
planning/aleris-meta-alignment.md— interface + structure-adoption note for June 3.
Refinement (same day, with Mohan): resolved into a three-meta model — three peer domain-metas with named owners: data (Niclas), framework (Mohan / aleris-meta), design (Torfinn / brand-os). Collaborative peers, not territorial. "Peers" and Mohan's "hierarki av inheritance" reconciled as two axes: peers sideways, inheritance vertically (shared root → metas → projects). Settles two earlier opens (brand-owner authority = peer; one-corpus-or-two = peer corpus). Note updated accordingly.
What remains (open for June 3): define the shared root (what all three metas inherit vs framework-specific) and where it lives; where cross-meta references physically live; the components/ naming collision; Baseline's fate (dissolve vs named sub-domain); the brand-owner text fix in brand-os-is-canonical.
2026-05-30 — Architecture: source-of-truth, assistant, asset curation
What happened: Architecture conversation continuing v2. Confirmed the markdown repo as the product (the app demotes to a disposable renderer), the brand assistant as the primary access layer, and a CDN-based asset curation model. Captured in planning/architecture-source-of-truth-and-assets.md. No content changed; direction agreed in principle, Torfinn remains decider.
Decisions (in principle):
- Markdown repo = product; renderers = consumers. Website, assistant, and download packages are all renderings of one markdown source. Resolves the software-vs-content confusion in
refactor-brief-structure-and-wow.md: content wins, software demotes to a swappable delivery layer. - Reading needs no backend. Static generation (Astro a candidate) over the current Next.js app — faster, more durable, cleaner for LLMs. Commenting confirmed non-central; if wanted, a self-built widget, never a reason for a server.
- Assistant uses context-stuffing, not RAG. Corpus is dozens of files, monthly cadence — fits in context.
llms-full.txtcan be its knowledge directly. - Assistant is the primary interface; site is the deep reference. Audience (marketing, freelancers, print/signage/interior contractors, internal comms) won't read the site linearly.
statustaxonomy becomes the assistant's confidence calibration; v1 scoped to solid domains, says "no guidance yet" rather than inventing. - Asset registry bridges text↔visual. Extends the pattern tokens + iconography allowlist already use. The
descriptionfield is the machine-readable representation of each visual. - Curate assets into a CDN; no live system connections. Blessed assets curated once into a CDN (canonical delivery home), not integrated from DAM/Figma/SharePoint. Same CDN hosts production-package ZIPs. Curation not migration; canonical declared per asset class; manual monthly maintenance.
- An asset specification must precede curation — formats, resolution, colour space, naming, metadata, motive criteria — so assets are captured once correctly. Structure captured; on-brand criteria are Torfinn's to define.
Files added:
planning/architecture-source-of-truth-and-assets.md— the decision note.planning/refactor-brief-structure-and-wow.md— structure/WoW refactor brief (raised earlier this session).
What remains: Re-platform timing; assistant build/host approach; canonical-source declarations per asset class; where registry + spec live in the refactored structure. Torfinn to write the formal WoW model the docs should be updated to.
2026-05-27 — Icon picker planning + F11 structural decisions
What happened: Planning conversation for the Qlik icon picker tool — first tool to be hosted under brand.dev.aleris.ai/tools/. Structural decisions for F11 made as a precondition. Canonical-domain sweep across code completed in parallel.
Decisions:
-
Tools earn sub-folder placement, not subdomain. Tools that help people use the brand correctly (icon picker, future voice tester, pattern preview, etc.) live at
brand.dev.aleris.ai/tools/[toolname]/. Namespace established now so the second tool doesn't trigger a retrofit. Brand OS scope expands from "reference site" to "canonical brand surfaces including tools." -
F11 adopts surface-tagged allowlist (F6 model applied to iconography). The allowlist becomes a constant core + per-surface contextual sets. Each entry carries a
surfacetag. First contextual set:qlik-admin. Session A entries (ui-primitives) are re-tagged to the constant core when the file shape is next revised. Seeplanning/f11-briefing-iconography.md§9. -
Role-naming: Identitetsikon + Gränssnittsikon. Replaces the prototype's
appandfunktion. The old terms didn't generalise — "app" presumes Qlik apps, "funktion" collides with Aleris vocabulary for clinical/staff functions and presumes a dashboard surface. New names work across surfaces and don't import English. Codebase usesrole: 'identity' | 'interface'; UI labels in Swedish. -
Alias resolution, not metadata backfill. Investigation of the FA Pro kit (
kit-6f31575c9d-desktop) found that the 1 019 SVGs without metadata are legacy FA 5 names — all resolve to canonical FA 6 icons viaicons.json'saliases.namesfield. Zero icons are truly orphaned. The picker resolves aliases and renders only the 3 772 canonical icons; alias names become search-match keys. The earlier flag about "selection bias from missing metadata" dissolved. Seeplanning/f11-briefing-iconography.md§10. -
Canonical domain swept through code. Following the 4b266d5 commit that updated
baseline/setup.mdand the llms.txt fallback tobrand.dev.aleris.ai, four remaining references updated:app/sitemap.ts(BASE_URL),app/api/notify-comment/route.ts(siteUrl fallback),planning/site-map-v1.md,planning/inline-commenting-brief.md. The Resendfromaddress innotify-comment/route.tsdeliberately left at the Vercel domain until the sending domain is verified in Resend. -
Domain policy until prod: use
brand.dev.aleris.aifor all purposes. Sweep tobrand.aleris.aihappens when production comes up.
Files updated:
planning/f11-briefing-iconography.md— §9 (surface-tagged allowlist + naming), §10 (alias resolution finding).app/sitemap.ts,app/api/notify-comment/route.ts,planning/site-map-v1.md,planning/inline-commenting-brief.md— canonical domain.
What remains:
- Build brief for the icon picker (next).
- F11 allowlist file shape revision to add
surfacefield (deferred; happens when the picker build needs it). - LLM-assisted metadata enrichment — not needed; alias resolution closes the perceived gap.
2026-05-07 — Architecture v2 conversation, synthesis brief placed
What happened: Multi-turn architecture-level conversation about reshaping brand-os and baseline as a consumption surface for both humans and AI, organised around the assistant widget as v1 consumer. No content changes this session — architectural alignment captured in planning/architecture-v2-synthesis.md, which Torfinn will write the formal decision doc from.
Direction confirmed: keep all existing content (Foundation pages, Baseline tokens, patterns, governance, voice docs); replace only the presentation and architecture of engagement. Additive, not rebuild.
Architectural decisions emerging from the session (to be ratified in architecture-v2-decisions.md):
- Three-layer model. Tokens (machine-readable JSON), Components (Storybook), Philosophy/Principles (markdown essays + structured ledes + AI prompt fragments). Each layer relates differently to "machine-readable."
- Engagement model: one source, multiple renderings. Foundation pages get a structured lede paragraph that stands alone as the "at a glance" view; full essay = working guide; AI prompt fragment derived for context bundles. The lede is written deliberately by the author — no AI summarisation.
- Lede convention rollout: lazy. New pages start with the convention; existing eight pages reshaped at next edit.
- Consumer architecture: hub-and-spoke. brand-os published on the internet; products live in own repos and reference brand-os live. Mirrors "structured independence" as the Aleris product architecture model. Resilience: live + pinnable + npm package fallback.
- Three v1 architectural commitments (cheap now, expensive to retrofit): forks declare overrides not values (with reason), components declare which tokens they consume (metadata files), patterns declare canonical status (
foundation | baseline | local | candidate). - Versioning model. Keep
planning/versioning-rules.mdeditorial model. Add semver at the published API surface as the consumer contract. Mapping mostly mechanical; semver adds a PATCH layer the editorial model doesn't currently express. - First consumer: assistant widget. Lives at
Dev/aleris-assistant/(own repo). Runs as FAB-activated widget on aleris.no (Optimizely CMS, Norwegian site) and as a standalone page later. Widget on aleris.no is the v1 focus. - Component framework: TypeScript + vanilla custom elements + Shadow DOM. No Lit, no React, no Vue. Shadow DOM required for isolation from Optimizely-rendered host; vanilla web components are platform APIs (durable, no maintenance risk over the lifetime of brand-os); Lit's DX value drops when AI is the coder; TypeScript adopted for typed contracts at the published API surface and better feedback for AI-generated code.
- Curation cadence: monthly review. Calendar block, triage candidate-status patterns: cull, refine, or promote.
Operating principle: actionable idealism. Build minimal, design with awareness of where this is going. Operational test: for every architectural decision in v1, ask — would this need to be redone if we built the full vision later? If yes, defer. If neutral or future-friendly, ship.
Risks named, prioritised: five failure modes — single-curator bottleneck, adoption without mandate, architecture-becomes-the-project, AI feedback loop, showcase-vs-production gap. Torfinn prioritised #3 (project capture) as most important to mitigate; the project itself mitigates #2 by being the demonstration (adoption-by-utility, not by mandate).
v1 deferred (expensive now, cheap later): published brand.aleris.se/v1/context.json endpoint, @aleris/baseline npm package, consumer manifest registry, drift/convergence tower, versioned URLs, multi-product showcase, static hosting infrastructure with custom domain.
Files placed:
planning/architecture-v2-synthesis.md— synthesis brief.
What remains:
- Torfinn writes
architecture-v2-decisions.md(ratified commitments). - Existing planning artefacts pre-dating this direction need review at later sessions:
site-spec-v2.md,build-brief-v2.md,versioning-rules.md(semver layer to add). - Next session: v1 build planning — assistant repo setup at
Dev/aleris-assistant/, Storybook configured for Web Components in TypeScript, the assistant chat UI demo, the first concrete component (likely ChatInput or MessageBubble), the FAB widget skeleton.
Architectural note: This is the inflection point from "brand-os as markdown reference" to "brand-os as a consumption surface that serves humans and AI together, with structured independence between consuming products." The shift is in how the brand is queryable, not in what the brand says. Work done up to this date is preserved and re-presented, not replaced.
2026-04-30 — F11 Session B andra rundans trim: 84 → 79, klinisk skärpt
Andra rundans trim i klinisk-kategorin efter att första urvalet förts in i FA-kit. Fem ikoner valdes bort: person-cane, brain, lungs, eye, ear. Klinisk landar på 7 (heart-pulse, stethoscope, pills, house-medical, handshake-angle, head-side-heart, wheelchair). Total v1: 79 ikoner. Allowlist version 0.4.0 → 0.4.1.
Brand-position skarpare än första urvalet:
-
Ingen person-iconografi alls. Person-cane var första urvalets enda person-ikon (för tillgänglighets-representation). I andra rundan tas även den bort. Aleris reserverar människor helt för fotografi per F10 — inga glyfer, inte ens för tillgänglighet. Det blir en renare disciplinerad position.
-
Anatomi reducerad till heart-pulse ensam. Första urvalet hade fem organsystem (heart-pulse, brain, lungs, eye, ear) för specialitets-kontexter. Andra rundan: bara heart-pulse kvar. Det betyder att heart-pulse nu bär 'vital/health' generellt snarare än kardiologi specifikt. Övriga organsystem deferreras till custom-drawn extensions per §5.4 om behov uppstår.
-
Specialitets-iconografi är ute. Ögonklinik, ÖNH, neurologi etc. får inte egna domain-ikoner. Specialitets-ytor använder text + universal vård-symbol (stetoskop) eller custom-drawn extensions.
Implikation för F11-scaffolden:
Teaching point #3 i foundation/iconography.md ("Specialist-domän, inte UI-affordance") använde eye som exempel. Eftersom eye nu är bortvalt håller exemplet inte längre. Punkten behöver antingen omformuleras med annat exempel, ersättas med en ny princip (t.ex. "anatomi reducerad till en symbol som bär helheten"), eller skrotas. Beslut tas vid prosa-skrivande.
Files updated:
baseline/iconography/allowlist.json— 5 entries borttagna; version 0.4.1; note uppdaterad med skarpare brand-position. heart-pulse description utökad så den ensamma roll-bördan står tydlig.
2026-04-30 — F11 Session B complete, v1 allowlist landed at 84 icons
Session B done. 29 valda ikoner across the four domain categories: 8 communication, 1 time, 8 documents, 12 clinical. Allowlist version bumped 0.3.1-session-a → 0.4.0. v1 curation complete across all 8 categories.
Brand decisions resolved through curation:
- No clinical role-iconography. user-doctor, user-nurse, user-injured all valdes bort. Aleris reserves people-imagery for photography per F10 — role-glyphs read as reductive in patient-facing contexts.
- Person-cane retained as the only person-icon in v1. Accessibility representation accepted; rollglyfer av vårdpersonal not.
- Common organ iconography included for specialty contexts: heart-pulse (kardiologi), brain (neurologi), lungs (pulm), eye (oftalmologi), ear (ÖNH). tooth and bone valdes bort — possibly because Aleris saknar stark närvaro inom tandvård / ortopedi, eller för att de upplevs som mer isolerade kroppsdelar än organ.
- mobile-screen replaces phone. Klassisk lur valdes bort, smartphone vald — Aleris signalerar mobilt-först även för 'ring oss'-kontaktytor.
- Tids-kategorin minimal. Bara
clockvaldes in. hourglass, stopwatch, history-varianter alla bortvalda. Aleris hanterar tids-koncept via clock + text snarare än rikare tids-iconografi. Beslutet noterat på clock-entry så framtida designers förstår den ensamma roll-bördan. - House-medical över hospital. Aleris driver primärt kliniker, inte stora sjukhus — ikonografi anpassad efter verklig verksamhet.
- Care/relational illustrations behållna från legacy: handshake-angle och head-side-heart. Stark on-brand för 'den nära experten'; används i illustration-läge per F11 §5.3 (presentationer, värdeskapsnarrativ).
Återinförda ikoner (valda bort i Session A, åter aktuella i Session B):
eye— kommer tillbaka som klinisk (oftalmologi). Note: ikon för 'view'-handling i UI ingår inte i v1.
Bortvalda i Session B trots min hypotes att de skulle landa här:
headset— telefon-support hanteras utan eget ikon i v1.hourglass— tids-väntan hanteras utan eget ikon.
v1-totaler:
- Total: 84 ikoner över 8 kategorier
- Inom comprehensive-spannet 80-90 som flaggades vid Session A-trim
- Per kategori: wayfinding 15, action 19, status 11, system 10, communication 8, time 1, documents 8, clinical 12
Files updated:
baseline/iconography/allowlist.json— version 0.4.0; 29 nya entries efter Session A:s 55; note uppdaterad med curation-beslut.
What remains for F11:
- FA Baseline kit-synk för Session B-valen (29 nya ikoner att lägga in på FA dashboard) + integration test via
index.html. - Data-fill-session —
label_sv,label_en,synonymsför alla 84 entries i ett svep. - Torfinn skriver prosan in i scaffold (
foundation/iconography.md).
2026-04-30 — F11 sizing and alignment verified, locked into §5.2
1.25× cap-height verified. Visual verification against Museo Sans 500/700 across the F2 type scale via baseline/iconography/size-test.html confirmed the ratio. Status moved from considered to decided.
Three alignment rules locked as new §5.2.1–5.2.3 in the briefing. The 1.25× rule alone wasn't enough to produce visually centered icons in real text contexts; iterative testing surfaced three implementation requirements that pair with the size rule.
-
Foundational slot rendering. Every
.icon-slotmust usedisplay: inline-flex; align-items: center; justify-content: center; line-height: 1. Without this the SVG inside the slot baseline-aligns within parent line-flow and sinks visibly low in any context with line-height > 1 (lists, body text, callouts). Discovered when list-context icons stayed visually low even after translateY corrections — the slot wasn't centering its SVG content. -
Inline alignment.
transform: translateY(-0.05em)on icon-slots in single-line contexts (button, heading, list item, label). Compensates the small residual gap between line-box center (where flex centers) and text optical center (slightly above). -
Multi-line anfang.
align-items: flex-starton the container +transform: translateY(0.22em)on the icon-slot. Anchors the icon to first-line optical center rather than block center — the "anfang" effect that makes icons read as integrated with leading text rather than floating between lines.
Cross-cutting decoration finding. text-decoration is painted by ancestor and cannot be cancelled by descendants. <a> rendered as button propagates underline beneath child icon-slots regardless of the slot's own text-decoration: none. Fix is to cancel decoration at the source (<a> itself) and re-add to text span if needed. Recorded as §5.2.4.
Caveats recorded. Different FA Pro icons have different intrinsic visual centers. Universal values (-0.05em inline, 0.22em anfang) work for most icons but bottom-heavy ones (stethoscope, heart-pulse, location-dot) may need slightly more correction; naturally-centered ones (calendar, plus, arrow-*) may need less. Per-icon micro-adjustment is acceptable for outliers.
Iteration shape worth noting. This session went through five value iterations (-0.05 → -0.1 → -0.125 → -0.2 → -0.05) plus a foundational fix (inline-flex on slot) plus a decoration fix. The early translateY values weren't producing visible change because the underlying slot-rendering issue was masking them. Once the slot-rendering was fixed, smaller translateY values (-0.05em) became sufficient. Suggests pattern for future calibration sessions: surface foundational rendering issues first, then tune values second — not the other way round.
Test page persists. baseline/iconography/size-test.html remains as the live reference for the size and alignment rules until F11 prose lands and the rules are documented in narrative form.
Files updated:
planning/f11-briefing-iconography.md— §5.2 expanded into full sizing-and-alignment specification (§5.2.1–5.2.4); §5b and §7 status notes updated to reflect verified state.planning/brand-os-inventory.md— F11 entry checks alignment-verified item.baseline/iconography/size-test.html— final values: -0.05em inline, 0.22em callout; foundational.icon-slotrule now in place; button text-decoration: none added.
What remains for F11:
- Session B curation (communication, time, documents, clinical).
- Data-fill session for label_sv, label_en, synonyms across all curated entries.
- Torfinn authors prose into scaffold (independent of curation/data-fill — can happen any time).
2026-04-30 — F11 Session A complete, allowlist curated, two FA Pro kits split
Session A done. 55 curated icons across the four ui-primitive categories after a second pass-through trim: 15 wayfinding, 19 action, 11 status, 10 system. Allowlist version bumped 0.2.0-seed → 0.3.0-session-a → 0.3.1-session-a. The 15 SEED placeholders are gone; this is now real curation.
Second pass-through trim. First commit landed at 59; review pass cut four: sliders (filter alone covers simple list-filtering; multi-axis filter can come back later if real cases emerge), magnifying-glass-plus and magnifying-glass-minus (zoom is a specialist need — medical imaging viewers, document zoom — not core enough for v1), wifi (connectivity status rarely needed in Aleris UI, can be handled by text + state-marker if it does come up). All four are revertable additions; none are blocking.
Curation tool built and used. baseline/iconography/curation.html rendered ~87 candidates against the FA Pro shopping kit, with pick/cut/pending state per icon and localStorage persistence. JSON export at the end fed into allowlist.json. The bug fix during the session (state value "pick" should have been "picked") is captured in the page; legacy localStorage entries are migrated on load.
Two FA Pro kits split by purpose. Operational distinction recorded in baseline/iconography/README.md:
- Baseline kit (
6e2214b9a6) — curated identity. Renders only icons approved into Aleris brand. Used byindex.html. Out-of-sync between this kit andallowlist.jsonis treated as a kit-side fix (FA dashboard), not a code fix. - Shopping kit (
6f31575c9d) — full FA Pro library. Used bycuration.htmlandsize-test.htmlso candidates render regardless of approval state. Pure tooling convenience.
Pushback round during Session A. First-pass picks had four high-impact gaps — no gear (settings), no spinner (UI loading), asymmetric eye without eye-slash, no shield-* for privacy/security. Surfaced; Torfinn revised — added gear, spinner, shield-check, removed eye and hourglass to avoid asymmetry and overlap. Net 59 unchanged. Documented as evidence the role-as-PO + push-back-on-decisions instruction in CLAUDE.md is doing useful work.
Other notable picks/cuts (recorded for future curators):
floppy-diskcut,bookmarkis the save metaphor. Modern-semantic stance — diskett som metafor är 40 år gammal.pen-to-squarecut;penis the single edit verb.expand/compressboth cut — fullscreen toggling won't have iconography in the v1 set.circle-exclamationcut —triangle-exclamationis the only warning,circle-xmarkcovers fail. No mid-severity warning.heartkept despite overlap withheart-pulse(clinical, Session B). Intentional — Aleris wants warm spara/gilla-ikon. Recorded in description.filterandslidersboth kept; description text now records the distinction (filter = simple list filter; sliders = multi-axis / range filter).life-ring/headsetcut — support iconography handled by Session B's communication category instead.
What's next (in dependency order):
- Torfinn adds the 55 picks to the FA Baseline kit on the dashboard. Most aren't there yet — currently the kit holds ~23 legacy icons, many of which were cut. Without this step
index.htmlwon't render the curated set. - Session B — communication, time, documents, clinical (~25-35 picks expected). Comprehensive v1 ambition lands at ~80-90 total; tighter than the original 60-80 frame but acceptable given surface scope.
- Data-fill session — label_sv, label_en, synonyms for every curated entry. Done in one pass after Session B so brand-voice judgment happens once across the full v1 set.
- F11 prose authoring — Torfinn writes into the scaffold. Independent of curation completion; can happen in parallel.
- 1.25× ratio verified visually against Museo Sans via
size-test.html.
Files updated:
baseline/iconography/allowlist.json— 55 curated entries replace 15 SEED placeholders. Version 0.3.1-session-a. Session B categories (communication, time, documents, clinical) currently empty.
2026-04-30 — F11 curation scope set, v2 taxonomy locked, FA Pro kit wired
What happened: Extended F11 from §5 structural decisions into curation scope. Three decisions made.
Surface scope. The icon kit serves five Aleris digital surfaces: presentationsverktyg, public website (aleris.se), intranet, myAleris UI, and general Aleris-built software. This is the brand-level icon library, not a presentationsverktyg-specific kit. Curation pressure scaled accordingly.
v2 taxonomy. Categories extended from 5 (clinical, status, action, communication, wayfinding) to 8 by adding time, documents, and system. The taxonomy splits into two groups in allowlist.json: ui-primitive (wayfinding, action, status, system — needed on every Aleris digital surface) and domain (communication, time, documents, clinical — content-area concepts). Schema updated accordingly; categories[] entries now carry a group field. Allowlist version bumped to 0.2.0-seed.
v1 ambition: comprehensive (~60-80 icons). Tight v1 (30-40) was the alternative. Comprehensive chosen because the surface scope is broad and gaps drive either non-brand icon drift or custom-drawn extension requests — both are friction the kit should absorb upfront.
Curation work shape. Two sessions:
- Session A — ui-primitive categories (wayfinding, action, status, system). Most universal, least brand-judgment-loaded. Closest to conventional UX needs.
- Session B — domain categories (communication, time, documents, clinical). More brand judgment — clinical especially is a real warm-relational vs medical-illustrative decision.
The current 23 icons in the FA kit are legacy residue ("no plan, just adding what's been used earlier"), not a curation starting point. Each one earns its v1 slot or gets cut.
Two specific picks already made:
square-info→circle-info(more conventional, more universally recognised)square-question→circle-question(same reasoning)
Specialist needs (medical imaging viewer icons, fertility-specific concepts, dental-specific) are deferred to the custom-drawn extension path (§5.4), even with comprehensive ambition.
Operational change. Both index.html and size-test.html now load the FA Pro Baseline kit (6e2214b9a6.js) instead of the FA Free kit (a076d05399.js). Regular outline now renders correctly across the icon set, which the FA Free kit could not deliver.
Files updated:
baseline/iconography/allowlist.json— categories extended to v2 taxonomy with group field; version bumped to 0.2.0-seed; note rewritten.baseline/iconography/README.md— schema doc reflects v2 taxonomy and group structure.baseline/iconography/index.html— FA Pro kit URL.baseline/iconography/size-test.html— FA Pro kit URL.planning/brand-os-inventory.md— F11 entry's "what's missing" reflects curation scope, sessions, ambition.
2026-04-30 — F11 §5 structural decisions resolved
What happened: Worked through the five open structural decisions in planning/f11-briefing-iconography.md §5. All five resolved in a single session. Briefing §5 rewritten as resolved decisions; inventory entry updated; allowlist seed data brought into line with resolved schema rule.
Decisions made:
-
Family choice — Regular outline default, Solid for status (categorical). Regular outline is the constant family; the status category uses Solid where filled state markers are the conventional UX (success check, info circle, warning triangle, error). Not a per-author judgment — tied to the category. Implementation: per-entry
fa_familyinallowlist.jsoncarries the decision in data. Light, Sharp variants, and Duotone ruled out. Mixing strokes within a single surface not allowed. -
Size derivation — 1.25× cap-height (
considered). Starting rule confirmed; statusconsidereduntil tested visually against Museo Sans 500/700 across the type scale. The ratio is constant; absolute pixel size is contextual. -
Pairing rule — text at the unit level + illustration usage as layout, not asset. Icons always pair with text at the unit level (slide, card, button, navigation entry, list item). Standalone reserved for universal chrome only — close, search, menu, back, expand/collapse. Two usage domains (UI/services and illustration in presentations/print/some web graphics) share one asset library; differentiated by layout (size + spacing + surrounding shape), not by a second variant. Duotone was considered and rejected — the complexity cost (doubling the allowlist, amending F1's single-colour rule, maintaining parallel asset treatments) was not justified by current cases. Decision can be revisited if real cases emerge where size + treatment fails to separate the two domains. Mode A surfaces (oro) stay sparing on illustration even when text is present; Mode B (nyfikenhet) tolerates more.
-
Multi-set policy — FA Pro only, custom-drawn extensions in Aleris house style. No second icon set introduced. When FA Pro lacks a concept Aleris genuinely needs, custom-drawn icons in the house style (matching the chosen family's stroke and proportions) join the allowlist with
status: "curated". -
Cross-market — abstract over culturally-specific representations. Constant: prefer abstract symbols (envelope ✅; mailbox ❌). Governance: NO/DK check when adding unfamiliar new icons to the allowlist.
Files updated:
planning/f11-briefing-iconography.md§5 rewritten as resolved decisions; §7 status note updated to reflect what remains (prose authoring, ratio verification, real curation).planning/brand-os-inventory.mdF11 entry — open decisions replaced with resolved decisions.baseline/iconography/allowlist.jsonbumped to 0.1.1-seed; status icons (circle-check,circle-info,triangle-exclamation) carry per-entryfa_family: "solid"; top-level note updated.baseline/iconography/README.mdschema doc reflects resolved family rule.
What remains before F11 leaves draft:
- Torfinn authors the prose into the scaffold (
foundation/iconography.md). - 1.25× cap-height ratio verified visually against Museo Sans 500/700.
- Real curated allowlist replaces the 15 seeds.
Architectural note: The two-domains-one-library framing is the substantive shift in this session. It reconciles the brand-discipline rule ("always pair with text") with the practical reality that presentations need icons that hold their own at slide scale. By keeping the asset library single and putting the difference in layout, F11 avoids the maintenance burden and F1 amendment that a duotone variant would have introduced.
2026-04-30 — F11 Iconography activated, scaffolded straight to Foundation
What happened: Presentationsverktyg project (Aleris web-based presentation tool, separate repo) surfaced the need for a curated icon allowlist. Decision: the allowlist isn't a presentation-tool concern — it's an Aleris brand standard with multiple potential consumers (presentation tool, web product UI, internal templates, future website). Promoted straight to Foundation, not via Baseline staging. Reasoning paralleled F10 Imagery's promotion: iconography is a teaching mechanic about how Aleris uses visual markers, not a tactical pattern in any single branch.
Foundation page scaffolded. foundation/iconography.md placed at status: draft. Frame, section structure, and author prompts in place; Swedish brand prose to be written by Torfinn in a focused session. Frontmatter wired to F1, F2, F4, F6, F7, F10 as dependencies. Propagates to Communication branches, Physical, and Baseline.
Briefing placed. planning/f11-briefing-iconography.md. Unlike the F6 briefing (which compiled scattered existing references), F11 is mostly new thinking — Aleris hasn't previously articulated iconography at brand level. Briefing therefore prompt-shaped, not compilation-shaped: lays out what the page needs to teach, candidate constants, candidate contextual flex, open questions for the author.
Baseline implementation placed. baseline/iconography/ with three artefacts:
allowlist.json— structured icon list. 15 SEED entries across 5 categories (clinical, status, action, communication, wayfinding) demonstrating shape. Explicitly not a curated brand decision; real curation pending.index.html— visualisation page. Reads the JSON, groups by category, renders icons. Self-contained static HTML; uses Font Awesome Free CDN as visual stand-in until FA Pro SVGs are committed toicons/.README.md— file-shape spec, governance for adding/removing icons, list of consumers.
Aleris owns FA Pro key + SVG assets. Decision: bundle allowlisted FA Pro SVGs as a custom Iconify icon set in consuming tools rather than subscribing to Iconify Pro. Lower bundle weight, full control, no ongoing subscription. The allowlist is the real constraint anyway.
Open structural decisions for Torfinn (deferred to authoring session):
- FA Pro family choice — Light, Regular, Solid, Sharp variants. Briefing recommends Regular outline.
- Size derivation rule — 1.25× cap-height is a starting hypothesis.
- Standalone-icon exception list — close, search, etc.
- Multi-set policy — FA Pro only, or future room for medical-specific Iconify sets.
Architectural implication. Foundation now 7 placed pages plus F11 in draft = 8 in the Foundation set. Continues the pattern from 2026-04-21: Foundation grows when an identity element is articulated, not constrained to the original "six Foundation pages" model.
Cross-system note. Presentationsverktyg consumes baseline/iconography/allowlist.json directly via filesystem path during v1 (single-user local tool). When the tool extends beyond Torfinn, the same allowlist becomes a published artefact rather than a direct file dependency.
2026-04-27 — B7 renamed Representation, playbook triaged, inbox cleared
Encounters → Representation. Branch name changed. "Encounters" too abstract; "Events" too narrow (excludes lectures, partner meetings, fairs). "Representation" covers how Aleris represents itself in the world and travels cleanly between Swedish and English.
Standalone branch confirmed. B7 Representation is a standalone branch alongside Communication, Physical, and Visual Expression. Cross-cutting nature (visual + verbal + behavioral) doesn't fit under any existing branch.
Scope expanded beyond initial MVP proposal. Full scope: presentation construction guide (visual + verbal, POV framing from target group not internal perspective), physical materials guidance (posters, roll-ups, printed event materials), event styling, posture page (constant vs contextual for hosting vs participating), participation checklist.
Self-contained by design. Cannot assume Foundation literacy — audience (comms, event leads, speakers) needs guidance they can apply directly. F4, F6, and F7 principles embedded within branch content. Creates maintenance dependency: Foundation updates must propagate to Representation.
Foundation embedding strategy: deferred. Will be resolved through drafting.
MVP scope: not yet determined. Drafting will surface what ships first.
Editorial owner: Sanna.
Playbook triaged. playbook-vardforsakringsdagen-2026-04-23.docx moved from _inbox/ to _sources/. Placed as reference/worked example for B7 Representation. Brand principles elevated separately — not absorbed wholesale.
Inbox cleared.
2026-04-23 — Encounters track activated, Vårdförsäkringsdagen-playbook in inbox
What happened: Sanna's playbook for Vårdförsäkringsdagen evaluated as candidate Brand OS content. Decided not to absorb the playbook itself — it is roughly 80% event project management, 20% brand content. But the playbook surfaced a real gap: Brand OS has no home for in-person presence (events, partner meetings, recruitment fairs, lectures), and the organisation handles these case-by-case with no shared framing.
Decision: Encounters activated as Phase 5 track. Parallel to F5 Writing Conventions (active since 2026-04-21). Scope: how Aleris shows up in person — covering both hosted events and participations.
Audience reframe. The Encounters section is for comms (and event leads like Sanna), not for the scattered clinicians who'll never self-serve a brand manual. Activation problem (people don't read brand guides before appearances) is real but separable — Brand OS provides the source comms draws from when pulled in. Lower bar, higher hit rate than expecting cultural change.
MVP shape (pending): posture page (what's constant, what shifts between hosting and participating), participation checklist, link to Sanna's playbook as worked example for hosting. Three artifacts, one new section. Not a 10-leaf branch.
Open structural decisions:
- Encounters as new branch (alongside Communication, Physical, Visual Expression) or as sub-section of Communication/Physical — pending.
- Sanna's playbook placement: Reference > Templates inside Brand OS, or external in operations land with Brand OS linking to it — pending.
- Editorial owner for Encounters domain — pending.
File: Playbook copied to _inbox/playbook-vardforsakringsdagen-2026-04-23.docx with triage note. Stays in inbox until placement decided.
Inventory updated (brand-os-inventory.md): new section B7 Encounters added under BRANCHES with status, sources, scope, and open questions. Phase 5 active-track table updated.
2026-04-22 — BASELINE.md structural edits and snarkiness trim
What happened: Audit of baseline/BASELINE.md against current architecture. Structural edits to reflect the Foundation/Baseline split that's been wired since Phase 4 (2026-03-26) but not yet visible in the Baseline AI-instructions page itself. Line-level edits to remove AI-aphoristic rhythm that had accumulated in earlier drafting.
Structural changes:
- Opening rewritten to orient readers to the Foundation → Baseline relationship and state precedence: when the two conflict, Foundation wins.
- Typography section stripped to implementation-only, points to F2 (
foundation/typography.md) for principles. Duplicated content kept to a minimum — single source of truth, no drift. - Surface temperature section reframed as applying F6 (
foundation/constant-contextual.md), with a worked example replacing the prior "situation, not user role" aphorism. - File reference table split into Foundation (7 files) and Baseline (11 files). Upward-wiring notes added for animation (implements F6/F7), images (implements F10), and voice (implements F4).
Snarkiness trim: Ten lines cut or rewritten — X-not-Y constructions, personified abstractions, terminal-sentence drama, and aphoristic framings that performed authority instead of teaching. The anti-pattern intro rewritten by Torfinn to be plainly actionable for both AI and human readers.
Held for placement decisions: Ordlistan items from 2026-04-21 (integritetsregel, avsändarregel, besök as paraplyord) not added. Placement pending — adding them now would pre-empt Foundation-vs-Baseline calls.
Version: Baseline stamp unchanged (v0.1 — March 2026). These are structural pointers to existing Foundation content, not Baseline content additions. Version bump deferred until ordlistan items land or Baseline-native content expands.
2026-04-21 — Ordlistan arrived, F5 track activated, imagery promoted to Foundation
What happened: Re-engagement after two-week pause. Three decisions and one new source document.
Imagery placement confirmed. foundation/imagery.md (shipped 2026-03-29) stays in Foundation as F10. Originally planned as B3 Visual Expression branch-level content, but the page's structure (principles, three categories, emotional mode mapping, constant/contextual framing, anti-patterns) operates at Foundation identity-element level. Inventory updated: F10 added to Foundation Identity Elements; B3 reduced to Graphics & Icons (+ deferred photography implementation details).
Architectural implication. Foundation is now 7 pages (colour, typography, voice, constant-contextual, emotional-modes, imagery, index). The original "six Foundation pages" model is superseded. Foundation grows when an identity element is articulated — not constrained to the initial plan.
Ordlistan received (source document). _sources/aleris-ordlista-besok-2026-04-21.pdf. Torfinn-owned, dated 2026-04-21. Defines besök as paraplyord across all channels, terminology table, integritetsregel (vårdtyp never named in notifications visible outside logged-in state), avsändarregel (Aleris, not sub-brand), Visiba-mallimplementation. Mixed-level source — spans Foundation rules, F5 conventions, and B2 patterns.
Placement decisions pending from ordlistan:
- Integritetsregel (no care type in unencrypted/external notifications): likely Foundation-level hard rule, not F5 convention. Resolves deferred 2026-03-23 decision from Visiba gap analysis.
- Avsändarregel (Aleris as sender, no sub-brand attribution): brand architecture, likely F3 Logo & Identity or a new Foundation sender-architecture section.
- Visiba-mallimplementation: B2 Communication branch — pattern family.
F5 Writing Conventions activated as Phase 5 track. Next: Claude builds consolidated source briefing covering ADW fragments, voice docs, Inera standard, ordlistan, and scattered conventions across BL. Open question before briefing: are there additional skrivregler-dokument inside the organisation (marknad, patientinfo-redaktion, NO/DK) not yet surfaced?
Proxy-test branch status: Still open, unmerged. Flagged as open thread — not blocking content work. Decision deferred.
2026-03-29 — F10 Imagery placed (retroactive entry)
What happened: Foundation page for bildspråk authored and placed at foundation/imagery.md. Covers three-category taxonomy (Life/Care/Porträtt), Foundation principles (äkta miljöer, naturligt ljus, människan i centrum), emotional mode mapping (oro leder med Care, nyfikenhet leder med Life), constant/contextual framing, anti-list, snabbtest.
Frontmatter: status: considered, type: foundation/identity-element, depends_on colour + constant-contextual + emotional-modes, propagates_to multiple branches.
Note: Changelog entry added retroactively 2026-04-21 — not recorded at time of shipping.
Phase status at time of ship: Phase 5 started informally without being logged. Formal Phase 5 kickoff recorded 2026-04-21.
2026-04-07 — Reverse proxy test (basepath-test branch)
What happened: Aleris web team testing reverse proxy from aleris.se/brand-guidelines → our Vercel app. Created basepath-test branch with basePath: '/brand-guidelines' in next.config. Preview deployed. Main branch untouched.
Changes on branch: next.config.ts (basePath), auth callback redirect prefix, sitemap URLs, robots.txt. All <Link> components auto-prefixed by Next.js. usePathname() returns path without basePath — no nav logic changes needed.
Also fixed: Supabase env vars were only set for Production scope in Vercel, not Preview. Added to Preview scope.
2026-04-02 — Progressive enhancement, comment UX, notifications, cookbook
What happened: Tech site review revealed navigation was invisible to non-JS crawlers. Full refactor to server-render nav links. Comment system got explicit cancel/save behavior, click-outside handling, and email notifications. Documented all learnings.
Key decisions:
- Nav links must be server-rendered HTML. JS adds interactivity, never gates discovery.
- Sitemap, robots.txt, and generateMetadata shipped as baseline for all pages.
- Comment forms: explicit Avbryt/Skicka buttons. Click outside empty field closes it; click outside with content does nothing.
- Email notification on new comments via Resend → Torfinn + Sanna.
- Auth middleware added for session refresh (required for RLS inserts to work).
New files: app/sitemap.ts, public/robots.txt, components/ui/SidebarNav.tsx (server), components/ui/SidebarInteractive.tsx (client), lib/brand-nav.ts, hooks/useClickOutside.ts, app/api/notify-comment/route.ts, middleware.ts, planning/cookbook.md, planning/slash-commands.md.
Learnings captured: Lazy init of external clients (cookbook #0), progressive enhancement in HTML (cookbook #1, memory), /new-project skill updated with robustness principles (cookbook #9).
2026-03-27 — Site spec v2 + build brief v2, start building
What happened: Updated spec and build brief from Claude.ai design session. v2 supersedes v1 (archived). Build brief provides exact routes, components, tokens, and three-phase build order.
Key decisions in v2:
- English slugs for URLs, localised nav labels (
/sv/identity/colour). English is primary language, Swedish is first localisation. - Feedback storage: Supabase from v1. Generic
feedbacktable as seed for cross-product ticketing. - Accessibility: WCAG 2.1 AA, keyboard nav, landmarks, focus indicators, touch targets.
- Error states: custom 404, 500, locale-not-available — all in Aleris styling.
- Sidebar: expand/collapse by section, planned pages dimmed, independent scroll.
- Baseline v1 approach: patterns render in Brand OS, full Baseline links to existing site. Subsite decision parked.
- Site builds in this repo (not separate).
Build started: Phase A — scaffold + first page (colour).
2026-03-27 — Site spec v1 placed
What happened: Comprehensive site specification produced in Claude.ai design session and placed at planning/site-spec-v1.md. Covers users, IA, teaching mechanics, visual feel, downloads, status badges, and open decisions.
Key decisions from the spec:
- Navigation is task-oriented (Kom igång routing by user need), not architecture-oriented. Trunk/branch/leaf lives in metadata, not nav.
- Badge labels Swedish on site: beslutad / etablerad / föreslagen. Frontmatter stays English (
decided/considered/suggested), mapped at build time. - Sand-50 (#FAF8F6) as site background — it's a work tool (instrumental). Sand (#F2ECE4) appears in teaching examples showing communicative surfaces.
- Baseline: leaning subsite inside Brand OS, parked for prototype.
- New pages: Kom igång (task routing), Nyheter (changelog feed), Om Aleris (brand core), Kontakt.
- Every page gets "Var den här sidan till hjälp?" feedback widget from launch.
- Concept breadcrumbs ("Bygger på: ...") at top of every page. Inverse ("Används av: ...") at bottom of Foundation pages.
Open decisions (6): Baseline absorb vs subsite, Foundation language for NO/DK, AI package format, feedback mechanism specifics, template overview content, Getting Started copy.
Next step: Visual design / prototype.
2026-03-26 — Pattern library shipped, Phase 4 complete
What happened: Five Baseline patterns placed at baseline/patterns/: error-message, confirmation, empty-state, button-label, chat-response-simple. Extracted from doc 3 (digital voice) into the pattern template format. Each has: when to use, principle, structure, ✅/❌ examples, tokens, accessibility, snabbtest.
Phase 4 complete: F4 Voice ✅, F9 Index ✅, B1 dependencies ✅, B1 patterns ✅ (5 of planned 4–6).
Full system status: Foundation (6 pages) → Baseline (14 files wired + 5 patterns). The vertical slice is complete from brain to leaf.
2026-03-26 — F9 index placed, B1 wired, F-numbers cleaned
What happened: Brand in Brief index page placed at foundation/index.md. B1 Baseline migration completed — 14 files now have depends_on references to Foundation pages. Fixed wrong reference in colour.md ("Situationen bestämmer registret" → "Konstant och kontextuellt"). Removed all F-number references (F6, F7 etc.) from Foundation content — these are internal work numbers, not reader-facing labels.
Phase status: Phase 4 nearly complete. F4 ✅ F9 ✅ B1 dependencies ✅. Remaining: B1 pattern library (4–6 patterns).
2026-03-26 — F4 Voice placed, Phase 4 started
What happened: Foundation voice page authored and placed at foundation/voice.md. Editorial separation from doc 1 (kärnguide): character, test, five principles, not-list, scope boundary, quick reference, and AI section extracted to Foundation. Genre guides (doc 2) stay for Communication branch. Digital voice (doc 3) and behavior (doc 4) stay in Baseline.
Decisions made in the content:
- Patientguide vs patientinformation framing: not a separate section — principle captured in principle #2
- AI instructions: included at Foundation level (six compressed bullets + adapted test)
- Du-tilltal: deferred to F5 (Writing Conventions)
- Error message structure: included in quick reference table (voice-level, not convention-level)
- Test sharpened: "Känns det som att avsändaren har tid för mig utan att ha en dold agenda?"
Phase status: Phase 4 in progress. F4 Voice ✅. Next: F9 Brand in Brief, then B1 Baseline migration.
2026-03-26 — F2 Typography placed, Phase 3 complete
What happened: Second Foundation identity page authored and placed at foundation/typography.md. F1 Colour also updated — removed "grenkapitel" (invented word) and replaced with proper branch names (Baseline, Communication, Physical). PDF Pantone/CMYK and gradient values re-applied to F1.
F2 decisions made in the content:
- Foundation carries principles + digital scale as reference implementation (option B from briefing)
- Weight guidance: "two workhorse weights — regular and bold" with font-specific values (Museo Sans 500/700, Arial 400/700)
- 8pt minimum difference rule: Foundation-level (constant)
- Museo Sans rationale included: geometric + rounded = precision + warmth = den nära experten in type
Phase status: Phase 3 complete. F1 Colour ✅ F2 Typography ✅. First vertical slice shipped.
2026-03-26 — F1 Colour placed, first Foundation identity page
What happened: First Foundation identity page authored and placed at foundation/colour.md. This is the first vertical slice — Foundation content that downstream branches depend on.
Decisions made in the content:
- Pantone/CMYK: Foundation-level, with PDF values to be transcribed (placeholders marked)
- Gradients: Foundation-level values, branch-level application
- Dark mode: Foundation hard rule ("Aleris använder ljusa gränssnitt")
- Chart/dataviz palette: Baseline-only (not in Foundation)
- Confirm green + goal-status: Baseline-only (not in Foundation)
- Orange button contrast: Documented as known tension. Petrol text on orange is the accessibility standard (4.30:1). White text on orange is a conscious trade-off, not an oversight.
- Strategy narrative: yes — opening paragraph explains the warm-shifted rationale
What it supersedes: ADW §2.1 colour system content is now canonically at foundation/colour.md.
Phase status: Phase 3 in progress. F1 Colour ✅. F2 Typography next.
2026-03-26 — F6 and F7 placed, F8 demoted
What happened: First two Foundation reasoning tools placed as canonical pages. F6 (Konstant/kontextuellt) and F7 (Emotionellt läge) authored by Torfinn and placed at foundation/constant-contextual.md and foundation/emotional-modes.md. F8 (Expression Calibration) demoted from Foundation to branch-level.
Decisions made:
- F6 and F7 are
status: constant— they are reasoning frameworks, not contextual guidance - F7 uses two modes (oro/nyfikenhet), not three. Latent anxiety stays in digital product context (Baseline branch)
- F8 demoted. Surface temperature folds into Baseline as a branch-level concept. Channel-specific guidance is a separate branch-level concern. F6 + F7 are sufficient as Foundation reasoning tools.
- Scaffold (
f6-constant-contextual-scaffold.md) moved to arc — superseded by authored content
Also this session:
- Klinfys Baseline package created (
_packages/klinfys-baseline.zip) — canonical BASELINE.md + tokens + voice, README frames it as redesign target - Established rule: Baseline packages always ship canonical BASELINE.md unchanged. Product divergences in README only.
Phase status: Phase 1 in progress. F6 ✅ F7 ✅ F8 ❌ (demoted). Two of two Foundation reasoning tools complete.
2026-03-26 — F1 colour conflicts resolved
Three decisions made:
- Orange 80/60: PDF is canonical. 80=#FAAA8D (hover), 60=#FFBE9F (light accent). Baseline had the labels swapped. Token file updated: added
orange-400(#FAAA8D) for hover,orange-300(#FFBE9F) stays as light accent.button-primary-hover-bgnow referencesorange-400. - Cream: Removed.
sand-50(#FAF8F6) replaces cream across all products. No token changes needed — cream was never in the canonical token file. - Turquoise: Legacy/transitional. Not part of the palette. Don't use in new work. Existing uses phased out on natural refresh cycles.
Phase status: Phase 2 (resolve conflicts) — colour values done. F1 unblocked for Foundation page.
2026-03-26 — Phase 2 complete, badges and font resolved
Status badges decided: decided / considered / suggested. Replaces all previous proposals. The constant/contextual distinction lives in the content (F6), not the badge. F6 and F7 frontmatter updated.
Font position decided: Museo Sans is the brand font. Arial is fallback. MS Office exception: Arial default because Museo Sans isn't widely available inside the organisation.
Phase status: Phase 2 complete. All conflicts resolved. Phase 3 (first vertical slice) unblocked — F1 Colour is the first Foundation identity page to write.
2026-03-25 — Interior chapter added, Option C pattern established
What happened: Conceptual draft for Interior & Physical Environment (B4) received. Converted to markdown and placed at physical/interior.md. First real content for the Physical branch. Also triaged three inbox items: Visiba gap analysis → _sources/, two colour logic observations → arc/.
Decisions made:
- B4 status upgraded from ⚪ Placeholder to 🟡 Partial
- Option C pattern adopted for WIP content: open questions and unresolved decisions are embedded as content in the page itself (using
[!decision]callouts), not separated into planning artifacts. The page'sstatus: draftbadge signals incompleteness; the decision callouts explain how it's incomplete and what needs to happen. This pattern applies to all future WIP pages across the site. - Interior decision items separated into two categories: brand decisions (what the manual says) and implementation dependencies (what the organisation needs to do to operationalise it). Both live in the page but are structurally distinct.
- Original docx filed to
_sources/aleris-brand-interior-chapter-v1.docx
Flagged for future decision:
- Nivåmodell (bas/utökad/premium) may be a Foundation-level reasoning tool rather than interior-specific. Deferred — worth revisiting when F8 (Expression Calibration) is written.
Visiba gap analysis triaged:
- Filed to
_sources/aleris-visiba-gap-analys-brand-os-2026-03.md - 6 gaps identified; added to B2 inventory: system-generated messages, async channels in matrix, positive notification principle, short-format voice operationalisation, internal/provider communication tone
2026-03-23 — Channel architecture research filed
What happened: Torfinn's research partner produced a comprehensive analysis of SMS, async chat, and live chat as distinct healthcare communication channels. Material evaluated and filed as source reference.
Decisions made:
- Research filed to
_sources/as reference material (not canonical Brand OS content) - B2 (Communication genres) inventory updated with SMS pattern family, async chat patterns, and privacy behaviour rule as identified gaps
- Privacy boundary ("no clinical content in unencrypted channels") flagged as potentially Foundation-level — placement decision deferred
- First SMS patterns should be developed from the BHSV coordinator app case and marked
hypothesisstatus
Decided: The privacy behaviour rule ("no clinical content in unencrypted channels") belongs at Foundation level, not B2. It's a constraint that applies across all channels. Exact placement TBD (likely F5 or a new behaviour rules section).
2026-03-23 — Project bootstrap
What happened: Established Brand OS as the consolidation project for all Aleris brand and design system work. Previously, material existed across multiple separate efforts (Baseline, Tone, ALERIS-DESIGN-WORKING, voice docs, ChatGPT agent configs) with significant duplication and unclear canonical status.
Decisions made:
- Site map v2 confirmed as architectural truth (trunk/branch/leaf model)
arc - previous thinking/confirmed fully superseded bybaseline/(12 files, March 20 → March 21 evolution)- Root-level skill files (
aleris_brand_skill.md,healthcare_ux_skill-context.md,design_thinking_working_skill.md) classified as Claude tooling, not Brand OS content voice/tone-plan.mdandvoice/tone-construction-plan.mdclassified as pre-Brand OS; "Tone" concept absorbed into Brand OS architectureALERIS-DESIGN-WORKING (kopia).mdconfirmed identical to ADW (zero diff). Marked for deletion.baseline-token-architecture.mdconfirmed as canonical latest thinking on token layer architecturealeris-patient-product-design-guidelines.mdflagged as important material with partly foundational patient-journey thinking not yet fully absorbed- ChatGPT agent source docs (patientinformation/) — principles captured in baseline; 4 files flagged for Phase 5 extraction (mechanical conventions, Inera standard)
Artifacts created:
planning/file-manifest.md— file-level status tracker (77 files classified)planning/versioning-rules.md— how Foundation and branch versions workplanning/changelog.md— this file_sources/— reference documents with provenance index_inbox/— intake pipeline for new material
Infrastructure decisions:
- Inbox pattern: new material →
_inbox/→ triage (absorb / reference / discard) → canonical structure orarc/ - Source documents (PDFs, external standards) live permanently in
_sources/, never edited - Versioning: Foundation gets major versions, branches get minor versions, leaves get status badges
- Dashboards (project health, usage tracking) deferred until site exists
- Visual timeline deferred; changelog serves as the record until there's enough history to visualise
Phase status: Pre-Phase 1. Infrastructure established. Next: F6 extraction briefing.
Merged: Baseline changelog (2026-03-20 – 2026-03-21)
Carried in verbatim on 2026-07-28 from baseline/planning/baseline-changelog.md, which had five entries all from 20–21 March and was then abandoned for four months. Entries below are the original text and original dates, unedited — they predate this file's earliest entry (2026-03-23), which is why they sit at the end. The source file is marked superseded with a pointer here. Rule established the same day: brand-os has one changelog, at planning/changelog.md. A second changelog is what happened here, and it died in two days.
2026-03-21 — Session 3c (Web view architecture + font fix)
Three iterations on index.html. Evolved from tab-nav-per-category (Nord-style) to expandable sidebar tree with dashboard overview. Final architecture: sidebar with expand/collapse categories + document subitems, overview page with stats bar (linked to pages) + category cards grid, three new built-in pages (content inventory, open questions, resolved decisions).
Fixed Museo Sans font loading. Copied woff2 files into fonts/. Added unified @font-face declarations mapping 300/500/700/900 to single family. Root cause: Fontspring files registered each weight as separate family name with font-weight: normal.
Content set to instrumental font size. Body text --font-size-sm (16px), tables --font-size-xs (14px). Code blocks changed to petrol-100 bg / petrol-500 text.
YAML frontmatter stripped from markdown rendering via stripFrontmatter().
OQ-24: Icon library. Added to open questions tracker. Font Awesome 6 Pro needs product audit and subset selection. Placeholder card on overview.
Files touched: index.html (3 rewrites), fonts/ (new — 4 woff2 files), aleris-design-system-open-questions.md, baseline-current-state.md, baseline-changelog.md.
Score: 7 actionable OQs, 6 parked, 22 resolved. ~22 documents + CSS + JSON + generator + HTML + fonts.
2026-03-21 — Session 3b (HTML rewrite + session wrap)
Rewrote index.html. Sticky tab navigation dogfooding --tab-nav-* component tokens. Updated all stats and content to reflect Session 3 work: 19 docs, 6 actionable OQs, 6 parked, 22 resolved. BASELINE.md and baseline-tokens.json added to sidebar navigation and download grid. Status page restructured with actionable/parked OQ split. "For AI tools" section rewritten with BASELINE.md as entry point. Lead text, z-index tokens, motion tokens, print styles, reduced-motion query all properly used.
Updated current-state.md. Reflects Phase 1 complete. Cleaner OQ summary (actionable/parked/resolved). Next steps simplified.
Files touched: index.html (full rewrite), baseline-current-state.md, baseline-changelog.md.
2026-03-21 — Session 3 (OQ cleanup + Phase 1 token JSON)
Resolved 6 open questions, promoted 1 to Anchor. Dropped from 19 open to 12.
- RQ-16 (was OQ-19): Email animation = no animation. Fixed constraint.
- RQ-17 (was OQ-20): Motion audit = line item in design review. Anchor.
- RQ-18 (was OQ-17): Dataviz reference implementations = written guidance sufficient. Visual examples deferred to Phase 3. Anchor.
- RQ-19 (was OQ-18): Skeleton component = standardized shimmer primitives, products compose layouts. Added 6 skeleton tokens to CSS. Anchor.
- RQ-20 (was OQ-16): Sequential color ramp = parked, no current product needs heatmaps.
- RQ-21 (was OQ-22): Clinical vs marketing image quality = different requirements. Clinical gets higher quality / lossless PNG. Anchor.
- RQ-22 (was OQ-02 promoted): Scanning vs attending is the density model. Core model resolved as Anchor. Sub-question (density beyond tables) stays open.
Added skeleton shimmer tokens to aleris-tokens.css: --skeleton-bg, --skeleton-shimmer, --skeleton-duration, --skeleton-easing, --skeleton-radius-rect, --skeleton-radius-circle.
Reviewed updated implementation plan against current state. Plan aligned to actual file structure and progress.
Generated baseline-tokens.json (Phase 1.1 complete). Node script (generate-tokens-json.js) parses CSS custom properties and extracts @usage/@constraint annotations. Added structured comments to ~35 highest-risk tokens across color, spacing, radius, surfaces, and font weights. Output: 318 tokens across 33 categories in three layers (primitives=93, semantic=61, component=164). 39 annotated with usage/constraint, remainder have values only (visible gap, not invisible omission).
Wrote BASELINE.md (Phase 1.2 complete). AI instruction file for repos consuming the design system. Consolidated from anti-patterns, governance, and token docs. Covers: hard rules (10), surface temperature, component shapes, typography, spacing, buttons, forms, tables, motion, anti-patterns (10), accessibility, and file reference. Lives at repo root.
Trimmed frontmatter. Removed surface_relevance and tokens_referenced from all markdown files. Nothing queried them; tokens_referenced was prone to drift. Frontmatter now: name, type, status, depends_on, propagates_to, open_questions, last_verified.
Restructured OQ tracker. Split into "Actionable" (6 OQs) and "Parked" (6 OQs, one line each). Cleaner separation of active work from blocked items.
Reclassified update protocol as dormant. Five-rule protocol built for multi-author editing. Session docs sufficient while sole author. Activate when second contributor starts.
Files touched: all markdown frontmatter (field removal), aleris-tokens.css (annotations), generate-tokens-json.js (new), baseline-tokens.json (generated), BASELINE.md (new), aleris-design-system-open-questions.md, baseline-update-protocol.md, baseline-changelog.md, baseline-current-state.md.
Score: 6 actionable OQs, 6 parked OQs, 22 resolved. 19 documents + CSS + HTML + JSON + generator script.
2026-03-21 — Session 2 (context continuation)
Resolved OQ-11, OQ-01, OQ-15. Added motion tokens, z-index hierarchy, table density persistence to CSS. Moved all three to resolved.
Absorbed animation document. Replaced two-tier motion model (150ms/300ms) with four-tier (100/200/350/500ms). Added ease-in-out as second easing curve. Updated RQ-13 to reflect new model. Added OQ-18, OQ-19, OQ-20.
Absorbed images document. Added 7 image tokens (4 aspect ratios, 3 radii). AVIF→WebP→JPEG format strategy. EXIF privacy guidance. Added OQ-21, OQ-22, OQ-23.
Added surface-page-instrumental (sand-50, #faf8f6). New near-white page surface for tool/documentation/admin interfaces. Cards become optional on instrumental surfaces. Based on analysis of Nord Design System patterns.
Added lead typography composition (--type-lead-*). 27px, regular weight, secondary color.
Added tab navigation component tokens (--tab-nav-*). 18 tokens for sticky horizontal tabs.
Fixed petrol-on-petrol button bug in HTML. Converted download buttons to plain links.
Renamed system to Baseline in HTML front-end.
Absorbed web view & LLM-readability research. Copied to source folder.
Added YAML frontmatter to all 17 markdown files. Machine-readable metadata with depends_on, propagates_to, tokens_referenced, open_questions, last_verified.
Wrote update protocol (baseline-update-protocol.md). Dependency map, five update rules, verification checklist.
Established session documentation pattern. baseline-current-state.md (living snapshot) + this changelog (append-only history).
Files touched: aleris-tokens.css, aleris-design-governance.md, aleris-design-system-open-questions.md, index.html, all 17 .md files (frontmatter), baseline-update-protocol.md (new), baseline-current-state.md (new), baseline-changelog.md (new).
Score: 19 open questions, 15 resolved. 18 documents + CSS + HTML.
2026-03-20/21 — Session 1 (initial build)
Built Baseline from scratch. Started with 4 scattered source documents and notes. Produced:
- aleris-tokens.css (~500 lines at session end)
- Design governance document (Phase 2)
- Open questions tracker (started at 13 open, 9 resolved)
- HTML front-end dogfooding the token system
- Integrated grids/tables/dataviz document
- Refined tokens against real prototype screenshots (radius, weights, card borders, confirm button, inputs)
Key concepts established: surface temperature, three-tier radius, scanning vs attending, confirm vs goal-achieved green, 22-token input system, progressive enhancement as foundational principle.
Files touched: everything — this was the founding session.