From b85aab6eb0ec6f470c3b85d3541dfa1cb9ba20cb Mon Sep 17 00:00:00 2001 From: amosblomqvist Date: Wed, 30 Sep 2026 00:23:15 +0300 Subject: [PATCH] chain table round 4: double the indentation (owner refinement) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each staircase step is now TWO columns (36px): chains pl-12 — the checkbox aligned with the cluster's FOLDER icon — and statements pl-21 — the checkbox where a chain icon WOULD sit (the chains lost theirs; the pattern continues anyway). Live-measured: chain checkbox = folder icon (336=336); statement checkbox = chain checkbox + 36 (372). --- components/chain-table.tsx | 24 +++++++++++++----------- docs/architecture.md | 2 +- next-env.d.ts | 4 ++-- tests/render-chain-table.test.tsx | 18 +++++++++--------- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/components/chain-table.tsx b/components/chain-table.tsx index 0a733a2..0ce7128 100644 --- a/components/chain-table.tsx +++ b/components/chain-table.tsx @@ -33,12 +33,14 @@ * conclusion marker down there). * * THE ALIGNMENT STAIRCASE (owner ruling, round 3): every level's - * checkbox sits DIRECTLY UNDER its parent's expander arrow — open a - * folder and the chain checkboxes line up with the arrow, same one - * level down. Geometry: the expander shares the checkbox's 14px width - * (h-5 w-3.5), so each nesting step is exactly checkbox(14) + gap(4) = - * 18px — clusters pl-3 (12px, checkbox at 12), chains pl-7.5 (30px = - * the cluster expander), statements pl-12 (48px = the chain expander). + * checkbox sits ONE COLUMN under its parent's expander — aligned with + * the parent's ICON slot: open a folder and the chain checkboxes line + * up with the FOLDER icon; the statements step one more unit out (the + * would-be chain-icon column — the chains lost their icons but the + * pattern continues, owner: "double the indentation"). Geometry: every + * column is 14px + 4px gap = 18px and each step is TWO columns (36px) — + * clusters pl-3 (12px), chains pl-12 (48px = the cluster's folder + * icon), statements pl-21 (84px = where a chain icon WOULD sit). * * SORTING: all three columns sortable (owner ruling — "consistency is * key"): Name (A→Z), Props (largest-first), Created (newest-first); @@ -265,9 +267,9 @@ export function ChainTableBody({ {/* The list: NO per-row lines (owner ruling) — the ONLY horizontal separators sit between cluster groups; the ALIGNMENT STAIRCASE - carries the hierarchy (owner round 3: each level's checkbox sits - under its parent's expander arrow — clusters pl-3, chains pl-7.5 - under the folder arrow, statements pl-12 under the chain arrow). + carries the hierarchy (owner round 4, "double the indentation": + two-column steps — chains pl-12 aligned with the folder icon, + statements pl-21 where a chain icon would sit). All three columns sort, small chevron on active. */}
@@ -355,7 +357,7 @@ export function ChainTableBody({
[id, { name: n.name }])))} - className="flex items-center gap-1 py-1 pl-7.5 pr-3" + className="flex items-center gap-1 py-1 pl-12 pr-3" > 0} @@ -402,7 +404,7 @@ export function ChainTableBody({
120s stale (a live run's 30s FILE watchdog keeps the heartbeat fresh even across dev's per-worker processes — the registry is not shared between them; the guard trio closed a live-stamping bug from the first live test) — the job runner's crash-honesty at set grain. Store writes serialize behind a per-set mutex; the pipeline is the sole writer of `data/practice`. -**API + UI.** Creation is GRAPH-DOMAIN, not chat-domain (owner ruling 2026-09-29: "problem sets always take in nodes — graph objects"): `POST /api/practice/sets` takes `{nodeIds, params, title?, note?}` ONLY — no chatId, no clusterId; the UI resolves a picked cluster to its member node ids and passes the cluster's label as the title. The `note` is the learner's optional free-form creation input (owner, same evening: more learner control, no other purpose): trimmed, capped at 2000 characters, persisted on the set, and carried to the partitioner and every generator as `THE LEARNER'S NOTE` — an additional input the agents WEIGH but are never obliged to follow (the doctrine pins the stance: it never moves a problem off its rung or out of its band, and the partition laws always win). `GET /api/graph/clusters` serves the picker: the birth-session partition itself (`birthSessionPartition`, same ordering — size desc, ties by id) — EVERY group regardless of size (owner ruling, same evening: MIN_CLUSTER_SIZE is a hull-drawing rule; practice availability must not inherit a visual threshold), each as `{key, kind: "chat"|"notes", label, nodeIds, nodeCount}`; a cluster whose naming record is gone keeps `label: null` — honest, never guessed. 202 `{id}` with generation fire-and-forget; 400 empty selection (empty intersection with live nodes — the vanished-nodes edge) / bad params; 503 model/key refusal. `GET /api/practice/sets` · `GET/DELETE /api/practice/sets/[id]` (delete aborts live runs first) · `POST /api/practice/sets/[id]/answers` (202; 409 when not ready; 400 re-answering a submitted problem — one attempt). Surfaces: `/practice` (the list — THE shared **TopBar** (`components/top-bar.tsx`, standardization ruling 2026-09-29: one element, every surface — h-15 matching the sidebar's wordmark band, `lh-drag` with `lh-no-drag` carve-outs on interactive children, `select-none` so drags never highlight, the SidebarToggle reopen with its Electron traffic-light gutter, `px-3` + one border line; chat's rename mode passes `drag={false}` — the one escape hatch) with the New action at the right edge, status/counts rows, delete confirm, the new-set dialog with the named-level controls (a 5-detent ladder slider, a 3-detent cue slider, a Theory|Application toggle — each showing the level's name + definition), the cluster picker, and the optional free-form note textarea (typing-only). The SECOND creation entry (owner redesign, same evening): the graph view's **chain table** — a `Chains` button in the graph chrome (empty graph: hidden) opens a FULL-BLEED table overlay (absolute inside the graph area; the blur + translucent background IS the surface, no inner card; the top bar is THE shared TopBar — the standard title size and drag mechanics, the sidebar reopen, and the close X at the right edge, so home's window-drag mask never eats it (the overlay covers home's floating header while open, and the TopBar's own toggle is the reopen path); Esc or X dismiss). The hierarchy is three levels, one click each: CLUSTER (the birth session — a chat or a notes file; a chain lives where its CONCLUSION was born) → REASONING CHAIN → PROPOSITION STATEMENTS — expanding a chain reveals its members' statement text directly (names live in the chain line, never twice; a singleton reveals its one statement in a single click); everything starts collapsed. The table look (owner refinements, evening round 2): PROMINENT indentation carries the hierarchy (chains deep under their folder, statements deepest) and the ONLY lines left are the horizontal separators between cluster groups — per-row lines and the vertical column hairlines are gone ("anything else is better without them"); statement rows are PLAIN (the derivation already lives in the chain line — no conclusion marker). ALL THREE columns sort (owner: "consistency is key") — Name (A→Z default), Props (largest-first), Created (newest-first) — click toggles asc/desc, ordering both cluster rows and chains within; a SMALL chevron marks the active header. In the chain line every proposition name is a faint-outlined CHIP with the → a clearly separated connector between chips — a lonely chip reads as a standalone proposition (the "(no premises)" annotation is dead in the visible UI; it survives only as the row's aria-label); icons: ONLY the cluster FolderIcon (open-folder when expanded) survives (owner ruling, evening round 3: the chain waypoints icons are not needed — the chips + connectors already read as chains); chevrons rotate with expansion and share the checkbox's 14px width, which makes the ALIGNMENT STAIRCASE exact (owner ruling, same round): every level's checkbox sits directly under its parent's expander arrow — open a folder and the chain checkboxes line up with the arrow, same one level down; 18px steps (clusters pl-3 → chains pl-7.5 → statements pl-12). Selection is checkboxes on whole clusters, chains, or single propositions — a flat node-id set (a node in several chains is one selection) — accumulated in a FLOATING BOTTOM BAR (the Google-Photos pattern: appears only when something is selected, never shifts the table layout) showing the agents' ACTUAL input (`chainsForSelection` over the selection: a premise picked without its conclusion counts as the singleton it becomes) as `N propositions → M chains`, with Clear and **Generate problem set** (the one action for now — the bar is a neutral action slot), which opens the SAME creation dialog in **custom mode**: no cluster picker — a selection summary (the derived shape + the dated default title the pipeline will stamp) — posting `{nodeIds, params, note}` with no title. A fuzzy search (names, aliases, statements) filters chains grouped + auto-expanded under their clusters (non-matching clusters hide; the collapsed view returns on clear). NO understanding-levels column (explicitly not built). The overlay is read-only toward the graph and `/practice/[id]` (the exam page — the shared TopBar, a levels + status line, numbered problems, markdown statements with math healing at the display seam, a textarea + focus-gated dictation bar per problem with its OWN Submit button — one attempt per problem, instant solution/rubric reveal, the score landing live). Entry: the sidebar's Practice nav row. +**API + UI.** Creation is GRAPH-DOMAIN, not chat-domain (owner ruling 2026-09-29: "problem sets always take in nodes — graph objects"): `POST /api/practice/sets` takes `{nodeIds, params, title?, note?}` ONLY — no chatId, no clusterId; the UI resolves a picked cluster to its member node ids and passes the cluster's label as the title. The `note` is the learner's optional free-form creation input (owner, same evening: more learner control, no other purpose): trimmed, capped at 2000 characters, persisted on the set, and carried to the partitioner and every generator as `THE LEARNER'S NOTE` — an additional input the agents WEIGH but are never obliged to follow (the doctrine pins the stance: it never moves a problem off its rung or out of its band, and the partition laws always win). `GET /api/graph/clusters` serves the picker: the birth-session partition itself (`birthSessionPartition`, same ordering — size desc, ties by id) — EVERY group regardless of size (owner ruling, same evening: MIN_CLUSTER_SIZE is a hull-drawing rule; practice availability must not inherit a visual threshold), each as `{key, kind: "chat"|"notes", label, nodeIds, nodeCount}`; a cluster whose naming record is gone keeps `label: null` — honest, never guessed. 202 `{id}` with generation fire-and-forget; 400 empty selection (empty intersection with live nodes — the vanished-nodes edge) / bad params; 503 model/key refusal. `GET /api/practice/sets` · `GET/DELETE /api/practice/sets/[id]` (delete aborts live runs first) · `POST /api/practice/sets/[id]/answers` (202; 409 when not ready; 400 re-answering a submitted problem — one attempt). Surfaces: `/practice` (the list — THE shared **TopBar** (`components/top-bar.tsx`, standardization ruling 2026-09-29: one element, every surface — h-15 matching the sidebar's wordmark band, `lh-drag` with `lh-no-drag` carve-outs on interactive children, `select-none` so drags never highlight, the SidebarToggle reopen with its Electron traffic-light gutter, `px-3` + one border line; chat's rename mode passes `drag={false}` — the one escape hatch) with the New action at the right edge, status/counts rows, delete confirm, the new-set dialog with the named-level controls (a 5-detent ladder slider, a 3-detent cue slider, a Theory|Application toggle — each showing the level's name + definition), the cluster picker, and the optional free-form note textarea (typing-only). The SECOND creation entry (owner redesign, same evening): the graph view's **chain table** — a `Chains` button in the graph chrome (empty graph: hidden) opens a FULL-BLEED table overlay (absolute inside the graph area; the blur + translucent background IS the surface, no inner card; the top bar is THE shared TopBar — the standard title size and drag mechanics, the sidebar reopen, and the close X at the right edge, so home's window-drag mask never eats it (the overlay covers home's floating header while open, and the TopBar's own toggle is the reopen path); Esc or X dismiss). The hierarchy is three levels, one click each: CLUSTER (the birth session — a chat or a notes file; a chain lives where its CONCLUSION was born) → REASONING CHAIN → PROPOSITION STATEMENTS — expanding a chain reveals its members' statement text directly (names live in the chain line, never twice; a singleton reveals its one statement in a single click); everything starts collapsed. The table look (owner refinements, evening round 2): PROMINENT indentation carries the hierarchy (chains deep under their folder, statements deepest) and the ONLY lines left are the horizontal separators between cluster groups — per-row lines and the vertical column hairlines are gone ("anything else is better without them"); statement rows are PLAIN (the derivation already lives in the chain line — no conclusion marker). ALL THREE columns sort (owner: "consistency is key") — Name (A→Z default), Props (largest-first), Created (newest-first) — click toggles asc/desc, ordering both cluster rows and chains within; a SMALL chevron marks the active header. In the chain line every proposition name is a faint-outlined CHIP with the → a clearly separated connector between chips — a lonely chip reads as a standalone proposition (the "(no premises)" annotation is dead in the visible UI; it survives only as the row's aria-label); icons: ONLY the cluster FolderIcon (open-folder when expanded) survives (owner ruling, evening round 3: the chain waypoints icons are not needed — the chips + connectors already read as chains); chevrons rotate with expansion and share the checkbox's 14px width, which makes every column exactly 18px and the ALIGNMENT STAIRCASE exact (owner rulings, rounds 3–4): each level's checkbox sits under its parent's ICON slot — open a folder and the chain checkboxes line up with the FOLDER icon; the statements step to where a chain icon WOULD sit (the steps are TWO columns = 36px — "double the indentation"): clusters pl-3 → chains pl-12 → statements pl-21. Selection is checkboxes on whole clusters, chains, or single propositions — a flat node-id set (a node in several chains is one selection) — accumulated in a FLOATING BOTTOM BAR (the Google-Photos pattern: appears only when something is selected, never shifts the table layout) showing the agents' ACTUAL input (`chainsForSelection` over the selection: a premise picked without its conclusion counts as the singleton it becomes) as `N propositions → M chains`, with Clear and **Generate problem set** (the one action for now — the bar is a neutral action slot), which opens the SAME creation dialog in **custom mode**: no cluster picker — a selection summary (the derived shape + the dated default title the pipeline will stamp) — posting `{nodeIds, params, note}` with no title. A fuzzy search (names, aliases, statements) filters chains grouped + auto-expanded under their clusters (non-matching clusters hide; the collapsed view returns on clear). NO understanding-levels column (explicitly not built). The overlay is read-only toward the graph and `/practice/[id]` (the exam page — the shared TopBar, a levels + status line, numbered problems, markdown statements with math healing at the display seam, a textarea + focus-gated dictation bar per problem with its OWN Submit button — one attempt per problem, instant solution/rubric reveal, the score landing live). Entry: the sidebar's Practice nav row. **Model wiring.** Three roles — `partitioner`, `problemGenerator`, `grader` — all pinned to the **strong** slot (quality-critical, one settings edit from cheap) and in `BACKGROUND_MODEL_ROLES` (per-role overrides). The stage agents are single-submit loops on the shared `runSubmitAgent` seam (`submit-agent.ts`, extracted from extraction for exactly this): a `validate` hook turns a bad submit into a tool-input error the model retries against, within `maxTurns 4`; exhaustion fails the stage honestly (the set says so, the chains re-run cleanly on a new set). diff --git a/next-env.d.ts b/next-env.d.ts index a419cbe..ce4e94a 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,7 +1,7 @@ /// /// -import "./.next/dev/types/routes.d.ts"; -import "./.next/dev/types/root-params.d.ts"; +import "./.next/types/routes.d.ts"; +import "./.next/types/root-params.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/tests/render-chain-table.test.tsx b/tests/render-chain-table.test.tsx index 1e129ec..1f2874c 100644 --- a/tests/render-chain-table.test.tsx +++ b/tests/render-chain-table.test.tsx @@ -139,9 +139,9 @@ describe("ChainTableBody — THE shared TopBar + the lines law (owner, round 2)" const sep = html.split("border-b border-border/60").length - 1; assert.ok(sep >= 2, "separators between groups"); // chain rows themselves carry NO border — their entire class list is - // the staircase law (pl-7.5 = under the cluster's expander arrow); + // the staircase law (pl-12 = aligned with the cluster's FOLDER icon); // the chip outlines' border-border/70 is an outline, not a row line - assert.ok(html.includes('class="flex items-center gap-1 py-1 pl-7.5 pr-3"'), "the chain row: staircase indent, no border"); + assert.ok(html.includes('class="flex items-center gap-1 py-1 pl-12 pr-3"'), "the chain row: staircase indent, no border"); }); }); @@ -186,13 +186,13 @@ describe("ChainTableBody — chain rows (chips + connectors)", () => { // owner ruling: the derivation is already encoded in the chain line — // no conclusion marker down here assert.ok(!html.includes("prop-conclusion"), "no conclusion marker anywhere"); - // THE ALIGNMENT STAIRCASE (owner ruling, round 3): each level's - // checkbox sits under its parent's expander arrow. The expander - // shares the checkbox's 14px width, so each step is 18px: clusters - // pl-3 (12) → chains pl-7.5 (30 = the cluster expander) → - // statements pl-12 (48 = the chain expander) - assert.ok(html.includes("pl-12"), "statements under the chain's expander"); - assert.ok(html.includes("pl-7.5"), "chains under the cluster's expander"); + // THE ALIGNMENT STAIRCASE (owner ruling, round 4 — "double the + // indentation"): each level's checkbox sits under its parent's ICON + // slot, TWO columns (36px) past its own. Every column is 18px: + // clusters pl-3 (12) → chains pl-12 (48 = the folder icon) → + // statements pl-21 (84 = where a chain icon WOULD sit) + assert.ok(html.includes("pl-21"), "statements at the would-be chain-icon column"); + assert.ok(html.includes("pl-12"), "chains aligned with the folder icon"); assert.ok(html.includes("h-5 w-3.5"), "the expander shares the checkbox's 14px width"); }); });