Skip to content

Windows parity: Sidebar and window layout match macOS (tree, colours, sections, minimum width) #767

Open

Description

@coneilen

Visual and functional parity work item, from the 2026-10-13 side-by-side review.

  • Comparison: macOS reference Run A (app at 09eda11a, Dark, 2x) against the Windows testing-gaps run (installed 0.1.78-windows.beta26, 2560x1440 at 100%).
  • Ground truth: macOS is the reference. Only platform chrome may differ: the title bar, window buttons, menu-bar location and native file pickers.
  • Evidence limits:
    • File names below refer to the two local evidence bundles. The next macOS ground-truth run will attach captures.
    • Difference IDs (D1, E2, …) refer to the review report.
    • The reviewer opened the paired images it cites. Some surfaces could not be reviewed; they are listed under "Captures needed" in the ledger entry.
  • Done when: every acceptance box is met and shown with paired screenshots in the PR. Before/after Windows captures go next to the macOS capture, and a reviewer from a different model vendor confirms them. A UIA readback or green unit test alone does not satisfy a visual criterion.

Evidence:

  • macOS: 740-parityref-above…, 727-canvas-final-states, 720-quick-chats, 720-min-width-canvas;
  • Windows: tg720-project-canvas-1440, tg-lane-740-above, tg735-promote-menu-state, tg720-project-canvas-min;
  • differences A1–A8, Y1–Y2;
  • source: Sidebar.zig:183-309, 485-489, App.zig:8877-8883.

Acceptance criteria:

  • Rows: Graph and Quick Chats with icons, a "Local Projects" caption, and project rows with folder icons, + and a chevron. No "GRAPH", "Projects" or "CHATS" headings, and no letter glyphs.
  • The loop tree is indented and expanded as on macOS, every loop is visible, and each row has its type-colour bar, an age, and a state dot.
  • No worktree rows or Needs-you section in the normal sidebar. Match the optional Activity strip below the canvas, including its filter and actions; the returned macOS capture records it there, not in the sidebar.
  • The workspace switcher appears in the sidebar footer, and no footer text overlaps rows.
  • Selection is a blue rounded pill.
  • The window enforces a 452pt-equivalent minimum width. Below the sidebar threshold, the sidebar overlays the canvas and the toolbar collapses into "»", as in 720-min-width-canvas.

Reference intake 2026-10-14: Primary macOS source 2ccf303a04f995184aae1d4b738a9eca0f71b79e comments out the normal-sidebar attention call at AppSidebarView.swift:334; toolbar/canvas attention still exists. The recording now shows the optional bottom Activity strip, so it is not an absent feature to remove. The third box above is narrowed to that distinction; all six boxes remain unchecked. This is the extras-are-not-parity scope decision, not a claim that sidebar-extra removal has landed. Such removal needs a separate bounded approval and reachable equivalent actions. The current approved first navigation slice and its named route-alignment corrections are not expanded.

Activity

  1. added
    enhancementNew feature or request
    parityWindows/macOS UI parity ledger row (investigation/ui-parity-matrix.md)
    P1 layoutParity priority 1: layout, clipping, overlap, broken rendering vs macOS
    visual parityVisual fidelity against the macOS reference captures
    on Oct 14, 2026
  2. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Escalation record (wave 1, lane C), 2026-10-14 04:53 PDT.

    Old model gpt-6.1-sol, high, long_context (lane C)
    Trigger 2 exact-head CI failures the session caused, per the abandon rule in investigation/windows-parity-orchestration-plan.md. Both were in new UIA gate assertions C added, not in product behaviour.
    New model claude-sonnet-5.5, xhigh, long_context (lane C2); reviewer gpt-6.1-sol

    State handed over: draft PR #800 at c82286b0, which C2 takes over.

    • The cross-vendor review accepted the product changes.
    • Full unit passed, visual-baseline passed, and there are 9 paired images.

    Open: the jump-block card oracle ("exact canvas cards") failed in CI run 38556773638. The suspected cause, from source only, is a retained Worktrees or summary card in a broad card count; it has to be confirmed.

    Coordinator decisions carried over:

  3. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Wave 1 stopped (operator decision, 2026-10-14). PR #800 was closed unmerged. All this work is abandoned. Findings to reuse in wave 2:

    Restarting from main under investigation/windows-parity-lane-kickoff.md.

  4. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Implementation plan (wave 2)

    Planning worker pc, lane C. Investigated current main 8ea8106; local HEAD and origin/main match the GitHub main SHA. This is a source-and-capture plan, not an implementation or a parity sign-off. No product files changed, builds/tests run, or desktop used.

    Complexity: standard

    Assessment applies only to the approved acceptance-box-1 navigation slice, not the deferred whole-sidebar work. It reuses Sidebar.appendRows, existing GDI painting/hit testing, App's sidebar accessibility and scrolling adapters, and existing temporary fixture mechanisms. The changes are a bounded navigation projection/geometry correction with corresponding named-roster tests; they introduce neither a new shared framework used by multiple surfaces nor a cross-lane architectural mechanism. Although App.zig and the gate are large files, the approved edits are confined to their sidebar adapters/oracle and fixture restoration. Tree semantics/persistence, selection, footer switching and responsive-window architecture are explicitly excluded. Under the supplied definition this is standard, not highly complex; the applicable implementation tier is gpt-6-luna, high.

    Decision: first PR is the navigation-row structure, not another whole-sidebar rewrite

    First slice: acceptance box 1 only -- Graph and Quick Chats first, then captioned open-project groups, real outline icons, compact project headers, and their existing add/disclosure actions. This repairs the sidebar's primary navigation hierarchy without changing loop hierarchy/persistence, selection semantics, Worktrees ownership, the workspace switcher, or window layout at the same time.

    Acceptance box First PR disposition
    1. Graph / Quick Chats icons; Local Projects; folder rows with add/disclosure; no GRAPH/Projects/CHATS headings or letter glyphs Target for closure, only after final paired image review and functional evidence
    2. Expanded indented loop tree, every loop, type bars, ages, state dots Leave unticked; preserve current loop hierarchy/expansion and loop-row presentation
    3. No sidebar worktrees / Needs-you / Activity, or record extras Leave unticked; explicitly record all three retained Windows extras
    4. Workspace switcher in nonoverlapping footer Leave unticked; retain existing footer clipping/reservation
    5. Blue rounded selection Leave unticked; no selection paint or selection-model change in this slice
    6. 452pt-equivalent width plus overlay and toolbar overflow Leave unticked in full; no minimum-width, overlay, or toolbar work in this slice

    The 220-DIP Windows rail versus Run A's approximately 240pt column, floating-panel silhouette, loop-row density, palette, inactive/active selection, and missing footer remain visible limits, not claimed matches. macOS allows a 200/240/320pt column (AppSidebarView.swift:102); do not change lane B's shared width token to conceal a comparison difference.

    Reused Wave 1 findings, verified against main

    Read the Wave 1 stopped comment first, and PR #800's scope/evidence notes. #800 is closed unmerged; none of its product changes are present on main, and this plan does not authorize cherry-picking its abandoned rewrite.

    • Selection follows the visible destination, not a selected canvas node. macOS graphcode/Sources/Features/App/AppSidebarView.swift:300-310 returns the open loop/chat first, otherwise project or Quick Chats. Windows App.zig:7614-7652 independently marks the selected project and selected node, while 7668-7700 marks destination/header aliases. Preserve today's behavior in this first slice; a subsequent selection slice must use one navigation-presentation decision. Count only Graph, Quick Chats, project, loop/chat navigation rows; not Worktrees, canvas cards, disclosure/add controls, or Needs-you aliases.
    • Jump destination remains Windows parity: Jump palette selection should open the loop like a sidebar tap, not just select it on the canvas #799. Verified App.zig:4189-4236: Jump selects project/node and sets .project, not the loop workspace. A scrolling fix below must not change that destination or require the loop row to become selected.
    • Native Graph/Quick Chats selection remains Windows accessibility: sidebar Graph and Quick Chats destinations don't expose UIA selection state #801. AccessibilityProvider.cpp:851-862 retains only static destination bounds and skips their selected flags; 1080-1084 excludes non-row keys from SelectionItem. An App sink result cannot establish native selection. This first slice must not edit that C++ behavior.
    • Needs-you is an extra. macOS AppSidebarView.swift:334 comments out attentionSection; Windows Sidebar.zig:289-318 paints it and App.zig:7735-7758 publishes it. Retained and labeled in this slice, not passed off as macOS content.
    • Activity is not absent from all of macOS. It is an optional full-width strip below the split (AppView.swift:21-31), not a sidebar section. Windows additionally paints sidebar Activity (Sidebar.zig:320-349). Retain it here rather than deleting an action whose alternative navigation path this PR has not established.
    • Canvas roster is not "all loop cards". The source has node and summary/notice cards (uia-live-gate.ps1:3227-3242), and connection banners can coexist. Preserve the existing exact named node/summary roster checks. Do not add the failed Wave 1 broad-card-count oracle.
    • workspace-footer- classification is conditional work. Main's multi-project chrome prefix list (uia-live-gate.ps1:3479-3484) lacks it. This PR adds no footer identity, so do not edit that classifier gratuitously. The later footer PR must add it and positive/negative functional tests.
    • Validation path budget is real. Use the real folder C:\gcpc (7 characters), not a junction or an 8-character root. This planner did not run a live gate.

    Acceptance-by-acceptance source comparison and root causes

    Paths below are repository-relative. Swift sidebar files are under graphcode/Sources/Features/App/; Zig files under graphcode-windows/src/.

    1. Navigation rows, sections, icons and project controls

    macOS: AppSidebarView.swift:332-377 emits the global project's group, Quick Chats, Local Projects and Remote Repositories, grouping open projects rather than recent folders. AppSidebarView+Rows.swift:11-27,43-110 gives each project one header, its children immediately below it, monochrome graph/folder/network icons, and hover-only + then trailing disclosure. AppSidebarView.swift:426-506 makes Quick Chats a peer header with an outline chat icon and indented chat children. SidebarIcon.swift:25-38 specifies a 15pt symbol in a 20pt monochrome column. Recent projects instead live in Add Folder (AppSidebarView.swift:239-244).

    Windows: Sidebar.draw:182-184 paints fixed GRAPH/Projects headings; 204-228 paints R/L/G and leading text chevrons; 267-286 paints Quick Chats later with an additional CHATS heading. appendRows:621-709 emits unopened recents first, then Graph, loaded/placeholder project blocks, Worktrees, and only then Quick Chats. top += 38 after loaded project groups and top += 62 for placeholders create the large gaps. projectIsVisibleInSection:797-804 groups unopened recent projects, so Local Projects can be absent despite an open local project.

    Root cause: appendRows models recents, graph navigation, open graphs and chats as sequential unrelated sections instead of the macOS open-project navigation list; draw separately paints fixed headings and letter substitutions. Project controls use hard-coded x/y regions in both App.onWindowMessage:9739-9757 and syncAccessibilityTo:7607-7634, so changing only paint will misroute pointer/UIA actions. Metadata-only open rows are appended but draw:218 and syncAccessibilityTo:7608 both require graphFor(path), making the placeholder unpainted/unpublished.

    Existing alternative for recents, verified: Windows already has File > Add Folder > Recent Folders (MainWindow.zig:594-608,799-825), populated from the unchanged model in App.updateNativeChrome:7190-7198. The live gate reads both fixture recents and invokes the remote command (uia-live-gate.ps1:8151-8200). Therefore removing sidebar presentation of unopened recents need not remove their data or access.

    2. Tree visibility, indentation and loop-row information

    macOS: AppSidebarView+Rows.swift:209-252 starts from LoopGraph.sidebarRoots, orders siblings by the stored sidebar order, walks all outgoing edges, and uses a visited set. 269-287 adds 16pt per depth. AppSidebarView.swift:276-295 uses a 3x14pt rounded type stripe, 12.5pt title, 10.5pt monospaced age and 7pt state indicator. LoopTypeAppearance.swift:43-47 gives Main/Goal/Timed/Turn/Composite RGB colors; composite serializes as proactive (GraphcodeKit/Sources/Domain/LoopType.swift:29-32). LoopStateAppearance.swift:46-51,74-83 defines hollow waiting/blocked/stopped versus filled states; Features/Canvas/LoopCardView.swift:393-409 renders that indicator.

    Windows: Sidebar.hierarchyItems/collectRootIndices/appendHierarchy:846-965 only nests handoff edges; it has cycle protection but different root/child semantics. draw:233-246 uses 12px indentation, a 3x19px non-rounded stripe, a compact state word, and a 24px slot. loopAccent:485-490 uses non-reference COLORREF literals and lacks sketch/turnBased/serialized proactive cases; their default becomes orange when interpreted as COLORREF. loopRowTextRects:371-379 reserves a whole state-text column.

    Expansion correction to Wave 1's abandoned design: A fresh macOS set is also empty (AppSidebarView.swift:45-49); it does not imply "always default-expanded". Initial edge observation is seeded silently, later new edges reveal the complete ancestor chain (78-85, AppSidebarView+Rows.swift:309-337). Windows only changes expanded IDs on explicit toggles/store load (Sidebar.zig:35-49,79-90; App toggles at 8329 and 9767). A capture of an expanded tree is not permission to replace persisted expansion with a default-true migration.

    Root causes: different edge/root projection, hand-maintained narrow columns, missing type dispatch and RGB-to-COLORREF conversion, word rather than indicator paint, and no equivalent new-edge ancestor reveal. Expanded Run A fixture versus a cleared Windows profile is also a state mismatch. Future work must seed matching expansion for visual evidence, preserve collapse persistence, and separately test new-edge auto-reveal. LoopGraph.swift:241-297 is the authoritative root/cycle/sketch policy; do not claim arbitrary "every node" parity just from a handoff chain.

    First slice: none of these semantics or colors change.

    3. Worktrees, Needs-you and Activity in the sidebar

    macOS: AppSidebarView.swift:332-377 contains no worktree rows and disables the sidebar attention section at 334; Activity is below the entire split (AppView.swift:24-31). Run A sidebar confirms these absences.

    Windows: appendRows:687-694 inserts inspection entries; draw:249-265,289-349 paints Worktrees, Needs-you and Activity; contentBottom:985-995 reserves their scrolled extent. App.syncAccessibilityTo:7657-7667,7735-7796 publishes these elements, and pointer handlers 9666-9722 route Stop/filter/event actions.

    Root cause: sidebar is being used as an additional inspector/monitor surface, not merely navigation. Removing only paint would leave invisible actionable/UIA elements or break Worktrees selection/scrolling. The live gate explicitly requires populated Needs-you at 5378-5413.

    First slice: retain all three as documented Windows extras. Do not silently weaken/remove their functional assertions. Worktree migration needs lane A/#777 coordination; sidebar attention/Activity relocation needs a deliberate alternative-action/evidence plan. This first PR does not reinstate Wave 1 approvals as current permission.

    4. Footer switcher and overlap

    macOS: AppSidebarView.swift:190-214 places update/error over WorkspaceFooter in the bottom inset. Features/Workspaces/WorkspaceSwitcher.swift:119-180 gives the switcher an opaque full-width bar, divider, workspace name, stack glyph and chevrons. Visibility is non-default OR more than one known workspace, not unconditional (AppFeature+Workspaces.swift:74). The switcher opens a structured panel with workspace summaries (WorkspaceSwitcher.swift:186-240); an existing Windows menu is not proof of full panel parity.

    Windows: Sidebar.draw:356-365 ends with duplicate status text. footerReserve/effectiveFooter/contentViewportBottom/statusTextRect:397-436 already reserve and clip a footer; there is no workspace switcher input or drawn row. App has workspace identity/list and existing commands (App.zig:1418-1421,7199-7212,8429-8442), but they are not a sidebar footer.

    Root cause: Sidebar receives a status string, not a workspace-footer presentation/action; no footer navigation node is emitted. The historical overlap report must not be treated as proof current clipping still fails: main has regression coverage at App.zig:11586-12475.

    First slice: preserve existing footer behavior, and test that moved rows remain above its clip. Do not add a menu-only footer and claim the macOS summary panel.

    5. Blue rounded selection

    macOS: AppSidebarView.swift:76-77,300-310 uses native sidebar List selection and the open-loop-first binding. Opened Run A 720-global-graph and 720-quick-chats show active blue rounded pills; 727-canvas-final-states shows an inactive gray project pill. Both active and inactive states matter.

    Windows: Sidebar.draw:218-224 merely brightens selected project text. The only row fill in that switch is a selected reclaimable worktree (252-253); no Graph/Quick Chats/loop selection pill is painted. App's independent selected booleans can select project and loop together (7610,7638), and the static-provider limitation above remains.

    Root cause: Sidebar has model selection but no destination/open-workspace-aware presentation; selected project/node mean different things from the macOS List selection. A single helper must later feed paint and App projection without conflating pointer hover, canvas selection, modal worktree ownership or native selection support.

    First slice: leave this box and #801 explicitly unverified; do not introduce a "one selected item across every UIA row" oracle.

    6. Minimum width, sidebar overlay and toolbar overflow

    macOS: AppView.swift:46-52 delegates split behavior to NavigationSplitView; AppSidebarView.swift:102-107 gives the sidebar its 200/240/320pt range and toolbar item. AppView.swift:91-145 supplies system toolbar items, including Jump. 452pt is a runtime observation, not a source literal: opened 720/min-width.txt says a request for 400pt clamped to 452pt, and opened 720-min-width-canvas@1x-srgb.png shows the visible sidebar and >> overflow. The exact collapse threshold is not established by that single image; do not invent a source constant or claim all narrower widths were measured.

    Windows: App.minimumWindowHeight:1078-1100 converts only client height to the outer frame. onWindowMessage:8880-8888 constrains only ptMinTrackSize.y; WM_SIZE:9202-9208 relayouts without a responsive-sidebar state. GraphCanvas.renderBounds:621-632 permanently subtracts rail width while visible. GraphCanvas.Header.layout:505-531 proportionally squeezes controls instead of creating overflow. WorkspaceControls.zig:14-23 only toggles a boolean rail; no overlay mode.

    Root causes: absent x minimum, docked-only canvas/input geometry (App.inputBounds:1147-1152), and no common overflow presentation/hit-test/UIA model. Later minimum-width work should preserve larger OS minima, use AdjustWindowRectExForDpi for client 452-DIP width, test 96/144/192 DPI plus native menu wrapping, and retain the existing height policy. Responsive overlay/overflow is a separate cross-lane change, not a WM_SIZE paint trick.

    First slice: no change and no claim for any part of this box.

    Implementation steps for the first PR

    Names below are proposed new test names, not tests this planner claims to have run. Add tests using existing public APIs first, so RED is a behavioral assertion failure on main, not a missing-new-helper compile error. Keep failing logs.

    1. Make the generated row sequence the navigation source of truth

    File/functions: Sidebar.zig, appendRows, project section predicates/lookup, content_top, sidebarSectionBottom, layout/row-extent helpers and their existing tests.

    • Emit Graph first (and its own loops, when present, under that single identity), then Quick Chats and its chat children, then Local Projects/open local folders, then Remote Repositories/open remote folders. Exclude the reserved global project from ordinary folder groups, including the model.graph fallback.
    • Enumerate open-project metadata in stable order, using loaded graph data where available and appending graph-only owners once. Deduplicate by exact project path, not title. A metadata-only open project gets a visible named folder/network row; do not advertise a New Loop/disclosure action before the required graph exists.
    • Remove unopened recents from sidebar projection only. Keep the model, File menu and Recent Folders command routing unchanged.
    • Remove fixed GRAPH/Projects/CHATS labels and their artificial blank bands. Use a single row extent/bounds helper for generated rows; compact 32-DIP navigation/header/chat slots, the caption's 18pt-before/2pt-after grouping, and no unconditional 38/62px blank space after folders. Keep loop slots and retained extras unchanged in this slice. The scroll viewport still excludes the existing footer.
    • Preserve the existing 517-DIP minimum client-height floor: App.min_client_height currently derives from Sidebar.content_top, and its existing test asserts 517 (App.zig:12277-12310). Moving the first row must not inadvertently shrink that floor. Adjust only this coupled height-floor expression if needed, and retain its existing regression; do not add a width constraint or responsive window mode.
    • Preserve independent local/remote/project/chat folds, existing loop expansion IDs, root ordering, stable path/node identities, and the contents/actions of the retained extra sections.

    RED tests in existing root src\Sidebar.zig:

    Proposed test Assertion What main demonstrably does instead
    parity sidebar navigation pins destinations before open folder groups With open local/remote graphs, one unopened recent, and a chat: Graph first, Quick Chats/chat next, Local Projects/open local next, Remote Repositories/open remote next; unopened recent has zero sidebar rows appendRows:625-653 emits the recent section before Graph; Quick Chats comes last
    parity sidebar navigation emits each open owner once including global and loading owners Same-named/different-path owners remain distinct; loaded+open duplicate is one row; global is only the Graph header; metadata-only owner has a resolvable displayed name Global graph enters ordinary .open_project loop at 659-671; loading row cannot be resolved by current paint/UIA guard
    parity sidebar navigation folds do not hide peer destinations Collapsing local/remote hides only that group's open owners/children, not Graph, chats or the other group Group collapse currently gates recent rows, not loaded graph blocks (625-671)
    parity sidebar navigation shares row extents across scroll and hit testing At 0/nonzero/max scroll, every named header center hits its exact path/kind; gaps/footer do not; long lists reach their last row Current fixed header offset, 24px row hit spans, and section formulas encode the old sequence

    Update old tests that assert the intentionally superseded recent-sidebar/CHATS/header policy; do not delete coverage for folds, root reorder, Worktrees/Needs-you/Activity, update/error footers or empty models.

    2. Paint bounded outline header rows and preserve real actions

    File/functions: Sidebar.zig, draw, row bounds/text rect helpers and small local icon drawing routines; App.zig, narrow sidebar branches of onWindowMessage (pointer click, hover and context hit regions).

    • Replace G/L/R and text chevrons with monochrome graph/folder/network/chat outlines and vector disclosure chevrons, sharing the macOS icon-column alignment. Use existing GDI/AppFont patterns, not a new icon-font dependency.
    • Header text must elide inside its own container, leaving disjoint trailing New Loop/New Chat and disclosure zones. Paint add/disclosure only on hover, with disclosure present only for actual children; do not show a permanent leading v or > in project/chat headers. No changes to loop state text in this slice.
    • Reuse the same control bounds in paint, pointer and UIA. A title click navigates; an add click uses the existing owner-scoped creation path; disclosure changes only its fold. Add the Graph header's add/disclosure affordances using the global owner and existing overview creation context, not the last selected local project. openGlobalOverview is at App.zig:2659-2667; createNode already captures its context at 3371-3378. Preserve existing form behavior; no lane A edits.
    • Keep keyboard and context actions reachable; header-only changes must not intercept loop or extra-section clicks.

    RED tests:

    • src\Sidebar.zig: parity sidebar navigation paint bounds long names and replaces letter glyphs -- render the existing draw entry point to a memory bitmap, as current paint tests do. Assert no text ink outside the rail/label rectangle, no fixed heading ink in the removed bands, and nonempty contained graph/folder/network/chat icon regions for each explicit fixture. Main's header drawText uses a right edge of 1200 (2172-2179) and paints the fixed headings/letter glyphs. Semantic icon choice plus nonempty pixel tests does not replace image review.
    • src\App.zig: parity sidebar navigation pointer zones preserve title add and disclosure ownership -- never-shown native window; send logical-center mouse messages through onWindowMessage, intercept existing form/creation seams rather than opening a modal. Assert each named owner stays exact, disclosure does not navigate/create, and Graph creation does not reuse a local owner. On main the proposed new header/control positions do not align with the old fixed 174/198 ranges and Graph has no equivalent header add branch.

    3. Wire accessibility and scrolling to those same rows

    File/functions: App.zig, syncAccessibilityTo sidebar loop; jumpToNode scrolling tail only; ensureWorktreeVisible; related existing never-shown test helpers. These are outside the narrow App window-procedure lease and require approval.

    • Publish exact same row/control extents, names, clipping and conditional actions as paint, including a named loading owner. Keep established identity prefixes; do not add another recent-project identity masquerading as an open owner.
    • Do not change selected flags, C++ selection patterns, Quick Chats aliases, Jump destination, canvas-card identity or Loop workspace behavior in this PR.
    • Replace jumpToNode:4230-4233's obsolete fixed row/700px viewport calculation with a lookup through generated rows/current folds and the actual content viewport. If the target is folded, reveal its visible owning project/group; do not expand nodes or change the destination as a side effect.
    • Replace ensureWorktreeVisible:6530-6539's old hand-computed top with the generated worktree row and its actual extent. Retaining this extra means retaining its keyboard navigation correctly after the list moves.
    • Retire obsolete production coordinate formulas rather than keeping old/new layout sources in parallel. Update tests that explicitly describe the removed fixed GRAPH/Projects/CHATS policy.

    RED tests in src\App.zig:

    • parity sidebar navigation UIA matches painted owners and clipped row controls -- at 96/144/192 DPI, assert a positive named owner count, matching name/identity and exactly-once-scaled bounds for Graph/Quick Chats/open local/open remote/loading owner; every nonempty control rect stays inside its row and the rail and above the footer. Main omits loading owners and uses fixed row.top - 3 .. row.top + 23 and control bounds at App.zig:7593-7600,7611-7633.
    • parity sidebar navigation scrolling uses current folds and viewport -- explicit short/tall clients and a collapsed group/project, generated target above/below viewport, then worktree rows after the changed list. Assert actual target or visible owner is reachable and scroll clamps to generated content. Main's Jump tail uses 700, old node coordinates and no current fold argument; ensureWorktreeVisible uses the old formula.

    Use only the never-shown/memory seams for local App execution. Any test that can show forms, a Jump palette, a terminal, or foreground a window belongs in CI or requires a lease; do not run it locally as a supposedly hidden test.

    4. Change the live oracle deliberately, with functional negative tests

    Files/functions: Tools\windows\uia-live-gate.ps1 initial sidebar navigation block (5360-5480), and Tools\windows\Tests\ValidationRunner.Tests.ps1.

    This is a requested gate lease extension, not permission to edit other canvas/Jump/Worktrees checks.

    • Replace the obsolete requirement for one recent project-row- plus one open-project- with zero sidebar recent rows AND an exact positive named open-owner roster. Verify Graph then Quick Chats then caption/open owners using their bounds, not broad child counts. Preserve all stable fragment-link checks.
    • Current UiaFixtureData.init has one open local graph and a recent-only remote (App.zig:999-1024); do not simply delete remote-collapse coverage. Add a bounded temporary sidebar-only fixture state/mutation with a named open remote owner using the existing fixture mechanisms, exercise local and remote group folds, and restore the ordinary fixture before the rest of the gate. No real SSH/Codespace connection or global fixture-wide graph roster change. The corresponding App.zig fixture mutation and never-shown ownership tests require the explicit scope extension below.
    • Keep the existing Recent Folders native menu inventory and remote-command invocation at 8151-8200; that is the alternative access preserved when sidebar recents disappear.
    • Preserve the collapsed-root loop tests, Needs-you positive checks, Worktrees behavior and exact source-supported canvas roster. No new selection-cardinality or post-Jump card-count oracle is needed for acceptance box 1.
    • Extract only the small roster/order validation needed to functionally test it. In ValidationRunner.Tests.ps1, use actual helper execution, not just source regexes: accept valid open local/remote owners plus Graph/Quick Chats; reject a missing/duplicate destination, swapped destination order, wrong owner path, unexpected sidebar recent, empty roster, or row/control outside the allowed clip. Test temporary fixture restoration explicitly. Main's current oracle accepts/requires the recent-only layout and has no equivalent new predicate; capture assertion RED after writing the predicate/test against the old fixture result, not a missing-function error.
    • Do not add workspace-footer- or alter multi-project card classification: no footer is added here.

    5. Documentation and regression evidence

    File: graphcode-windows\README.md, only the Sidebar scrolling and short windows subsection (357-384).

    Replace the removed fixed GRAPH/Projects header description with the actual navigation order and clipping policy; state that recents remain in File > Add Folder > Recent Folders, and list retained Windows sidebar extras. Do not change width/overlay/selection/expansion claims. The parity ledger stays coordinator-owned and Partial.

    File scope for approval

    File Required scope Lease status
    graphcode-windows\src\Sidebar.zig Navigation row projection/geometry, bounded header/icon paint, existing-root tests; no loop hierarchy/state-color/persistence/footer rewrite In lane C
    graphcode-windows\src\App.zig Sidebar-specific mouse/hover/context window-procedure branches using shared row/control geometry In lane C's narrow window-procedure lease
    graphcode-windows\src\App.zig Preserve the existing 517-DIP client-height floor if the sidebar content-top change affects its derived expression; retain existing minimum-height tests In lane C's min-size lease; no width or responsive-layout change
    graphcode-windows\src\App.zig syncAccessibilityTo sidebar projection, ensureWorktreeVisible, jumpToNode scrolling tail only, bounded sidebar fixture mutation, and focused related tests Outside narrow lease: coordinator approval required, with no palette entry-building, workspace layout, menu construction or destination changes
    Tools\windows\uia-live-gate.ps1 Named sidebar navigation/section oracle and temporary fixture restore only Shared harness: approval required
    Tools\windows\Tests\ValidationRunner.Tests.ps1 Positive/negative functional tests for exactly that gate change Outside lane: approval required
    graphcode-windows\README.md Directly related sidebar navigation/recents/evidence limits Outside lane: approval required

    No first-PR changes to GraphCanvas.zig, DesignTokens.zig, GraphModel.zig, AccessibilityProvider.cpp, MainWindow.zig, NativeForms, TerminalSurface, shared Swift, the parity ledger, or workflow/classifier configuration. All Zig tests remain in already registered Sidebar/App roots (WindowsShell.Tests.ps1:1114,1214); no new test root or runner registration is needed. If the temporary fixture requires broader code changes than the bounded App mutation, stop and ask for a scope correction rather than modifying GraphModel or the stub daemon.

    Evidence and execution plan

    Focused RED/GREEN (implementation worker)

    Bootstrap only when starting implementation, using canonical pc roots: seed from read-only mirrors/cache into C:\gct\pc / C:\gcp\pc, bootstrap those roots, then dot-source C:\gct\pc\environment.ps1. Use C:\gcs\pc for logs/caches/TEMP/TMP and a process-scoped disposable support/profile. Do not write into the abandoned lane C/C2 directories.

    From graphcode-windows, the focused Sidebar command is:

    & $env:GRAPHCODE_ZIG0152 test src\Sidebar.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 "-I$env:GRAPHCODE_WINGHOSTTY_ROOT\include" --cache-dir C:\gcs\pc\zig-cache --global-cache-dir C:\gcs\pc\zig-global --test-filter "parity sidebar navigation"

    For focused App tests, use the existing native link invocation in Tools\windows\Tests\WindowsShell.Tests.ps1:1214-1218, with this same test-name filter and private cache directories: App.zig plus AccessibilityProvider.cpp/FilePicker.c/FolderPicker.c, pinned provider host library and this worker's built ghostty-vt-static.lib; the listed Win32 libraries/UNICODE defines are required. Build missing provider/artifact prerequisites through the pinned repository build path, not a hand-created compiler environment or another worker's artifacts. Run only the new hidden cases and existing relevant known hidden geometry/scroll cases. Preserve compilation/link failures as environment/build failures, not RED.

    After gate changes, from repository root:

    pwsh -NoProfile -File Tools\windows\Tests\ValidationRunner.Tests.ps1

    This is an executable script ending in ValidationRunner.Tests.ps1: PASS, not an assumed Pester test file. Assert nonzero new functional case counts. Prepend C:\v5modules to this process's PSModulePath only when a called task needs Pester 5.7.1.

    Header/icon/spacing paint changes warrant:

    pwsh -NoProfile -File Tools\windows\validate.ps1 -Task visual-baseline

    Keep separate RED/GREEN logs and quote the real positive executed-test count; a zero-test filtered success is failure. No passing count is forecast as already observed.

    Full regression: exact-head CI only, not full local unit/integration. Require every exact-head workflow green; record run/job IDs and positive test counts. Review every hunk after rebasing onto current origin/main. Get the different-vendor code/evidence reviewer before screenshot time; preserve named-card oracles and review new harness predicates carefully.

    Final screenshots (one coordinator-granted slot, after CI and code review)

    Use main-before and final-head-after binaries with the same privacy-safe fixture, 100%/96 DPI, matched client size and destination, and PrintWindow(hwnd, hdc, 2) plus pixel verification. The beta26 return captures are diagnosis only, not a substitute for a current-main before image. Seed onboarding-seen, chats, and windows-sidebar-state.tsv in the fixture's GRAPHCODE_SUPPORT_DIR. To match an expanded reference, seed its expanded IDs; do not alter product default expansion to manufacture it. Never upload 734/*.zip or the prohibited Needs-you-click capture.

    Surface/state to capture before and after Fixture macOS pairing actually opened by planner First-PR claim
    Global graph, Graph header hovered and then unhovered Global owner plus two open local projects; one unopened recent kept in menu only 720/720-global-graph@1x-srgb.png Header order, graph glyph, aligned text, hover add/disclosure; not blue selection
    Quick Chats canvas, empty chat group Same owners, zero chats 720/720-quick-chats@1x-srgb.png Peer chat header/icon and its add action; no CHATS heading
    Project canvas, one chat plus expanded folder RefMain > RefGoal > RefTurn > RefTimed with same persisted expansion, Composite/sketch siblings, ParityRef2 740/740-parityref-above-threshold-8-linked-canvas@1x-srgb.png and 727/727-canvas-final-states@1x-srgb.png Local Projects/header structure, folder outline and hovered +/chevron, no artificial project gap; tree colors/state dots explicitly deferred
    Long list at top and bottom, short client height, long project name More open local folders than fit; update and ingress footer variants; retained Worktrees/attention extras Above reference for static structure; no equivalent Run A overflow capture exists Windows containment/action regression only, not cross-platform visual closure
    Mixed open local/remote groups and loaded/loading owner Isolated fake fixture paths, no network; exact distinct owner names No matching remote/loading macOS capture in Run A Functional/icon/containment evidence only; annotate missing paired runtime state

    Do not label the first PR a footer/minimum-width/selection/tree fix because neighboring defects appear in these images. 720-min-width-canvas was opened for investigation but its overlay/overflow criterion is not part of this PR.

    After captures, File > Exit and stop only owned PIDs, including zmx daemons, verifying none remain; release the desktop lease. Upload only privacy-safe fixtures and get the reviewer to open every paired image and decide box 1. Use Refs #767, not Fixes; fill the repository PR template, list all unticked criteria and missing evidence, and validate literal RED/GREEN/REGRESSION lines with Tools\tdd\Test-TddEvidence.ps1.

    Risks and explicit guardrails

    • Shared App file: lanes A/D/E may touch App simultaneously. The current open-PR list showed no active Wave 1 product PR; Bring sidebar navigation and minimum width toward macOS parity #800 and Fix Quick Chat workspace chrome #787 were closed. PR Bound UIA Worktrees gate traversal #587 is an open UIA Worktrees-gate change, so its gate hunks/assumptions are a specific collision risk. Re-check exact open changes before implementation and rebase review, not just at this planning snapshot.
    • Structural gate policy changes: recents are currently a mandatory sidebar row; removing them requires replacing, not bypassing, those assertions and preserving menu invocation, remote fold coverage and stable fragment links.
    • Coordinates: changed header pitch/order must flow through pointer/UIA/scroll and retained extra sections. Fixing paint alone is insufficient; hard-coded Jump/Worktree scroll formulas are tightly coupled.
    • DPI: calculate logical bounds first, scale once at the existing App/UIA boundary. Do not rescale terminal tabs or rewrite lane D workspace geometry. Hidden geometry at 96/144/192 does not establish mixed-monitor hardware behavior.
    • Theme: preserve current dark-only shell policy; compare native-active/inactive selections separately when box 5 is addressed. Do not introduce a new font, change shared palette tokens, or claim native material parity from an outline icon.
    • State: keep stable exact project/node identities, current expansion persistence and selection semantics. Do not default-expand all nodes, auto-open remote connections, or change Jump as incidental cleanup.
    • Evidence: memory-bitmap/hidden native tests are implementation evidence, not a rendered live comparison or assistive-technology certification. Full CI and the final paired review are still required before box 1 can be checked.

    Approval requested for this one-box first slice and the explicitly marked App/harness/documentation extensions. Remaining boxes need later reviewed slices; this plan intentionally does not resurrect Wave 1's multi-surface PR.

  5. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Plan approved (coordinator)

    Approved: #767 (comment), acceptance-box-1 navigation structure only, with the following exact file leases. Boxes 2-6 remain unticked. In particular, no default-expanded migration, selection rewrite, footer switcher, minimum width or overlay/overflow-toolbar change.

    Approved files/functions: graphcode-windows/src/Sidebar.zig for the described open-owner row projection, row/control geometry, bounded header/icon paint and tests; graphcode-windows/src/App.zig sidebar-only pointer/hover/context adapters, sidebar portion of syncAccessibilityTo, ensureWorktreeVisible, jumpToNode scrolling tail only, bounded temporary sidebar fixture mutation/tests, and only a coupled expression change needed to preserve the existing 517-DIP client-height floor. graphcode-windows/README.md Sidebar scrolling and short windows subsection only is approved outside the original lease. A owns a separate text-prompt paragraph in that file; preserve it after rebasing.

    Harness extension approved: Tools/windows/uia-live-gate.ps1 initial sidebar navigation/section oracle and temporary sidebar-only fixture restore only; Tools/windows/Tests/ValidationRunner.Tests.ps1 named positive/negative functional cases for that oracle/restore only. Prefix helpers/tests distinctly for sidebar navigation. E is approved for separate menu expectation/connector-create sections in these files; do not rewrite its helpers or cases. Open #587 changes the isolation/timeout boundary and historical Worktrees traversal hunks; I inspected its diff. Do not touch those mechanisms, merge its branch, or claim it is integrated. Rebase and preserve accepted main before every push.

    I verified AppSidebarView.swift:332-377,390-506 and AppSidebarView+Rows.swift:11-110: Graph, Quick Chats, open local/remote groups, hover-only plus/disclosure, actual outline symbols. SidebarIcon.swift:25-38 establishes 15pt symbol/20pt column. AppSidebarView.swift:239-244 and Windows MainWindow.zig:594-608 confirm that recents have an existing Add Folder/Recent Folders route, so removing their sidebar projection must not remove data or menu access. AppSidebarView.swift:45-49,78-85 does not authorize default-expanding everything; 300-310 is the open-workspace-first selection rule, deferred here. Its attentionSection is commented at 334. I opened the Run A Quick Chats, min-width and final-card sidebar captures: they support navigation structure, not completion of all sidebar criteria.

    RED must be a compiling production appendRows/draw/routing/publication assertion: current recents precede Graph, Quick Chats follows folders, folds gate recents rather than loaded owners, and loading metadata is omitted by current paint/UIA guards. Test exact named positive owner rosters, independent folds, long names, hit/action ownership and clipped bounds; do not count arbitrary UIA/card sets or demand one selected element across aliases. Reuse current selection/destination semantics and maintain Worktrees/Needs-you/Activity actions as recorded Windows extras. Jump stays on the project canvas (#799); native selection support remains #801.

    Replace, do not delete, obsolete sidebar gate coverage; keep Recent Folders invocation, remote folding with a temporary named open owner, stable fragment links and reliable fixture restoration. Run ValidationRunner.Tests.ps1 as its executable script (pwsh -NoProfile -File), assert the new functional cases execute, and run visual-baseline. No other gate, C++, GraphModel, shared Swift, or ledger edits. Code review plus green exact-head CI precede the single screenshot lease. Check box 1 only if paired review accepts it; use Refs #767. Implementer ic uses C:\gcs\ic, C:\gct\ic, C:\gcp\ic and real C:\gcic, not the planner's pc paths.

    Implementation model (user-directed policy, 2026-10-14): gpt-6-luna, high, default context. The approved one-box slice reuses the existing Sidebar row projection, draw/hit/UIA adapters and fixture mechanisms; it excludes the broader tree, selection, footer and responsive-window architecture. Its harness edits are a bounded named-roster replacement, so no highly-complex exception is needed. Reviewer remains claude-sonnet-5.5, high.

    Launch hold (new user-directed planner-rating step): the approval/model assessment above was posted before this additional policy arrived. Scope/source review is complete, but effective model approval and implementation launch are pending the original planner's own standard/highly-complex rating and reason in its plan comment. No implementation session for this lane has launched.

    Final model/launch approval: the original planner has now added standard with its existing row-projection/geometry/adapter/named-oracle rationale to the plan comment. I read and accept the assessment for this one-box slice, not the deferred whole-sidebar work. The launch hold is lifted: use gpt-6-luna, high, default context, reviewer claude-sonnet-5.5, high. Exact leases and exclusions above remain unchanged.

  6. added a commit that references this issue on Oct 14, 2026
  7. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Named navigation correction approved (coordinator)

    The recoverable independent review of cd9e51a is NOT ACCEPTED until the Graph row stops offering project-destructive actions against graphcode://global. Suppress that invalid project context-menu target through the existing approved App navigation/context dispatch; do not add a speculative global-project menu.

    N1 is approved as a bounded active-route alignment, not a selection-style/tree redesign. Exact existing surfaces: Sidebar.draw plus one shared Sidebar row-selection predicate; App.syncAccessibilityTo uses that same predicate for overview-destination, quick-chats-destination and open-project rows. GraphCanvas.paint may pass its already-known active surface into Sidebar.draw at that one call site; this is the sole additional GraphCanvas.zig lease. Do not edit card rendering, fonts, geometry, topology or other B-owned paint logic. Graph remains visibly selected in blue on overview, Quick Chats on its canvas, and project rows use the selected project only on project/normal-workspace routes. Preserve the existing background/icon design, tree/keyboard/worktree/dialog selection behavior, scroll tails and 517-DIP floor. Do not absorb the unknown edit that removes Graph's selected background.

    Add named focused tests for overview, quick_chats, project and workspace routes, paint/UIA predicate agreement, retained Graph selection, and project-header suppression on non-project canvases. Do not silently equate every workspace with a normal project: a bound Quick Chat workspace must not acquire new incorrect project selection; report any additional input/dependency needed instead of broadening selection semantics. Full native-list selection cardinality remains outside this slice.

    N2: replace the seven-fixture claim with counts derived from actual executed positive/negative fixtures, and add the real never-shown App temporary-row append(22) then restoration(20) assertion, not merely a synthetic reset proxy. Use existing App test root; no WindowsShell/validate/gate refactor is authorized. N3/N4 need actual exact-head unit/App/integration log dispositions rather than an unauthorized local full UIA run. N5 needs an explicit visible/accessibility-label comparison against the pinned macOS source/reference; only directly coupled navigation-label corrections are in scope.

    Preserve unknown dirty edits intact and excluded; use clean worker-owned source and stage only independently implemented approved changes. Preserve prior failures/review, read the complete final diff, obtain fresh focused counts, exact-head CI/body evidence and the same different-vendor acceptance. No screenshot lease or broad local runner is granted. This does not close the sidebar issue or establish whole-sidebar parity.

  8. added a commit that references this issue on Oct 14, 2026
  9. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Coupled owner-selection oracle extension approved (coordinator)

    I read the exact 8a0f68e gate assertion comparing projectSelected and loopSelected to the same selected owner. Its positive project/loop identity, names, ownership, bounds and exact card checks remain valid; only the parent-project selected-state expectation is stale after N1. Pinned macOS AppSidebarView selectionBinding chooses the open node/chat, not its parent project, while a workspace is open.

    Approve ONLY the named two-project owner-evidence selection assertion in Tools/windows/uia-live-gate.ps1 and directly coupled functional controls in ValidationRunner.Tests.ps1: expect a project row selected only when the surface is the matching project canvas; expect no parent project-row selection on overview, workspace or Quick Chats. Retain the existing loop selected-owner expectation, exact card roster/card selection, ownership/identity/name/bounds, captured-snapshot and foreign-fragment guards. Do not replace these checks with a count-only or truthy fallback, and do not introduce a broad all-tree selection-cardinality oracle.

    Add focused positive/negative controls that reject a selected parent in workspace, a missing selected project on its matching project surface, a stale selected project on overview/Quick Chats where applicable, and loss of the expected loop selection. Preserve the failed exact-head integration evidence, then run the existing focused offline functional contracts and fresh exact-head CI. No local full UIA/integration or desktop execution is authorized. This clarifies active N1 state semantics, not a blue-pill/style redesign or expansion of the current slice.

  10. coneilen commented on Oct 15, 2026

    @coneilen
    CollaboratorAuthor

    The approved sidebar navigation code slice merged in #817, commit 24209d2a70efee78be2a8214edf013e9b787ccd0. The accepted combined B/D/C source review, current-head hosted checks and fresh Ready-event DCO were admitted; the six merged source blobs match the reviewed head.

    Visual acceptance: NotExecuted, tracked by #826. All six whole-issue criteria remain unchecked and this issue stays open. The navigation projection/route/UIA changes do not establish the selected fill's macOS visual match, full tree/footer/responsive parity or a paired screenshot. The offscreen renderer and selected exact-HWND route have their own separate source/plan/execution gates; this merge authorizes no capture or manual screenshots.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1 layoutParity priority 1: layout, clipping, overlap, broken rendering vs macOSenhancementNew feature or requestparityWindows/macOS UI parity ledger row (investigation/ui-parity-matrix.md)visual parityVisual fidelity against the macOS reference captureswindowsWindows port

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions