Claude Code's status reports never reach harrier on Windows #12

Closed
opened 2026-10-13 20:08:14 -05:00 by jeff · 3 comments
Owner

Problem

harrier handles OSC 7501 (Program Status Protocol) end to end and answers the ? probe (libghostty replies because Session.zig sets the program_status effect). On Windows none of it is used: Claude Code (2.1.295+) sends OSC 7501 only after a reply to OSC 7501 ; ?, and in a harrier pane its debug log says

programStatus=no (probe: no reply to OSC 7501 ; ?)

Windows' own console host, which src/backend/ConPty.zig gets from CreatePseudoConsole, drops OSC 7501: a report written in a pane (Write-Host -NoNewline "`e]7501;state=working:app=test`e\") never reaches the status button. It also answers some queries itself: DECRQM 2026 came back with status 0 though harrier has the mode.

Claude Code never sends harrier OSC 9;4 (it does only for ConEmu, Ghostty ≥1.2.0 and iTerm2 ≥3.6.6, by environment), so OSC 7501 is the only path.

Decision recorded in docs/adr/0003-ship-our-own-conpty.md.

Tasks

  • Add Microsoft.Windows.Console.ConPTY's .nupkg (a zip) to build.zig.zon as a lazy dependency, fetched only for Windows targets; regenerate build.zig.zon.nix. Zig takes the archive type from the URL path alone, so the .nupkg is mirrored, unchanged, as https://git.jcollie.dev/api/packages/jeff/generic/conpty/1.25.260930003/microsoft.windows.console.conpty.1.25.260930003.zip.
  • build.zig installs x64 conpty.dll and OpenConsole.exe beside harrier.exe in zig-out/bin.
  • msi.nix copies both from ${exe}/bin into stage/; dist/windows/harrier.wxs installs them beside harrier.exe.
  • ConPty.zig loads CreatePseudoConsole, ResizePseudoConsole and ClosePseudoConsole from that conpty.dll, by full path from harrier.exe's directory (never the DLL search order; elevated harrier, ADR 0001), with function types from zigwin32. If it will not load, fall back to Windows' own and log a warning once per run. No config option.
  • Check, in a harrier pane, that OSC 7501 ; ? is answered through our console host, and not through Windows' own.
  • Check, in a harrier built from main: claude --debug logs programStatus=yes (probe: OSC 7501 reply), and DECRQM 2026 is answered by harrier (status=2); Claude Code's reports reach the pane.
  • Check the same from the installed MSI.

Then: Claude Code's OSC 133;A

Claude Code sends OSC 133;A;redraw=0 when a turn starts (found later: only in screen reader mode, --ax-screen-reader, and at each step of a turn). On a prompt start harrier drops every working and blocked record (status.zig endWork), as the spec requires, and Claude Code resends a record only when its text changes. So a background-task child that is still working across turns, or a root working report sent before the 133;A, is dropped and never comes back.

  • With reports flowing, record the order Claude Code sends 7501 reports and 133;A in.
  • Add a harrier test showing the lost record.
  • If it happens, draft an upstream report for Claude Code (anthropics/claude-code) for a human to file: don't send 133;A mid-session, or resend records after it. harrier keeps the spec's behaviour.
  • File the upstream report (a human).
## Problem harrier handles OSC 7501 (Program Status Protocol) end to end and answers the `?` probe (libghostty replies because `Session.zig` sets the `program_status` effect). On Windows none of it is used: Claude Code (2.1.295+) sends OSC 7501 only after a reply to `OSC 7501 ; ?`, and in a harrier pane its debug log says ``` programStatus=no (probe: no reply to OSC 7501 ; ?) ``` Windows' own console host, which `src/backend/ConPty.zig` gets from `CreatePseudoConsole`, drops OSC 7501: a report written in a pane (``Write-Host -NoNewline "`e]7501;state=working:app=test`e\"``) never reaches the status button. It also answers some queries itself: DECRQM 2026 came back with status 0 though harrier has the mode. Claude Code never sends harrier OSC 9;4 (it does only for ConEmu, Ghostty ≥1.2.0 and iTerm2 ≥3.6.6, by environment), so OSC 7501 is the only path. Decision recorded in `docs/adr/0003-ship-our-own-conpty.md`. ## Tasks - [x] Add `Microsoft.Windows.Console.ConPTY`'s `.nupkg` (a zip) to `build.zig.zon` as a lazy dependency, fetched only for Windows targets; regenerate `build.zig.zon.nix`. Zig takes the archive type from the URL path alone, so the `.nupkg` is mirrored, unchanged, as `https://git.jcollie.dev/api/packages/jeff/generic/conpty/1.25.260930003/microsoft.windows.console.conpty.1.25.260930003.zip`. - [x] `build.zig` installs x64 `conpty.dll` and `OpenConsole.exe` beside `harrier.exe` in `zig-out/bin`. - [x] `msi.nix` copies both from `${exe}/bin` into `stage/`; `dist/windows/harrier.wxs` installs them beside harrier.exe. - [x] `ConPty.zig` loads `CreatePseudoConsole`, `ResizePseudoConsole` and `ClosePseudoConsole` from that `conpty.dll`, by full path from harrier.exe's directory (never the DLL search order; elevated harrier, ADR 0001), with function types from zigwin32. If it will not load, fall back to Windows' own and log a warning once per run. No config option. - [x] Check, in a harrier pane, that `OSC 7501 ; ?` is answered through our console host, and not through Windows' own. - [x] Check, in a harrier built from `main`: `claude --debug` logs `programStatus=yes (probe: OSC 7501 reply)`, and DECRQM 2026 is answered by harrier (`status=2`); Claude Code's reports reach the pane. - [x] Check the same from the installed MSI. ## Then: Claude Code's OSC 133;A Claude Code sends `OSC 133;A;redraw=0` when a turn starts (found later: only in screen reader mode, `--ax-screen-reader`, and at each step of a turn). On a prompt start harrier drops every `working` and `blocked` record (`status.zig` `endWork`), as the spec requires, and Claude Code resends a record only when its text changes. So a background-task child that is still `working` across turns, or a root `working` report sent before the 133;A, is dropped and never comes back. - [x] With reports flowing, record the order Claude Code sends 7501 reports and 133;A in. - [x] Add a harrier test showing the lost record. - [x] If it happens, draft an upstream report for Claude Code (anthropics/claude-code) for a human to file: don't send 133;A mid-session, or resend records after it. harrier keeps the spec's behaviour. - [ ] File the upstream report (a human).
Author
Owner

The ConPTY work is in: 5053855 (ADR 0003), a6e7328, 8207473 and 5f61747.

  • ConPty.zig makes each pseudoconsole with the conpty.dll beside harrier.exe, loaded by its full path, and only when x64\OpenConsole.exe is there too: without it, conpty.dll quietly runs Windows' own console host. When either is missing, or ours cannot make one, harrier logs a warning once and uses Windows' own. A pane's pseudoconsole is closed and resized by the one that made it.
  • Our console host counts cells as the pane's grapheme_width says (PSEUDOCONSOLE_GLYPH_WIDTH_GRAPHEMES / _WCSWIDTH).
  • The package is 1.25.260930003, its SHA-512 checked against nuget.org's catalog before mirroring. The MSI installs conpty.dll, x64\OpenConsole.exe and Microsoft's license as LICENSES\conpty.txt; nix build .#msi builds, and its File table lists all three.
  • 8207473 and 5f61747 fix nix build .#msi itself: the Windows build was passed -Dbindir, which only Linux declares, and the package's source left out tools/ico.zig.

Probe in a pane: OSC 7501 ; ? then DA1, recording what comes back.

Console host Reply
Ours ESC ]7501;?:states=idle,working,done,blocked,error:kinds=permission,question,auth ESC \
Windows' own (no conpty.dll) none
conpty.dll without OpenConsole.exe none

Cursor after ➡️X (CPR): column 4 under .unicode, 3 under .legacy, through ours.

Left: the check from the installed MSI, and the OSC 133;A part.

The ConPTY work is in: 5053855 (ADR 0003), a6e7328, 8207473 and 5f61747. - `ConPty.zig` makes each pseudoconsole with the `conpty.dll` beside harrier.exe, loaded by its full path, and only when `x64\OpenConsole.exe` is there too: without it, `conpty.dll` quietly runs Windows' own console host. When either is missing, or ours cannot make one, harrier logs a warning once and uses Windows' own. A pane's pseudoconsole is closed and resized by the one that made it. - Our console host counts cells as the pane's `grapheme_width` says (`PSEUDOCONSOLE_GLYPH_WIDTH_GRAPHEMES` / `_WCSWIDTH`). - The package is 1.25.260930003, its SHA-512 checked against nuget.org's catalog before mirroring. The MSI installs `conpty.dll`, `x64\OpenConsole.exe` and Microsoft's license as `LICENSES\conpty.txt`; `nix build .#msi` builds, and its File table lists all three. - 8207473 and 5f61747 fix `nix build .#msi` itself: the Windows build was passed `-Dbindir`, which only Linux declares, and the package's source left out `tools/ico.zig`. Probe in a pane: `OSC 7501 ; ?` then DA1, recording what comes back. | Console host | Reply | |---|---| | Ours | `ESC ]7501;?:states=idle,working,done,blocked,error:kinds=permission,question,auth ESC \` | | Windows' own (no `conpty.dll`) | none | | `conpty.dll` without `OpenConsole.exe` | none | Cursor after `➡️X` (CPR): column 4 under `.unicode`, 3 under `.legacy`, through ours. Left: the check from the installed MSI, and the OSC 133;A part.
Author
Owner

Claude Code now reports its status to harrier on Windows: in a harrier built from main, claude --debug logs programStatus=yes (probe: OSC 7501 reply) and synchronizedOutput=yes (probe: DECRPM 2026 status=2), the second now answered by harrier rather than Windows' console host.

OSC 133;A. Claude Code marks prompts only in screen reader mode (--ax-screen-reader, CLAUDE_AX_SCREEN_READER, or the axScreenReader setting); with the defaults it sends none, and nothing is lost. In screen reader mode it marks 133;A at each step of a turn, and after the first turn the mark comes after its root working report. Traced in a harrier pane, each report and mark as the terminal saw them, with the records held just before:

# Received Records before
1–6 idle, A, working, C, D, done turn 1: A before working, nothing lost
7 7501 working 1 turn 2 starts
8 133 A 1 harrier drops working, as the spec says
9 7501 working 0 resent only because its text changed
17 7501 working 1 turn 3 starts
18 133 A 1 dropped
19–20 C, D 0 the turn ran with no status shown

A background shell command (sleep 90) is not reported as a child record: Claude Code reports only background subagents and workflow tasks as children. Those would be dropped the same way, but this trace has none.

3c40fca adds a test replaying it. harrier keeps the spec's rule; the fix is Claude Code's, and the upstream report is drafted for a human to file: don't send 133;A while working or blocked records are out, or resend every record after it.

Left: the check from the installed MSI, and filing the upstream report.

Claude Code now reports its status to harrier on Windows: in a harrier built from `main`, `claude --debug` logs `programStatus=yes (probe: OSC 7501 reply)` and `synchronizedOutput=yes (probe: DECRPM 2026 status=2)`, the second now answered by harrier rather than Windows' console host. **OSC 133;A.** Claude Code marks prompts only in screen reader mode (`--ax-screen-reader`, `CLAUDE_AX_SCREEN_READER`, or the `axScreenReader` setting); with the defaults it sends none, and nothing is lost. In screen reader mode it marks `133;A` at each step of a turn, and after the first turn the mark comes after its root `working` report. Traced in a harrier pane, each report and mark as the terminal saw them, with the records held just before: | # | Received | Records before | | |---|---|---|---| | 1–6 | `idle`, A, `working`, C, D, `done` | | turn 1: A before `working`, nothing lost | | 7 | `7501 working` | 1 | turn 2 starts | | 8 | `133 A` | 1 | harrier drops `working`, as the spec says | | 9 | `7501 working` | 0 | resent only because its text changed | | 17 | `7501 working` | 1 | turn 3 starts | | 18 | `133 A` | 1 | dropped | | 19–20 | C, D | 0 | the turn ran with no status shown | A background shell command (`sleep 90`) is not reported as a child record: Claude Code reports only background subagents and workflow tasks as children. Those would be dropped the same way, but this trace has none. 3c40fca adds a test replaying it. harrier keeps the spec's rule; the fix is Claude Code's, and the upstream report is drafted for a human to file: don't send `133;A` while `working` or `blocked` records are out, or resend every record after it. Left: the check from the installed MSI, and filing the upstream report.
Author
Owner

Checked from the installed MSI: Claude Code's status shows in harrier. nix build passes, so 3c40fca's test runs on Linux too. Closing; the upstream report to Claude Code is drafted but not yet filed, and is tracked outside this issue.

Checked from the installed MSI: Claude Code's status shows in harrier. `nix build` passes, so 3c40fca's test runs on Linux too. Closing; the upstream report to Claude Code is drafted but not yet filed, and is tracked outside this issue.
jeff closed this issue 2026-10-13 23:23:34 -05:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeff/harrier#12
No description provided.