Repository navigation
GitPulse v1.3.0
A consolidation release. It joins live delivery observability — GitHub Actions
workflow runs and Firebase App Hosting rollouts tied directly to repository
commits — with the bodies of work that were in flight beside it: native
notifications for agent sessions, Claude Code supervised in the managed lane,
code-age history in Blame, a terminal dock that belongs to a repository rather
than the workspace, terminal output whose links are safe to click and land where
they point, and walkthrough replay moved out of the title bar.
Re-cut on 2026-09-21 over a wider tree, adding native notifications for agent
sessions, quiet hours and sound control, managed Claude Code runs hosted over
stream-json, regression suspects from the code graph, a Health view that reaches
a verdict, repository tab groups, and the coverage and impact surfaces reduced
to what they can actually claim. Two process-lifecycle defects are fixed behind
them: an ordinary policy-harness shutdown that leaked whatever the harness had
forked, and a test suite whose verdict depended on how busy the machine was.
Added
-
Native notifications for agent sessions. An agent running in a GitPulse
terminal tab was completely silent: Claude Code only sends desktop
notifications in a handful of terminals it recognises, Codex's OSC 9 probe
recognises a similar short list, and GitPulse's terminal matched neither — so
the only signal a waiting agent could give was an unread dot on a tab you had
to be looking at. Three layers now carry it, each independently sufficient:- GitPulse reads the PTY itself and understands every notification convention
a terminal program has to choose between —BEL, OSC 9, OSC 777 and kitty's
chunked OSC 99 — so a CLI that signals any of them is heard, whatever it
is. ConEmu's OSC 9 sub-commands for progress and working directory, which
Claude Code's progress bar emits continuously, are not notifications and are
not treated as any. - Sessions GitPulse launches are given each CLI's own documented notification
flag (claude --settings,codex -c tui.notifications) for that session
only — no file of yours is written — so the CLIs actually emit something.
Turn it off under Configure agent CLIs GitPulse launches. - The GitPulse plugin's
NotificationandStopFailurehooks report why an
agent stopped — permission, idle, input, finished, asking, error — over a
private local socket, which also covers a Claude Code running outside a
GitPulse tab.
- GitPulse reads the PTY itself and understands every notification convention
-
Quiet hours, sound and per-kind control for those notifications, and a
counter panel that distinguishes a notice suppressed by a rule you set from
one that was lost. Notifications are suppressed while you are looking at the
session they are about. -
Claude Code runs in the managed lane. Previously only Codex could be
supervised by GitPulse — every Claude Code handoff opened a terminal and
GitPulse stepped back. A managed Claude run is now hosted over Claude Code's
ownstream-jsonprotocol, so its permission requests arrive as structured
approvals you answer in GitPulse, its output is captured, and its completion
is a receipt rather than a guess. All six permission modes map to real Claude
Code modes, and the one that does not round-trip (askis reported by the CLI
asdefault) is verified as such rather than assumed.Two honest differences from managed Codex, both visible in the run's recorded
configuration: Claude Code has no OS sandbox, so the record says so instead of
naming a confinement that does not exist — a managed Claude run is supervised,
not confined — and its model is not in that record, because the CLI names it
only after the record has been written and made immutable. Repository settings
files are deliberately not loaded for a managed run, so a checkout cannot
widen the permissions of the run inspecting it. -
Claude Code is now the default agent for a new task handoff, and leads the
provider list. The connection still defaults to a terminal inaskmode: a
first launch should be the reversible one, and the managed lane stays a
deliberate choice. An existing remembered preference is untouched. -
Platform coverage is written down. Platform
coverage separates what is known
absent off macOS — desktop notifications for activity and agent sessions, the
agent hook socket, the menu-bar status item — from what is merely unverified
there, such as managed runs. macOS is the platform GitPulse is developed and
hand-tested on, and the docs now say so instead of leaving it to be discovered. -
Regression suspects — which commits could have caused this? Blame joined to
the code graph, and the order of operations is the whole argument: a
blame-first tool reads a file's gutter and ranks by recency, this reads the
graph first, so candidates are ranked by what actually reaches the symptom.
Ships as two vendored crates (dc-regress,dc-regress-store) so every host
asks the same question of the same graph, plus a Regression Suspects panel. -
The Health view gives a verdict, rather than a wall of readings for you to
add up, and every repeated decision behind it now has one owner instead of a
copy per caller. -
Repository tab groups. Open repositories can be grouped and named, so a
workspace with a dozen checkouts is navigable rather than merely complete. -
Markup and stylesheet extraction. A template file's HTML and CSS halves go
through a real grammar, closing the gap left when only its<script>blocks
did. -
A substance gate for verification. Every other gate answers "is something
wrong with this change"; this one answers "is there anything in it", which
nothing did before. -
Live GitHub Actions Run Polling: Automated, bounded polling for in-flight
workflow runs that refreshes only while runs are queued or executing, backing
off on failures, pausing while the window is hidden, and announcing newly
failed runs via dismissible notices. -
Delivery Timelines: Visual duration and outcome timeline for workflow runs
and Firebase App Hosting rollouts, scaled to sample bounds with pass rates,
median execution times, and clean handling of queue time vs execution time. -
Firebase App Hosting Rollout Monitoring: On-demand live refresh for
authorized project and backend pairs, providing live rollout status alongside
commit history. -
Action Dispatch & Rerun Controls: Enhanced GitHub Actions panel with rerun
actions, branch filtering, and granular status reporting. -
Clickable terminal output. A URL opens in the OS browser only when its
scheme is http or https, verified with a real URL parse at both detection and
activation. A file reference opens in the code viewer only when it resolves
inside the session's repository, with containment checked on normalised path
segments — a string prefix test calls/repo-evil/xand/repo/../etc/passwd
inside/repo, and segments do not. A span GitPulse will refuse to open is
never decorated as a link: underlining it and then doing nothing teaches
people to distrust the underline instead of the output. OSC 8 hyperlinks obey
the same allowlist, judged on the escape sequence's target rather than its
display text. -
A file reference lands on the line it names, with the line selected; one
naming a line past the end of the file opens near the end rather than
refusing. The line travels as a one-shot request with an explicit consume, not
on the repository session: "scroll to line 12" is a navigation intent that must
not survive a restart or replay when the file is reopened for another reason. -
Screen reader support in the terminal. The terminal had no accessible
text at all —screenReaderModeappeared nowhere — so a preference now builds
xterm's row tree and applies to running sessions. The Console command field had
no accessible name either; a placeholder is a hint, not a name. -
The terminal harness is CI-gated.
npm run test:browser -- --harness terminal(and--webkit) runs in the automated sweep instead of waiting for
someone to press a button, with 75 real-DOM checks over the production
components and a simulated PTY transport. -
Code-age timeline in Blame: a chronological axis above the gutter showing
what percentage of the open file was last changed in each period. Resolution
adapts to the file's history (daily through yearly) and is bounded; a history
too long for even a yearly axis folds its oldest lines into the leading column
and says so. Clicking a column filters the gutter to exactly the lines that
column counted — the picture and the filter share one classifier, so a column
cannot claim a share it would not select. Quiet periods are drawn as empty
columns rather than omitted. -
Off-axis lines are named rather than dropped: worktree-only, clock-skewed
and undated lines carry their own labelled, selectable shares beside the axis.
Columns plus chips account for 100% of the file. -
Commit blocks in the gutter: consecutive lines from one commit state their
hash and author once at the head of the block, keep the hash reachable on
hover or focus, and mark the whole commit down the left edge when hovered. -
Line age in the gutter: each line's commit age, following the shared
timestamp preference, with the other form on hover.docs/FEATURES.mdhad
described this since the page was written; it was never drawn. -
Code-age rail in Blame: a heat strip down the right edge showing where
in the file each age lives, with a band marking what is on screen and
click/drag to navigate. It maps the list actually drawn, so a filter re-maps
it; the freshest tone wins each bucket and the mark's intensity says how much
of the bucket that tone really is. Geometry is the diff minimap's, reused
rather than re-derived. -
The legend now carries the distribution and filters on it: each age band
states its share of the file and selects those lines. Period columns,
off-axis chips and age bands are one selection model with one matching rule. -
Blame browser harness:
npm run test:browser -- --harness blame(and
--webkit) mounts the production pane over fixture IPC, 59 real-DOM checks. -
Go to, in the cross-repository Sessions list. The list could already name
every shell across every open repository; now it can bring one on screen —
switching repository tabs and opening that repository's dock when the session
lives in another one. -
Live-session marks on repository tabs. A repository tab carries a terminal
glyph while it holds sessions, with a count past the first, so a shell running
where you are not looking is visible — and so "what is using the 32 slots" is
answerable at a glance. -
Command palette entries for each launcher — New Terminal Session, New
Claude Session, New Manvi Session, New Codex Session — which open this
repository's dock if it is hidden and start the session in one step. Starting a
session previously required opening the dock and then finding the launcher
dropdown and the + beside it.
Changed
-
Coverage headers collapsed from five bands to three, and the measurement now
names what qualifies it instead of presenting a number as unconditional. -
The impact question is answered in one line instead of reprinting the
engine's prose, and that prose is bounded where it enters rather than where it
is rendered — a cap at the render site leaves the oversized string already in
memory and in the payload. -
One owner for collapse-and-expand in long rail listings. Workflows, run
cards and the delivery timeline all previewed then expanded, each with its own
copy of the logic. -
The Task pane has one owner for what it shows and in what order.
-
The terminal dock belongs to a repository tab, not the workspace. Opening a
shell in one repository no longer opens the dock over every other repository you
switch to. Because hosting a terminal panel starts a shell, it also no longer
spawns a PTY — and spends one of the 32 global session slots — in repositories
you were only reading. Each repository remembers whether its dock was showing,
across a restart, and its dock reappears when you return to it. `Ctrl+``, the
status bar button, the command palette entry and the native Show/Hide Terminal
menu item all act on the active repository. Persisted additively on the existing
workspace schema, so an older build reads the same session file unchanged.After upgrading, every repository starts with its dock closed once; the previous
workspace-wide preference is not carried over. -
The blame age scale (row tint, timeline column fill, legend and rail) now
comes from one table and one per-line classifier instead of a colour function
beside a hand-listed legend. -
A blame line with no knowable age — worktree-only, undated, or dated after
now by a skewed clock — carries no row tint. It used to tint as the freshest
code in the file, which also disagreed with the band filter that excludes it. -
Walkthrough moved out of the title bar: the persistent Walkthrough
pill no longer sits in the header. Replay now lives in Settings →
Appearance → Guided walkthrough, beside First-run coach marks, and
Settings closes as the tour opens so the guide is not drawn underneath it.
The first-run tour itself is unchanged.
Fixed
- The notification bridge no longer fails Windows clippy. Its socket
is Unix-only, but the accept-loop timeouts and the rejection counter were
still compiled everywhere, so-D warningstreated them as dead code. - Quiet hours compile on Windows. The local clock is read through
libc,
and that dependency was declared only for Unix.localtime_s, the Windows
spelling of the same call, was already written, but the crate was not linked,
so the Windows CI leg stopped at clippy before any test ran. - Windows CI can finish, which the release gate requires before it will
prepare a draft. Three failures on the lastmainrun would have refused
v1.3.0atrelease-state ready: clippy-D warningson Unix-only devmap
test seams that were still compiled for Windows, a watcher fixture whose
relative path cannot exist when the temp directory and the runner cwd are
different volumes, and a terminal-hosting stress test that crossed Vitest's
5s default on a loaded Ubuntu runner. The seams arecfg(unix)with their
callers, the fixture is created on the cwd's volume, and that stress test
has room past the default timeout. The same run's menu-payload and issue-task
stress tests crossed their limits once coverage instrumentation was on, so
those limits moved with the work. The process-admission storm that failed
the same run because every spawn was refused — a result that proves nothing
— is retried, and an escape still fails on the first attempt. - The activity ledger recorded any shell whose path merely contained an agent's
name —/Users/claude/bin/zsh— as an agent session. It now matches the
program actually launched. - Managed Codex could not start at all against codex-cli 0.153.4. The harness
still passed--listen stdio://, which that build no longer accepts; rather
than failing it exits successfully having written nothing, so the run reported
only "managed provider connection ended" and named no cause. - An ordinary policy-harness shutdown no longer leaks process groups. Closing
the sidecar escalated with a kill aimed at the direct child only, so anything
manvi servehad forked survived — observed outliving the process that started
it, reparented to init. The whole group is now signalled, and signalled in the
one order that is safe: while nothing has waited on the pid, because the pid is
the group id and a reaped pid may be recycled. A graceful wait that polls
try_waitreaps as it watches, and so had nothing left to signal by the time
it knew the child was gone. - Blame rows now honour the density setting. Rows carried a fixed 24 px
height whileVirtualListpositioned them fromrowHeight("blame", …), so at
Compact density every row overhung its 20 px slot and drew over its neighbour. - A long blame line has somewhere to go. Lines were clipped at the pane edge
with no scrollbar to reach the rest; the pane now shares one horizontal
scroller, as the diff does. - Blame pane continuous flashing and state loss eliminated. Background status
polls and content revision updates no longer unmount the code-age timeline strip
or replace the VirtualList with a loading screen. Implemented a stale-while-revalidate
(SWR) architecture, deep line equality checking (areBlameLinesEqual), and
subject-aware state tracking that preserves the user's scroll position and active
timeline filter selections across background revalidations.
Hardened
- The Rust suite is a gate again. Nineteen tests decided their outcome by
whether a child process answered inside a deadline, so a busy machine failed
them and a quiet one passed; they now assert the property directly, at the seam.
The rest of the cascade was not flakiness at all: one test installed a
process-global stub-binary override and cleared it on its last line, so a
timeout there left every later test in the run pointing at a deleted temporary
directory — one slow test presenting as nineteen failures across seven
unrelated modules, each of which passed when run alone. The override is now an
RAII binding that cannot outlive the test that took it. - Native menu main-thread safety and command gate stress test coverage.
- Serde/TypeScript type sync expanded to 1188 data fields across 169 structs and 225 IPC handlers.
- Closing a repository tab that holds live shells now asks first, and says how many
will be ended. Close Other Tabs and Close Tabs to the Right are covered
too, counting every repository they would discard; both bypassed the single-tab
close path entirely. The check fails open if the session count is unavailable —
an unclosable repository tab is worse than a missing warning. - The harness pages are typechecked. They are the real-UI verification layer and
sat outsidetsconfig.json'sinclude, so a harness host referencing a store it
no longer imported typechecked clean and surfaced only as a browser-run timeout. - Randomized-sequence coverage for the terminal hosting rule, asserting over
hundreds of open/close/switch steps that no repository is ever hosted — and so
no shell ever started — without the user opening a terminal on it.
Removed
- The
showWalkthroughButtonpreference and its Layout toggle, which
existed only to hide the pill. Stored copies in existing profiles are
ignored on read.