Skip to content

Fix Flowchart QA issues (T9: FC-01–06, PF-03) - #206

Merged
Programcode merged 1 commit into
mainfrom
fix/t9-flowchart
Oct 23, 2026
Merged

Programcode merged 1 commit into
mainfrom
fix/t9-flowchart

Conversation

@Programcode

@Programcode Programcode commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Fixes the Flowchart issues from the 22 Oct 2026 QA report (batch T9). Most changes are in src/pages/FlowchartPage.tsx and a new src/lib/flowchart.ts, which Generate's "Import to Flowchart" now uses as well. Rebased on main at 508f243 (#205). There are no migrations and no Edge Function changes.

Issues

FC-01: dark mode, phone layout, attribution

  • In both themes, the canvas controls, minimap, minimap mask, attribution and edge labels now use the theme tokens (--card, --border, --accent, --muted-foreground), via src/index.css. In dark mode the control background was #fefefe and is now rgb(29,40,57).
  • Below sm (640px), the minimap is hidden. The controls move out of the canvas into a row of four 44px buttons underneath it, so they no longer sit over nodes.
  • On phones the toolbar buttons (Load, Save, Auto layout, Export, Build from case) show only their icons. Their text stays as their accessible name. At 375px the toolbar now takes 2 rows in English (was 3) and 3 in Hungarian (was 5). Add Node keeps its text as the main action.
  • The project has no Pro licence, so the React Flow attribution stays. It moves to the top right, which is clear of the controls and the minimap, and the fit padding leaves room for it. Its link name is localized.
  • The node, legend and stats colours are now shades dark enough for white text, and the type and strength badges are outlined. axe colour-contrast on /app/flowchart went from 18 (dark) and 23 (light) failures to 0.

FC-02: edge names

  • The localized "From to " edge names were already on main from Fix the 11 bugs from today's QA pass (H1, M1–M5, L1–L5) #184. On a main build, Hungarian read "Kapcsolat: A tézised → 1. érv", so the QA builds were older than that fix.
  • This PR adds localized edge labels: Support, Attack and Rebuttal are now stored as a kind, and charts saved with the old English labels map back to that kind. It also localizes the attribution link name. React Flow 11 has no ariaLabelConfig, which arrived in v12, so the node and edge keyboard help is still replaced through the LocalizedA11yDescriptions added in Fix the 11 bugs from today's QA pass (H1, M1–M5, L1–L5) #184.

PF-03: heap growth on navigation laps

I could not reproduce a Flowchart leak. React Flow 11 injects no styles at runtime, and reactflow/dist/style.css is imported once at module level. CSS rules and <style> elements stay flat across laps.

Setup: a local production build (vite preview) with a stubbed session, Chromium driven over CDP, and GC forced twice before each reading. Each lap goes from /app to /app/flowchart and back.

Run Heap (MB) DOM nodes JS listeners Documents CSS rules <style>
main, Flowchart, Jam allowed, 10 laps 7.47 to 10.46 356 to 394 330 to 383 1 to 2 1422 to 1422 2 to 2
main, Flowchart, Jam blocked, 10 laps 7.47 to 8.57 356 to 358 330 to 330 1 to 1 1422 to 1422 2 to 2
main, History, Jam allowed, 12 laps 7.70 to 10.20 363 to 401 330 to 381 1 to 2 n/a n/a
this PR, Flowchart, Jam allowed, 10 laps 7.69 to 10.68 391 to 429 329 to 380 1 to 2 1447 to 1447 2 to 2
this PR, Flowchart, Jam blocked, 10 laps 7.69 to 8.80 391 to 393 329 to 329 1 to 1 1447 to 1447 2 to 2
this PR, Flowchart, Jam blocked, 30 laps 7.69 to 9.13 391 to 393 329 to 329 1 to 1 1447 to 1447 2 to 2
this PR, History, Jam blocked, 10 laps 7.69 to 8.52 391 to 404 329 to 329 1 to 1 1447 to 1492 2 to 3
this PR, Flowchart, after Jam loaded, 10 more laps 10.34 to 10.80 429 to 429 380 to 380 2 to 2 1447 to 1447 2 to 2
  • The step change (3 MB more heap, one more document, 51 more listeners) happens once, on any route, when the delayed Jam recorder iframe (recorder.jam.dev/capture, delayed by Delay Jam and align cache, CSP, and robots #196) loads part-way through the run. After that the Flowchart adds about 0.05 MB per lap, the same as History.
  • A heap snapshot has exactly 4 ResizeObserver objects before and after the laps. The new canvas ResizeObserver is disconnected on unmount and when a chart is replaced.
  • The StyleRule growth in the report (1,842 to 3,349) matches a second document's stylesheets (the Jam iframe), not rules added to ours.

C3 (#199) left the forced reflows on this page for this batch. Adding a node now centres it from the viewport and the size the ResizeObserver reports, instead of getBoundingClientRect, and Export uses the observed size instead of offsetWidth/offsetHeight.

FC-03: starter text in the creation language

  • Starter nodes now store a translation key (labelKey, labelNumber, descriptionKey) next to the text in the language they were made in. They are shown in the current language, so switching language updates them on the canvas.
  • Nodes the student writes are marked labelKey: null and are never translated, even if they read "Argument 2".
  • A key only counts while the stored text still matches that key's text in some language. If an older client edits the text and keeps the key, the edit wins.
  • Charts saved before this change have no keys. For those, exact starter strings in EN, ES or HU, and the old English Generate titles ("Thesis", "Argument N", "Attack N", "Rebuttal N"), are shown translated. Saved data is never rewritten.
  • Only a fixed list of keys can be translated, so a saved chart cannot name arbitrary catalog text.

FC-04: build from the current case, Save placeholder

  • A "Build from current case" button appears when Generate holds a case from the same debate as the Flowchart (or one-off work when no debate is chosen). It is hidden in Workspace context.
  • The build maps the thesis, each contention (claim plus evidence), the predicted opposing arguments, and the counters. Each counter attaches to the attack it answers: exact match first, then two or more shared words, then the attack in the same position. Each tier wraps at four nodes, so a five-contention, eight-attack case fits a phone.
  • The toast offers Undo. The blank-start template is unchanged.
  • The Save dialog placeholder is now "Flowchart title..." (ES "Título del diagrama...", HU "A folyamatábra címe...").
  • Debate isolation:
    • The Flowchart remembers which debate a loaded, imported or built chart belongs to. Switching debates puts the template back and says so in a toast.
    • Generate's Import to Flowchart refuses a case from a different debate.
    • Undo on Generate restores an autosaved snapshot whose debate is unknown. Such a case is not offered to the Flowchart, and regenerating a section after Undo no longer overwrites the replaced case's saved row. That last bug was already on main; it was fixed here because this PR changes handleUndo.

FC-05: empty label or title did nothing

  • An empty node label shows "Add a label." / "Añade una etiqueta." / "Adj meg egy címkét." under the field, with aria-invalid, aria-describedby and focus on the field.
  • An empty save title shows "Add a title." in the same way. The Hungarian matches the Library's existing message.

FC-06: right-most node clipped at 375px

  • Each new chart (loaded, imported, built, or the template) gets a fresh canvas, which fits once its nodes have sizes, with padding of 0.2.
  • A ResizeObserver refits when the canvas width changes, but not its height, so a phone's address bar can't trigger it.
  • minZoom is 0.2 so a large built case can fit.
  • No node is clipped at 375px on first load, after shrinking a 1280px window to 375px, after rotating a phone to landscape and back, with a 15-node built case in Hungarian, or after adding a node.

Verification

npm run verify (local, 23 Oct, on the tree of head b3c60e6)

GitHub Actions minutes are used up, so this is a local run, not CI.

  • docs check, lint (eslint + knip + repo rules), typecheck: pass
  • unit: 76 files, 542 tests passed
  • collaboration: 5 files, 30 tests passed
  • edge (Deno): 419 passed, 0 failed
  • build with prerender, sitemap and check-site ("48 HTML files, sitemap and llms.txt resolve; no problems"): pass
  • test:sw: all offline checks true
  • e2e smoke (desktop Chromium): 43 passed

New and updated tests

  • src/test/lib/flowchart.test.ts:
    • starter text follows the language;
    • edits keep app text only when unchanged;
    • student text, including text an old client edited, is never translated;
    • old saved charts and forged keys;
    • localized edge labels, including old English labels;
    • case-to-flowchart mapping with counters attached to the right attack (exact, shared words, position);
    • large cases stay at most four nodes wide with no overlaps.
  • src/test/pages/FlowchartPage.test.tsx:
    • starter nodes re-render in Hungarian after a language switch;
    • inline validation messages for an empty label and an empty title;
    • switching debates after Build, or after an import from Generate, puts the template back.
  • src/test/pages/GeneratePage.test.tsx:
    • Import to Flowchart only takes a case from its own debate, and not after Undo;
    • a regeneration after Undo does not update the replaced case's saved row.
  • The debate-isolation and Undo tests were each checked to fail with the fix removed.

In the browser (local production build, stubbed session)

Screenshots are on the pr-assets/t9-flowchart branch, which is not for merging; delete it after merge.

Dark mode canvas, before and after:

before: dark desktop canvas after: dark desktop canvas

375px, dark HU and light EN, before and after:

before: dark 375 HU after: dark 375 HU before: light 375 EN after: light 375 EN

After, more views: dark desktop HU, light desktop EN, dark 375 EN, light 375 HU:

dark desktop HU light desktop EN

dark 375 EN light 375 HU

Built from a case with five contentions and eight attacks (desktop), the same at 375px in Hungarian, the save validation, and a 1280px window shrunk to 375px:

built from case, desktop built from case, 375 HU

save validation resized to 375

verify-debatelab skill

  • New recipe gen-flowchart in recipes/case-generation.spec.ts: build from the debate's case, no node clipped, the edge name, then switch the interface to Hungarian and check that the app-written titles change while the case text stays the same.

  • Updated features/case-generation.md and the features/README.md index.

  • Not run yet. Another worktree (T8) has held the shared Supabase lock all night, and the batch rules say to wait rather than take it. I will drive recipes/case-generation (including gen-flowchart) and recipes/account (flow-edit) on desktop-chromium and mobile-chromium as soon as the lock is free, and add the results here.

Left open

  • agentic/replay/495abc…json still targets the Save textbox by its old name "Node title...". The name has been "Title" since Name controls, headings, and the toast region (AX-01, AX-05, AX-06, AX-08, AX-09) #200. On replay this falls back to a model call rather than failing. It should be re-recorded on the next agentic run.
  • Old charts (saved before this PR) whose student-written node is titled exactly "Thesis" or "Argument N" show that title translated. New charts mark student text, so this cannot happen there.
  • Template edits made before switching debates stay on the canvas, as on main. Only loaded, imported or built charts reset.
  • Real-browser checks I could not run: Safari/WebKit rendering of the themed controls, a real phone rotation, and a screen reader reading the edge names.

Review rounds

Codex is rate-limited tonight, so each round was one fresh-context Opus subagent reading the full diff against main.

  1. One High: the recipe used the wrong language button name. Two Mediums: counters attached to the wrong node and stacked, and PF-03 had no measurement. Lows covered old charts, a restored case with no debate, recipe polling, role=toolbar, duplicate titles, Hungarian wording and test overlap. All fixed.
  2. Four Mediums, all fixed. Large cases didn't fit a phone (now rows of four and minZoom 0.2). The old-chart heuristic could translate new student text (now null keys). Counter matching was weak. A built case could be saved into another debate (now reset on switch). Lows on toast dismissal and evidence line breaks were fixed.
  3. Two Mediums, both fixed. An old client's edit could hide behind starter text (now keptAppText), and charts imported from Generate skipped the debate guard (now every chart records its debate). Lows on Undo scope, stale Undo toasts, the reset toast and filler-word matching were fixed. One finding was wrong: the recipe already runs in debate mode.
  4. One Medium, fixed: Generate's Import ignored the case's debate. Lows on the reset toast wording, setting the scope ref in a layout effect, and a fragile test were fixed.
  5. One Medium, fixed: Undo on Generate kept the newer case's debate id. A Low, evidence hidden by the line clamp, was fixed.
  6. No new Medium. A Medium already on main, where a regeneration after Undo overwrote a recorded row, was fixed because it sits in the same handler.
  7. No Medium or High. Clearing the recorded id had brought Save back after Undo, so that became a restoredSnapshot flag and Save behaves as on main. The import refusal wording was fixed.

Migrations and Edge Functions

None. No migration files and no Edge Functions to deploy.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Build a flowchart from a case generated in the current debate, with an option to undo the import.
    • Flowcharts include labeled arguments and relationships, and support translated labels while preserving student edits.
    • Use responsive canvas controls and export flowcharts fitted to their displayed dimensions.
  • Bug Fixes

    • Flowcharts reset when switching debates, preventing charts from carrying over between debates.
    • Clearer validation highlights missing titles or node labels and directs focus to the relevant field.
    • Restrict case imports to the current debate and prevent regeneration after undo from updating a replaced saved case.

FC-01: the canvas controls, minimap, attribution and edge labels follow
the theme. On phones the minimap is hidden, the controls sit in a row under
the canvas, and the toolbar shows icons only. The attribution moves to the
top right. Node and badge colours pass 4.5:1 contrast.

FC-02: edge labels follow the language, including charts saved with the
old English labels, and the attribution link has a localized name.

FC-03: starter node text is stored with its translation key and shown in
the current language. Text the student writes is never translated.

FC-04: "Build from current case" maps the thesis, contentions with
evidence, predicted attacks and counters onto the canvas, from a case in
the same debate. Switching debates puts the template back. Generate's
import uses the same mapping and the same debate check, and a case
restored by Undo is not sent to the Flowchart or written over a recorded
row. The Save dialog asks for a flowchart title.

FC-05: an empty node label or save title shows an inline message.

FC-06: each new chart fits the canvas with padding, and the chart refits
when the canvas width changes.

PF-03: no Flowchart leak; the growth comes from the delayed Jam recorder
iframe. Layout reads on the page now come from a ResizeObserver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 23, 2026 01:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 508f243 and b3c60e6.

📒 Files selected for processing: 13
  • .claude/skills/verify-debatelab/features/README.md
  • .claude/skills/verify-debatelab/features/case-generation.md
  • .claude/skills/verify-debatelab/recipes/case-generation.spec.ts
  • src/index.css
  • src/lib/flowchart.ts
  • src/locales/en.json
  • src/locales/es.json
  • src/locales/hu.json
  • src/pages/FlowchartPage.tsx
  • src/pages/GeneratePage.tsx
  • src/test/lib/flowchart.test.ts
  • src/test/pages/FlowchartPage.test.tsx
  • src/test/pages/GeneratePage.test.tsx

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

Generated cases can now be converted into flowcharts for the associated debate. The flowchart canvas adds debate-scope handling, translated chart text, validation, and responsive controls. Tests and documentation cover the import, canvas, and localization changes.

Changes: Case-to-flowchart generation

Layer / File(s) Summary
Flowchart data and case conversion
src/lib/flowchart.ts, src/test/lib/flowchart.test.ts
Shared helpers define chart data, resolve translated or student-authored text, build starter charts, and convert generated cases into positioned nodes and edges. Tests cover localization, conversion, matching, and layout.
Debate-scoped import and snapshot handling
src/pages/GeneratePage.tsx, src/test/pages/GeneratePage.test.tsx
GeneratePage imports eligible cases through the shared conversion helper and checks debate association. Undo marks restored snapshots, which are not written back to the previously recorded case during section regeneration.
Canvas lifecycle and translated interface
src/pages/FlowchartPage.tsx, src/index.css, src/locales/*.json, src/test/pages/FlowchartPage.test.tsx, .claude/skills/verify-debatelab/features/*, .claude/skills/verify-debatelab/recipes/case-generation.spec.ts
The canvas builds charts from eligible cases, resets when its debate scope changes, and adds validation, responsive controls, and translated labels. Styling, tests, and Playwright instructions cover the interface changes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GeneratePage
  participant flowchartFromCase
  participant pendingFlowchartData
  participant FlowchartPage
  GeneratePage->>flowchartFromCase: Convert eligible generated case
  GeneratePage->>pendingFlowchartData: Store converted chart
  GeneratePage->>FlowchartPage: Navigate to flowchart
Loading
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

Comment @coderabbitai help to get the list of available commands.

@Programcode
Programcode merged commit 1b518d5 into main Oct 23, 2026
3 of 10 checks passed
Programcode added a commit that referenced this pull request Oct 23, 2026
…02, PF-04, PF-05, PF-06, PF-09) (#215)

* Cache every public icon file for 30 days

C3 (#199) added icon-1024.png and the 192/512/maskable manifest icons,
which still fell back to revalidating on every request. The test now
reads public/ so a new icon size can't miss the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Load only the active locale and keep app-only code off public pages

- Spanish and Hungarian strings are their own chunks, fetched when chosen;
  English stays in the entry as the default and fallback. The first render
  waits for the chosen language, and a later choice wins over one still
  loading.
- AppLayout (header, navigation, debate dialogs) loads only under /app.
  mountApp starts the page chunk and the layout while the entry starts,
  instead of when React first reaches them.
- The landing reveal is a CSS transition, so / no longer loads
  framer-motion's 118 KB chunk.
- The session check and layout load show the route's skeleton instead of a
  full-screen spinner.
- check-build-boundaries fails if the es/hu catalogs or AppLayout re-enter
  the entry or the landing, login, home, generate or attack graphs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Open the main app pages offline and keep each page's own HTML

The build writes a precache list into sw.js from the Vite manifest: the
shell's scripts and styles, the signed-in layout, Home, Library, History,
Timer and Settings, the es/hu strings and Inter's Latin fonts. A missing
manifest key fails the build.

Offline, app routes get the precached app-shell.html (the paths
vercel.json rewrites) and a public page gets only its own HTML, stored
under its own path. Before, every navigation was stored as /index.html,
so any route could come back as the last page visited. /auth/callback,
Supabase paths and /_vercel/ stay network-only. Caches carry the build's
id and the next build's worker deletes them.

vite preview applies the vercel.json rewrites, so the offline test loads
what production serves. The test's async waitForFunction calls passed at
once without checking (Playwright treats the returned Promise as truthy);
they now poll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Hold Supabase reads while offline instead of retrying them

Offline, supabase-js retried every failed read three more times, so
opening Settings sent about 70 doomed requests in 30 seconds (user_roles
four times, user_settings 18). REST, Functions and Storage reads now wait
for the 'online' event and go out once; an abort still ends them. Writes
fail at once as before, and Auth is left alone because auth-js holds its
session lock during a user lookup.

TanStack Query also starts with the browser's real online state, so a page
opened offline pauses its queries instead of running them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Don't reload for a missing chunk while offline

Choosing a language whose strings were never cached, offline, failed its
import and triggered the stale-deploy reload, which without a service
worker replaces the page with the browser's offline error. Offline the
chunk is unreachable, not stale, so the error stands and the app keeps
the current language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Treat /expocit/sources as a public page offline, and always leave the startup shell

/expocit/sources is prerendered and Vercel serves that file before the
rewrite, so offline it now gets its own stored HTML instead of the shell
(whose page chunk is not precached). vite preview serves prerendered
pages before the rewrites too. The non-prerendered first render now also
runs if waiting for the language rejects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Disable Upgrade Now while offline and say why

Checkout needs the network. Offline the button stays disabled with a short
notice under it (one new string, pricingModal.offlineUpgrade, in all three
catalogs) and re-enables on the 'online' event. Done here now that T4
(#207) has merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Load every language before the flowchart opens

T9 (#206) recognises flowchart starter text saved in any language by
comparing it with each catalog. With Spanish and Hungarian loaded only on
demand, a chart saved in Hungarian and opened in English kept its
Hungarian starter text. The flowchart route's loader now fetches every
catalog before the page renders. Only that page (and a hover prefetch of
it) pays for them, and they are precached for offline.

The flowchart tests now load through the route loader; without this
change four of them fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Address review round 1: stop retries offline instead of holding reads

- Offline Supabase reads are no longer held until 'online'. A held read kept
  the token of whoever started it and could replay after sign-out, and a
  browser that wrongly reports offline would never send it. Every request
  is now sent; one that fails while the browser is offline ends as an
  abort, which postgrest-js does not retry. TanStack Query is no longer
  seeded from navigator.onLine; failed queries refetch on 'online' instead.
- A language chosen offline before its strings were cached is applied when
  the connection returns.
- The first render waits at most 3 s for the language's strings.
- The service worker's build id also covers the unhashed public files it
  serves cache-first (theme-init.js, icons), and an offline public page
  prefers the copy saved on the last visit over the one stored at install.
- The landing page's code is precached with its HTML, so / opened offline
  is not replaced by the error page.
- test:sw now visits /auth/callback before checking it is never cached,
  and checks the precached files sit in the install-time shell cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Address review round 2: load layout and page together after sign-in

- After a client-side navigation into /app (sign-in, the callback), the
  page chunk now starts with the layout chunk instead of after it.
- The offline no-retry rule also matches reads through the dev:preview
  staging proxy path.
- hero-visual.jpg and placeholder.svg also feed the worker's build id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants