diff --git a/src/components/nook/FeatureBreakdowns.tsx b/src/components/nook/FeatureBreakdowns.tsx index a86a2fc..cfbae76 100644 --- a/src/components/nook/FeatureBreakdowns.tsx +++ b/src/components/nook/FeatureBreakdowns.tsx @@ -851,7 +851,7 @@ export default function FeatureBreakdowns() { title="Your whole fleet, in one pixel strip" body="Every session, every agent, one square each. The pill never wraps, never grows — an arriving question flashes the fire and swaps the slot, then hands the notch back." bullets={[ - "13 agents: Claude Code, Codex, Gemini CLI, OpenCode, Pi, Muse, and more", + "Claude Code, Codex, Gemini CLI, OpenCode, Pi, Muse, Cursor and more", "Custom agents: your own coding agent can wire itself in from the published contract", "Slot rankings — you choose which data earns the notch", "You don't need a notch — external displays get the same island, floating top-center" diff --git a/src/components/nook/IslandExtras.tsx b/src/components/nook/IslandExtras.tsx index 9b014f7..dd06b16 100644 --- a/src/components/nook/IslandExtras.tsx +++ b/src/components/nook/IslandExtras.tsx @@ -722,7 +722,7 @@ function LayoutMock() { /* ── Marquee ─────────────────────────────────────────────────────── */ const CHIPS_A = [ - "13 agents", + "Agents wired in", "Custom agents", "Six chiptunes", "Focus-aware slots", diff --git a/src/routes/nook/agents.test.ts b/src/routes/nook/agents.test.ts index 2309628..94830a5 100644 --- a/src/routes/nook/agents.test.ts +++ b/src/routes/nook/agents.test.ts @@ -1,5 +1,6 @@ /** - * Regression test for the /agents roster (NOOK-336). + * Regression test for the /agents roster (NOOK-336) and its coverage wording + * (FRE-359). * * The page mirrors the app's registry, `AgentSpec.all` in * the-nook/Sources/NookCore/Agents/AgentRegistry.swift. When Muse joined @@ -8,6 +9,12 @@ * them. The registry lives in another repo, so the expected roster is * restated here; when an agent is added there, add it here and on the page. * + * FRE-359 verdict B: the page no longer states a count or claims coverage + * ("all thirteen", "every coding agent The Nook supports"). The page still + * lists 13 of the registry's 18 rows — the table is hand-mirrored and its + * regeneration is its own ticket — so the roster check here is a subset + * check in registry order, not an equality check. + * * STATIC SOURCE AUDIT, the same pattern as the downloads regressions. */ import { describe, it, expect } from "bun:test"; @@ -16,7 +23,10 @@ import { join } from "node:path"; const SOURCE = readFileSync(join(import.meta.dir, "agents.tsx"), "utf8"); -/** AgentSpec.all display names, in registry order. */ +/** User-facing copy only: comments are not copy and may name the old wording. */ +const COPY = SOURCE.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, ""); + +/** AgentSpec.all display names, in registry order (18 rows). @ The-Nook f4258034, 2026-10-29 */ const REGISTRY = [ "Claude Code", "Codex", @@ -30,25 +40,59 @@ const REGISTRY = [ "Gemini CLI", "Cline", "Qwen Code", - "DeepSeek" + "DeepSeek", + "Kimi", + "GitHub Copilot CLI", + "Crush", + "Cursor", + "Amp" ]; -const NUMBER_WORDS = ["twelve", "thirteen", "fourteen", "fifteen"]; +const NUMBER_WORDS = ["twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen"]; const entries = [...SOURCE.matchAll(/^\s{4}name: "([^"]+)",$/gm)].map((m) => m[1]); +/** The page's rows are a subset of the registry, in registry order. */ +function subsetInOrder(page: string[], registry: string[]): boolean { + let cursor = 0; + for (const name of page) { + const at = registry.indexOf(name, cursor); + if (at === -1) return false; + cursor = at + 1; + } + return true; +} + describe("Nook /agents roster (NOOK-336)", () => { - it("lists every registry agent, in registry order", () => { - expect(entries).toEqual(REGISTRY); + it("lists only registry agents, in registry order", () => { + expect(entries.length).toBeGreaterThan(0); + expect(subsetInOrder(entries, REGISTRY)).toBe(true); }); - it("states the same count everywhere it names one", () => { - const word = NUMBER_WORDS[entries.length - 12]; - for (const other of NUMBER_WORDS.filter((w) => w !== word)) { - expect(SOURCE.toLowerCase()).not.toContain(other); - } - expect(SOURCE).toContain(`all ${word}`); - expect(SOURCE).toContain(`The ${entries.length} agents`); - expect(SOURCE).toContain(`watches ${word} coding agents`); + it("lists each agent at most once", () => { + expect(new Set(entries).size).toBe(entries.length); + }); +}); + +describe("Nook /agents coverage wording (FRE-359)", () => { + it("states no agent count anywhere on the page", () => { + for (const word of NUMBER_WORDS) { + expect(COPY.toLowerCase()).not.toContain(word); + } + // A bare numeral count ("13 agents", "The 13") is just as precise and just + // as unverified; the page must not state one in any form. + expect(COPY).not.toMatch(/\b\d{1,2} agents\b/); + expect(COPY).not.toMatch(/\ball \d{1,2}\b/); + }); + + it("makes no completeness claim over the agent set", () => { + for (const banned of [ + "every coding agent", + "the agents the nook supports", + "agents the nook supports", + "supported agents" + ]) { + expect(COPY.toLowerCase()).not.toContain(banned); + } }); }); diff --git a/src/routes/nook/agents.tsx b/src/routes/nook/agents.tsx index 945e4c8..c43ff93 100644 --- a/src/routes/nook/agents.tsx +++ b/src/routes/nook/agents.tsx @@ -1,5 +1,7 @@ /** - * `/agents` on the Nook subdomain — supported agents and their event coverage. + * `/agents` on the Nook subdomain — the agent roster and each row's event + * coverage. It states no agent count and claims no completeness over the + * registry (FRE-359 verdict B). * * The registry below mirrors `AgentSpec.all` in the app * (~/Code/the-nook/Sources/NookCore/Agents/AgentSpec.swift) and each agent's @@ -282,19 +284,19 @@ export default function NookAgentsPage() { <>

- Supported agents + Agent coverage

- The Nook watches thirteen coding agents in one place — your menu bar and - notch show every session that is running, thinking, or blocked, and a - card answers permission prompts without leaving your keyboard. + The Nook watches coding agents in one place — your menu bar and notch + show sessions that are running, thinking, or blocked, and a card can + answer a permission prompt without leaving your keyboard.

Each agent reports its own set of events. "Allow / Deny from a card"