Skip to content

Windows parity: Quick Chats canvas and Quick Chat workspace show the chat, not the last loop #774

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: 720-quick-chats, 720-quick-chat-terminal;
  • Windows: tg720-quick-chats-1440, tg720-quick-chat-terminal-later-1440;
  • differences I1–I3, J1–J3.

Acceptance criteria:

  • The empty state has the bubble glyph, the full macOS copy, a small dark "New Chat" button, and "+ New Chat" at the top right. No attention rail is drawn.
  • In a chat workspace, the loop bar shows a violet stripe, "Chat — • IDLE", "Not piloted", the age and "Restart session". No loop panel. The single tab is the backend agent; no shell tab.

Activity

  1. added
    bugSomething isn't working
    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 D), 2026-10-14 07:17 PDT.

    Old model gpt-6-luna, high (lane D2)
    Trigger 2 substantive cross-vendor review rejections, per the abandon rule in investigation/windows-parity-orchestration-plan.md
    New model gpt-5.6-luna, high, long_context (lane D3); this is the operator-chosen fallback. Reviewer claude-sonnet-5.5.

    The two rejections:

    1. After Show in Graph, the workspace toggle stayed hidden.
    2. A stale workspace_is_quick_chat binding survives other exits from the workspace: open project, overview, composite selection, and the Quick Chats header / UIA routes.

    State handed over: PR #787 at 29b4e3eb, on the fork branch.

    Decisions carried over:

    D3's task: fix the stale binding at its root, with a table-driven exit-route test written first.

  3. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

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

    • The Quick Chat bar (LoopWorkspaceLoopBar.swift): a violet stripe, "Chat — • IDLE", "Not piloted", Restart session (a no-op for chats on macOS, AppFeature+LoopSessions.swift:105-107, tracked in macOS: Quick Chat loop bar shows a Restart session button that does nothing #804) and Show in Graph; no loop panel.
    • Don't use a sticky workspace_is_quick_chat flag. It survived exits from the workspace: Show in Graph, open project, overview, composite, and Quick Chats header routes. Derive "is a chat" from the bound workspace instead, and test every exit route.
    • Publish the bar's UIA nodes even with no project graph loaded.
    • Keep Windows: make the right loop panel readable and match its macOS hierarchy #789's loopDetailSparklineBounds and loopDetailFooterBounds. A rebase silently reverted them once.
    • NotVerified: the persisted-daemon chat attach. The stub fixture showed "Opening quick chat..." with no Agent tab.

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

  4. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Implementation plan (wave 2)

    Planning baseline: origin/main = 8ea81062a6220e1d2ecfdaa8a73d8eb59ac575b4 (#806). Repository investigation was read-only. I read the canonical kickoff from origin/main in full, CONTRIBUTING, AGENTS, the orchestration plan, both issue comments, the relevant ledger rows, macOS and Windows source, the reference README, and the four paired images named below. No desktop, build, or runtime test was used for this planning pass. Proposed test failures below are expected RED, not claimed executions.

    Complexity: standard. This rates only the approved chat identity/bar/no-panel slice. It derives one chat subject from existing workspace scope and wires that subject through the existing paint, layout, hit-test and UIA paths in App/TerminalSurface. The supporting out-of-lease edits are bounded: one nullable creation date using the existing decoder/date helper, chat-specific geometry using the existing bar contract, and one native AutomationId prefix with hidden regression tests. Although App and TerminalSurface are large files and their presentation paths must agree, this slice introduces no new multi-surface framework, cross-lane architecture, or session mechanism. The deferred daemon-agent attachment, default creation title/backend and empty-canvas redesign are not included in the rating.

    The Wave 1 stopped decision is the starting point, not the abandoned #787 diff. Its decisions are confirmed by current source. In particular, Restart session remains visible and is a no-op for Quick Chats; do not implement a daemon restart for a nonexistent graph node (#804).

    1. Acceptance-box audit

    Box 1: empty Quick Chats canvas

    The empty state has the bubble glyph, the full macOS copy, a small dark "New Chat" button, and "+ New Chat" at the top right. No attention rail is drawn.

    Requirement macOS ground truth Current Windows behavior and root cause
    Bubble, title, full explanation graphcode/Sources/Features/App/QuickChatsCanvasView.swift:242-254 supplies bubble.left.and.bubble.right, No chats yet, and A quick chat is a bare session with no goal, no trigger, and no place in any graph — for the questions that aren't a loop's work. graphcode/Sources/Features/Project/CanvasEmptyState.swift:63-78 lays out a 34-point symbol, title, centered copy limited to 340 points, and the action. graphcode-windows/src/GraphCanvas.zig:857-868, drawQuickChats, draws only title and shortened copy. There is no glyph or equivalent empty-state composition. This is missing rendering, not an unavailable daemon field.
    Two independent New Chat controls, including when empty QuickChatsCanvasView.swift:78-81 adds the top-trailing CanvasAddButton unconditionally; :242-254 separately supplies the centered action. Confirmed in 720/720-quick-chats@1x-srgb.png. App.zig:7063-7112, layoutEmptyStateControls, moves one standard native button between centered-empty and top-right-populated placements. It cannot expose both at once, and its 220 x 32 empty action does not match the compact dark macOS action.
    No canvas attention rail The Quick Chats canvas above has no attention-rail child; the separate window-level attention chip may remain. Reference capture has the chip but no band over the canvas. GraphCanvas.zig:596-598,748 paints the generic canvas attention rail for Quick Chats; its surface predicate excludes only .workspace, not .quick_chats. The Windows paired image shows both the attention band and worktree-status band. This overlaps lane B's #766.

    Relevant ledger: investigation/ui-parity-matrix.md:134,166,188-190. The older Validated Quick Chats canvas row at line 188 is not proof of the newly reopened visual criteria.

    Box 2: chat workspace

    In a chat workspace, the loop bar shows a violet stripe, "Chat — date • IDLE", "Not piloted", the age and "Restart session". No loop panel. The single tab is the backend agent; no shell tab.

    Requirement macOS ground truth Current Windows behavior and root cause
    Chat identity, IDLE, violet stripe, Not piloted AppFeature+QuickChats.swift:17-24,105-121 creates the dated default title and opens a synthetic composite node using the chat's own ID, title, backend, and creation date. LoopWorkspaceLoopBar.swift:24-60 renders that node. LoopCardPresentation.swift:128-141 derives Not piloted from the unpiloted composite with no subgraph; Features/App/LoopTypeAppearance.swift:43-47 gives composite #9085e9. App.zig:3039-3064, openQuickChat, sets a sticky workspace_is_quick_chat flag and a list index, but WM_PAINT at :9128-9158 still obtains its bar node from workspaceGraph and model.selectedIndex(). workspaceGraph at :1249-1258 even falls back to another cached project's graph. TerminalSurface.zig:2126-2181, paintLoopBar, then draws that loop's title/state/activity/metadata and Stop. A correct window header at App.zig:7312-7335 does not correct the independently selected bar.
    Real chat age, no invented loop passes or usage LoopWorkspaceLoopBar.swift:93-102 shows duration from node.createdAt, and usage only if actually present. Duration formatting is LoopCardPresentation.swift:272-279: seconds, minutes, hours/minutes, days/hours, with negative elapsed time clamped to zero. GraphModel.zig:45-51 has no creation-date field on QuickChat, and upsertQuickChat at :951-979 discards createdAt. The generic bar at TerminalSurface.zig:2155-2168 instead displays the selected loop's backend, coarse elapsed bucket, pass count, and usage n/a. This cannot be fixed honestly by stamping the time at open.
    No loop panel or panel-toggle hotspot LoopWorkspaceFeature.swift:17-26 gives a chat an empty graph; LoopWorkspaceView.swift:20-47,150-151 and LoopWorkspaceRail.swift:165-184 draw a rail only when it has content. The synthetic chat has no relevant edges, metrics, summary, or board. openQuickChat forces panel_visible = true (App.zig:3045), layoutWorkspace reserves loop_detail_width (:6996-7039), and WM_PAINT paints the old graph's rail (:9169-9181). Header-only GraphCanvas.loopPanelHasContent(..., quick_chat) guards do not protect these paths, the mouse panel-collapse path at App.zig:9501-9506, or the unconditional workspace UIA toggle at :7881-7884.
    Restart session visible, safe no-op LoopWorkspaceLoopBar.swift:112-117 shows Restart on the unresolved synthetic node. AppFeature+LoopSessions.swift:78-79,105-107 routes it to a handler that explicitly returns without a daemon command for chats. The generic Windows bar exposes Stop (TerminalSurface.zig:2170-2173); mouse and UIA invoke stopSelectedNode (App.zig:9513-9520,8359-8360), which targets the selected graph loop (:4297-4301). This is the wrong object and destructive action.
    Identity survives navigation and model refresh correctly macOS owns the active workspace in state.openLoop; Quick Chats header clears it (AppFeature+QuickChats.swift:27-30), and Show in Graph clears it (AppFeature.swift:601-609). The flag is set at App.zig:3043,8347 and cleared only on selected entry routes (:4282,6976). It survives Show in Graph (:6830-6836), open project (:2565-2569), overview (:2659-2666), composite activation (:4264-4276), and Quick Chats header routes (:8287-8292,8956-8962,9786-9803). The stored chat index also becomes stale when the chat list is replaced or reordered.
    Workspace accessibility, including no graph loaded The macOS workspace is created from the synthetic chat independently of a project. syncAccessibilityTo publishes a chat-workspace marker (App.zig:7707-7736), but actual bar/actions/tabs are nested under .project, .workspace => if (self.model.graph) (:7823-7916) and use the selected graph node. With no graph, the marker can exist while no bar actions are published.
    One initial backend agent tab, not a shell LoopWorkspaceView.swift:231-243,286-295 derives the tab label from the backend for a composite/chat; the capture shows Copilot CLI and an agent pane. AppFeature+QuickChats.swift:105-118 uses TerminalLayout.opening(forNode:). Additional shell tabs explicitly created by the user are allowed; the acceptance language must not be implemented as destroying saved user tabs. App.zig:3053-3056 calls scopeToLoop(..., false) then openNode, the plain-shell-capable path documented at TerminalSurface.zig:1185-1192. It does not mount the agent layout as openLaunchedNode does at :1195-1244. TerminalSurface.zig:7142-7146 also names the first tab agent solely by index. A real daemon starts/ensures the chat's backend on open (GraphcodeKit/Sources/ProjectRegistry.swift:95-107,769-781); create just persists and broadcasts (:753-767). Other daemon lifecycle paths may also ensure sessions; this statement is about the compared create/open commands. A protocol stub returning a chat change is not proof that a persistent agent exists.

    Relevant ledger: ui-parity-matrix.md:106,194. The historical claim that a bounded Quick Chat terminal workspace fragment establishes a persistent chat is insufficient; the row remains Partial.

    Images actually opened: macOS 720/720-quick-chats@1x-srgb.png, 720/720-quick-chat-terminal@1x-srgb.png; Windows tg720-quick-chats-1440.png, tg720-quick-chat-terminal-later-1440.png. These confirm the source-derived I1-I3/J1-J3 diagnosis; the beta26 captures are historical, not evidence of a wave-2 fix. The visual review also records default-title difference J4.

    2. First PR: chat workspace identity, bar, and panel isolation

    Choose the primary P1 defect first: a Quick Chat must never display or act on the previous graph loop's bar/panel. Include the correct chat bar, real age plumbing, safe Restart/Show in Graph routes, correct layout, and accessibility derived from the same bound identity. Do not combine empty-canvas redesign or session-attachment repair into this PR.

    Acceptance accounting:

    • No entire original acceptance box closes in this slice. Box 1 remains untouched. Box 2 is intentionally only partially addressed: chat-specific bar/age/actions/no-panel and the navigation regression close, but the initial backend-agent tab and persisted-daemon attachment remain outstanding. Keep both original checkboxes unticked, use Refs #774, and enumerate these exact limits in the PR.
    • The displayed title must be the persisted chat title, including an existing date or a user rename. Do not fabricate a date for a chat saved as Chat. Changing createQuickChat's default date/title/backend is deferred with the creation/session continuation; macOS newQuickChatTapped is the reference for that follow-up.
    • Do not replace a real shell or a pending attach with a fake agent label and claim J3 is fixed. Do not change graph-loop Restart/Stop behavior in this slice; Windows parity: Loop workspace chrome: loop bar, tab bar, split controls and pane headers #772 owns general loop-workspace chrome.
    • Keep Windows: make the right loop panel readable and match its macOS hierarchy #789's GraphCanvas.loopDetailSparklineBounds and loopDetailFooterBounds and their App call sites unchanged.

    This is smaller and safer than repeating wave 1's apparent visual success while leaving a sticky mode bit or claiming an agent from a stub.

    3. Implementation steps and expected RED

    All test names below are proposed. Put tests in already registered roots; no new Zig test root or runner registration is needed. Use a common Quick Chat parity filter prefix. For a new observation seam/type, first add only a behavior-preserving extraction/default so the test compiles against the old behavior; observe an assertion failure before changing that behavior. A missing function, enum case, or struct field is not behavioral RED.

    Step A — authoritative chat binding, not another mode flag

    Files/functions: TerminalSurface.zig Workspace.scopeToLoop/workspace scope access; App.zig openQuickChat, refreshOpenTerminals, headerPresentation, syncAccessibilityTo, and the Quick Chat branch of applyUiaDynamicInvoke.

    • Remove workspace_is_quick_chat. Derive the active subject from the current destination plus the workspace's actual layout.project_key == quick_chat_scope_project and nonempty loop_id. Resolve the chat record by that ID on each publication, not by selected_quick_chat index.
    • Distinguish "bound chat whose record is missing" from "graph-loop workspace"; never fall back to workspaceGraph for a chat. On deletion of the bound chat, leave the workspace for Quick Chats using the normal hide/collapse path. Rename/reorder/relist refreshes must continue to resolve the same bound ID.
    • Stage scope binding before entering .workspace, including the native UIA fixture route. Check rebind/scope failures, publish the existing explicit status error, and stop instead of displaying the old loop under a chat header. Do not retain catch {} as apparent success for the changed binding path.
    • The UIA gate currently fabricates chat state instead of binding a workspace (App.zig:8341-8356). Route this through the same scope-binding code. With no initialized workspace, report unavailable rather than manufacturing a workspace marker. Production CI passes a real zmx path (Tools/windows/validate.ps1:1292-1294), which allows workspace initialization at App.zig:1789-1806.
    • Do not change scopeToLoop(..., false)/openNode to an agent-launch path in this slice; that is the explicitly deferred J3 repair.

    Tests/root/assertions/main RED:

    • App.zig: Quick Chat parity bound identity ignores previous loop and chat list order. Bind chat B with graph loop A still selected; reorder/relist chats and rename B. Assert header/bar subject remains B, with B's renamed title, and graph selection remains A. Main selects A for the bar and uses the stale index for the chat header.
    • App.zig: Quick Chat parity exit routes never retain a chat-only presentation. Table-drive production route state logic, with only native layout/focus/publication boundaries injected: Show in Graph; openProjectWithLayout; overview; composite activation; Quick Chats pointer header; dynamic UIA header; fixed UIA destination command; subsequent normal-loop activation and workspace-toggle entry. Assert the route's actual destination, hidden chat bar, normal loop-panel availability after opening a real loop, no unexpected graph mutation, and correct re-entry when the chat is opened again. Main retains the sticky flag on the listed exits, but some existing surface guards already hide it; those individual rows may be regression-only passes. The intended behavioral RED is the wrong-subject/panel/action publication while the chat is open and the stale-index refresh case, not an unsupported claim that every exit already visibly fails on main. Do not write a test that merely clears/sets the flag or manually assigns the expected destination.
    • App.zig: Quick Chat parity binding failure and deleted record never expose selected loop actions. Inject missing workspace, missing home/bind failure, failed scope, and deletion/replacement of B. Assert no old-loop Stop/Restart target appears and a failure is surfaced or the deleted chat exits. Main transitions before binding and continues after caught failures.

    Use existing showInGraph injected finish/publication and openProjectWithLayout patterns. Extract equivalent narrow native-boundary seams only where required for safe hidden tests; do not invoke SetForegroundWindow, SetFocus, ShowWindow, or desktop-wide invalidation from a focused planning/implementation test.

    Step B — retain the daemon's chat creation time

    File/functions: GraphModel.zig QuickChat, upsertQuickChat, existing JSON/date helpers.

    • Add nullable Unix-second created_at to QuickChat. Decode the chat object's immediate createdAt field using existing JsonFields and unixSecondsFromReferenceDate (:1886-1891); the wire uses Swift reference-date seconds, not Unix seconds.
    • Keep unknown/placeholder/malformed values unknown. Preserve creation date across title-only updates that omit it; update from a full authoritative chat record when supplied. Do not read a nested activity/metadata date.
    • No protocol, shared Swift, persistence-format, provider-pin, or whole-model-parser rewrite.

    Tests/root/assertions/main RED:

    • GraphModel.zig: Quick Chat parity creation date survives list changed rename and reorder. Use v2 quickChatsListed/quickChatChanged frames with createdAt:812912680.0330639; assert 1791219880 Unix seconds after decoding and after title updates/reorder. Stage the nullable field with default null before running RED; the old decoder leaves it null.
    • GraphModel.zig: Quick Chat parity creation date rejects nested and invalid values. Cover immediate versus nested fields, missing/null/zero/negative/overflow/string values and positive fractional seconds, using the existing node-date policy rather than a new clock heuristic. Ensure the valid case executes and fails on old decoding; negative-only success is not RED.

    Step C — a chat bar with one geometry/action contract

    Files/functions: LoopBarLayout.zig add a chat-specific computation/action without changing existing loop geometry; TerminalSurface.zig add Workspace.paintQuickChatBar and a deterministic chat-age formatter; App.zig WM_PAINT, layoutWorkspace, workspaceLoopBar/chat counterpart, WM_LBUTTONDOWN, toggleWorkspaceDetailPanel and contextual header invocation.

    • Use the bound chat presentation for all paint, mouse, keyboard/header enablement, and UIA paths. The chat bar spans the whole terminal content width; no loop_detail_width deduction, no rail paint, no expand/collapse button, and no invisible panel hotspot, even if the previous loop left panel_visible = true.
    • Render the violet #9085e9 stripe (Win32 COLORREF byte order), persisted title, IDLE pill/dot, Not piloted, and creation age; match the macOS 46-point band, title/subtitle hierarchy, spacing, and quiet actions. No project subtitle, raw backend ID, loop pass count, fabricated usage, or selected-loop activity.
    • Keep Restart and Show in graph at the trailing edge. Restart needs enough width for its actual label; do not relabel the old 84-pixel Stop slot without changing its bounds. The same returned rectangles must drive paint, pointer hit tests and UIA.
    • Restart session for a bound chat consumes the action without a graph command or terminal recreation, matching macOS/macOS: Quick Chat loop bar shows a Restart session button that does nothing #804. Guard both direct and stale UIA invocations. Show in Graph uses the existing navigation outcome, hides chat chrome, and does not retarget the selected graph loop.
    • Match LoopCardPresentation.duration: e.g. 0s, 59s, 1m, 1h 1m, 1d 1h; future timestamps clamp to 0s. Unknown dates do not become 0s or an invented open-time age.
    • Preserve current normal-loop layout/action behavior and Windows: make the right loop panel readable and match its macOS hierarchy #789 detail geometry. This is not a general workspace/tabs redesign.

    Tests/root/assertions/main RED:

    • LoopBarLayout.zig: Quick Chat parity bar geometry has restart and no panel slot. At 1440, 1200, 960 and constrained widths, with sidebar shown/hidden, assert positive in-client action rects, no overlap among title/pill/age/actions, full-width chat bar, sufficient Restart width, no Stop/panel-toggle rect, and matching hits inside/outside every action. Run existing loop-layout tests unchanged alongside it. Old bar reserves a panel slot or exposes Stop, and its Stop width is insufficient for Restart.
    • TerminalSurface.zig: Quick Chat parity presentation has chat-only identity and age. Exercise the production presentation/formatting used by paint, with a fixed now and creation date; assert the exact title, IDLE/Not piloted, violet, age boundaries, unknown-date omission, and absence of loop-only metadata. A behavior-preserving extraction of the old bar inputs first gives the expected selected-loop/pass/usage mismatch rather than a compile failure.
    • App.zig: Quick Chat parity restart and show graph never stop selected loop. With an unstarted real DaemonClient outbound queue and graph A selected, dispatch both the pointer-resolved action and the dynamic-UIA action. Assert Restart enqueues zero commands and leaves the bound session/layout unchanged; assert Show in Graph changes destination only. Main's bar action maps to stopSelectedNode, enqueueing a command for A.
    • App.zig: Quick Chat parity chat panel stays absent across DPI and prior panel state. Assert effective terminal/bar bounds use full content width, neither toggle path changes a chat into a loop rail, and no selected-loop detail nodes or hotspots are present. Include DPI 96/144/192 and prior panel true/false. Keep the existing normal-loop sparkline/footer geometry assertions intact.

    Step D — publish the same chat chrome without any project graph

    Files/functions: App.zig syncAccessibilityTo, applyUiaDynamicInvoke targets; AccessibilityProvider.cpp dynamic AutomationId prefix mapping; Tools/windows/Tests/AccessibilityProvider.Native.Tests.cpp.

    • Publish chat workspace/bar/title/metadata and Show in Graph/Restart outside the graph-dependent switch. Use the bound chat ID for identity. Do not expose the previous loop's project cards/bar/Stop/detail rail as active chat content.
    • Register a distinct workspace-restart-session target and native AutomationId prefix; do not encode a harmless chat Restart as a misleading workspace-stop row.
    • Leave tab/attach claims unchanged: publishing a bar must not fabricate an Agent tab.

    Tests/root/assertions/main RED:

    • App.zig: Quick Chat parity UIA publishes bar and actions without a graph. Through syncAccessibilityTo and the existing sink pattern, require exactly one bound-chat bar, named Restart, and Show in Graph; zero Stop/panel/detail nodes; all bounds nonempty, inside client, DPI-scaled exactly once. Repeat with a stale loaded graph/selected loop, reordered chat list, and no graph. Main publishes only the outer marker in the no-graph case, and loop actions in the loaded-graph case.
    • Native C++ root Tools/windows/Tests/AccessibilityProvider.Native.Tests.cpp: add nativeQuickChatRestartChecks. Publish workspace-restart-session:chat-b into a hidden provider window; assert UIA_AutomationIdPropertyId starts with workspace-restart-session-, Name is Restart session, bounds match the published rectangle, Invoke posts the exact hashed identity once, and stale retained fragments cannot invoke a different row after replacement. Main falls through to the generic canvas-card- prefix. Extend the existing native test executable; no script/runner changes.

    4. File scope requiring coordinator approval

    File Lease Exact intended scope
    graphcode-windows/src/App.zig Lane D workspace layout and Quick Chat state Only the functions/routes above and embedded focused tests. Native pointer/UIA entry adapters for workspace navigation require narrow shared-file coordination with C/E; no unrelated window-procedure or menu changes.
    graphcode-windows/src/TerminalSurface.zig Lane D Bound scope access if needed, chat-bar paint/presentation/age, embedded focused tests. No VT/input/provider/session-lifecycle rewrite.
    graphcode-windows/src/GraphModel.zig OUTSIDE LEASE — approve explicitly QuickChat.created_at, immediate-field date decoding/preservation in upsertQuickChat, embedded tests. Necessary because otherwise real chat age is unavailable.
    graphcode-windows/src/LoopBarLayout.zig OUTSIDE LEASE — approve explicitly Add chat-bar geometry and a distinct restart action while preserving existing loop layout behavior; embedded geometry tests. Necessary for one paint/hit/UIA contract and a correctly sized Restart action.
    graphcode-windows/src/AccessibilityProvider.cpp OUTSIDE LEASE — approve explicitly One native workspace-restart-session: AutomationId mapping; no provider redesign.
    Tools/windows/Tests/AccessibilityProvider.Native.Tests.cpp OUTSIDE LEASE — approve explicitly Hidden native Restart prefix/Invoke/stale-fragment regression, run by the existing PS1 wrapper.

    No GraphCanvas.zig, Sidebar.zig, DesignTokens.zig, NativeForms.zig, shared Swift, provider pins, uia-live-gate.ps1, validate.ps1, workflow, or runner edits in the first PR. Ledger updates belong to the coordinator after accepted evidence. A directly related README note may document the deliberate chat Restart no-op and outstanding attach limitation if the coordinator approves graphcode-windows/README.md; do not promote ledger status.

    5. Follow-up slices, not part of this PR

    1. Finish the remainder of box 2 (J3/J4). Reuse scopeToLoop(..., true)/ensureAgentFirst and openLaunchedNode, not openNode, after the daemon has ensured the session. Audit create versus open: create currently broadcasts without starting, and both open and rename reuse quickChatChanged; do not attach/spawn a shell just because any chat-change event arrives. Match macOS's dated default title and configured default backend, and derive the initial tab's backend display name/pane role from actual agent ownership rather than index. Preserve saved user-created tabs/splits. Required new evidence: production daemon + pinned zmx + a controlled backend fixture, actual backend identity/output in the initial agent pane, no shell session created, restart/reopen restores the same session/scrollback, and failed launch is visible with no shell fallback. The wave-1 stub's Opening quick chat... and missing Agent tab remain NotVerified, not a passed attach test. If this needs daemon/client/shared Swift changes, get a new explicit lease; do not expand the approved first slice.
    2. Box 1 empty-canvas parity. Coordinate with B/Windows parity: Attention rail, toolbar chips and header status match macOS #766 before changing GraphCanvas.drawQuickChats/attention-rail gating, and with C/A for shared App/native-button surfaces. Implement separate top-right and centered compact dark add actions, full copy, glyph, and no canvas attention/worktree band. Geometry/render/action tests plus a paired empty capture are required. Keep window-level attention functionality; remove only the extra canvas band. This scope cannot be smuggled into lane D's existing lease.

    6. Risks and guardrails

    • Shared App and concurrent lanes: C/Windows parity: Sidebar and window layout match macOS (tree, colours, sections, minimum width) #767 owns sidebar/window handling and E owns menu construction; B owns Windows parity: Canvas loop cards match the macOS card (layout, states, roles, colours) #764/Windows parity: Attention rail, toolbar chips and header status match macOS #766 and the merged Windows: make the right loop panel readable and match its macOS hierarchy #789 right-panel geometry. Current open-PR query found no active wave-2 parity PR yet; this is a point-in-time observation, not a lease. Rebase immediately before each push and read every hunk of git diff origin/main...HEAD; preserve both Windows: make the right loop panel readable and match its macOS hierarchy #789 helpers/call sites.
    • Hidden UIA test shortcut: changing just the production route while the gate still sets a boolean would conceal the defect. Both must bind the same actual scope. The full UIA gate stays in CI; local tests must not show/foreground/focus a shell.
    • Date decoding: Swift dates are reference-date seconds; fractional values and nested activity objects must not select another field or overflow. Do not write a time-of-open cache as a substitute for the daemon's creation date.
    • DPI/theme/text: chat and loop geometry use logical pixels; terminal HWND/UIA bounds use physical pixels. Scale once at the existing boundary. Restrict the new correct violet/pill/font styling to the chat presentation; do not silently recolor all loop types. Run visual-baseline locally because palette/font presentation changes, even without editing DesignTokens.
    • Actions: a stale restart/toggle fragment must not mutate graph A after leaving the chat. Do not disable/remove the visible macOS Restart button merely because it is a no-op. Keep normal-loop actions unchanged.
    • Harness: no gate/runner edits approved by this plan. If an actual gate change becomes necessary, ask the coordinator first with exact function/reason; add positive and negative named-element functional cases to ValidationRunner.Tests.ps1 and run that root before push. Do not count all canvas children as chats.
    • Environment: use implementer's own C:\gcs\ID, C:\gct\ID, C:\gcp\ID; seed verified caches then bootstrap pinned tools. Validation root must be a real directory of at most seven characters, e.g. C:\gcd2, not a junction. Set TEMP/TMP process-only; use process-scoped support/profile and seed onboarding-seen in GRAPHCODE_SUPPORT_DIR. Do not touch the stale default profile. Pester is 5.7.1 at C:\v5modules. No local full unit/integration run or desktop use before the final approved screenshot lease.

    7. Validation and evidence plan

    Local RED/GREEN: preserve separate failing/passing logs under the implementer's scratch root. Use the pinned Zig 0.15.2, --cache-dir/--global-cache-dir under that root, and a positive executed test count for each focused command. Existing roots/flags are registered in Tools/windows/Tests/WindowsShell.Tests.ps1:979,1214-1218:

    • zig test src\GraphModel.zig --test-filter "Quick Chat parity" with the pinned executable/cache flags.
    • zig test src\LoopBarLayout.zig --test-filter "Quick Chat parity" and the existing full pure LoopBarLayout root as the small geometry regression.
    • zig test src\TerminalSurface.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -ladvapi32 "-I$include" $terminalVtLib --test-filter "Quick Chat parity" with pinned executable/cache flags.
    • zig test src\App.zig src\AccessibilityProvider.cpp src\FilePicker.c src\FolderPicker.c $winghosttyLib -DUNICODE -D_UNICODE -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus -lmsimg32 -lopengl32 -lkernel32 -limm32 -lole32 -loleaut32 -luiautomationcore -lshell32 -ladvapi32 -lwinhttp "-I$include" $terminalVtLib --test-filter "Quick Chat parity" with pinned executable/cache flags. Use only the new native-effect-injected/hidden tests locally, not the full App root.
    • pwsh -NoProfile -File Tools\windows\Tests\AccessibilityProvider.Native.Tests.ps1 with process TEMP/TMP pointing at owned scratch; the wrapper keeps MSVC setup and compile/run in the same cmd process.
    • pwsh -NoProfile -File Tools\windows\validate.ps1 -Task visual-baseline using pinned tool environment.

    These are execution templates, not commands claimed run by this planner. The implementation PR must record expanded actual commands, positive counts, and real outputs. No script modifications means no local ValidationRunner edit-test requirement for this slice.

    Regression/review: push a draft PR, require every exact-head workflow/job green with nonzero run/job counts; use the issue-block's cross-vendor reviewer before screenshots. The reviewer opens all cited logs, checks rebase scope, and verifies RED is behavioral. Follow the repository PR template and TDD-body checker. No local whole-suite/desktop substitute for CI.

    Fixture: privacy-safe graph project with selected loop Ref Main that has at least two metric samples/edges (so its panel/toggle truly exists before opening the chat), plus two persisted chats with stable UUIDs, different titles/backends and a recent positive createdAt. Include one title Chat — Oct 14 and one renamed/long Unicode title; use JSON-encoded Swift-reference seconds. Add a no-project chat-only fixture and a refreshed/reordered chat list. onboarding-seen belongs in the fixture support directory. Fixture shells/backends must be owned and must not use user credentials or personal prompts.

    Final desktop captures, once after green CI and accepted code review: obtain LEASE REQUEST lane=ID purpose=774-chat-bar-and-navigation est=15, wait for grant, and take main-before/head-after at 100% with matching client size and fixture. Use PrintWindow(hwnd, hdc, 2) with pixel verification. Do not call ActivateWindow. Keep a literal three-column macOS/before/after table and label the subcriteria shown:

    Windows state macOS pairing What it establishes / does not establish
    Open metric-bearing Ref Main, then chat B with prior panel expanded 720/720-quick-chat-terminal@1x-srgb.png Chat identity/violet/IDLE/Not piloted/real age/Restart/Show in graph; no Ref Main bar, Stop, panel or toggle. Do not claim the backend-agent tab or terminal attachment from this image.
    Open chat with no project graph loaded Same chat-terminal capture for workspace chrome; explicitly note fixture difference Bar/actions still present and correctly bounded; no dependency on cached graph. This is not new macOS no-project runtime evidence.
    Show in Graph, open normal Ref Main with panel available, return to chat; also Quick Chats-header exit and re-entry 720/720-ref-main-workspace@1x-srgb.png / chat-terminal capture Neighboring loop workspace recovers its normal panel/toggle; chat-only mode does not leak after exits. Pure tests cover the larger route table.
    Long renamed chat at constrained width; primary capture at 1440 x 900 content and 100% Same chat-terminal source contract; do not claim a dedicated macOS narrow-chat capture exists No text/action overlap or clipping. DPI 150%/200% geometry is unit evidence unless separately captured.

    The existing macOS reference does not prove persistent backend attach after a Windows daemon restart. Do not spend the screenshot lease trying to turn that separate gap into a stub success. The empty Quick Chats pair is reviewed context only for this PR, not an improved-after claim.

    Exit via File > Exit; stop only owned PIDs including zmx daemons, verify none remain, then release the lease. Upload only privacy-safe fixture images using the media skill. Never upload/quote 734/*.zip contents or 721-needs-you-chip-clicked.

    Not verified / left open: full acceptance box 1; backend-agent initial tab and daemon persistence/scrollback portion of box 2; physical keyboard, mixed-monitor DPI, general loop workspace styling (#772), default-title/backend creation parity, and any state for which no paired capture was taken. The coordinator, not this worker, updates ledger evidence/status after review.

  5. coneilen commented on Oct 14, 2026

    @coneilen
    CollaboratorAuthor

    Plan approved (coordinator)

    Approved: #774 (comment), bound Quick Chat workspace identity/bar/panel isolation only. No whole acceptance checkbox closes: empty-canvas redesign, initial backend-agent tab, dated default creation title/backend and persisted-daemon attach remain deferred. Do not manufacture an Agent label or a successful attach from the stub.

    Approved original scope: graphcode-windows/src/App.zig only Quick Chat binding, workspace presentation/layout/paint/action/UIA routes and the named hidden/effect-injected tests; graphcode-windows/src/TerminalSurface.zig only actual bound scope access, chat-specific bar/presentation/age and related tests. Preserve normal-loop Stop/Restart behavior and #789 loopDetailSparklineBounds/loopDetailFooterBounds and call sites.

    Explicit out-of-lease approvals: graphcode-windows/src/GraphModel.zig only QuickChat.created_at, immediate-field createdAt decoding/preservation in upsertQuickChat and embedded tests; graphcode-windows/src/LoopBarLayout.zig only chat-specific geometry/restart action and regressions preserving normal loop layout; graphcode-windows/src/AccessibilityProvider.cpp only the workspace-restart-session: prefix mapping; Tools/windows/Tests/AccessibilityProvider.Native.Tests.cpp only its hidden prefix/name/bounds/Invoke/stale-fragment cases. No README, GraphCanvas, Sidebar, DesignTokens, other decoder, shared Swift, daemon/client protocol, provider pin, gate/runner or ledger edits.

    I verified AppFeature+QuickChats.swift:17-30,100-121 (persisted title/backend/date and synthetic composite identity); LoopWorkspaceLoopBar.swift:24-60,93-117 (46pt band, age, visible Restart); AppFeature+LoopSessions.swift:103-107 (chat Restart returns without a daemon command); LoopCardPresentation.swift:128-141,272-281 (Not piloted and duration); LoopTypeAppearance.swift:47 (violet); LoopWorkspaceView.swift:20-47 and LoopWorkspaceRail.swift:165-184 (content-gated rail). I opened both Run A Quick Chats images: one Copilot CLI tab is observed there, but this PR must not claim to repair that deferred path.

    Remove the sticky workspace_is_quick_chat flag; derive identity from current destination plus the workspace's actual project_key/loop_id and resolve by stable chat ID, never a list index or fallback graph. Binding failures must report failure before publishing an old workspace as a chat. Deleted/missing chat records must not expose graph-loop Stop/actions. Table-drive real exit-route state logic with native effects isolated, including Show in Graph, project/overview/composite, pointer/UIA/fixed Quick Chats headers and normal-loop re-entry. Preserve C's sidebar row geometry/identities when touching these route adapters; B owns the separate project-card UIA block and E menu construction.

    Required RED is a compiling assertion on the old wrong-subject bar/panel, stale-index refresh, Stop targeting a selected graph loop, missing no-graph bar/action publication, or actual dropped positive createdAt value. New enum/struct/helper compile errors are not RED. Keep all failed logs and positive test counts; new native tests remain hidden. One geometry source drives paint/hit/UIA, including a measured Restart slot and no invisible panel hotspot. Restart consumes the action with zero graph/session mutation, including stale UIA invocations, matching #804. Unknown creation dates remain unknown; never substitute time-of-open. Do not repaint normal loop chrome as an incidental cleanup.

    Run focused roots, native provider wrapper and visual-baseline locally; full regression is green exact-head CI, followed by code acceptance and one <=15-minute screenshot lease. The suspected Opening quick chat status is unconfirmed on main: preserve that limit and do not file or diagnose a product bug from the stub. If a production-daemon main-before observation can be made within the approved capture lease, report the exact result; do not spend the slot expanding into attach repair. Use Refs #774; both issue boxes stay unticked. Implementer id is id: C:\gcs\id, C:\gct\id, C:\gcp\id and real C:\gcid.

    Implementation model (user-directed policy, 2026-10-14): gpt-6-luna, high, default context. The approved coherent slice derives one bound chat identity and reuses existing workspace/layout/provider paths; the extra files carry a nullable date, chat-only geometry and one native prefix. It excludes session attachment, empty-canvas redesign and general workspace architecture. 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 bound-workspace/presentation/date/helper/native-prefix rationale to the plan comment. I read and accept that assessment for the identity/bar/no-panel slice, excluding session and empty-canvas mechanisms. 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.

    Narrow metadata-label exception (D clarification): TerminalSurface.tabLabel and App workspace-tab UIA names may derive Agent/Shell from existing pane.launches_agent metadata instead of tab index. Verified WorkspaceLayout.Pane/ensureAgentFirst and current tabLabel; this corrects misleading role presentation without changing role state or launching/attaching anything. Remove chat-specific force-Shell overrides so saved Agent metadata is not hidden. Main paintChrome already resolves the mounted session through the selected tab and its role metadata; preserve that behavior, allowing only behavior-preserving extraction, not a chat override. Require focused first-Shell, saved-chat-Agent, nonzero-index-Agent, split and normal-loop label regressions and paint/UIA consistency. No backend display-name, lifecycle, style or geometry expansion. Declared role labels are not runtime backend/attach proof; the initial Agent-tab/session-attach acceptance criterion remains deferred and unticked. Document this small plan deviation explicitly.

  6. coneilen commented on Oct 15, 2026

    @coneilen
    CollaboratorAuthor

    Narrow scope extension: Quick Chat bar AutomationId mapping

    Post-B source review of local D head 8d5b914 found App.zig publishing workspace-quick-chat-bar:ID while the native provider has no corresponding prefix, so clients receive the canvas-card fallback. The coordinator verified the actual producer and provider mapping. This is directly coupled to the approved D workspace-bar identity, not a new parity feature.

    Approve exactly one mapping addition in graphcode-windows/src/AccessibilityProvider.cpp, identity workspace-quick-chat-bar: to AutomationId prefix workspace-quick-chat-bar-, plus a corresponding native assertion in Tools/windows/Tests/AccessibilityProvider.Native.Tests.cpp. These are already D-owned files. Preserve hashed stable-ID derivation, correct name/non-invokable behavior, other prefix mappings and all existing restart tests. No control-type, pattern, role, paint, geometry, lifecycle, gate or shared capture-core redesign is approved.

    Test first and preserve a genuine failure of the new native assertion against the missing map, then the same assertion passing after the map. This may be established by the existing hosted test gate. A local hidden-provider invocation is NOT granted by this comment: first submit the exact resolved pinned/installed compiler script/executable, cwd, owned fresh temp/cache/output paths and actual command for a separate allow-list decision. No full local runner, product, zmx, UIA live gate or capture is authorized.

    After the fix, retain the original source-only B/D interaction verdict and obtain a narrow delta review of this exact mapping/test change by the same different-vendor reviewer, then fresh exact-head required CI and honest full-template body. The conditional zmx gate-path concern is not a proved product bug: inspect the actual hosted invocation/result rather than weakening it or adding unrelated fixes. Workspace Restart remains the already-approved visible no-op. The operator has authorized this code slice to merge with visual verification NotExecuted due to #826; #774 remains open with visual criteria unchecked for a later coordinated batch. The D capture adapter and all screenshots remain parked.

  7. coneilen commented on Oct 15, 2026

    @coneilen
    CollaboratorAuthor

    Merged the bounded Quick Chat workspace code slice in #815 as 2916099 after accepted source/interaction review, exact-head CI, and fresh ready-triggered DCO. The directly coupled workspace-bar AutomationId fix has genuine hosted native RED (204 checks, one prefix failure) and GREEN (204 checks, zero failures). The inherited capture-contract failure and one successful same-head failed-job retry remain preserved separately; no product/capture-core changes were made to obtain that retry. Visual verification is NotExecuted due to #826 under the operator-directed immediate fallback. Both whole-issue criteria stay unchecked and #774 remains open: no paired visuals, physical input, backend-agent attachment, persistence or whole-issue parity is inferred. The enabled workspace-bar Restart remains the source-approved no-op, distinct from disabled global menus. Later visual work requires a separately qualified approach/admission; no manual screenshots or current D capture lease are authorized. No parity-ledger row is promoted by this merge.

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 macOSbugSomething isn't workingparityWindows/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