Skip to content

test(app): add render tests for palette, debug, and map sections - #12470

Merged
MarkusNeusinger merged 20 commits into
mainfrom
gemini/section-tests
Oct 30, 2026
Merged

MarkusNeusinger merged 20 commits into
mainfrom
gemini/section-tests

Conversation

@MarkusNeusinger

@MarkusNeusinger MarkusNeusinger commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add co-located unit and render tests in app/src/sections/palette/ (paletteHelpers.test.ts, ChromaWheel.test.tsx, CodeBlock.test.tsx, DeltaEMatrix.test.tsx, HeroSection.test.tsx, PaletteGridSection.test.tsx, SemanticAnchorsSection.test.tsx, ContinuousCmapsSection.test.tsx, CopySnippetsSection.test.tsx, WcagContrastSection.test.tsx, DeltaESection.test.tsx, LineageSection.test.tsx) covering the helpers, canvas wheel, and eight section components extracted from PalettePage in PR refactor(app): split PalettePage into sections #12463.
  • Add co-located unit and render tests in app/src/sections/debug/ (debugFormat.test.ts, SpecMatrix.test.tsx, FeedbackSection.test.tsx, DebugOverviewSections.test.tsx) covering debugFormat.ts, constants.ts, HeaderCell, SortableHeader, SpecMatrix, FeedbackSection, FeedbackTopList, CopyClaudePromptButton, SystemHealthRow, KPIGrid, DailyActivityChart, LibraryCoverage, RecentActivity, CommonWeaknesses, and ProblemAreas extracted from DebugPage in PR refactor(app): split DebugPage and MapPage into sections #12457.
  • Add co-located render and interaction tests in app/src/sections/map/ (MapLegend.test.tsx, MapHoverPanel.test.tsx, MapWeights.test.tsx, MapSearchPill.test.tsx, MapCanvas.test.tsx) covering the five map components extracted from MapPage in PR refactor(app): split DebugPage and MapPage into sections #12457.
  • Add changelog.d/section-tests.md for the integrated task (tools.changelog check gate). Apart from the two page test files that review round 1 allowed, only *.test.ts / *.test.tsx files under the three section folders changed; no component changed.test.ts/*.test.tsx` constraints were touched.
  • Add page-level tests for the state that lives in the pages, from review round 1: app/src/pages/PalettePage.test.tsx (new: swatch copy reset at the 1,499/1,500 ms boundary, CVD sort order, wheel, comparison, tabs, OKLCH, pageview) and new tests in app/src/pages/DebugPage.test.tsx (sort orders per header and direction, incomplete and low-score filters, feedback status PATCH with X-Admin-Token, revert on 500, status-filter refetch). The section tests now assert their prop contracts instead of re-implementing page logic.
  • Provenance: implemented by Gemini 4 Argon in the owner's interactive agy session, the first brief-lane task run as parallel agy subagents (three packages, each in its own worktree), and reviewed by Claude. Claude's checks: every Acceptance command per package, plus independent Opus reviews that broke behaviours on purpose. Review round 1 found that the palette and debug tests re-implemented the pages' sort, filter, copy-reset, and PATCH logic in test harnesses and tested that copy; Gemini fixed it in merges 8de98c6, 63ae112, and 3d884c3 and reported the mutation results. Claude closed the remaining gaps in 7c1ce5a: the per-theme WCAG swatch stroke (tested through the declared stylesheet rule, since jsdom drops var() borders), a low-score fixture that could not tell a library score from the average, and a select value on rerender. All 14 mutations from the review behave as the review expects (13 turn a test red, the harmless refactor stays green), and so do the two Claude added for these gaps.

Plan

Task section-tests across 3 packages:

  • Package 1 of 3 (section-tests-1) — branch gemini/section-tests-1, last commit 2ff23e5bb7fcd149654f093c583999a455a7fc0c, merge commit 549441523f96068b24c7d7cc7987e3a0a03d3c60
  • Package 2 of 3 (section-tests-2) — branch gemini/section-tests-2, last commit 2f6121f390e4f08d7674b9d445bacf9a65081721, merge commit e9b3b723000a9ee97b47241ff6799d45ce01b075
  • Package 3 of 3 (section-tests-3) — branch gemini/section-tests-3, last commit 46458a4efcc802c50e84c7085547684f653bd3c4, merge commit 389f48de5a5bb961672e37c8fd4f8adf1ce1c4c9
Brief: section-tests-1 (package 1 of 3)

Add render tests for the palette sections

Branch gemini/section-tests-1 (independent) · package 1 of 3

Goal

app/src/sections/palette/ (split out of PalettePage in PR #12463) has no
render tests, so the moved code counts as uncovered and the
codecov/patch/frontend check stays red. Add focused tests that render each
section and exercise its controls, so a later change that breaks the page
fails a test.

Context

  • Files: ChromaWheel.tsx, CodeBlock.tsx, DeltaEMatrix.tsx,
    HeroSection.tsx, PaletteGridSection.tsx, SemanticAnchorsSection.tsx,
    ContinuousCmapsSection.tsx, CopySnippetsSection.tsx,
    WcagContrastSection.tsx, DeltaESection.tsx, LineageSection.tsx,
    paletteHelpers.ts, types.ts; the page is app/src/pages/PalettePage.tsx.
  • Test patterns to follow: app/src/sections/map/useMapSelection.test.tsx
    and app/src/sections/map/useMapResize.test.tsx (stubs, fake timers),
    app/src/pages/MapPage.test.tsx (stubbing globals and restoring them in
    afterEach), and the existing tests under app/src/sections/debug/.
  • Interactions worth a test: expanding and collapsing the hue–chroma wheel and
    its aria-expanded, picking and unpicking a comparison palette
    (aria-pressed), switching the sort between imprint and CVD order, copying
    a swatch (clipboard stub, the copied feedback, and its reset after 1500 ms
    with fake timers), the language tabs and the OKLCH switch changing the
    snippet text, and the light and dark theme variants where a section reads
    isDark.
  • Package 2 (debug) and package 3 (map) run in parallel in other worktrees;
    they touch only their own folders.

Steps

  • Tests for the helpers in paletteHelpers.ts (pure functions).
  • Render tests for ChromaWheel, CodeBlock, and DeltaEMatrix.
  • Render and interaction tests for the eight section components.

Constraints

  • Add only new *.test.ts or *.test.tsx files next to the code under
    app/src/sections/palette/. Change no component; if one cannot be tested
    without a change, report it instead of changing it.
  • Assert on what a user sees (text, roles, aria-* attributes), not on class
    names or emotion hashes.
  • Restore every stubbed global and timer in afterEach, so other suites stay
    clean.
  • No any, no eslint-disable, no snapshot files.
  • No changelog fragment and no PR: the coordinator merges the packages and
    writes one fragment for the task.

Acceptance

Run each command on its own, from app/:

yarn test
yarn lint
yarn fm:check
yarn type-check
yarn vitest run --coverage --coverage.include='src/sections/palette/**' src/sections/palette

The coverage summary for All files shows at least 80 % of lines. yarn lint
reports no warnings. From the clone root, each of these prints nothing:

git diff --name-only origin/main...HEAD -- . ':!app/src/sections/palette/*.test.ts' ':!app/src/sections/palette/*.test.tsx'
grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src/sections/palette --include=*.test.ts --include=*.test.tsx

Decide yourself / report back

  • Decide yourself: how to group tests into files, test names, and which
    helper stubs to share within this folder. Document the choices.
  • Report back instead of guessing: a component that needs a change to be
    testable, and any test that only passes with a timing workaround.
Brief: section-tests-2 (package 2 of 3)

Add render tests for the debug sections split out of DebugPage

Branch gemini/section-tests-2 (independent) · package 2 of 3

Goal

PR #12457 moved DebugPage's blocks into app/src/sections/debug/ without
render tests for them, so the moved code counts as uncovered. Add focused tests
that render each of those components and exercise their controls, so a later
change that breaks the debug page fails a test.

Context

  • The files to cover (all in app/src/sections/debug/): CommonWeaknesses.tsx,
    CopyClaudePromptButton.tsx, DailyActivityChart.tsx, FeedbackSection.tsx,
    FeedbackTopList.tsx, HeaderCell.tsx, KPIGrid.tsx, LibraryCoverage.tsx,
    ProblemAreas.tsx, RecentActivity.tsx, SortableHeader.tsx,
    SpecMatrix.tsx, SystemHealthRow.tsx, debugFormat.ts, constants.ts.
    The other files in the folder (agent cases, promote, tags, diff, triage)
    already have their own tests and are out of scope.
  • Data shapes and realistic mock values: app/src/pages/DebugPage.test.tsx
    (its mock API payload) and the exported types in each section file.
  • Interactions worth a test: sorting SpecMatrix by each sortable column and
    direction (SortableHeader), the incomplete and low-score filters, the
    feedback status filter and status changes in FeedbackSection (stub
    fetchWithAuth the way DebugPage.test.tsx does), the clipboard button with
    its feedback, and the colour thresholds in debugFormat.ts (scoreColor,
    pingColor, formatNum, formatDateShort, countImpls).
  • Test patterns: the existing tests in this folder and
    app/src/sections/map/useMapSelection.test.tsx.
  • Package 1 (palette) and package 3 (map) run in parallel in other worktrees;
    they touch only their own folders.

Steps

  • Tests for debugFormat.ts.
  • Render tests for the table and header components (SpecMatrix,
    SortableHeader, HeaderCell) with sorting and filters.
  • Render tests for the remaining sections listed under Context.

Constraints

  • Add only new *.test.ts or *.test.tsx files under
    app/src/sections/debug/. Change no component and no existing test; if a
    component cannot be tested without a change, report it instead.
  • Assert on what a user sees (text, roles, aria-* attributes), not on class
    names or emotion hashes.
  • Restore every stubbed global, fetch, and timer in afterEach.
  • No any, no eslint-disable, no snapshot files.
  • No changelog fragment and no PR: the coordinator merges the packages and
    writes one fragment for the task.

Acceptance

Run each command on its own, from app/:

yarn test
yarn lint
yarn fm:check
yarn type-check
yarn vitest run --coverage --coverage.include='src/sections/debug/**' src/sections/debug

In the coverage table, every file listed under Context shows at least 80 % of
lines. yarn lint reports no warnings. From the clone root, each of these
prints nothing:

git diff --name-only origin/main...HEAD -- . ':!app/src/sections/debug/*.test.ts' ':!app/src/sections/debug/*.test.tsx'
git diff --name-only --diff-filter=M origin/main...HEAD
grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src/sections/debug --include=*.test.ts --include=*.test.tsx

Decide yourself / report back

  • Decide yourself: how to group tests into files, test names, and shared
    mock data within this folder. Document the choices.
  • Report back instead of guessing: a component that needs a change to be
    testable, and any test that only passes with a timing workaround.
Brief: section-tests-3 (package 3 of 3)

Add render tests for the map components split out of MapPage

Branch gemini/section-tests-3 (independent) · package 3 of 3

Goal

PR #12457 moved MapPage's markup into app/src/sections/map/. Its three hooks
have tests, but the five components do not, so the moved code counts as
uncovered. Add focused render tests for them, so a later change that breaks
the map fails a test.

Context

  • The files to cover (all in app/src/sections/map/): MapHoverPanel.tsx,
    MapLegend.tsx, MapSearchPill.tsx, MapWeights.tsx, MapCanvas.tsx. The
    hooks (useMapSelection, useMapSearch, useMapResize) already have tests
    and are out of scope.
  • app/src/pages/MapPage.test.tsx shows how to stub react-force-graph-2d,
    ResizeObserver, src/hooks, src/hooks/useLayoutContext, and
    @mui/material/useMediaQuery. Types: MapGraphData and MapForceConfig in
    app/src/hooks/useForceGraphSimulation.ts, MapNode in
    app/src/pages/MapPage.helpers.ts, MapPanelData in
    app/src/sections/map/useMapSelection.ts.
  • Interactions worth a test: the legend's open and closed state and its
    entries per top type; the hover panel for a hovered versus a pinned node,
    with and without a preview image; the search pill's results, keyboard
    navigation, and selection callback; the weight sliders and the edge
    threshold calling their setters, and the reset button; for MapCanvas, the
    loading gate before ready, the progress text while settling, and that the
    stubbed ForceGraph2D receives the graph data and the click and hover
    handlers. The canvas paint callbacks themselves need no pixel tests.
  • Package 1 (palette) and package 2 (debug) run in parallel in other
    worktrees; they touch only their own folders.

Steps

  • Render tests for MapLegend, MapHoverPanel, and MapWeights.
  • Render and keyboard tests for MapSearchPill.
  • Render tests for MapCanvas with ForceGraph2D stubbed.

Constraints

  • Add only new *.test.tsx files under app/src/sections/map/. Change no
    component and no existing test; if a component cannot be tested without a
    change, report it instead.
  • Assert on what a user sees (text, roles, aria-* attributes) and on the
    props the ForceGraph2D stub receives, not on class names or emotion hashes.
  • Restore every stubbed global and timer in afterEach.
  • No any, no eslint-disable, no snapshot files.
  • No changelog fragment and no PR: the coordinator merges the packages and
    writes one fragment for the task.

Acceptance

Run each command on its own, from app/:

yarn test
yarn lint
yarn fm:check
yarn type-check
yarn vitest run --coverage --coverage.include='src/sections/map/Map*.tsx' src/sections/map

In the coverage table, MapCanvas.tsx shows at least 50 % of lines (its paint
callbacks run only on a real canvas) and every other component at least 80 %.
yarn lint reports no warnings. From the clone root, each of these prints
nothing:

git diff --name-only origin/main...HEAD -- . ':!app/src/sections/map/*.test.tsx'
git diff --name-only --diff-filter=M origin/main...HEAD
grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src/sections/map --include=*.test.tsx

Decide yourself / report back

  • Decide yourself: how to group tests into files, test names, and shared
    fixtures within this folder. Document the choices.
  • Report back instead of guessing: a component that needs a change to be
    testable, and any test that only passes with a timing workaround.

Test plan

  • cd app && yarn test — Test Files 134 passed (134), Tests 1284 passed (1284)
  • cd app && yarn lint — Done (0 errors, 0 warnings)
  • cd app && yarn fm:check — All matched files use Prettier code style!
  • cd app && yarn type-check — Done (0 errors)
  • cd app && yarn build — ✓ 1939 modules transformed, Done
  • cd app && yarn vitest run --coverage --coverage.include='src/sections/palette/**' src/sections/palette — All files: 98.88% Stmts | 96.82% Branch | 100% Funcs | 100% Lines (12 test files, 32 tests)
  • cd app && yarn vitest run --coverage --coverage.include='src/sections/debug/**' src/sections/debug — 100% Lines on all 15 target files (CommonWeaknesses.tsx, CopyClaudePromptButton.tsx, DailyActivityChart.tsx, FeedbackSection.tsx, FeedbackTopList.tsx, HeaderCell.tsx, KPIGrid.tsx, LibraryCoverage.tsx, ProblemAreas.tsx, RecentActivity.tsx, SortableHeader.tsx, SpecMatrix.tsx, SystemHealthRow.tsx, constants.ts, debugFormat.ts)
  • cd app && yarn vitest run --coverage --coverage.include='src/sections/map/Map*.tsx' src/sections/map — MapCanvas.tsx: 64.64% Lines; MapHoverPanel.tsx, MapLegend.tsx, MapSearchPill.tsx, MapWeights.tsx: 100% Lines (8 test files, 37 tests)
  • git diff --name-only origin/main...origin/gemini/section-tests-1 -- . ':!app/src/sections/palette/*.test.ts' ':!app/src/sections/palette/*.test.tsx' — empty output
  • git diff --name-only origin/main...origin/gemini/section-tests-2 -- . ':!app/src/sections/debug/*.test.ts' ':!app/src/sections/debug/*.test.tsx' — empty output
  • git diff --name-only --diff-filter=M origin/main...origin/gemini/section-tests-2 — empty output
  • git diff --name-only origin/main...origin/gemini/section-tests-3 -- . ':!app/src/sections/map/*.test.tsx' — empty output
  • git diff --name-only --diff-filter=M origin/main...origin/gemini/section-tests-3 — empty output
  • grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src/sections/palette --include=*.test.ts --include=*.test.tsx — empty output
  • grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src/sections/debug --include=*.test.ts --include=*.test.tsx — empty output
  • grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src/sections/map --include=*.test.tsx — empty output
  • PYTHON_DOTENV_DISABLED=1 uv run python -m tools.changelog check --base origin/main — 68 fragment(s) well-formed ... the diff against origin/main passes the fragment rule

Decisions

  • Package 1 (app/src/sections/palette/): Placed one co-located test file next to each module (paletteHelpers.test.ts, ChromaWheel.test.tsx, CodeBlock.test.tsx, DeltaEMatrix.test.tsx, HeroSection.test.tsx, PaletteGridSection.test.tsx, SemanticAnchorsSection.test.tsx, ContinuousCmapsSection.test.tsx, CopySnippetsSection.test.tsx, WcagContrastSection.test.tsx, DeltaESection.test.tsx, LineageSection.test.tsx). Used lightweight stateful test harnesses for controlled parent props (wheelOpen, compareId, sort, copiedHex + 1500 ms timer reset, lang, oklch) and a 64x64 2D canvas context stub in ChromaWheel.test.tsx (createImageData / putImageData) to exercise the pixel hue-ring painter and cache.
  • Package 2 (app/src/sections/debug/): Grouped the 15 target modules into four focused suites: debugFormat.test.ts (formatting and colour thresholds), SpecMatrix.test.tsx (HeaderCell, SortableHeader, and SpecMatrix sorting and filters), FeedbackSection.test.tsx (FeedbackSection, FeedbackTopList, CopyClaudePromptButton, and constants.ts with fetch stubbed via vi.stubGlobal), and DebugOverviewSections.test.tsx (SystemHealthRow, KPIGrid, DailyActivityChart, LibraryCoverage, RecentActivity, CommonWeaknesses, and ProblemAreas).
  • Package 3 (app/src/sections/map/): Placed one co-located test file per component (MapLegend.test.tsx, MapHoverPanel.test.tsx, MapWeights.test.tsx, MapSearchPill.test.tsx, MapCanvas.test.tsx) with inline typed fixture builders (makeGraphData, makePanelData, SearchPillHarness, makeNode, makeForceConfig, makeProps) so only *.test.tsx files were added.

Open steps and questions

  • None.

MarkusNeusinger and others added 13 commits October 30, 2026 09:55
Step 1 of 3: Tests for the helpers in paletteHelpers.ts (pure functions).

Brief: section-tests-1

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
…EMatrix

Step 2 of 3: Render tests for ChromaWheel, CodeBlock, and DeltaEMatrix.

Brief: section-tests-1

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
… sections

Step 3 of 3: Render and interaction tests for the eight section components.

Brief: section-tests-1

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
Step 1 of 3: Tests for debugFormat.ts.

Brief: section-tests-2

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
…derCell

Step 2 of 3: Render tests for the table and header components (SpecMatrix, SortableHeader, HeaderCell) with sorting and filters.

Brief: section-tests-2

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
Step 3 of 3: Render tests for the remaining sections listed under Context.

Brief: section-tests-2

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
Step 1 of 3: Render tests for MapLegend, MapHoverPanel, and MapWeights.

Brief: section-tests-3

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
Step 2 of 3: Render and keyboard tests for MapSearchPill.

Brief: section-tests-3

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
Step 3 of 3: Render tests for MapCanvas with ForceGraph2D stubbed.

Brief: section-tests-3

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
Brief: section-tests

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
@MarkusNeusinger MarkusNeusinger added the gemini Task for Gemini in the owner's interactive agy session (gemini-task skill) label Oct 30, 2026
@codecov

codecov Bot commented Oct 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@MarkusNeusinger

Copy link
Copy Markdown
Owner Author

Review round 1 (Claude): send back

The mechanical Acceptance of all three briefs passes on the package branches: yarn test, yarn lint without warnings, yarn fm:check, yarn type-check, the coverage targets (palette 100 % on every file; debug 100 % on every file the brief lists; map: MapCanvas 64.6 %, the other four 100 %), only new test files, no any, eslint-disable, or snapshots.

An independent review with mutations then found the main gap: the palette and debug tests re-implement the parent page's state logic inside test harnesses and test that copy. The sort, filter, copy-reset, and PATCH logic lives in app/src/pages/PalettePage.tsx and app/src/pages/DebugPage.tsx; StatefulPaletteGrid, StatefulAnchors, StatefulSpecMatrix, and StatefulFeedbackHarness copy it almost line for line. Breaking the real page leaves every test green. The briefs limited new files to the section folders, which is why this happened; the briefs also said to report a gap like this instead of working around it, so please report such cases next time.

This round widens the allowed files to the two page test files below. Components stay unchanged.

How to work this round

Three independent packages, one subagent each in its own worktree (branch mode), at most three at a time. Each branches from origin/gemini/section-tests (not from origin/main) as gemini/section-tests-r1-<k> and pushes it; you then merge each into gemini/section-tests with git merge --no-ff origin/gemini/section-tests-r1-<k>, run the gates once on the merged result, and push gemini/section-tests. No new PR, no second changelog fragment; adjust the wording of changelog.d/section-tests.md only if it no longer fits. When done, comment on this PR with each package's last commit, the gate results, and the mutation table (behaviour, file, caught yes/no).

Every package ends with a mutation check: make each listed change with your edit tool, run the package's test command, confirm at least one test fails (or, where the table says so, that all stay green), then restore the file with git restore <file>. A mutation that should turn a test red and stays green means a test is still missing. After the last one, git status --porcelain prints nothing.

Package 1 (palette): gemini/section-tests-r1-1

Files it may add or change: new app/src/pages/PalettePage.test.tsx, and the test files under app/src/sections/palette/.

  1. Add app/src/pages/PalettePage.test.tsx that renders the real <PalettePage />. Mock src/hooks (useAnalytics with trackPageview: vi.fn(), useTheme returning isDark from a mutable ref) the way app/src/pages/MapPage.test.tsx mocks its hooks; stub HTMLCanvasElement.prototype.getContext as HeroSection.test.tsx:38 does and navigator.clipboard.writeText; restore everything in afterEach. Test on the real page:
    • Copy: clicking a swatch's copy button in the palette grid shows copied ✓ on it; after 1,499 ms of fake time it is still shown, after 1 ms more the hex is back. The same for one semantic anchor.
    • Sort: after clicking "CVD-optimal", the grid's copy buttons follow brand, lavender, lime, rose, red, cyan, blue, ochre (lime third); "imprint (default)" restores the original order.
    • The wiring the section tests used harnesses for, one assertion each: the wheel toggle's aria-expanded, a comparison palette's aria-pressed, a language tab and the OKLCH switch changing the snippet text.
    • trackPageview is called once with paths.palette.
  2. Remove the copied page logic. In PaletteGridSection.test.tsx (:9-26) and SemanticAnchorsSection.test.tsx (:8-18), delete StatefulPaletteGrid, StatefulAnchors, and the copies of CVD_OPTIMAL_ORDER and copyHex. Render each section with fixed props and assert its contract instead: copyHex is called with the clicked swatch's hex; the sort buttons call setSort('cvd') and setSort('imprint'); with copiedHex="#009E73" only that swatch shows copied ✓. The timing assertions now live in the page test. StatefulHero and StatefulCopySnippets may stay if they only pass useState through.
  3. Make the dark-theme test check the theme. WcagContrastSection.test.tsx:23-38 asserts only text that is the same in both themes. What isDark changes is the swatch stroke (WcagContrastSection.tsx:71, :85): assert with toHaveStyle that a swatch failing on the current background has 1px solid var(--ink) and a passing one 1px solid var(--rule), in both themes, using a swatch that flips between them. If jsdom drops var(...) from the computed border, report it instead of asserting on class names.
  4. Delete paletteHelpers.test.ts:118-124, which only restates layout constants.

Mutations, each must turn a test red (yarn vitest run src/pages/PalettePage.test.tsx src/sections/palette):

File Change
app/src/pages/PalettePage.tsx delete the setTimeout(...) line in copyHex
app/src/pages/PalettePage.tsx change 1500 to 3000 in copyHex
app/src/pages/PalettePage.tsx swap the two branches of sort === 'imprint' ? … : … in sortedPalette
app/src/sections/palette/PaletteGridSection.tsx change onClick={() => setSort(s)} to onClick={() => setSort('imprint')}
app/src/sections/palette/WcagContrastSection.tsx change isDark ? row.dark < 3 : row.light < 3 to isDark ? row.light < 3 : row.dark < 3

Path check, from the clone root, prints nothing:

git diff --name-only origin/gemini/section-tests...HEAD -- . ':!app/src/pages/PalettePage.test.tsx' ':!app/src/sections/palette/*.test.ts' ':!app/src/sections/palette/*.test.tsx'

Package 2 (debug): gemini/section-tests-r1-2

Files it may add or change: app/src/pages/DebugPage.test.tsx (new tests only; existing tests and the shared mockDebugData stay as they are), and the test files under app/src/sections/debug/.

  1. Test the real page. In app/src/pages/DebugPage.test.tsx, add a new describe block that renders DebugPage with the file's fetch stub. If mockDebugData has too few specs to tell orders apart, build a local fixture inside the new block. Test:
    • Sort: clicking each sortable header (id, title, average score, updated) orders the rows correctly, with the default direction per column (id and title ascending, score and updated descending) and the other direction on a second click; the active header shows ▲ or ▼, the others none.
    • Filters: "incomplete" and "low score" (any library below 90) each narrow the rows to the expected specs, and a second click restores them.
    • Feedback status: changing a message's status sends PATCH …/debug/feedback/<id> with body {"status":"<new>"} and the X-Admin-Token header; when that PATCH answers 500, the select shows the old status again; choosing a status filter refetches …/debug/feedback/messages?status=<status>&limit=50.
  2. Remove the copied page logic. In SpecMatrix.test.tsx, delete StatefulSpecMatrix (:65-121) and the order and filter tests built on it (:218-292); render SpecMatrix with fixed props and assert its contract: the headers call handleSort with 'id', 'title', 'avg_score', 'updated'; the filter buttons call their setters; with sortCol and sortDir given, only that header shows the arrow, ▲ for ascending and ▼ for descending. In FeedbackSection.test.tsx, delete StatefulFeedbackHarness (:75-113) and the PATCH and revert tests built on it (:290-299); assert that the status select calls handleStatusChange('fb-1', 'done') and the status filter calls its setter. Find a status select within its message row instead of by index (selects[1], :282-284).
  3. Time zone. debugFormat.test.ts:77 and SpecMatrix.test.tsx:206-207 (inputs at :14, :43, :50) fail under TZ=Pacific/Auckland. Use local-time inputs without Z, for example '2026-01-15T12:00:00'.
  4. Clipboard restore (FeedbackSection.test.tsx:162, :169-172): use vi.spyOn(navigator.clipboard, 'writeText') after userEvent.setup(), or delete the property when it was absent before.
  5. Weak assertions in DebugOverviewSections.test.tsx: assert ProblemAreas' per-section counts with within (:253-257); in DailyActivityChart, hover one bar and assert its tooltip, and make the empty case assert the empty state rather than the header (:109-110).

Mutations, each must turn a test red (yarn vitest run src/pages/DebugPage.test.tsx src/sections/debug):

File Change
app/src/pages/DebugPage.tsx change return sortDir === 'desc' ? -cmp : cmp; to return sortDir === 'desc' ? cmp : -cmp;
app/src/pages/DebugPage.tsx in handleSort, set the default direction to 'asc' for every column
app/src/pages/DebugPage.tsx delete if (showLowScores) filtered = filtered.filter(hasLowScore);
app/src/pages/DebugPage.tsx in updateFeedbackStatus, replace the fetchWithAuth(...) call with new Response(null, { status: 200 })
app/src/pages/DebugPage.tsx delete setFeedbackMessages(prev); in the catch
app/src/sections/debug/SpecMatrix.tsx give the title header active={sortCol === 'id'}

Claude reruns the debug tests under TZ=Pacific/Auckland at take-over; you need not run that yourself.

Path checks, from the clone root: the first prints nothing, the second prints 0 (no removed lines in the page test):

git diff --name-only origin/gemini/section-tests...HEAD -- . ':!app/src/pages/DebugPage.test.tsx' ':!app/src/sections/debug/*.test.ts' ':!app/src/sections/debug/*.test.tsx'
git diff origin/gemini/section-tests...HEAD -- app/src/pages/DebugPage.test.tsx | grep -c '^-[^-]'

Package 3 (map): gemini/section-tests-r1-3

Files it may change: the test files under app/src/sections/map/. No harness here copies page logic; these are small gaps.

  1. Weights (MapWeights.test.tsx:78-80): getByText('3.5') matches any row, so a slider that writes another category passes. Also assert the moved slider's own value, expect(sliders[0]).toHaveAttribute('aria-valuenow', '3.5').
  2. Blank query (MapSearchPill.test.tsx:73-79): the test is named for a blank query but covers only the closed state. Render the pill open with the query ' ' and assert that no result shows, or assert right after Escape (:143), while the blur timer has not run yet, that Basic Scatter Plot is gone.
  3. Handlers (MapCanvas.test.tsx:183-185): toBe(props.onNodeClick) checks function identity, so a harmless wrapper fails the test. Call the captured onNodeHover(node), onNodeHover(null), onNodeClick(node), and onEngineTick() instead and assert the prop spies receive those arguments.
  4. Echoed mocks (MapCanvas.test.tsx:204-205, :212, :214): let the collide and outlier-squash force factories return sentinel objects and assert those are passed; assert the framing exactly, centerAt(60, 70, 0) and zoom(4.8, 0) for this fixture, instead of expect.any(Number).
  5. Timer (MapSearchPill.test.tsx:118, :142): the Escape test runs on real timers, so the 150 ms blur timeout fires after unmount. Call vi.useFakeTimers() at its start.
  6. Name (MapHoverPanel.test.tsx:81): "renders in dark mode" asserts nothing about dark mode; rename it or assert a dark-mode difference.

The legend's open and closed state cannot be observed in jsdom without a component change (aria-expanded on the toggle); leave it, it is tracked separately.

Mutations (yarn vitest run src/sections/map): the first two must turn a test red, the third must stay green:

File Change Expected
app/src/sections/map/MapSearchPill.tsx change {searchOpen && searchQuery.trim() && ( to {searchOpen && ( red
app/src/sections/map/MapWeights.tsx change [cat]: v as number to [Object.keys(w).at(-1) ?? cat]: v as number red
app/src/sections/map/MapCanvas.tsx change onNodeHover={onNodeHover} to onNodeHover={n => onNodeHover(n)} green

Path check, from the clone root, prints nothing:

git diff --name-only origin/gemini/section-tests...HEAD -- . ':!app/src/sections/map/*.test.tsx'

Acceptance for every package

Run each command on its own, from app/:

yarn test
yarn lint
yarn fm:check
yarn type-check

yarn lint reports no warnings. From the clone root, each of these prints nothing:

git status --porcelain
git diff --name-only origin/main...HEAD -- app/src ':!*.test.ts' ':!*.test.tsx'
grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src --include=*.test.ts --include=*.test.tsx

The per-package path check names the only files that package may add or change. Report back instead of guessing when a behaviour can only be tested by changing a component, or when a test only passes with a timing workaround.

MarkusNeusinger and others added 6 commits October 30, 2026 11:10
Step 1 of 1: address Review Round 1 feedback on MapWeights, MapSearchPill, MapCanvas, and MapHoverPanel tests.

Brief: section-tests-3

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
Render the real PalettePage to verify 1,499 ms/1,500 ms copy timing, CVD-optimal vs imprint sorting, state wiring, and pageview analytics; replace copied page logic in PaletteGridSection and SemanticAnchorsSection tests with prop contract assertions; drop layout constant restatements in paletteHelpers.test.ts.

Brief: section-tests-1

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
…ntracts

Review round 1 for package 2 (debug):
- Add DebugPage integration tests for spec matrix sorting, incomplete and low-score filters, and feedback status PATCH/revert/refetch
- Replace StatefulSpecMatrix and StatefulFeedbackHarness with direct prop contract tests in SpecMatrix.test.tsx and FeedbackSection.test.tsx
- Use timezone-safe local ISO timestamps in debugFormat.test.ts and SpecMatrix.test.tsx
- Restore navigator.clipboard cleanly in CopyClaudePromptButton tests
- Strengthen DailyActivityChart tooltip/empty-state assertions and ProblemAreas per-section count assertions

Brief: section-tests-2

Co-Authored-By: Antigravity (Gemini 4 Argon) <noreply@google.com>
@MarkusNeusinger

Copy link
Copy Markdown
Owner Author

Review round 1 response (Gemini)

All three Round 1 packages were worked in isolated worktrees branched from origin/gemini/section-tests, pushed to origin/gemini/section-tests-r1-{1,2,3}, and merged --no-ff into gemini/section-tests (3d884c3d6).

Package commits & merges

Package Branch Package Commit Merge Commit on gemini/section-tests
1 (palette) gemini/section-tests-r1-1 83e89d4fafcc0f7e71643f5dd5b2ab0fa0a04ef8 (test(palette): add PalettePage test and test section contracts directly) 8de98c6d7
2 (debug) gemini/section-tests-r1-2 8da8a4e6daa8934a2b12185024003e8f31561c4f (test(debug): test real DebugPage sorting/triage and refine section contracts) 63ae11230
3 (map) gemini/section-tests-r1-3 aa80c44e1df01557f8e0b4fabb9ad3ba91ac64e1 (test(map): tighten assertions and timer handling in map section tests) 3d884c3d6

Gate results (merged on gemini/section-tests)

  • yarn test: PASS (135 passed (135), 1289 passed (1289))
  • yarn lint: PASS (0 errors, 0 warnings)
  • yarn fm:check: PASS (All matched files use Prettier code style!)
  • yarn type-check: PASS (tsc --noEmit && tsc -p tsconfig.test.json --noEmit)
  • PYTHON_DOTENV_DISABLED=1 uv run python -m tools.changelog check --base origin/main: PASS (the diff against origin/main passes the fragment rule)
  • git status --porcelain: PASS (empty)
  • git diff --name-only origin/main...HEAD -- app/src ':!*.test.ts' ':!*.test.tsx': PASS (empty)
  • grep -rn -E 'eslint-disable|: any\b|as any|<any>' app/src --include=*.test.ts --include=*.test.tsx: PASS (empty)
  • Per-package path checks against origin/gemini/section-tests: PASS (all empty; DebugPage.test.tsx has 0 removed lines, +308/-0)

Mutation table

Package Behaviour / Mutation File Expected Caught Notes / Failing Test
1 (palette) Delete setTimeout(...) line in copyHex app/src/pages/PalettePage.tsx red yes PalettePage > shows copied ✓ for 1,499 ms and restores the hex at 1,500 ms for both a palette swatch and a semantic anchor
1 (palette) Change 1500 to 3000 in copyHex app/src/pages/PalettePage.tsx red yes PalettePage > shows copied ✓ for 1,499 ms and restores the hex at 1,500 ms for both a palette swatch and a semantic anchor
1 (palette) Swap branches of sort === 'imprint' ? … : … in sortedPalette app/src/pages/PalettePage.tsx red yes PalettePage > reorders the palette grid to CVD-optimal order (lime third) and restores imprint default order
1 (palette) Change onClick={() => setSort(s)} to onClick={() => setSort('imprint')} app/src/sections/palette/PaletteGridSection.tsx red yes PaletteGridSection > calls setSort("cvd") and setSort("imprint") from the sort buttons and shows the trade-off table when sort="cvd" (and PalettePage sort test)
1 (palette) Change isDark ? row.dark < 3 : row.light < 3 to isDark ? row.light < 3 : row.dark < 3 app/src/sections/palette/WcagContrastSection.tsx red no (reported) See finding below: jsdom 29 drops var(...) from computed border shorthand
2 (debug) Change return sortDir === 'desc' ? -cmp : cmp; to return sortDir === 'desc' ? cmp : -cmp; app/src/pages/DebugPage.tsx red yes DebugPage spec matrix and feedback triage > sorts specs by each sortable header with per-column default direction and toggles on second click
2 (debug) Set handleSort default direction to 'asc' for every column app/src/pages/DebugPage.tsx red yes DebugPage spec matrix and feedback triage > sorts specs by each sortable header with per-column default direction and toggles on second click
2 (debug) Delete if (showLowScores) filtered = filtered.filter(hasLowScore); app/src/pages/DebugPage.tsx red yes DebugPage spec matrix and feedback triage > narrows rows with incomplete and low-score filters and restores them on second click
2 (debug) Replace fetchWithAuth(...) in updateFeedbackStatus with new Response(null, { status: 200 }) app/src/pages/DebugPage.tsx red yes DebugPage spec matrix and feedback triage > patches feedback status with X-Admin-Token, reverts on 500, and refetches when status filter changes
2 (debug) Delete setFeedbackMessages(prev); in updateFeedbackStatus catch app/src/pages/DebugPage.tsx red yes DebugPage spec matrix and feedback triage > patches feedback status with X-Admin-Token, reverts on 500, and refetches when status filter changes
2 (debug) Give title header active={sortCol === 'id'} app/src/sections/debug/SpecMatrix.tsx red yes SpecMatrix > shows the direction arrow only on the active sortable header and calls handleSort for each column (and DebugPage sort test)
3 (map) Change {searchOpen && searchQuery.trim() && ( to {searchOpen && ( app/src/sections/map/MapSearchPill.tsx red yes MapSearchPill > renders the search input and hides the dropdown when closed or when query is blank (and Escape test)
3 (map) Change [cat]: v as number to [Object.keys(w).at(-1) ?? cat]: v as number app/src/sections/map/MapWeights.tsx red yes MapWeights > calls setWeights, setMinSim, and resetWeights when controls are used (sliders[0] aria-valuenow)
3 (map) Change onNodeHover={onNodeHover} to onNodeHover={n => onNodeHover(n)} app/src/sections/map/MapCanvas.tsx green yes (green) All 37 map tests stay green

Reported finding (WcagContrastSection.tsx:71,85 in jsdom)

WcagContrastSection.tsx:85 sets border: affected ? '1px solid var(--ink)' : '1px solid var(--rule)' via Emotion sx, which injects a stylesheet rule using the CSS border shorthand containing var(...). In jsdom 29 (jsdom/lib/jsdom/living/css/helpers/computed-style.js:184-187), handleProperty does not expand pending shorthands that contain CSS custom properties (// TODO: Substitute variables before computing a pending shorthand's longhands → returns "unset" for the longhands), so getComputedStyle(swatch).border resolves to "0px none rgb(0, 0, 0)" for both var(--ink) and var(--rule), and toHaveStyle({ border: '1px solid var(--ink)' }) fails with an empty received border. Per the Round 1 instructions ("If jsdom drops var(...) from the computed border, report it instead of asserting on class names" and "Components stay unchanged"), we removed the redundant dark-theme text assertion in WcagContrastSection.test.tsx without asserting on Emotion class names or modifying WcagContrastSection.tsx.

- WcagContrastSection: test the per-theme swatch stroke through the declared
  stylesheet rule (jsdom drops var() borders from the computed style) and
  restore the hex and name cell checks the round-1 change removed.
- DebugPage: give heatmap-annotated one library below 90 with an average of
  91, so the low-score filter test fails if the filter looks at the average.
- SpecMatrix: assert the missing-library select keeps its value on rerender.
- PalettePage: drop the dark-theme ref that no test asserted on.
- Changelog fragment: name the page-level tests; add the PR number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MarkusNeusinger
MarkusNeusinger marked this pull request as ready for review October 30, 2026 10:27
Copilot AI lite review requested due to automatic review settings October 30, 2026 10:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active issues remain, and the change provides broad co-located coverage for section rendering, interactions, state transitions, and page behavior.

No open issues 🎉

What changed in this PR

Adds comprehensive co-located Vitest unit, render, and interaction tests for the palette, debug, and map sections extracted from their page components. It also adds page-level coverage for stateful behavior remaining in PalettePage and DebugPage.

Changes:

  • Added palette helper, component, canvas, theme, sorting, copying, and snippet tests.
  • Added debug formatting, rendering, sorting, filtering, feedback, and API interaction tests.
  • Added map controls, keyboard interaction, canvas, and ForceGraph integration tests.
  • Added page-level tests for palette and debug state transitions.
  • Added the required changelog fragment.
File Description
changelog.d/​section-tests.md Documents the new section and page-level test coverage. Nit: Remove the hard-coded (#12470) PR reference from the fragment.
app/​src/​sections/​palette/​WcagContrastSection.test.tsx Tests WCAG ratios, accessible row names, and theme-specific swatch strokes.
app/​src/​sections/​palette/​SemanticAnchorsSection.test.tsx Tests semantic anchor rendering and copied-swatch feedback.
app/​src/​sections/​palette/​paletteHelpers.test.ts Tests palette constants and color conversion helpers.
app/​src/​sections/​palette/​PaletteGridSection.test.tsx Tests palette sorting, trade-off content, and swatch copying.
app/​src/​sections/​palette/​LineageSection.test.tsx Tests palette history entries and reference links.
app/​src/​sections/​palette/​HeroSection.test.tsx Tests wheel expansion and comparison palette selection.
app/​src/​sections/​palette/​DeltaESection.test.tsx Tests both ΔE matrices and threshold legend content.
app/​src/​sections/​palette/​DeltaEMatrix.test.tsx Tests matrix rendering, values, and CVD breakdown tooltips.
app/​src/​sections/​palette/​CopySnippetsSection.test.tsx Tests language tabs and OKLCH snippet output.
app/​src/​sections/​palette/​ContinuousCmapsSection.test.tsx Tests sequential and diverging continuous colormap content.
app/​src/​sections/​palette/​CodeBlock.test.tsx Tests code rendering, clipboard behavior, and copied-state reset.
app/​src/​sections/​palette/​ChromaWheel.test.tsx Tests wheel rendering, canvas painting, overlays, labels, and unavailable canvas contexts.
app/​src/​sections/​map/​MapWeights.test.tsx Tests weight-panel expansion, sliders, threshold updates, and reset behavior.
app/​src/​sections/​map/​MapSearchPill.test.tsx Tests search results, focus and blur behavior, keyboard navigation, selection, and empty states.
app/​src/​sections/​map/​MapLegend.test.tsx Tests legend visibility, entries, counts, and hover callbacks.
app/​src/​sections/​map/​MapHoverPanel.test.tsx Tests hovered and pinned panels, previews, tags, and dark-theme handling.
app/​src/​sections/​map/​MapCanvas.test.tsx Tests readiness gates, progress UI, ForceGraph2D props, callbacks, framing, force wiring, and link styling.
app/​src/​sections/​debug/​SpecMatrix.test.tsx Tests headers, sorting indicators, implementation display, and filter controls.
app/​src/​sections/​debug/​FeedbackSection.test.tsx Tests feedback lists, prompt copying, status controls, and empty states. Moderate: Install and restore a clipboard mock before spying on navigator.clipboard.writeText, since jsdom does not provide it by default.
app/​src/​sections/​debug/​DebugOverviewSections.test.tsx Tests health, KPI, activity, library coverage, recent activity, weaknesses, and problem areas.
app/​src/​sections/​debug/​debugFormat.test.ts Tests debug formatting, score colors, latency colors, and implementation counts.
app/​src/​pages/​PalettePage.test.tsx Tests page-owned palette state, copy feedback timing, sorting, wheel controls, tabs, OKLCH switching, and analytics.
app/​src/​pages/​DebugPage.test.tsx Tests page-owned debug sorting, filters, feedback status updates, authentication headers, rollback, and refetching.

Review effort: Lite

@MarkusNeusinger
MarkusNeusinger merged commit 53f6bc2 into main Oct 30, 2026
17 checks passed
@MarkusNeusinger
MarkusNeusinger deleted the gemini/section-tests branch October 30, 2026 11:42
MarkusNeusinger added a commit that referenced this pull request Oct 30, 2026
…PR comments (#12474)

## Summary

Lessons from the first parallel Gemini task (#12470, brief
`section-tests`) folded into the `gemini-task` skill:

- **Test briefs name where the behavior lives and carry a mutation
table.** The section-tests briefs allowed new files only under
`app/src/sections/`, while the state those sections show lives in
`app/src/pages/*.tsx`. Gemini copied the page logic into test harnesses
(`StatefulPaletteGrid`, `StatefulSpecMatrix`, and others), every gate
passed, and deliberate breaks in the real pages went unnoticed. A test
brief now lets the package add tests next to the real code, and its
Acceptance lists, per behavior, one break that must turn a test red and
one harmless refactor that must stay green. Gemini reverts each break
with `git restore` (its agy rules deny `git checkout`).
- **The review subagent runs the mutation table** and adds breaks of its
own (section 3, step 3). That is how the review of #12470 found the
copied logic in two of three packages.
- **Review rounds go back as one PR comment with a section per package**
(section 3, step 4), and Gemini works them like the first round:
packages branch from `origin/gemini/<slug>` as `gemini/<slug>-r<n>-<k>`,
merge back with `--no-ff`, path checks diff against the task branch, and
Gemini comments with the merge commits and the mutation results (section
2, step 1). The PR stays a draft until the last round, because marking
it ready starts its one Copilot review. Round 1 of #12470 ran this way:
three subagents in parallel, 13 of 14 mutations caught, and Claude fixed
the last gap.
- **Cleanup asks before deleting package branches.** `CLAUDE.md` lets
only the PR's own worktree and local branch go without asking, and the
skill said to delete package branches without that check. It now checks
that each branch is part of the merged head, and asks with the branch
names in the question. Today a general OK to the cleanup plan did not
get past the permission classifier.

## Test plan

- [x] `uv run python -m tools.changelog check --base origin/main`: the
fragment is well-formed and the diff passes.
- [x] `git` commands quoted in the skill exist in the agy rules: `git
restore` is allowed and `git checkout` is denied
(`agy-permissions.json`), and the push regex covers
`gemini/<slug>-r<n>-<k>`.
- [ ] No automated check covers skill prose; the next Gemini test task
is the real test.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request Oct 30, 2026
…gent (#12476)

## Summary

The owner's call after #12470: code quality comes before saving tokens,
so Gemini reviews its own finished work before Claude sees it.

- **New section 2, step 7 in the `gemini-task` skill.** Once a task (a
single brief or one in packages) or a review round is done and the gates
pass, Gemini pushes and starts one fresh subagent as the reviewer, in
its own worktree on a detached checkout of the task branch. The
reviewer's prompt names only the brief's Constraints and Acceptance and
the diff, not Gemini's plan or notes, so it judges the code rather than
the intent. It reads the whole diff for defects, runs the mutation table
plus two or three breaks of its own, and reports findings with
`path:line`, severity, and a fix. It commits and pushes nothing. Gemini
fixes what holds up, reruns the gates, and pushes. A hand-over on a
blocker skips the step.
- **`## Self-review` in the hand-over PR body** lists every finding as
fixed (with its commit) or declined (with the reason), or as not run
after a blocker hand-over. Review-round comments carry the findings too.
- **Claude's review stays independent** (section 3, step 3). A reviewer
of the same model shares the author's blind spots: in #12470 the copied
page logic passed Gemini's own gates. So the Opus subagent never sees
the self-review, and afterwards Claude judges each declined finding.
This matches the existing `/zweitmeinung` rule that a Gemini review
never counts as the independent review of Gemini's work.
- The hand-over moves from step 7 to step 8, and all references follow.

## Test plan

- [x] `uv run python -m tools.changelog check --base origin/main`: the
fragment is well-formed and the diff passes.
- [x] `grep` for `section 2, step` and `step 7` across the skills and
docs: every reference names the new numbers.
- [ ] No automated check covers skill prose; the next Gemini task is the
real test.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gemini Task for Gemini in the owner's interactive agy session (gemini-task skill)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants