Skip to content

Decode production daemon graph frames and guard empty sidebar text - #630

Merged
coneilen merged 1 commit into
mainfrom
coneilen-microsoft-windows-preview-rebuild
Oct 5, 2026
Merged

coneilen merged 1 commit into
mainfrom
coneilen-microsoft-windows-preview-rebuild

Conversation

@coneilen

@coneilen coneilen commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The Windows shell could not decode any graph sent by the real production
daemon. The beta6 Dev Box run registered the Core fixture and the CLI listed it,
but the shell never showed the project. Locally, the exact beta6 packaged shell
terminates with 0xC000041D a few seconds after a project is registered.

Root cause:

  • The Swift daemon encodes DaemonEvent.graphChanged(LoopGraph) with
    synthesized Codable as {"graphChanged":{"_0":<graph>}}. The Windows model
    read the _0 wrapper as the graph itself, so every real daemon project
    decoded with an empty path, an empty name, and no nodes.
  • Painting the empty project name passed Zig's undefined zero-length pointer
    (0xaaaaaaaaaaaaaaaa in the Debug build) to DrawTextW with
    DT_END_ELLIPSIS. USER32 dereferences it, the access violation crosses the
    window procedure, and the shell exits with 0xC000041D.
    • cdb stack: USER32!DrawTextExWorker ← Sidebar.drawText (Sidebar.zig:1733)
      ← Sidebar.draw (Sidebar.zig:211) ← GraphCanvas.paint ← WM_PAINT.
    • The faulting wide slice had len = 0 and ptr = 0xaaaaaaaaaaaaaaaa.

Why every earlier gate passed:

  • The real-daemon round-trip test normalized {"_0":...} frames before
    handing them to the model.
  • The CI UIA gate and the Windows stub daemon emit the unwrapped form.
  • The scrubbed startup gate never registered a project.

The beta3 crash after accepting a folder has the same USER32
c0000005 → c000041d signature, so it was most likely this defect.

Changes

  • GraphModel.decodeGraphFrame now unwraps the daemon's _0 associated
    value. The unwrapped form is still accepted for the existing fixtures and
    the stub daemon.
  • Every DrawTextW helper skips empty text before selecting a font, so an
    empty label can no longer hand USER32 an undefined pointer. The helpers are
    in Sidebar, GraphCanvas, NativeForms, TerminalSurface, WindowsOnboarding and
    WindowsProductSettings.
  • DaemonRoundTripTests.zig passes real daemon frames to the model
    unmodified, and now also asserts the decoded project name.
  • ScrubbedShellStartup.Live.Tests.ps1 adds a fifth registered-project
    case, run inside the existing developer-free 11-key environment:
    • registers a plain non-Git folder through the production daemon with
      graphcode status;
    • launches the shell;
    • requires the UIA open-project-* ListItem named Core;
    • requires the shell to stay alive for 5 more seconds;
    • requires graphcode projects to list the project.
  • WindowsShell.Tests.ps1 contracts keep the registered-project case and
    forbid reintroducing a frame-normalization shim.

Test plan

RED: zig test src\GraphModel.zig --test-filter "production daemon graphChanged" with pinned Zig 0.15.2 -> 0 passed, 1 failed (ProductionGraphProjectMissing)
GREEN: zig test src\GraphModel.zig --test-filter "production daemon graphChanged" with pinned Zig 0.15.2 -> 1/1 passed
REGRESSION: exact-head CI windows-shell integration for 578cf63 (run 37351425076) -> PASS; scrubbed startup executed=5 incl. registered-project, live UIA gate passed; unit shards, packaging and DCO green

Additional RED/GREEN evidence, all with pinned toolchains:

  • Sidebar empty-name paint: zig test src\Sidebar.zig ... --test-filter "empty".
    • RED: segmentation fault inside USER32 at Sidebar.zig:211 → drawText
      → DrawTextW. This is the production stack.
    • GREEN: 4/4 passed.
  • Real daemon round trip: Tools\windows\Tests\DaemonRoundTrip.Live.Tests.ps1
    with the real beta6 graphcoded.exe and the shim removed.
    • RED, decoder fix temporarily reverted: expectEqualStrings expected
      C:/gc-rt-red/.../project and found an empty string.
    • GREEN: REAL_DAEMON_ROUNDTRIP: PASS, covering five mutations and the
      restart readback.
  • Scrubbed live gate, with the real beta6 graphcoded.exe and graphcode.exe:
    • RED, against the immutable beta6 packaged shell: the four original cases
      passed, then registered-project did not retain live shell/daemon after onboarding.
    • GREEN, against a source-built ReleaseSafe shell with
      -Dworktrees-deferred=true: SCRUBBED_SHELL_STARTUP: PASS; executed=5,
      with projectRow=open-project-* and CLI output Core C:/.../Core.
  • Full suites: zig test src\GraphModel.zig 152/152;
    zig test src\Sidebar.zig ... 182/182.

Limits:

  • The beta6 package was used only as an immutable reproduction input. It was
    not modified.
  • Windows shell validation used the source-built daemon runtime.
  • An attended Dev Box rerun on a new candidate is still required. No tag,
    package, release, backend turn, Worktrees action, Nod test, dump, or
    publication is included.
  • The stub daemon and UIA fixtures still emit the unwrapped graph form. The new
    real-daemon live case is what covers the production wire shape.
  • macOS make test and make check were not run. The change is limited to the
    Windows Zig shell and Windows validation scripts.

Checklist

  • I have read the Contributing Guidelines
  • I have signed off my commits (git commit -s) per the DCO
  • Tests pass locally (make test)
  • Code follows the existing style (make check)
  • I added the test/contract before the implementation and observed the intended RED failure

The Swift daemon encodes DaemonEvent.graphChanged(LoopGraph) with synthesized
Codable as {"graphChanged":{"_0":<graph>}}. The Windows model read the
wrapper as the graph, so every real daemon project decoded with an empty
path and name. Painting that empty name passed an undefined zero-length
pointer to DrawTextW with DT_END_ELLIPSIS, which faults inside USER32 and
terminates the shell with 0xC000041D as soon as a registered project paints.

Unwrap the associated value, skip DrawTextW for empty text in every helper,
remove the round-trip test shim that normalized real frames before the
model saw them, and add a scrubbed real-daemon live case that registers a
plain folder and requires the shell to stay alive with its project row.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
@coneilen
coneilen merged commit 8ffa38e into main Oct 5, 2026
25 checks passed
coneilen added a commit that referenced this pull request Oct 5, 2026
Record the immutable, unpublished 0.1.78-windows.beta7 candidate built from
e770438 with #630's production daemon graph decode and empty-text drawing
fix. Beta6 is failed/superseded by the Dev Box Production core flow failure
and kept unchanged. Exact artifact stays complete; the other six gates and the
98/62/36 parity ledger are unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
@coneilen coneilen mentioned this pull request Oct 5, 2026
2 of 5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant