Skip to content

feat(ui): sidebar groups, dock editor, worklist rows [no-ticket] - #38

Closed
matthias-scale wants to merge 30 commits into
fork/pr-basefrom
mat125/ui-churn-split
Closed

feat(ui): sidebar groups, dock editor, worklist rows [no-ticket]#38
matthias-scale wants to merge 30 commits into
fork/pr-basefrom
mat125/ui-churn-split

Conversation

@matthias-scale

@matthias-scale matthias-scale commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • carries the sidebar grouping and pinning, dock/editor, and compact worklist-row UI commits from the split onto the current fork/pr-base
  • retains the UI slice only; detect and closing-block contract changes already present in the base were removed by the normal rebase
  • repairs current-base API and default-keymap compatibility so the branch is green
  • removes the obsolete v1 demo/ duplicate; canonical payload-v2 closing-block assets remain under src/integration/assets/closing-block/

Rebase

  • base: 4f82ffc18ff3cca4d41381b4eb856b05b53ae1d8
  • source: 725cf298d03b7c353b57e4d1704aed1e22e98010
  • the UI implementation commits and the two original empty provenance commits are preserved
  • detect, contract, and stale demo commits whose patch contents were already upstream became empty during the normal rebase; no non-upstream commit was skipped

Validation

  • just check-parallel completed with the documented work_context_git parallel flake; both affected tests pass when rerun serially
  • canonical closing-block asset tests: 13 passed
  • focused sidebar, dock, reload-config, schema, and multi-client tests pass

Surface the closing block as three independent channels -- blocking gates,
running agents, idle -- instead of collapsing them into one screen-scraped
status.

Screen scraping sees Claude's prompt box the instant a turn ends and calls the
pane idle (manifests/claude.toml live_prompt_box, priority 950). That is right
about the harness and wrong about the work: agents may still be running, or a
Gate may be waiting on a human.

Adds the closing-block sources to full_lifecycle_hook_authority so the Stop
hook outranks the prompt box, with a regression test asserting the authority
does not outlive the agent process. Demo harness under demo/.
Replace prose-shaped reporting with a structured payload any coding agent can
emit from its own turn-end hook:

    {"v":1,"agent":"claude","blocking":2,"agents":3,"gates":[...]}

blocking (items only a human can clear) and agents (still working) are
independent, so "nothing for you, but 3 agents are still running" becomes
visible for the first time.

The hook is the trigger, not the model: a turn-end hook fires deterministically,
a tool call the model must remember does not. Transport is the existing herdr
socket, with an atomic JSON mirror under XDG_STATE_HOME as durable fallback.

is_closing_block_source admits any herdr:<agent>-closing-block whose suffix
names the agent it speaks for, so adopting the contract needs no herdr release.
hook_authority_is_effective still requires that agent's process to be present.

Adapters for Claude (Stop) and Codex (notify), plus a dependency-free CLI.
The turn-end status contract was accepted at the RPC edge and then dropped
inside TerminalState, while the caller still saw {"type":"ok"} because
handle_pane_report_agent discards the mutation.

Two gates rejected it, both keyed on session identity:

- route_full_lifecycle_hook_report requires an AgentSessionRef, but
  agent_resume::session_ref_from_report mints one only for sources on the
  is_official_agent_source allowlist. A closing-block source reports what the
  agent just said, not where to resume it, so its ref is always None and every
  admitting path was unreachable.
- set_hook_authority_at rejected the report as an owner conflict on any pane
  where the real agent had already announced itself under herdr:<agent> -- that
  is, every pane that matters.

Accept on process presence instead, which is the only guarantee this source
claims; accept_hook_report still enforces per-source sequence monotonicity.

The existing test passed a session ref the RPC can never produce, so it stayed
green while the live path dropped every report. It now reports with None
against a pane whose session is owned by herdr:claude, and fails without the
fix.

Verified end-to-end against a live claude process: blocked survives the visible
prompt box, repeated detection ticks, and focusing the tab.
Sidebar labels are now "gate" and plain "working". One gate or three, the
action is the same; running agents just mean "not done", which the working dot
already says. The counts still ride along in the closing_* tokens.

Adds INSTALL.md with the wiring verified live for both agents, including the
two traps: --settings never loads hooks, and project-scoped hooks are silently
skipped until the folder is trusted.
Codex sets its terminal title to the cwd and its rollout files carry no
title, so a codex pane has nothing to name it by. The notify handler now
derives one from the first turn's user message -- the same thing codex's
own resume picker previews -- and pins it in the mirror so later turns do
not rename the pane. Rides along as a `session_title` metadata token.
A full-lifecycle source that stays silent leaves its last report standing
forever, so a pane that reported a gate kept showing it with nothing able
to clear it.
Use recent PTY output as a bounded fallback when Codex has no positive idle or blocker evidence. Codex's non-empty OSC cwd title still resolves Idle but no longer sets visible_idle, so pending-idle confirmation and blocker arbitration can observe the difference. Claude's live prompt box and blocker rules remain unchanged.
Verified against live `agent.read` captures of a codex 0.147 pane: a real
turn prints no block marker, so the block-marker region emptied the rule and
the only working evidence codex has stopped firing.
A live codex 0.147 turn goes silent for 3.6s between erasing its progress
line and the first streamed token; a two-second window showed that gap as
`done` mid-turn.
The Blocked and Pinned groups shipped with header rows that reserved a
line but never drew one, so the groups read as unexplained gaps above
the tree. Paint them with the same chevron a space row uses, and make
that chevron real: clicking a header folds its group away, count kept
so the header still says how much is hidden. The tree earns a Spaces
header -- and a fold of its own -- whenever a group sits above it.
The Blocked and Pinned groups repeated the tree's own row template --
two lines ending in claude or codex -- which answers a question nobody
asks of a gate. A grouped row is now a single line: state dot, thread
title, then the space it belongs to. Cards carry their row index so a
pane listed both in a group and in the tree renders each row in its own
shape instead of twice in the first one's.
@matthias-scale
matthias-scale marked this pull request as ready for review August 10, 2026 07:04
@matthias-scale matthias-scale changed the title feat(ui): sidebar groups, dock editor, worklist rows (split from #31) [no-ticket] feat(ui): sidebar groups, dock editor, worklist rows [no-ticket] Aug 10, 2026
@matthias-scale

Copy link
Copy Markdown
Owner Author

Superseded by repaired PR #46, which carries the rebased head mat125/ui-churn-split-repair against fork/pr-base.

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