Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 22 additions & 10 deletions apps/website/qa/agents-spec-contract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { join } from 'node:path'
import { generate, SKILLS_OUT, CRONS_OUT, AGENTS_OUT, FUNCTIONS_OUT, TOOLS_OUT, FUNCTIONS_MANIFEST, EXPERIENCE_PATH, AGENT_COUNT, AGENT_LAYERS, HOSTS, CONTROLS, ON_FAILURE, FN_TRIGGERS, FN_ON_FAILURE, FN_SIDE_EFFECTS, FN_PROBE_RESULTS, FN_CONTROLS, functionDifferences, I18N_SPEC_DIR, I18N_FIELD_SOURCE, REPO_ROOT } from '../scripts/agents-from-specs.mjs'
import { canonicalSpecEditUrl, vendoredSpecUrl, specSlug } from '../src/lib/agentSpecs.ts'
import { MODULES } from '../src/lib/queenModules.ts'
import { HUD_VIEWS, HUD_KEYS, RAIL_VIEWS, SPEC_LAYERS, BOARD_VIEWS, railViewOf } from '../src/components/queenHud.ts'
import { HUD_VIEWS, HUD_KEYS, RAIL_VIEWS, SPEC_LAYERS, BOARD_VIEWS, PROJECT_VIEWS, railViewOf } from '../src/components/queenHud.ts'

const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex')
const skills = JSON.parse(readFileSync(SKILLS_OUT, 'utf8'))
Expand Down Expand Up @@ -397,24 +397,29 @@ for (const tab of shown) {
assert.ok(tab !== 'comb' && tab !== 'specs', `'${tab}' has a block of its own; showcasing it would render it twice`)
}
assert.match(app, /<ModulesBlock \/>/, 'the homepage does not mount the index, so every module the showcase leaves out is unreachable')
// The index draws three groups -- the rail's buttons, the ladder inside SPECS,
// the board inside KANBAN -- and they have to stay a partition of MODULES or the
// homepage silently drops a module. The predicates are asked of the shell's own
// The index draws four groups -- the rail's buttons, the ladder inside SPECS,
// the board inside KANBAN, the record inside PROJECT -- and they have to stay a
// partition of MODULES or the homepage silently drops a module. The fourth came
// with PASSPORT's move inside PROJECT (2026-09-21): until the index learned it,
// PASSPORT was a module with no card anywhere. The predicates are asked of the shell's own
// lists, so this is checkable here rather than only readable there.
const modulesBlock = src('components/ModulesBlock.tsx')
assert.match(modulesBlock, /RAIL_VIEWS as readonly string\[\]\)\.includes\(tab\)/, 'the index no longer reads the rail from RAIL_VIEWS, so its groups can drift from the buttons')
assert.match(modulesBlock, /MODULES\.filter\(\(m\) => isRailModule\(m\.tab\)\)/, 'the rail group is no longer MODULES on the rail, so a module can fall off the homepage')
assert.match(modulesBlock, /MODULES\.filter\(\(m\) => isLadderRung\(m\.tab\)\)/, 'the ladder group is no longer the spec layers the rail does not draw')
assert.match(modulesBlock, /MODULES\.filter\(\(m\) => isBoardReading\(m\.tab\)\)/, 'the board group is no longer the board views the rail does not draw')
assert.match(modulesBlock, /MODULES\.filter\(\(m\) => isProjectRecord\(m\.tab\)\)/, 'the project group is no longer the project views the rail does not draw')
for (const m of MODULES) {
const group = RAIL_VIEWS.includes(m.tab)
? 'rail'
: SPEC_LAYERS.includes(m.tab)
? 'ladder'
: BOARD_VIEWS.includes(m.tab)
? 'board'
: null
assert.ok(group, `module '${m.tab}' is in none of the index's three groups, so its card is drawn nowhere`)
: PROJECT_VIEWS.includes(m.tab)
? 'project'
: null
assert.ok(group, `module '${m.tab}' is in none of the index's four groups, so its card is drawn nowhere`)
}
assert.match(modulesBlock, /modules\.map\(\(module\)/, 'the index no longer draws a card for every module in a group')
// lib/queenModules opens by calling itself "the only place their identity is
Expand Down Expand Up @@ -520,9 +525,11 @@ assert.equal(HUD_KEYS.slice(0, HUD_VIEWS.length).join(''), '1234567890tprbwmlx',
// member on both its family's list and the rail is drawn twice.
assert.ok(SPEC_LAYERS.every((layer) => HUD_VIEWS.includes(layer)), 'a ladder layer is not a view, so it has no address and no key')
assert.ok(BOARD_VIEWS.every((view) => HUD_VIEWS.includes(view)), 'a board view is not a view, so it has no address and no key')
assert.ok(PROJECT_VIEWS.every((view) => HUD_VIEWS.includes(view)), 'a project view is not a view, so it has no address and no key')
assert.ok(RAIL_VIEWS.every((view) => HUD_VIEWS.includes(view)), 'the rail draws a button for something that is not a view')
assert.equal(SPEC_LAYERS[0], 'specs', 'SPECS is the first rung: it is the module the rail opens and the ladder stands in')
assert.equal(BOARD_VIEWS[0], 'kanban', 'KANBAN is the board the rail opens and the row stands in')
assert.equal(PROJECT_VIEWS[0], 'project', 'PROJECT is the module the rail opens and PASSPORT stands inside (2026-09-21)')
assert.deepEqual(
[...SPEC_LAYERS],
['specs', 'skills', 'crons', 'agents', 'tools', 'functions'],
Expand All @@ -534,9 +541,9 @@ assert.deepEqual(
'the board is kanban, mission map, factory, tech tree -- the order of their keys, 3/4/5/6',
)
assert.deepEqual(
[...new Set([...RAIL_VIEWS, ...SPEC_LAYERS, ...BOARD_VIEWS])].sort(),
[...new Set([...RAIL_VIEWS, ...SPEC_LAYERS, ...BOARD_VIEWS, ...PROJECT_VIEWS])].sort(),
[...HUD_VIEWS].sort(),
'rail plus ladder plus board is no longer every view: something is unreachable',
'rail plus ladder plus board plus project is no longer every view: something is unreachable',
)
assert.deepEqual(
RAIL_VIEWS.filter((view) => SPEC_LAYERS.includes(view)),
Expand All @@ -548,10 +555,15 @@ assert.deepEqual(
['kanban'],
'only KANBAN may be on the rail and in the board row; another reading on both is drawn twice',
)
assert.deepEqual(
RAIL_VIEWS.filter((view) => PROJECT_VIEWS.includes(view)),
['project'],
'only PROJECT may be on the rail and in the project family; PASSPORT on both is drawn twice',
)
assert.equal(
RAIL_VIEWS.length,
HUD_VIEWS.length - (SPEC_LAYERS.length - 1) - (BOARD_VIEWS.length - 1),
'the rail is the views less the five layers folded into SPECS and the two readings folded into KANBAN',
HUD_VIEWS.length - (SPEC_LAYERS.length - 1) - (BOARD_VIEWS.length - 1) - (PROJECT_VIEWS.length - 1),
'the rail is the views less the layers folded into SPECS, the readings folded into KANBAN and the record folded into PROJECT',
)
// Every view still answers railViewOf, and answers with a button the rail draws
// -- that is what lights the rail while a folded member is open.
Expand Down
7 changes: 5 additions & 2 deletions apps/website/qa/docs-spec-contract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,11 @@ if (witnessTable) {
const constitution = readFileSync(join(CORPUS, 'docs/T27-CONSTITUTION.md'), 'utf8')
const alphabet = readFileSync(join(CORPUS, 'docs/agents/AGENTS_ALPHABET.md'), 'utf8')
const laws = parseLawTable(constitution), phases = parsePhases(alphabet)
assert.equal(laws.rows.length, 7, 'the constitution defines L1-L7')
assert.deepEqual(laws.rows.map((l) => l.law), ['L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'L7'])
// L0 PURPOSE (adopted 2026-09-23) opens the table and sits outside the L1-L7
// ordering by the constitution's own words; L1-L7 follow in order, unchanged.
const lawIds = laws.rows.map((l) => l.law)
const orderedLaws = lawIds[0] === 'L0' ? lawIds.slice(1) : lawIds
assert.deepEqual(orderedLaws, ['L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'L7'], 'the constitution defines L1-L7, optionally preceded by L0 PURPOSE')
if (lawsTable) assert.deepEqual(lawsTable.rows, laws.rows)
assert.deepEqual(docs.figures['law-hierarchy'].laws.map((l) => l.law), laws.rows.map((l) => l.law))
assert.equal(phases.length, 7, 'the alphabet draws six phases plus GIT WORKFLOW')
Expand Down
31 changes: 24 additions & 7 deletions apps/website/src/components/ModulesBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useI18n } from '../i18n/context'
import { MODULES } from '../lib/queenModules'
import { BOARD_VIEWS, RAIL_VIEWS, SPEC_LAYERS, isBoardView, isSpecLayer } from './queenHud'
import { BOARD_VIEWS, PROJECT_VIEWS, RAIL_VIEWS, SPEC_LAYERS, isBoardView, isProjectView, isSpecLayer } from './queenHud'
import './ModulesBlock.css'

// A compact index of the modules: one card each, linking to its tab. Not on the
Expand All @@ -13,63 +13,74 @@ import './ModulesBlock.css'
// FUNCTIONS — are rungs of the ladder inside SPECS, because each is a catalogue
// generated from .t27 specs and each names the one below it. Two — MISSION MAP
// and FACTORY — sit under KANBAN, because the three are three readings of the
// one board. A reader who scrolled past fourteen equal cards and then opened a
// rail of seven was reading a map of a shell that no longer existed.
// one board. PASSPORT sits inside PROJECT (2026-09-21), the record the project
// keeps beside itself. A reader who scrolled past fourteen equal cards and then
// opened the rail was reading a map of a shell that no longer existed.
//
// Which group a module is in is asked of the shell's own lists rather than
// restated here: RAIL_VIEWS already is HUD_VIEWS less whatever has been folded,
// so folding a view under another moves its card by itself. The three groups
// so folding a view under another moves its card by itself. The four groups
// are `MODULES.filter` of predicates that partition by construction -- every
// module is in exactly one, and a new one lands in its group with no second
// list to remember.

/** On the rail: the shell's own list, so this cannot drift from the buttons. */
const isRailModule = (tab: string) => (RAIL_VIEWS as readonly string[]).includes(tab)
/** Folded into SPECS, and folded into KANBAN: the two families less their heads. */
/** Folded into SPECS, KANBAN and PROJECT: the three families less their heads. */
const isLadderRung = (tab: string) => isSpecLayer(tab) && !isRailModule(tab)
const isBoardReading = (tab: string) => isBoardView(tab) && !isRailModule(tab)
const isProjectRecord = (tab: string) => isProjectView(tab) && !isRailModule(tab)

const RAIL_MODULES = MODULES.filter((m) => isRailModule(m.tab))
// Sorted into each family's own order, which is not the order of their keys:
// TOOLS is the fifth layer and FUNCTIONS the sixth, while t comes after 0.
const order = (list: readonly string[]) => (tab: string) => list.indexOf(tab)
const rung = order(SPEC_LAYERS)
const seat = order(BOARD_VIEWS)
const shelf = order(PROJECT_VIEWS)
const LADDER_MODULES = MODULES.filter((m) => isLadderRung(m.tab)).sort((a, b) => rung(a.tab) - rung(b.tab))
const BOARD_MODULES = MODULES.filter((m) => isBoardReading(m.tab)).sort((a, b) => seat(a.tab) - seat(b.tab))
const PROJECT_MODULES = MODULES.filter((m) => isProjectRecord(m.tab)).sort((a, b) => shelf(a.tab) - shelf(b.tab))

// Every count is read, never a word typed here: the eyebrow once said "EIGHT"
// while the title said "ten" and the rail showed ten.
const N = MODULES.length
const RAIL_N = RAIL_MODULES.length
const LADDER_N = LADDER_MODULES.length
const BOARD_N = BOARD_MODULES.length
const PROJECT_N = PROJECT_MODULES.length
const COPY = {
en: {
eyebrow: `QUEEN / ${N} MODULES`,
title: `One screen, ${N} ways to read it`,
lede: `The Queen is a single-page module: the 3D map is the ground and every panel floats on it. These ${N} are what that ground can be read as — each one opens on the same map, in the same shell, from the key beside it. ${RAIL_N} are buttons down the left edge; ${LADDER_N} are rungs of one ladder inside SPECS, where every catalogue generated from a spec lives together; the last ${BOARD_N} are the board's other two readings, under KANBAN.`,
lede: `The Queen is a single-page module: the 3D map is the ground and every panel floats on it. These ${N} are what that ground can be read as — each one opens on the same map, in the same shell, from the key beside it. ${RAIL_N} are buttons down the left edge; ${LADDER_N} are rungs of one ladder inside SPECS, where every catalogue generated from a spec lives together; ${BOARD_N} are the board's other two readings, under KANBAN; and ${PROJECT_N} is the record kept beside the project, inside PROJECT.`,
railHead: `On the rail · ${RAIL_N}`,
ladderHead: `Inside SPECS, on the ladder · ${LADDER_N}`,
ladderNote:
'Specs, skills, crons, agents, tools and functions are six layers of one thing: each is generated from .t27 and each names the one below it. They open on the same keys as before, one step inside SPECS.',
boardHead: `Inside KANBAN, on the board · ${BOARD_N}`,
boardNote:
'The kanban, the mission map and the factory read one board: the same issues as columns, as a map, and as what the swarm is building from them. Same keys, one step inside KANBAN.',
projectHead: `Inside PROJECT, beside it · ${PROJECT_N}`,
projectNote:
'The passport is part of how the project describes itself, not an instrument of its own: the record every result has to carry. Same key, one step inside PROJECT.',
open: 'Open',
all: 'Open the shell →',
},
ru: {
eyebrow: `КОРОЛЕВА / ${N} МОДУЛЕЙ`,
title: `Один экран и ${N} способов его прочитать`,
lede: `Королева — одностраничный модуль: 3D-карта здесь основание, а каждая панель плавает над ним. Эти ${N} — то, чем это основание можно прочитать; любой открывается на той же карте, в том же шелле, по клавише рядом с ним. ${RAIL_N} — кнопки вдоль левого края, ${LADDER_N} — ступени одной лестницы внутри СПЕК, где лежат вместе все каталоги, порождённые из спек, а последние ${BOARD_N} — два других прочтения доски, внутри КАНБАНА.`,
lede: `Королева — одностраничный модуль: 3D-карта здесь основание, а каждая панель плавает над ним. Эти ${N} — то, чем это основание можно прочитать; любой открывается на той же карте, в том же шелле, по клавише рядом с ним. ${RAIL_N} — кнопки вдоль левого края, ${LADDER_N} — ступени одной лестницы внутри СПЕК, где лежат вместе все каталоги, порождённые из спек, ${BOARD_N} — два других прочтения доски, внутри КАНБАНА, и ${PROJECT_N} — запись, которую проект держит рядом с собой, внутри ПРОЕКТА.`,
railHead: `На панели · ${RAIL_N}`,
ladderHead: `Внутри СПЕК, на лестнице · ${LADDER_N}`,
ladderNote:
'Спеки, скиллы, кроны, агенты, инструменты и функции — шесть слоёв одного: каждый порождается из .t27 и каждый называет следующий. Клавиши прежние, просто на шаг внутрь СПЕК.',
boardHead: `Внутри КАНБАНА, на доске · ${BOARD_N}`,
boardNote:
'Канбан, карта миссий и фабрика читают одну доску: те же задачи — колонками, картой и тем, что рой из них строит. Клавиши прежние, просто на шаг внутрь КАНБАНА.',
projectHead: `Внутри ПРОЕКТА, рядом с ним · ${PROJECT_N}`,
projectNote:
'Паспорт — часть того, как проект описывает себя, а не отдельный инструмент: запись, которую должен нести каждый результат. Клавиша прежняя, просто на шаг внутрь ПРОЕКТА.',
open: 'Открыть',
all: 'Открыть шелл →',
},
Expand Down Expand Up @@ -130,6 +141,12 @@ export default function ModulesBlock() {
<p className="modules-block-group-note">{t.boardNote}</p>
{cards(BOARD_MODULES)}

<h3 className="modules-block-group" id="modules-project">
{t.projectHead}
</h3>
<p className="modules-block-group-note">{t.projectNote}</p>
{cards(PROJECT_MODULES)}

<a className="modules-block-all" href="#/queen">
{t.all}
</a>
Expand Down
Loading
Loading