From d2c2e6a4ee1defb4629d94ec3d8de88d4e136723 Mon Sep 17 00:00:00 2001 From: Freno CMO Date: Thu, 29 Oct 2026 14:22:11 +0000 Subject: [PATCH] agents: drop the count and the coverage claim (FRE-359 verdict B) FRE-359's verdict is B: the page's thirteen is the registry's first thirteen of eighteen rows, so the count, "every coding agent The Nook supports" and "Supported agents" all overstate or understate the app. The permitted sentences are the CTO's, verbatim. - agents.tsx: head description, ogTitle and H1 paragraph replaced with the verdict's sentences; title/H1 "Supported agents" -> "Agent coverage". - agents.test.ts: the stale NOOK-336 guard asserted "all thirteen" and the 13-name roster, so it passed in lockstep with the page. It now asserts no count of any form, no completeness phrase, and that the rows are a subset of the 18-name registry list (copied 2026-10-29, @ f4258034) in registry order. The table still lists 13 rows; regenerating it from the registry is its own ticket, and the guard no longer pretends otherwise. - FeatureBreakdowns.tsx / IslandExtras.tsx: the landing "13 agents:" bullet and the "13 agents" marquee chip lose the count; the chip becomes "Agents wired in". bun test src/routes/nook/agents.test.ts: 4 pass. Server security and appcast suites fail in this checkout on missing env (Environment validation failed / mainDBConnection), unrelated to these files. Co-Authored-By: Paperclip --- src/components/nook/FeatureBreakdowns.tsx | 2 +- src/components/nook/IslandExtras.tsx | 2 +- src/routes/nook/agents.test.ts | 72 ++++++++++++++++++----- src/routes/nook/agents.tsx | 18 +++--- 4 files changed, 70 insertions(+), 24 deletions(-) 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"