Repository navigation
Collie v0.46.0
The upstream 1.9.0 redesign round, ported whole onto Pup: the composer's actions belt, the
dashboard's workspace grouping, and the one-name rule — on a branch first, so the herd on the phone
can try it before it becomes Pup's face. The cache subsystem, config.toml, crew machinery, and the
mux abstraction the redesign upstream rides on are not part of this port; see Declined.
Bridge
- One name rule, one place rule, mirrored bridge-side.
bridge/pane-name.tsis the push's half
of the rule the screens use: NAME — paneLabel, else sessionName, else a non-stale terminalTitle,
else the agent word; PLACE —space › tab. Shared fixtures (pane-name.fixtures.json) run on both
sides, so a rule changed on one side alone turns red. A multi-agent push digest names the panes
instead of reading "claude, claude, claude", appending the place only when two panes would read
the same; a single alert's body is the place, notworkspace · absolute-path. (upstream 6e8eeaf,
15f9db6) - Panes arrive in the multiplexer's own arrangement. The bridge no longer tie-breaks panes by
pane id (an opaque string whose alphabetical order nobody can see); space, then tab, then pane
position all read off Herdr's listing order, and a poll caught mid-create sorts last. (upstream
6e8eeaf, on Herdr's listing — Pup does not port the mux layer upstream built this on) terminalTitleStaleis declared on the wire type for the mirrored rule to read; Herdr reports no
foreground command, so on this bridge it is never set. Known limit: Claude/renamesession
names are already sniffed and ride the wire as before.- Workspaces carry their repo. Herdr's
workspace.listalready reportsworktree.repo_root
andis_linked_worktree(live-probed on this herd: 11 spaces, 2 with repos); the wire type now
declares the pair and the snapshot carries it, so the web can nest a worktree under the space
holding its repo. (upstream 6e8eeaf's worktree half)
Web — the names pass and the dashboard's second axis
- One name and one place, on every screen. A pane is called the same thing everywhere — your
label, else the session's/renamename, else the terminal title, else the agent word — and
space › tabsits beneath it as the second line, never as the first. The fork's project-first
row titles are superseded by this on the branch. A numbered tab readstab 2in a lighter ink
everywhere it renders alone.lib/pane-name.tscarries the rule once;paneDisplayNameis
gone. (upstream 6e8eeaf, 583e561) - Rows keep the multiplexer's arrangement. Triage buckets no longer re-sort by activity — a
row moves only when it changes bucket, never while you reach for it. The space list keeps
Herdr's own workspace order (same as the strip), recency sort deleted; the row still shows its
time. Worktrees nest one step under the space holding their repo, a filtered list stays flat.
(upstream 6e8eeaf) - The dashboard asks two questions, in order. Needs-you and Ready·unseen stay pinned on top
by urgency; everything else sits under its WORKSPACE, one counted group per space, shells
joining their tab after its agents. An urgent pane is pulled out of its group, never copied.
Working/Recent headings, Recent's fold and its sort toggle are gone — no clock is left in the
order to reverse. Every row is the one 44px two-line form (name + dot and mark inline, place
beneath), urgent rows in the same framed list shape as workspace rows, and a finished-unseen
pane carries a small dot after its name. (upstream 64b6f49, 10cd055, 458876b, cd70aa3) - Pup-shaped omissions: no host or cache chips on the name line (no crew, no cache subsystem),
and Pup has no i18n layer, so upstream's locale strings are the literals they rendered to.
Web — the compact strips and the pane header
- The tab row and the pane row get compact, one continuous band. Tabs draw as 32px plain cells
(11px text) that still answer a 44px tap through a transparent hit-area extension; the open tab
is an outlined pill on the row's own ground, there is no horizontal rule and nothing under the
tab row, and the pane pills beneath shrink to 24px on the same band (--chrome, a new token for
the ground chrome stands on when it cannot stand on the page). A numbered tab readstab Nin
the lighter ink on the strip too. (upstream c2a1650, 02ac7a5, 0ded2d2, 583e561) - The raw pane id is gone from every pill.
p3is Herdr's coordinate, not a name; a pill is
numbered only when a neighbour would otherwise read the same, and the number is its 1-based
place in the row — the thing the reader is actually looking at. (upstream 2eefd38, new
lib/pane-ordinal.ts) - The pane header follows the one name rule. Line 1 is the pane's name with the agent's mark
on it; line 2 names the workspace alone (the tab strip below already names the open tab), and
the cwd left the header — it lives on the dashboard's tab-scoped rows and in History. The title
block keeps its 44px tap to the space overview. (upstream 6e8eeaf, b14ffd4) - New
ui/labelled-strip.tsx/ui/section-label.tsxprimitives (the strip row's shared recipe:
tap floor, scroller padding, above-label) andui/list-group.tsx(the framed flat-row region)
arrive with them, as upstream ships them. Pup drops upstream's per-tab agent tile on the strip.
Web — the actions belt
- One row of actions above the keyboard, with the running agent's own commands in it. Keys,
Type, Quick, Agent and the display gear share a single scrolling belt with the harness's
commands, which sit in a segment tinted with that harness's brand colour (Claude's orange, omp's
purple; Codex and pi are monochrome by design and take the muted ground) and carry an icon each.
Every button sends its bare command and the harness paints its own picker in the mirror, so the
list of models or effort levels never lives in Collie. The old Controls row and the 30px
swipe-handle band are gone; a bare Layers mark pinned at the belt's right end opens the pane
switcher, a drag up from anywhere on the band opens the same sheet, and the composer keeps its
row back. Settings gains a per-device "Harness shortcuts" switch (on by default).
(upstream fc8d1be, 9b53078, 5e7f626, 63513ae, 931f857, adaa1fb + the belt's follow-ups,
ported as the end state) - Operators add or replace the segment with
bar = trueincommands.toml, with an optional
bar_label(twelve characters kept whole). The replacement rule is per surface (ADR 0018's
posture, applied to the bar alone): bar rows never blank the Agent palette, and a shipped
dangerous command's two-tap confirm is inherited as a floor. (upstream fc8d1be) - Pup-shaped limits, stated: the belt carries no host tag and no cache reading (no crew, no
cache subsystem); the composer's verified-paste transport, guarded submit, draft previews and
docks are untouched — only the row that held their toggles changed shape; omp's Tree button
stays off the bar until this repo holds anomp--tree.txtcapture to vouch for it (upstream
cc38c2d's own rule, followed rather than shortcut).
Web — the keys tray
- A compact seven-column Keys pad. One fixed grid, two rows — Esc/Tab/⇧/Ctrl/Alt/Up/⏎, then
Ctrl C / a 3-wide Space / ← ↓ → with Down under Up — replaces the Keys/123 segmented toggle and
the stacked rows. The digits, the Ctrl presets and F1–F12 fold behind one row of accordion
chips (123 / Presets / F keys, one panel open at a time). The tray's resting height drops from
~275px to ~119px at 390px with every key still ≥36px tall; Enter and Shift draw glyphs with the
full word as the accessible name. A composed key queue still dies with its dock (ADR 0005) and
still survives switching to the digit grid mid-chord. (upstream e12b433)
Declined (this round, with reasons)
- The prompt-cache subsystem (~6,400 lines upstream): countdown chips, cold-cache push
warnings, dated vendor TTL claims,cache-rules.toml. A feature, not a fix, and still settling
upstream (six follow-up fixes in the days after 1.9.0). Revisit once quiet. - config.toml +
collie config show/check/init(~3,000 lines): a config layering machine for a
single-user install that has.env. - The crew/mux substrate: protocol floors and compat removals, host-keyed grouping, the mux
adapter layer the redesign upstream rides on, tmux auto-rename. Pup is one host on Herdr. - The first-launch screen: onboarding for a fresh install, shaped around crews.
- Screen slide transitions, tour, playground, i18n: polish and scaffolding Pup doesn't carry.
Upstream's locale strings landed as the literals they rendered to. - omp's Tree bar button: waits on an
omp--tree.txtcapture in this repo's corpus (the same
rule upstream held it to before their capture existed).
Fixed
-
Devin panes get their mark. A devin pane rendered the generic "DE" initials tile on every
surface (dashboard row, pane header, switcher) — the brand table had no entry and the resolver
no rule, so it fell through to the fallback. Devin's own pinwheel mark now rides a black tile
with a white fill, monochrome like Codex's and pi's (no belt accent — its absence is a real
answer for a monochrome brand). Source: the SVG Logos collection (CC0), rescaled from its
256×294 viewBox to 24×24 and centred; verified rasterized at render time — ink box dead-centre,
53–61% tile coverage, no clipping. Tried from the phone on day one of the redesign trial. -
Sheets no longer pick up stale terminal paint while sliding in. Both floating panels (the
switcher and Agent commands) showed bands of terminal output across their surface during the
entrance, gone the next frame — the animated sheet shared its paint with the filtered mirror
layers beneath. The panel is now one isolated paint boundary, promoted for the life of the
sheet (f770a73); and the dim settles before the slide — a 150ms ease-out scrim on its own
compositor layer, so the panel moves against a stable ground instead of a dim still mid-fade
(7fbfad8). Found on the phone during the redesign trial.
Fixed (upstream round, ported 2026-09-16)
- A plugin action resolves the service's state directory (upstream AltanS#226, d07ec4c). Herdr
injectsHERDR_PLUGIN_STATE_DIRinto every plugin action; the systemd service never receives
it. Honouring it split one install in two: push-test, pairing and devices — every verb that
reads or writes state through an action — read~/.local/state/herdr/plugins/herdr.collie,
found no subscriptions, and failed, while the service kept them under~/.local/state/collie.
resolveStateDir(now pure and exported, likedefaultSocketPath) readsCOLLIE_STATE_DIR,
else the user state dir, and ignores Herdr's variable on purpose. - A finished agent that Herdr reports as
idlereaches Ready · unseen (upstream AltanS#222,
769cdaa). Herdr 0.9's API saysidlefor an agent whose turn ended; only its own client
projectsdone.isUnseennow accepts either settled status, gated by our own read receipts
and excluding bare shells; and only a turn that ends (working/blocked→ idle/done) counts
as new work, so Herdr's own acknowledgement and detection flicker never re-mark a read pane.
The ledger wiring moved tobridge/activity-tracking.tsso the gate has one home. ADR 0003
and HERDR_API.md carry the compatibility note. - Light fills match by luminance, not by one observed value (upstream AltanS#224, 6fb0f1f +
ebd63ff). Codex's submitted-message band was matched against the literalrgb(240,240,240),
and a 0.154.0 pane paintsrgb(244,244,244)— four levels apart, so the whole band inverted
to a black bar again. The rule moved tolib/harness/light-fill.ts(Rec. 709 luma, Codex's
floor 220, omp's 180), shared by both adapters; a real 0.154.0 capture pins the shape. omp's
belt of pastel card fills now gets the same marking on Pup for the first time — upstream has
marked them since before the fork point; Pup's raw-only omp path just never carried it. - The belt stands at 40px, and the field's focus ring clears it (upstream a0ae39e). The
belt's scroller readspy-1— 40px on the phone instead of a thin 32px strip; the harness
section grows toh-10with-my-1so its tint still runs rule to rule; and the field row's
pt-1is the one place that puts the focus ring's 4px reach inside the row rather than on the
belt's bottom edge.
Update
Update the plugin — advances the checkout, rebuilds, restarts (runs from any directory):
herdr plugin action invoke update --plugin herdr.collie
Or just restart the bridge to pick up an already-built change:
herdr plugin action invoke restart --plugin herdr.collie