Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
2fd7fe2
Add the New World photo set: thirteen gathering photos mapped to the …
claude Nov 3, 2026
ab2415b
Merge main into the New World photos branch
claude Nov 3, 2026
e2f6745
Fix the LIVE-2060 probe: a node -e check that the photo exists, not t…
claude Nov 3, 2026
3d7b6ac
Merge main into the New World photos branch
claude Nov 3, 2026
3710124
Register New World as its own hidden theme: tokens, id, surface, cont…
claude Nov 3, 2026
0213440
Merge the earlier photo branch history
claude Nov 3, 2026
116cb59
Port the Spotlight layouts and kit doors to New World: six layouts, s…
claude Nov 3, 2026
664273c
Record earlier branch history (already merged to main)
claude Nov 3, 2026
d7b4a39
feat(sites): New World site skin (Night and Day registers, fonts, acc…
claude Nov 3, 2026
eba3721
Reconcile with already-merged branch history
claude Nov 3, 2026
aa1b7e1
docs(ledger): LIVE-2058 waits on New World email defaults; record the…
claude Nov 3, 2026
1d3d394
Reconcile with already-merged branch history
claude Nov 3, 2026
f30a138
feat(new-world): restore Spotlight layouts, add the eleven email temp…
claude Nov 3, 2026
002b4b8
fix(new-world): keep the Tide email accent out of the sample catalog …
claude Nov 3, 2026
74fb0a8
Merge main (ratchet fix #4529)
claude Nov 3, 2026
bdc1ba4
feat(sites): New World block sheet derived from the shared block sour…
claude Nov 3, 2026
11c283b
Reconcile with already-merged branch history
claude Nov 3, 2026
6736e30
Fix the New World site header and hero: size the bare header logo, ke…
claude Nov 3, 2026
94943a0
Reconcile with the already-merged branch history
claude Nov 3, 2026
2be7801
Offer New World in the theme pickers and make /themes/new-world public
claude Nov 3, 2026
52927e0
Reconcile with the already-merged branch history
claude Nov 3, 2026
40a8f10
Keep /themes/new-world noindexed and its probe untouched; fix the kit…
claude Nov 3, 2026
f98fef6
New World accents: Signal, Bloom, Ember and Fern join Tide
claude Nov 3, 2026
cbd0207
Reconcile with the already-merged branch history
claude Nov 3, 2026
22bea14
New World accents: no violet in the Signal and Bloom glow stops
claude Nov 3, 2026
f06f23e
Make /themes/new-world indexable, add it to the sitemap, and update i…
claude Nov 3, 2026
9fef6b9
Reconcile with the already-merged branch history
claude Nov 3, 2026
6955657
Update New World theme page test: indexable and in the sitemap
claude Nov 3, 2026
103a5e5
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 3, 2026
1f6c9ff
BLD-002: builder radius steps, editor tokens and shared breakpoints
claude Nov 3, 2026
72d03f4
Merge old branch history (ours)
claude Nov 3, 2026
a9fe58d
BLD-002: keep the breakpoint name type file-local (dead-export ceiling)
claude Nov 3, 2026
b0ee1d0
BLD-002: declare the builder tokens on the editor roots, not in the a…
claude Nov 3, 2026
2572a18
Merge main
claude Nov 3, 2026
9974654
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 3, 2026
a223b39
Merge remote-tracking branch 'origin/main' into pr4553
claude Nov 3, 2026
d0b3fc0
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 3, 2026
8fe53c1
BLD-101: the new builder frame, top bar and one state module, behind …
claude Nov 3, 2026
5718916
BLD-101: load the editor sheet that declares the builder tokens
claude Nov 3, 2026
ac87e4d
BLD-101: keep the layout action on the current editor after main adde…
claude Nov 3, 2026
dae8439
BLD-101: point the row's probe at its file
claude Nov 3, 2026
69fec71
Merge old branch history (ours)
claude Nov 3, 2026
379d832
Merge main
claude Nov 3, 2026
13b0416
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 3, 2026
204fd05
New World sites: the kit's bar header, headings in the register's col…
claude Nov 3, 2026
bfe9e8a
New World sites: the kit's footer
claude Nov 3, 2026
335b904
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 3, 2026
8aed93d
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 3, 2026
3b78bbf
BLD-102: one rail state machine for the builder's outline and setting…
claude Nov 3, 2026
3bf3fbf
Merge old branch history (ours)
claude Nov 3, 2026
d1cd92a
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
8849abb
BLD-103: the builder outline and spine, reordered by drag or keyboard…
claude Nov 3, 2026
b03fbd2
Merge old branch history (ours)
claude Nov 4, 2026
c130af7
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
30cdbb2
BLD-104 (first half): section toolbar actions as one undo step each, …
claude Nov 3, 2026
3fb778b
Merge old branch history (ours)
claude Nov 4, 2026
392418a
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
978cf66
BLD-105: the generated Tune rail, role slots first with their limits …
claude Nov 3, 2026
07cb354
Merge old branch history (ours)
claude Nov 4, 2026
031332e
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
97f7531
BLD-106: the builder's command bar, one registry for every action
claude Nov 3, 2026
c479cb0
Merge old branch history (ours)
claude Nov 4, 2026
146504a
BLD-106: name the command bar's search field for screen readers (chec…
claude Nov 4, 2026
a49dc4d
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
4003696
BLD-107: the builder on phones, a bottom bar and bottom sheets with 4…
claude Nov 3, 2026
fb36365
Builder: outline rows read left, Tune fields stack, phone top bar fits
claude Nov 3, 2026
ff70746
Merge old branch history (ours)
claude Nov 4, 2026
aa757d6
Builder: use the Input, Textarea, Select and Checkbox primitives in t…
claude Nov 4, 2026
ada8f37
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
bec7c72
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
5dddc98
BLD-104: parts outline on the canvas, and a picture click opens its p…
claude Nov 4, 2026
14cb60b
Merge old branch history (ours)
claude Nov 4, 2026
6b4ac91
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
fd211e6
BLD-001: the token contract resolves on Commons, Commons night and St…
claude Nov 4, 2026
2e6d518
Merge old branch history (ours)
claude Nov 4, 2026
07dd6b0
Merge branch 'main' into claude/project-thread-mu2cbb
hellofrequencylab Nov 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/ledger/rows/BLD-001.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
"lane": "program",
"priority": "P2",
"size": "S",
"status": "open",
"status": "done",
"source": {
"file": "docs/EDITOR-NEXT.md",
"ref": "Owner approval 2026-11-02: build the next-gen builder from the role and block contracts. BLD-001."
},
"detail": "B0 Align, pass BLD-001 (spec: docs/EDITOR-NEXT.md#bld-001). Token map to the 45 contract names. Scope: design system + lib/theme. Build: Generate one map from Frequency tokens (Commons, Commons night, Stage) to the contract groups: ground, ink, accent, line, status, band, shape, depth, type, rhythm, motion. Retire --th-* in element-library.md and theme-pack.json; point them at contract names. Done when: Pin test: every contract name resolves on Commons, Commons night and Stage; Computed values equal today's on the 31 design pages. Gates: check:tokens, check:theme-conformance, Design system adherence check, Pass probes, check:backlog, check:one-list, check:adr, check:id-collisions. After: nothing. Loop: build, validate, repair (3 cycles, classes in EDITOR-NEXT.md), record.",
"detail": "B0 Align, pass BLD-001 (spec: docs/EDITOR-NEXT.md#bld-001). Token map to the 45 contract names. Scope: design system + lib/theme. Build: Generate one map from Frequency tokens (Commons, Commons night, Stage) to the contract groups: ground, ink, accent, line, status, band, shape, depth, type, rhythm, motion. Retire --th-* in element-library.md and theme-pack.json; point them at contract names. Done when: Pin test: every contract name resolves on Commons, Commons night and Stage; Computed values equal today's on the 31 design pages. Gates: check:tokens, check:theme-conformance, Design system adherence check, Pass probes, check:backlog, check:one-list, check:adr, check:id-collisions. After: nothing. Loop: build, validate, repair (3 cycles, classes in EDITOR-NEXT.md), record.\n\n2026-11-04: the forty-five contract names now ride every element that re-declares Frequency's aliases (root, Stage surfaces, night sections, skinned subtrees), so a Stage section reads Stage values instead of inheriting the root's Commons ones; the pin test resolves every name in Commons, Commons night and Stage and compares it with the token it points at there (it failed on Stage before). The handoff's --th-* roles (element-library.md, theme-pack.json, and the editor seed) point at contract names in TH_TO_CONTRACT; the handoff files are read-only, so the retirement lives in that table. No computed value of an existing token moves; only --c-* names gain declarations.",
"verify": {
"kind": "manual",
"checked": "2026-11-02",
"evidence": "Filed from the builder spec. The pass writes scripts/probes/builder/BLD-001.mjs, proves it fails before the change and passes after, and switches this row to kind cmd in the same PR."
"kind": "cmd",
"cmd": "node scripts/probes/builder/BLD-001.mjs"
},
"closed": "2026-11-04",
"wave": "W7"
}
83 changes: 78 additions & 5 deletions lib/theme/block-contract-tokens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { DESIGNER_TOKENS_CSS } from '@/lib/theme/designer'
import { FREE_HUMAN_TOKENS_CSS } from '@/lib/theme/free-human'
import { SERVICES_TOKENS_CSS } from '@/lib/theme/services'
import { NEW_WORLD_TOKENS_CSS } from '@/lib/theme/new-world'
import { CONTRACT_THEME_IDS, CORE_TOKEN_NAMES, coreTokenMap, coreTokensCss, type ContractThemeId, type CoreTokenName } from '@/lib/theme/block-contract-tokens'
import { CONTRACT_THEME_IDS, CORE_TOKEN_NAMES, coreTokenMap, coreTokensCss, TH_TO_CONTRACT, type ContractThemeId, type CoreTokenName } from '@/lib/theme/block-contract-tokens'

// THE PIN (LIVE-1856). The contract adds names; it must move no value. This resolves every contract name against the
// sheets a live site wears today and compares it with the committed golden (block-contract-map.json). A sheet edit that
Expand Down Expand Up @@ -113,7 +113,7 @@ function themeScope(id: ContractThemeId): Decls {
}

/** Replace every var() in a value, recursively, in the cascade scope the declaring element gives it. */
function expand(value: string, theme: Decls, inTheme: boolean, depth = 0): string {
function expand(value: string, theme: Decls, inTheme: boolean, depth = 0, root: Decls = ROOT): string {
if (depth > 24) throw new Error(`var() chain too deep: ${value}`)
let out = '', i = 0
while (i < value.length) {
Expand All @@ -130,9 +130,9 @@ function expand(value: string, theme: Decls, inTheme: boolean, depth = 0): strin
const name = (comma < 0 ? inner : value.slice(at + 4, comma)).trim()
const fb = comma < 0 ? null : value.slice(comma + 1, j - 1).trim()
if (isExternal(name, theme)) out += `var(${name})`
else if (inTheme && theme.has(name)) out += expand(theme.get(name)!, theme, true, depth + 1)
else if (ROOT.has(name)) out += expand(ROOT.get(name)!, theme, false, depth + 1)
else if (fb !== null) out += expand(fb, theme, inTheme, depth + 1)
else if (inTheme && theme.has(name)) out += expand(theme.get(name)!, theme, true, depth + 1, root)
else if (root.has(name)) out += expand(root.get(name)!, theme, false, depth + 1, root)
else if (fb !== null) out += expand(fb, theme, inTheme, depth + 1, root)
else out += `var(${name})`
i = j
}
Expand Down Expand Up @@ -283,3 +283,76 @@ describe('core token contract (LIVE-1856)', () => {
})
}
})

// BLD-001. Frequency is three rooms, not one: Commons (the root by day), Commons night (the root under `.dark`) and
// Stage (a `data-surface="stage"` section, the marketing site). Each redefines the `--fq-*` tokens, and
// app/themes/frequency.css re-declares the aliases on each of those elements so they resolve there. The contract must
// ride the same elements: a name declared on the root alone inherits the root's computed Commons value into a Stage
// section. Here every contract name is resolved in each room the way the browser would, and must equal the token it
// points at, resolved in that same room.
describe('Frequency contract in every room (BLD-001)', () => {
const GLOBAL_ROOT = scopeOf(readFileSync('app/globals.css', 'utf8'), [':root'])
const FQ = [readFileSync('app/themes/frequency/core.css', 'utf8'), readFileSync('app/themes/frequency.css', 'utf8')]
const fq = (selectors: string[]): Decls => new Map(FQ.flatMap((css) => [...scopeOf(css, selectors)]))
const COMMONS = [':root[data-design="frequency"]', ':root[data-design="frequency"]:not(.dark)']
const NIGHT = [':root[data-design="frequency"]', '.dark[data-design="frequency"]', ':root.dark[data-design="frequency"]']
const STAGE = ['[data-design="frequency"] [data-surface="stage"]', ':root[data-design="frequency"] [data-surface="stage"]']
const prelude = coreTokensCss('frequency').slice(0, coreTokensCss('frequency').indexOf('{')).split(',').map((x) => x.trim())
const contract = new Map(Object.entries(coreTokenMap('frequency')))

const ROOMS: Record<string, { root: Decls; element: string[] | null }> = {
commons: { root: new Map([...GLOBAL_ROOT, ...fq(COMMONS)]), element: null },
'commons night': { root: new Map([...GLOBAL_ROOT, ...fq(NIGHT)]), element: null },
stage: { root: new Map([...GLOBAL_ROOT, ...fq(COMMONS)]), element: STAGE },
}

/** The computed value of a contract name in a room: declared on the room's element, or inherited from the root. */
function computed(room: { root: Decls; element: string[] | null }, name: CoreTokenName): string {
const root = new Map([...room.root, ...(prelude.includes(COMMONS[0]) ? contract : [])])
if (!room.element) return expand(root.get(name)!, new Map(), false, 0, root)
const element = new Map([...fq(room.element), ...(room.element.some((s) => prelude.includes(s)) ? contract : [])])
return element.has(name) ? expand(element.get(name)!, element, true, 0, root) : expand(root.get(name)!, new Map(), false, 0, root)
}

/** What the name points at, resolved where the room's own tokens live. */
function today(room: { root: Decls; element: string[] | null }, name: CoreTokenName): string {
const element = room.element ? fq(room.element) : new Map<string, string>()
return expand(contract.get(name)!, element, room.element !== null, 0, room.root)
}

for (const [label, room] of Object.entries(ROOMS)) {
it(`every contract name resolves on ${label} to the value its token has there`, () => {
for (const n of CORE_TOKEN_NAMES) {
const v = computed(room, n)
expect(v, `${label} ${n}`).not.toBe('')
expect(v, `${label} ${n}`).toBe(today(room, n))
}
})
}

it('the three rooms are told apart (Stage and night do not read Commons ground)', () => {
const canvas = Object.fromEntries(Object.entries(ROOMS).map(([k, r]) => [k, computed(r, '--c-canvas')]))
expect(new Set(Object.values(canvas)).size).toBe(3)
})
})

describe('the handoff\'s --th-* roles point at contract names (BLD-001)', () => {
const sources = [
'design_handoff/frequency/guidelines/element-library.md',
'design_handoff/frequency/assets/infographics/theme-pack.json',
'lib/sites/editor/theme-contract.json',
]
const used = new Set(sources.flatMap((f) => [...readFileSync(f, 'utf8').matchAll(/--th-[a-z0-9]+(?:-[a-z0-9]+)*/g)].map((m) => m[0])))

it('every --th-* name the handoff or the editor seed uses is mapped or marked theme-private', () => {
expect([...used].filter((n) => !(n in TH_TO_CONTRACT)).sort()).toEqual([])
})

it('every mapping lands on one of the forty-five', () => {
for (const [th, c] of Object.entries(TH_TO_CONTRACT)) if (c !== null) expect(CORE_TOKEN_NAMES, th).toContain(c)
})

it('the six roles the element library names all carry a contract name', () => {
for (const th of ['--th-bg', '--th-text', '--th-muted', '--th-accent', '--th-on-accent', '--th-r']) expect(TH_TO_CONTRACT[th], th).not.toBeNull()
})
})
58 changes: 56 additions & 2 deletions lib/theme/block-contract-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,22 @@ export const CONTRACT_THEME_IDS = ['frequency', 'bold', 'wildheart', 'network',

export type ContractThemeId = (typeof CONTRACT_THEME_IDS)[number]

/** The element each theme's tokens are declared on. Bold's code id is `menswork`; Default (`frequency`) wears the root. */
/** Frequency declares its aliases on every element that redefines its `--fq-*` tokens (app/themes/frequency.css,
* "one rule, two surfaces"): the root (Commons, and Commons night under `.dark`), each Stage surface, a night section
* and a skinned subtree. A custom property resolves its var() where it is declared, so a contract name declared on
* the root alone would freeze Commons values into a Stage section. The contract rides the same elements (BLD-001). */
const FREQUENCY_CONTRACT_SCOPE = [
':root[data-design="frequency"]',
'[data-design="frequency"] [data-surface="stage"]',
'[data-design="frequency"][data-surface="stage"]',
'[data-design="frequency"] [data-time="night"]',
'[data-design="frequency"] [data-skin]',
'[data-design="frequency"][data-skin]',
] as const

/** The element each theme's tokens are declared on. Bold's code id is `menswork`; Frequency wears its alias elements. */
const SCOPE: Readonly<Record<ContractThemeId, string>> = {
frequency: ':root[data-design="frequency"]',
frequency: FREQUENCY_CONTRACT_SCOPE.join(','),
bold: '[data-house-theme="menswork"]',
wildheart: '[data-house-theme="wildheart"]',
network: '[data-house-theme="network"]',
Expand All @@ -74,3 +87,44 @@ export function coreTokensCss(theme: ContractThemeId): string {
const map = coreTokenMap(theme)
return `${SCOPE[theme]}{${CORE_TOKEN_NAMES.map((n) => `${n}:${map[n]}`).join(';')}}`
}

/** The handoff's editor roles (`--th-*`, design_handoff/frequency/guidelines/element-library.md and
* assets/infographics/theme-pack.json, plus the editor seed lib/sites/editor/theme-contract.json) pointed at the
* contract name that carries the same role (BLD-001). The handoff is read-only, so the retirement lives here: a reader
* of either file looks the name up in this table. `null` marks a role that is one theme's private vocabulary (a season
* colour, a gilt, an arch) and has no contract name; a role block never reads it. */
export const TH_TO_CONTRACT: Readonly<Record<string, CoreTokenName | null>> = {
'--th-bg': '--c-canvas',
'--th-surface': '--c-surface',
'--th-raised': '--c-raised',
'--th-hairline': '--c-line',
'--th-text': '--c-text',
'--th-muted': '--c-text-muted',
'--th-secondary': '--c-text-muted',
'--th-accent': '--c-accent',
'--th-accent-hover': '--c-accent-hover',
'--th-accent-soft': '--c-accent-soft',
'--th-accent-bg': '--c-accent-bg',
'--th-accent-deep': '--c-accent-deep',
'--th-accent-text': '--c-accent-text',
'--th-on-accent': '--c-on-accent',
'--th-r': '--r-card',
'--th-clip-md': '--frame',
'--th-display': '--f-display',
'--th-dw': '--w-display',
'--th-body': '--f-body',
'--th-voice': '--f-voice',
'--th-quote': '--f-voice',
'--th-solid': '--c-accent',
'--th-on-solid': '--c-on-accent',
'--th-solid-hover': '--c-accent-hover',
'--th-hero': '--f-display',
'--th-clip-sm': null,
'--th-clip-lg': null,
'--th-mono': null,
'--th-season': null,
'--th-on-season': null,
'--th-gilt': null,
'--th-on-gilt': null,
'--th-arch': null,
}
21 changes: 21 additions & 0 deletions scripts/probes/builder/BLD-001.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// BLD-001: the token map. The forty-five contract names (lib/theme/block-contract-tokens.ts) resolve in all three of
// Frequency's rooms, Commons, Commons night and Stage: the contract rides every element that re-declares Frequency's
// aliases, so a Stage section reads Stage values instead of the root's Commons ones. The handoff's --th-* roles point
// at contract names. The pin test resolves every name in each room and compares it with the token it points at there.
import fs from 'node:fs'
const bad = []
const read = (f) => (fs.existsSync(f) ? fs.readFileSync(f, 'utf8') : (bad.push(`missing ${f}`), ''))
const tokens = read('lib/theme/block-contract-tokens.ts')
for (const sel of ['[data-design="frequency"] [data-surface="stage"]', '[data-design="frequency"] [data-time="night"]', '[data-design="frequency"] [data-skin]']) {
if (!tokens.includes(`'${sel}'`)) bad.push(`the Frequency contract scope lacks ${sel}`)
}
if (!/frequency: FREQUENCY_CONTRACT_SCOPE\.join\(','\)/.test(tokens)) bad.push('the Frequency contract must be declared on every alias element, not the root alone')
if (!/export const TH_TO_CONTRACT/.test(tokens)) bad.push('no --th-* to contract-name map')
for (const [th, c] of [['--th-bg', '--c-canvas'], ['--th-text', '--c-text'], ['--th-muted', '--c-text-muted'], ['--th-accent', '--c-accent'], ['--th-on-accent', '--c-on-accent'], ['--th-r', '--r-card']]) {
if (!tokens.includes(`'${th}': '${c}'`)) bad.push(`${th} must point at ${c}`)
}
const test = read('lib/theme/block-contract-tokens.test.ts')
if (!/Frequency contract in every room \(BLD-001\)/.test(test)) bad.push('the per-room pin test is missing')
for (const room of ["commons:", "'commons night':", 'stage:']) if (!test.includes(room)) bad.push(`the pin test does not resolve ${room.replace(/[':]/g, '')}`)
if (!/every --th-\* name the handoff or the editor seed uses is mapped/.test(test)) bad.push('the --th-* coverage test is missing')
if (bad.length) { console.error(bad.join('\n')); process.exit(1) }
Loading