Repository navigation
Releases: bharathvbcr/GitPulse
Release list
GitPulse v1.3.1
Fixes and visual polish for code navigation, viewer layout scaling, and tooltip placement.
Fixed
- Code viewer zoom row height scaling. Code line heights in
CodeViewernow scale proportionally with editor zoom stops (70%–160%) usingscaledRowHeight. The virtual row containers and text line heights stay perfectly matched, eliminating visual line overlap and row clipping at compact zoom. - Boundary-aware tooltip bubble placement. Tooltip positioning now calculates viewport edges through
placeTooltipBubble, keeping tooltips fully on-screen across arbitrary window sizes and respecting preferred directional hints (abovevsbelow). - Responsive horizontal header controls and scroll cues. File viewer headers across
CodeViewer,FileTreePanel,MarkDevViewer, andMediaViewernow gracefully scroll horizontally when horizontal space is constrained, paired withScrollCuevisual indicators to ensure all controls and action buttons remain reachable on narrower viewports. - MarkDev rendered view layout and scrolling. The markdown rendered preview pane now uses bidirectional scroll containers and proper flex bounds, preventing text clipping on oversized formatted blocks.
- Breadcrumb path navigation typing. Fixed navigation strip element bindings in
FileViewerto ensure strict type compliance.
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 e...
GitPulse v1.2.0
A release about checks that were not telling the truth. Every fix here is a
report that read as an answer without being one: a doctor that compared a
version against itself and said ok for a binary running week-old code, an
installation check that named a cause it did not have, warnings dropped on the
way to the panel that promised to show them, and a measurement that silently
never happened on a window that had stopped painting. A check that could not
run must not read like one that ran and passed.
Repository trust is the other half. An approval made before 1.1.0 left every
linked worktree refused with nothing offering a way out — the app reported the
repository as trusted and never asked again, so worktree comparisons, collision
checks and the fleet view quietly dropped every sibling. Trust is now reported
as a scope rather than a yes/no, and the sidebar offers to extend an
approval that predates worktree coverage. Approvals are still never widened by
being read: extending is a decision you take, through the same dialog.
The new surface in this release, Firebase App Hosting, is built on the same
rule. Its backend and rollout listings are click-only rather than loaded on
render, because the upstream commands enable the App Hosting API on the project
they read — a read that bills is a decision, not a side effect of opening a
panel. What it buys is a join rather than a guess: App Hosting reports the full
commit SHA it deployed, so "this commit is live" is looked up against the graph
instead of inferred from a branch name and a timestamp.
That panel can now also deploy, and one action in this release changes what
production serves. Creating a rollout is neither reversible from GitPulse — App
Hosting publishes no rollback verb — nor idempotent, so it is the one place
here guarded twice rather than once: the target must be a full 40-character
SHA, the confirmation names the exact project, backend and commit and says
plainly that GitPulse cannot undo it, and editing any part of that target
disarms the confirmation so the values sent are always the values reviewed. The
policy gate runs before the process, not alongside it. And what the installed
CLI can actually do is probed rather than assumed, because the upstream
subcommand is registered only behind an experiment that is off by default — a
capability that could not be probed is reported as unknown, never as absent.
Added
-
App Hosting can deploy a named commit, not only report one.
Create rollout
is the only command in the Firebase module that changes what production
traffic serves, and it is built to be refused easily and taken deliberately.
The commit must be a full 40-character SHA — an abbreviation is an ambiguous
target for something that reaches production, and is declined with the
reason — and the deploying button does not exist until a confirmation naming
the project, the backend, the commit and the fact that GitPulse cannot undo
it has been read; changing any part of that target disarms it again. The
write gate judges the argv before any process is spawned, and a contract test
asserts that ordering directly rather than trusting it, because this argv is
built in the Firebase module instead of being spelled out in the handler and
is therefore exempt from the literal comparison the other commands get.
--tokenis never passed, so no credential is placed on a command line other
local processes can read, and--forceis never passed either, so nothing
here suppresses a prompt the user would otherwise have seen. A rollout that
started is never reported as failed: if the CLI exits zero without confirming
the result, the panel says so and warns against a blind retry, because
upstream allocates a new rollout id per call and a false failure is what
turns one deployment into two. -
The Firebase panel asks the installed CLI what it can do instead of assuming.
apphosting:rollouts:listis registered upstream only behind the
internaltestingexperiment, which is off by default, and an unregistered
subcommand exits non-zero having written nothing to either stream — which
would have read as "no rollouts" rather than as "this CLI cannot answer". The
probe lists a command group, so it needs no login, no project and no
network, and the action is offered only when the subcommand is really there.
A capability that could not be probed is reported as unknown rather than as
absent, because the two have different remedies. -
The board's quick-add line can draft. Return still saves the typed line
exactly as before, and then asks the configured model for a title and a
description to review. Two rules keep it honest, and neither is a tiebreak
applied afterwards: the task is written from the typed markers first and the
model proposes against the saved result, so markers always win structurally;
and the model can only ever touch title and description, because that is what
the enhancement field type admits — priority, labels, owner, type, repository
and due date are out of reach by type rather than by policy. The sentence
shown before you press Return and the request made after it are derived from
one value, so the promise on screen cannot name a field the request does not
carry. Off by default, and remembered: it changes what a keystroke does, so
it is chosen once rather than every time, and a stored value this build
cannot read falls back to manual — the mode that spends a model call must not
be the one a failed decode selects. -
Due is a control the sheet owns rather than the browser's
datetime-local
box. A trigger, a portaled month grid, and a word box that shares the board's
quick-add grammar — typefriday 09:30and press Return, the same phrasing
that works on the quick-add line, which is also what proves the grammar is
wired rather than duplicated.taskDueowns only what was unowned: the
grammar of a date word stays withparseQuickAddDueand how urgent a deadline
is stays withdueState, because the board sorts and filters on that and the
sheet must not answer it a second way. All of it is local time — a due date is
a day in the reader's week, not an instant, so the sheet cannot show "Sep 18"
for a timestamp the board files under the 19th. -
The plugin package has a display name, and a mark where a client will actually
render it.nameis the install identifier, so the list showedgitpulse.
The logo lives in.cursor-plugin/plugin.jsonand nowhere else: Cursor is the
only one of the three hosts that renders alogo, Claude Code documents no
field for one, and the Agent Plugins schema setsadditionalProperties: false,
so the same key in the portable manifest invalidates the package for a
conformant client.check-release-versionwalks the Cursor manifest with the
other three, so its version cannot drift from the release. -
Firebase App Hosting backends and rollouts, read through the
firebaseCLI
and joined to the commit graph.Build.source.codebasecarrieshash— a
full SHA-1, the same keygit cat-filespeaks — so "is this commit live?"
becomes a lookup rather than an inference, and presence is read from the
command's ownsuccessflag rather than fromis_ok(), because
git_capturedreturnsOkon a non-zero exit: absence is data, not failure,
and reading it the other way would have made every rollout look locally
present.The two listings are click-only and wear
Guarded<T>on purpose, and this is
the part worth stating plainly:apphosting:backends:listand
apphosting:rollouts:listboth declare.before(ensureApiEnabled)upstream,
so reading them enables the App Hosting API on the Cloud project. REST does
not avoid it — App Hosting's only OAuth scope iscloud-platform, full
read/write, with no read-only counterpart of the kind classic Hosting accepts.
A read that mutates a billed resource must be something you choose, never
something a panel does because it rendered. Passing--jsonalso implies
non-interactive, which turns a would-be enablement prompt into a loud error
rather than a silent one.apphosting:rollouts:listexists only in
firebase-tools' source and is documented nowhere, so its envelope is parsed
strictly and fixture-pinned instead of trusted to keep its shape.
Changed
- Writing a task and asking the model to help with it are one pane again.
Splitting the sheet into four panes fixed a sheet where nothing read as
important and introduced a worse problem: the AI pane's output was drawn on
the Task pane, beside the fields each suggestion would replace, so a reader
pressed a button on one pane and the result appeared on another. Writing a
task, scheduling it and improving its wording are one sitting, so they are now
one pane in two columns. Only Agent still earns a tab — handing a saved
revision to something that will act on it is a different decision, with its
own risk and its own run history. A draft is still never tabbed. - The suggestion picker can tell two attempts apart. Its rows read
state · modelplusTask revision N, which was the same string for two
attempts by the same model at the same revision. A timestamp separates
attempts made minutes apart and an ordinal separates attempts made in the
same second; the ordinal is a reading aid derived from the page total, never
an identity — the row's value is still the proposal id. State wording now
comes from one table read by both the picker row and the heading it selects,
which were previously the same literal list written twice. extended_child_pathleaves aPATHalready at theexecveargument-size
ceiling alone instead of appending to it. Growing a near-limit environment
turns a working spawn intoE2BIG, surfac...
GitPulse v1.1.0
A feature release that began as a patch. What was staged as 1.0.1 — the
repository-trust gate and the hardened file saves — was never published, and
ships here instead of on its own.
The tasks page was rebuilt around the work you do most: adding a task, finding
one, and handing one to an agent. Nothing on the board is hidden without saying
so, and the agent handoff is one form rather than two that had drifted apart.
Branch names get a line to themselves, drafting can run on the Mac instead of
over a socket, and DevMap builds its own index rather than answering every
question with an empty result.
The fixes are mostly of one kind: failures the app was misreporting rather
than merely failing. Truncated Git output that read as a complete short answer,
a plugin whose hooks were installed nowhere while its doctor said ok, and an
untrusted checkout described as a broken one.
Added
- Quick add: one line creates a task, with
!priority,#label,@owner,
due:, repository and::note markers parsed as you type and shown as
chips before anything is written. The line is capped and refuses rather than
truncating, and theakey focuses it. - A View menu that persists: hide columns you do not use, choose which fields
each card shows, and reset. A board hiding work says how much and where. - On-device drafting with Apple Intelligence on macOS 26 and later, as a second
engine beside Manvi. Titles and descriptions are written on the Mac and never
reach a socket. Where the framework is unavailable, the reason it gives is
shown rather than a generic failure, and a build without the bridge says so
instead of blaming the hardware. - A handoff sheet on the board: launch an agent in two clicks, with the checkout
already resolved, without opening the task. - DevMap builds its own store on first use. Opening a repository with no index
used to answer every code-intelligence question with an empty result, which
reads exactly like a clean one. Component status is reported per component
with its warnings kept separate, so a partial DevCouncil install is never
rendered as a complete one, and a rebuild chooses between a full manifest
pass and an incremental one instead of rebuilding on every change. The suite
and agent-integration panels and a fleet-wide index sweep come with it. gitpulse-hook --versionanswers with its build and every subcommand it
serves. No host sends it;mcp:doctorneeds it because silence is a
legitimate answer to every real hook invocation, so nothing else can tell an
absent binary from a current one.- First-run walkthrough: an interactive tour that highlights the controls
themselves as you open a repository, try views, and explore Tasks and
Settings, rather than describing them from a panel off to one side. The card
anchors to the control it is describing — taking the space below it where
there is room and above it where there is not — scrolls that control into
view, and follows it as the window resizes. It sits below menus, trust
prompts and Settings in the stacking order, so it never covers what it is
pointing at, and it observes its target only while a step is on screen:
nothing polls, and no listener outlives the step that created it. Replayable
at any time from Walkthrough in the title bar. - An Archive dock on the tasks board: completed tasks for the current
scope, with their own search and Load more, and a header badge carrying
the server's total rather than the loaded page. A selection restores to any
other status, or is deleted, through the same confirm-and-retry dialog every
other bulk change uses — the dock owns no write path of its own. It says
whether the Done column is still on the board and offers the same hide the
View menu owns, rather than keeping a second flag that could disagree with
it, and the board's hidden-column banner offers the archive by name once
Done is off screen. The panel reports the loaded count against the total for
as long as they differ, and an archive that has not been read — the query is
deferred while the window is in the background — says so instead of reading
as an empty one.
Changed
-
The task editor is four panes — Task, Organize, Assist and Agent — instead of
one long scroll, with the title first. -
The agent handoff is a single shared form used by both the board sheet and the
editor, so the two cannot disagree about what will run. Provider, connection
and permission are remembered between launches;bypassis not, and has to be
chosen with its acknowledgement each time. -
The right-click menu groups status, priority, due date and owner into
submenus, and disables what it cannot currently write. -
Sidebar branch rows are two lines. The name has the first line to itself and
every measured number — lines added and removed, files changed, ahead and
behind upstream, commits ahead of the base, provenance, author and commit
age — sits on the second. On a default-width sidebar the single line gave the
name whatever its row of counters and buttons left over, which was often
three characters: real branches rendered asb..,d..,r... Measured on
a 320px sidebar with churn and divergence present, the name went from 52px to
198px — 8 characters to 31 — at a cost of 22 visible rows to 16. -
The commit age carries staleness itself, tinted amber, instead of a separate
stalechip repeating the same verdict less precisely beside it. -
Author and commit age were already on every branch record and shown on no
row; they are now on the second line, and are the first things dropped when
a narrow sidebar runs out of room. Counts are never clipped. -
Appearance › Sidebar branch rows switches back to the dense single-line
list, which fits roughly 45% more refs on screen. -
Four macOS-only surfaces that shipped on every platform are hidden where they
cannot work, decided by a capability the host reports once rather than a
platform test repeated at each call site. Hidden rather than disabled: a
permanently greyed control invites a hunt for the setting that enables it.
The settings catalog still declares them, so they are provably hidden rather
than quietly deleted. -
RUSTSEC-2024-0429 (
glib 0.18.5) no longer applies: the September 2026 GTK
migration moved the Linux stack toglib 0.22.9. On 2026-09-13 the tree
passedcargo audit --deny warningsacross 550 Rust dependencies and
npm auditacross 175 npm dependencies with no reported advisories. These are
dated advisory-database checks, not exhaustive source or runtime verification. -
The IPC surface grows to 220 handlers, and the Rust/TypeScript contract check
to 65 contracts across 1,104 fields. -
Repository trust is granted to the repository, not to one checkout. A linked
worktree is no longer a second decision: approving any working tree covers
the checkout the repository lives in and every worktree of it, including ones
created later. Agent worktrees under.claude/worktrees/now open, index and
report without a prompt each, and removing a worktree no longer asks for the
target separately.The shared surface a trust decision is actually about — one configuration,
one hook directory, one object database — was always repository-wide, so
asking per checkout bought no authority it did not already grant. What
membership means is now proved rather than assumed: a checkout is covered
only when the approved Git directory vouches for it, by holding the
repository as a real.gitdirectory or by carrying a registered
worktrees/entry whosegitdirnames that checkout back. Pointing a
gitfile or a symlink at a trusted common directory is a claim, not evidence,
and is refused.Revoking now reaches the whole repository, including approvals recorded
separately for its other worktrees, so a revocation cannot be partial.Approvals made before this release keep working and are not widened: each
authorizes exactly the checkout it named, so upgrading re-prompts for nothing
and silently grants nothing either. The scope of the decision changed, and a
decision taken under narrower terms should be re-taken rather than
reinterpreted — so the first approval after upgrading is the one that covers
the family, granted through a dialog that now names that scope. -
Repository hygiene settings resolve in two layers: one host-wide default that
every repository inherits, and an explicit per-repository override. Scope now
decides where a setting lives. Retention had no default before, so every newly
opened repository restarted at 30 days and a house rule had to be retyped per
repository; and the shared-cache review — which measures caches owned by the
host rather than by any repository — was stored per repository too, so opting
in from five repositories scheduled five weekly scans of the same caches while
opting in from one applied nowhere else. Settings › Repo hygiene gains a
Hygiene defaults panel for the inherited values, and each control names the
scope it governs.Settings stored by earlier versions are adopted when their repository is next
opened rather than in one sweep, because the browser storage holding them
cannot be enumerated for them up front; the old record is removed as it is
adopted. A stored retention becomes an override only where it departs from the
default, so a repository that had simply been left at 30 days stays an
inheriting one.This layer governs the previewed cleanup in Insights › Storage. The
scheduled global cleaner keeps its own retention in the backend policy file,
because the headless worker that reads it has no browser storage to read — the
two remain separate mechanisms.
Fixed
- Turning off ...
GitPulse v1.0.0
First major GitPulse release. The v0.1.0 tag was cut the day before, but
GitHub never published it: installer uploads rewrote the draft's tag_name
to untagged-<hex> and finalize aborted with every installer already on the
draft. That desktop work — native menus, the macOS status popover, repository
hygiene, the rebuilt command palette, and the workbench — ships here together
with core Git workflow improvements, DevMap / code-intel hardening, and a
release pipeline that can finish after GitHub detaches a draft tag.
Added
- Expand the native application menu with Go submenus for all 16 view sections,
zoom controls, and Help entries for documentation, shortcuts, diagnostics,
optional-tool setup, release notes and issue reporting. Section navigation
reveals the repository pane from Fleet. Repository commands disable when unavailable. - Add live menu checkmarks and progress labels, an open-repository switcher,
clear-recents, staging and branch actions, parked-operation controls,
copy/reveal/remote utilities and manual update checks. - Add an optional icon-only macOS status item opening a compact light/dark
popover: repository switching, changed/staged/conflict/stash cards, last-fetch
sync counts, parked-operation and busy-work chips, History/Pulse/Fleet/Terminal
shortcuts, copy/reveal/remote/appearance utilities, expandable details and
contextual review/recovery actions. Refresh and appearance stay in the panel.
Open GitPulse restores the main window; closing the main window while enabled
preserves its session. Copy, refresh and appearance stay in the popover; Reveal
and Remote dismiss it without bringing GitPulse forward. Right-click retains
native app controls and adds the current primary action and Refresh when
available. Escape collapses the switcher or details first;Rrefreshes and
1–4open a nonzero metric. The status window still cannot mutate Git. - Menu bar status enhancements: attention-dot glyph, optional tray title counts,
per-repo switcher badges, realFETCH_HEADage, hide-Dock-while-closed, and
Launch at login (LaunchAgent with--background). - Add a global build cleaner to Fleet and Settings, with selected roots,
exclusions, retention and run budgets, opt-in native scheduling, closed-repo
discovery, durable history and an optional macOS headless LaunchAgent. - Reuse DevCouncil's Rust hygiene policy and generated agent guidance. Harden
traversal, case-insensitive preservation, partial scans, revision revocation,
lock release, registration failures and Git filesystem-monitor suppression. - Add Repository hygiene to Storage: stale-output previews across supported
language ecosystems, shared-cache inspection and tool-owned maintenance,
saved retention/weekly-review preferences, cancellation and explicit outcomes. - Drag task cards across Kanban columns with a movement threshold, mid-card
insertion, and neighbor-column keyboard moves. Positions stay strictly between
neighbors instead of appendingDate.now()in the middle of a column. - Add currently open GitPulse tabs as workspace or task members from the board
and workspace editor, registering a path only when it is not already linked. - Keep selected task title and description locked while automatic enhancements
run. - Add task board/list layouts, priority/type/owner/label/due filters over loaded
cards, multi-selection and keyboard-accessible context menus. Actions include
duplication, status/priority changes, agent copying and confirmed deletion with
per-task outcomes and bounded delete passes. - Add notes-to-draft editing, inline Manvi title/description suggestions and a
Quick Enhance sheet for saved tasks. Proposals respect field locks and require
explicit acceptance against the saved revision. - Add agent copy for new unsaved drafts and saved task briefs. Saved copies name
their revision and exclude unsaved edits; board copying reports its eight-task
limit and partial results. - Modular DevCouncil install in Setup: DevMap only (default), analysis suite, or
full host. Copy the documented command or run it in Terminal → Console.
Settings can disable or uninstall a GitPulse-owned binary without going
throughcargo uninstall. There is no uv / Python install path. - Save named stashes from the existing repository panel, with controls for
including untracked files and keeping staged changes. Preview entries inline
in the read-only code viewer before applying, popping, or dropping them. - Stage or unstage a selection in one native request. Bound path counts, bytes,
and argument chunks; evaluate every planned command before the first write,
hold one repository mutation lock, and report partial Git failures explicitly. - Scan GitHub Dependabot and code scanning alerts when a repository opens, and
warn when critical or high findings are open. Turn off under Settings →
Analysis. Failures stay in Health and diagnostics, not as an all-clear toast.
Changed
- Rebuild the command palette with eight discoverable search modes, fuzzy ranked
commands, file and repository search, complete view/section navigation, Clone and
Rebase dialog entry points, contextual availability, result paging and accessible
keyboard/focus behavior. Successful-use history is validated and bounded. - Keep palette searches current across query/mode/repository changes; show loading,
deadlines, failures, retry and partial coverage. Resolve cross-repository symbols
through the workspace registry before opening files, preserve help-mode transitions,
and make the status-bar palette entry work before its first lazy load. - Compact the Work inbox to open-and-read, fold notification settings, and give
automatic suggestions a board-sized control. Fold task enhancement locks and
repository membership into editor details. - Give the macOS status popover the app's native background blur, translucent
surfaces and rounded material bounds. Preserve the compact layout and provide
opaque accessibility fallbacks. Match the native window to the panel on resize
and reopen; the browser fixture includes a labeled glass simulation. Select
the tray display using physical bounds so Retina coordinates can open the panel. - Refine the macOS status popover around three large Changed, Staged and Conflicts
cards, a monochrome repository header, last-fetch counts and one blue primary
action. Keep stashes, workspace insights, the command palette and secondary
shortcuts inside Details, with Open GitPulse, Settings and Quit in the footer. - Named GitPulse as the successor to the deprecated LiquiTask workbench in the
README, wiki home, and Tasks documentation. - Document DevCouncil as independently selectable components and modules, Manvi
as the wrap around them, and GitPulse as the host that uses each for its
respective job. - Refresh README, feature, architecture, contributor and wiki guidance for Tasks,
schema-20 DevMap, current shortcuts, module ownership and verification limits.
Add a Tasks and workspaces guide; preserve older implementation plans as plans. - Resolve repository maps per worktree in both agent guides and document
devmap build --manifestfor missing generated state. - Re-vendor DevCouncil analysis crates at v0.2.0 (
devcouncil@a31918e2) from
rust/after that workspace flattened out ofrust-port/crates. Include
dc-evidencesodc-verifycan link. Re-vendordc-verifyso ledger writes
keep usingrigor::redact_secrets. - Add a CodeQL config that excludes
src-tauri/framework/**. Bindgen
offsetof tests and WRY cookie conversion in those ports are not GitPulse
sinks. Attach the file to default setup in GitHub Settings. - Refresh
@lucide/svelte1.44.0, Vite 8.3.0, and@types/node26.5.1.
Fixed
- Resume a GitHub draft whose
tag_namebecameuntagged-<hex>after
installer uploads, instead of posting a second draft./releases/tags/{tag}
does not return drafts, even while the tag is still attached; prepare lists
drafts by that tag or by the name it POSTs, and refuses a truncated page
rather than treating it as "no draft". Identify a draft by its tag and
release ID. GitHub often rewritestarget_commitishto a branch name after
associating an existing tag; a SHA intarget_commitishmust still match the
pinned commit, and a ref name is not a second pin. Anuntagged-placeholder
is accepted only in GitHub's hex form, and finalize writes the intended tag
name back with the notes. Platform builds upload by release ID only so
tauri-action cannot retarget the draft. Prepare now also deletes leftover
draft installers before the matrix rebuilds: GitHub 422s a second Windows
NSIS/MSI upload of the same name, and tauri-action retries that as a
flaky upload until the Windows leg fails. A draft still pinned to the
previous tagged SHA is retargeted onto this commit after that wipe, never
mutated until it is proven a mutable unpublished draft. - Sign every nested helper in the macOS universal bundle before the main
binary. Tauri copies every[[bin]]intoContents/MacOSand signs them in
manifest order — main first. OnceInfo.plistis in the bundle, codesign
treats that file as the bundle executable and every other Mach-O as nested
code that must already be signed.lipoproduces unsigned helpers, so the
sign ofgitpulsefailed withcode object is not signed at all / In subcomponent: gitpulse-hook.scripts/bin/codesignpiggy-backs on the main
binary's sign the same wayscripts/bin/lipostitches the helpers. - Quiet Linux GTK framework ports that drowned the release clippy log:
explicitextern "C"on libappindicator-sys, gir builder visibility and
TypedArrayData<'_>in javascriptcore-rs, crate-levelunexpected_cfgs
on web...
GitPulse v0.0.9
Changed
-
Consolidate all outstanding worktrees and align the embedded SQLite dependency
with MANVI and DevMap. Add explicit DevMap navigation to both agent guides,
with regression coverage that preserves the workflow across regeneration. -
Synchronize the canonical devmap query, HTML projection, extractor and store
modules. HTML assets are owned by the upstream query crate; GitPulse no
longer rewrites the HTML implementation while vendoring it. -
Document the Rust-library, Go-module and NDJSON integration boundaries in
docs/MODULE_INTEGRATION.md, including configurable executable paths and
independent host/protocol/database compatibility checks.
Fixed
- Pin release builds to one verified commit and require successful CI and coverage
for that commit. Refuse published or mismatched releases, resume matching drafts
by ID, verify uploaded asset digests, and confirm changelog notes after writing. - Accept GNU checksum records with escaped filenames and normalize Windows index
transaction paths for Git. Preserve hostile filename tests on supported systems. - Resolve regular-file conflicts on Windows with pinned directories, exact staged
bytes and retained recovery content on replacement or staging failure. Preserve
executable Git modes in the index; Windows reparse points require external resolution. - Bound Unix terminal input delivery and lock waits, cancel pending input on close,
and report partial delivery without retrying accepted bytes. Closing a Linux PTY
no longer leaves a full input-queue write blocked after its child exits. Wake
output readers on readiness so interactive and bulk output avoid polling delays. - Avoid redundant JSON parsing during diagnostic breadcrumbs while retaining the
existing credential redaction and stress-test budgets. Reuse fixed credential
matchers instead of rebuilding them for every navigation field. - Release watcher sessions when their backend event stream closes so watching
again starts a live backend. An old backend cannot remove a replacement session. - Rebuild the desktop, MCP and daemon from the current DevMap 0.1.1 integration.
Embedded readers preserve distinct symbol identities and withhold source
snippets when the indexed content hash no longer matches the current file.
The external builder checks lexical binding scope, source freshness and
bounded input reads. See DevCouncil's September 8 reliability audit for
adversarial regression and stress-test evidence. - Prepare complete vendor updates before replacing the current tree, refuse
concurrent updates and symlinked sources, and retain rollback state on
installation failure. Drift checks now detect upstream deletions and resolved
Cargo manifest changes using the same snapshot builder as updates. - Bound unsolicited MANVI sidecar output between requests and terminate an
overflowing child. The per-request byte budget now has an idle-queue bound
enforced by the same stdout pump in production and live-process tests. - Git subprocess reads retain captured output when descendants hold pipes open.
Unix pumps input and output without reader threads; Windows cancels blocked
I/O and retains resource permits until workers exit. Queueing and lock retries
share the command deadline, and the Windows tree-kill helper has a deadline. - Optional tool installation shares the bounded runner, including cancellation
and progress. Machine-output consumers refuse incomplete output, failed version
commands cannot pass verification, and schema integers cannot wrap. Digest
output uses the existing hexadecimal validator. - Blame handles new, staged, ignored and unborn-repository text files without
fabricating committed attribution. Oversized or incomplete blame is reported
as unavailable instead of returning an unmarked prefix.
GitPulse v0.0.8
Fixed
- Code → Map no longer pane-crashes on duplicate dead-symbol ids. Real
repo_map.jsonfiles can list the samedead_symbol_candidatesentry more
than once; Svelte 5's keyed{#each}then threweach_key_duplicateand the
Map pane's error boundary reported a pane-crash (six times in diagnostics on
open). Dead / unwired / unreachable lists are deduped before render, and
keyed lists across Map, Work, stacks, harness, and the setup wizard use
index-suffixed keys so a duplicate payload cannot take the pane down again.
Added
-
Optional Manvi / DevMap install ladder. Settings → Agents, Code → Map, the MANVI
harness pane, and a resumable Setup wizard can install or updatedevmapand
manviwithout treating them as required. Resolution order: configured binary →
PATH/~/.cargo/bin→ checksum-verified GitHub release asset → remote
install script → local source (cargo install --path …/devmap-cli --locked --force,
go -C …/manvi install ./cmd/manvi). Progress is cancellable. Broken
GITPULSE_*_BIN/GITPULSE_*_ROOToverrides are refused rather than searched past;
clone missing checkouts via onboarding. Override roots with
GITPULSE_DEVCOUNCIL_ROOT/GITPULSE_MANVI_ROOT. -
devmap doctor --jsonverify on status. Tool status prefers the doctor JSON
contract (expected_schema_version, code-graph schema) over scraping--version
prose, so schema drift is named instead of a silent mismatch with the vendored store. -
Capability gating when optional tools are absent. Map / harness UI degrades to
explicit “not installed” empty states and CTAs instead of empty panels that look like
“nothing found”. Wizard and palette entry: “Set up optional tools (devmap / manvi)”.
Changed
-
Dependency refresh (2026-09-07). Frontend floors raised to current stable
(svelte^5.57.0,@tauri-apps/api^2.11.1,@tauri-apps/cli^2.11.4,
@tauri-apps/plugin-opener^2.5.5). Rust direct reqs raised where already
resolved (rayon1.12,notify8.2,regex1.13,tempfile3.27) and
cargo updatepulled latest compatible transitive crates. Intentionally
unchanged: classictypescript@~6.0.3+@typescript/native-preview@7.0.0-dev.20260707.2
(svelte-check peers still ^5|^6; tsgo split preserved), Tauri git pin
406feea…(crates.io still 2.11.5; unpublished 2.12 line keeps urlpattern 0.6),
rusqlite0.31 (vendoreddc-store/devmap-store),notify8.x (9.0 is RC),
libc0.2 (1.0 is alpha). No rust-toolchain pin; toolchain left alone. No
re-vendor. -
Typecheck uses
tsgo(TypeScript 7 native preview) for Node/script configs.
npm run checkstill runssvelte-checkagainst classictypescript@~6.0.3
becausesvelte-check@4.7.6peers onlytypescript^5|^6 and does not drive
tsgo. The formertsc -p tsconfig.node.json --noEmitstep is now
npm run typecheck→tsgo -p tsconfig.node.json --noEmitvia
@typescript/native-preview@7.0.0-dev.20260707.2. App.sveltetyping
remains on svelte-check until it accepts TypeScript 7 / tsgo.
Note
- v0.0.7 was tagged then withdrawn after the Map pane-crash above was found in
the local build. It was never published as a GitHub Release; this cut is the
first ship of that work.
Added
-
DevMap code intelligence works again at store schema 19, with a handshake that names drift. The
vendoreddevmap-*crates now match the on-disk.devcouncil/codeintel/devmap.sqlitestores every
sibling repo already builds (schema 19). A mismatched build reports map built by schema N, this
build reads M rather than a raw SQLite refusal, andnpm run check:vendor-schemafailsci:local
when the installeddevmapCLI and the vendored constant disagree. -
Build, refresh, status, and edit preview are driven by the installed
devmapCLI. GitPulse still
queries the store in-process; it shells out for indexing (build/ incremental refresh /status --json/preview --file … --content -). Lookup reports which path answered (env override vs
PATH/~/.cargo/bin), refuses a configured path that does not resolve, bounds wall clock and
stdout, and refuses a second build for the same repo while one is in flight. -
Pre-commit blast radius: what this commit would break. The commit composer and the diff file rail
share onedevmap previewbatch over staged paths. Each file carries parse status, degradation,
compared-against, and broken callers — and a walk that stopped early or a file with no grammar is
marked unreliable rather than shown as "nothing breaks". -
CI:local can scope tests to the change set, and fails closed to the full suite. Affected-test
seeds come from the working-tree status; results are test files, chunked at the kernel's neighbor
target cap. A stale map, incomplete walk, or unmatched seed runs the full suite and says so — the
GitHub CI panel never badges a fail-closed full suite as "affected tests passed". -
Layered blast radius and a resolution-rung filter on Diff. Change-set impact is composed over
changed files by hop (impact_layered), rendering each band's sample andnodes_omitted/
node_count. Flat impact offers a min-rung filter plus a rung histogram ("what you did not see");
the control is suppressed wherever layered impact is active, because the kernel refuses that pairing. -
Code → Map: subsystem navigator, code/doc graph canvas, and repo docs. A third Code section
(⌥3when Code is active) reads.devcouncil/repo_map.jsonwith capped samples named as such,
draws code-graph and map-preview canvases (and a MarkDev doc graph), and searches tracked markdown
plus broken links / cross-repo link candidates. Freshness comes fromdevmap status; Build /
Refresh live on the status strip. -
The code map refreshes from the file watcher. Debounced
repo-changedevents ask for an
incremental refresh when status says the index is stale, with the same single-build-per-repo guard. -
MarkDev's Rust core owns markdown parse/render and dual-backend highlighting. The file viewer
uses the flat UTF-16 model (no TypeScript reimplementation). Tree-sitter covers rust / JS / TS /
Python / JSON / shell; everything else stays on the existing regex tokenizer under one owner in
diff/highlight.ts. Commit message bodies and MANVI verdict detail render through the same path.
The docs vault is built fromgit ls-filesof markdown (not a filesystem walk), with backlinks in
the viewer; rename isgit mvplus staged link rewrites via MarkDev's pure rewriter. -
Open tabs register in DevMap's workspace for cross-repo search. Palette
::searches symbols
across registered repos (trailing~requests TF-IDF name ranking); bare:stays single-repo.
Truncation and unavailable repos are named on the result strip.
Honesty (read these as product rules, not caveats)
walk_incompletemeans the list is a floor, not a complete answer.- Capped samples carry shown/total/truncated (repo map, graph legend, docs search, broken links).
- Schema mismatch and missing/
devmapbinary failures are named; they are not empty "all clear" panels. - Affected-tests scoping that cannot prove coverage runs the full suite and says why.
Added
-
Tags now say where their work stands, so work parked on a tag is no longer invisible. A tag is a
ref, so the graph walks it and draws its commit a lane — one shaped exactly like an unmerged branch.
Butlist_branchesand the cleanup plan read onlyrefs/headsandrefs/remotes, so nothing in the
app could answer "is this merged?" for a commit held by a tag alone, which is exactly what a
retired/…tag does.TagInfonow carriescommits_ahead_of_base,commits_behind_baseand
compared_to, measured against the same default base branches are measured against and read in the
same singlegit tag -lprocess via%(ahead-behind:). The sidebar shows a+Nchip on a tag that
holds commits the base does not, and the tooltip says either how far ahead it is or that every commit
is already in the base.compared_to: nullmeans the comparison could not be made — an old git, or a
tag that does not peel to a commit — and is never rendered as "merged". -
Ops now reports what the repository retains on tags, which branch cleanup counts nowhere. A new
Tag retention card splits every tag into "holding commits the base does not have" and "every commit
already in the base — safe to delete", with per-tag counts and the existing tag-delete path behind a
confirmation. A tag the comparison could not be made for is reported as its own number and is never
proposed for deletion, and all three caps (the tag listing, the retained rows, the deletable rows)
say so rather than letting a sample read as the whole repository. No tag is pre-selected: unlike a
merged branch, a tag is usually kept on purpose. Deletion is disabled unless the three buckets add
up to the tag total, mirroring the branch plan's own coverage guard. -
Graph tag chips carry the count too. A tag lane is shaped exactly like an unmerged branch lane,
so the chip on the commit row and in the graph tooltip now reads+Nfor a tag holding commits the
base does not have, and its hover says either that or that every commit has already landed. It reuses
the tag listing the sidebar has already loaded, so the graph pays no extra command on a path that
reruns on every repo switch, and a tag past the listing cap simply says nothing rather than implying
its work has landed.ManviOpsPaneljoins the runtime harness (harness/stress.html?c=ManviOpsPanel), -
Language breakdown now supports on-demand rescan. The language segment dropdown now provides a manual
rescan button (locMetric.refreshwithforce: true) with an active spin indicator, allowing instant
recalculation of code percentages after checking out br...
GitPulse v0.0.6
Added
- Eight new preferences, and a way to find any of them. Settings gained a Diff & code category and five appearance controls, which took the page past thirty switches — the point where grouping stops being enough, because knowing that word wrap lives under "Diff & code" means already knowing GitPulse's taxonomy. A filter box now narrows the category rail and the rows inside each panel, matching synonyms the labels never say ("side by side" finds the split-diff default). Every control is stamped with the catalog id it is registered under, and the catalog and the markup are compared in both directions: a control with no entry is unfindable by search, an entry with no control is a result that highlights nothing, and neither can ship.
- An accent colour. Six choices tint selection, focus rings, the macOS glass and the commit graph's current-row marker. Each carries a separate light and dark shade — accent text sits on
--c-surface-hover, which is near-black in one theme and near-white in the other, so one colour cannot be legible on both — and every pair is checked against WCAG AA by a test derived from the palette itself.--shadow-glowand--ring-focusare now expressed in terms of--c-accentrather than restated, so a teal window does not wear a blue focus ring; picking the default removes the override entirely and hands the theme back to the stylesheet. - Reduce motion, as an in-app preference. Deliberately one-way: leaving it off follows the system, and turning it on can only add reduction — a reader whose OS has asked for less motion cannot be overruled from a checkbox. It reaches both places motion is produced, the Svelte transitions and the CSS entrance animations, because either half alone is a setting that visibly half-works. The two triggers are mirrored rule for rule and a test pins the lists equal.
- Timestamps as dates. Commit times can read
2026-09-06instead of3d ago— fixed width, sortable, and the same in every locale, which06/09is not. It reaches the commit list, branch tooltips, the diff's change picker, Work and the stack through one owner; whichever form is shown, hovering gives the other, and the commit list's column widens for a date rather than truncating one. - Diff defaults that survive the next file. Layout, word wrap, syntax highlighting and ignore-whitespace were fixed literals reset on every mount, so a reader who preferred split diffs re-selected split every time. They are now preferences that a diff opens at; the toolbar still owns the file in front of you. The whitespace default seeds each newly opened repository, and the copy names the wrap ceiling (4,000 rows) rather than offering a switch that silently does not apply above it.
- Tab width — 2, 4 or 8 — everywhere code is drawn. Declared once on the document root, where
tab-sizeinherits, so the diff, the file viewer, blame and the conflict editor all follow without each learning the preference exists. 8 is the CSS initial value and stays the default, so nothing renders differently until it is asked to. - Arrow keys in the settings category rail. It has always been a
tablistoftabs and was one in name only: eight separate tab stops, none of which answered the arrow keys the role promises. It is now a single tab stop with Up/Down (wrapping), Home and End. - The macOS window is transparent, and the desktop behind it is blurred by the window server. GitPulse previously synthesised its own backdrop because native transparency was switched off; the blur you saw was the app blurring itself.
macos-private-apiplus a transparent window and anNSVisualEffectView(underWindowBackground) now put the real desktop behind the interface, blurred by macOS rather than by the application, which costs nothing per frame. Three settings have to be present together and any one alone is inert — the feature gate is deeper than its name, becausetauri/macos-private-apiforwardswry/transparentand wry compiles itssetOpaque(false)call only behind that feature, so without it the WKWebView stays opaque and paints over a material that is working perfectly. This forfeits Mac App Store acceptance, which is the reason it had been disabled. macOS-only settings live intauri.macos.conf.json, which Tauri merges by JSON Merge Patch — arrays are replaced, not merged, so that file restates the whole window entry and a test derives the expected object from the base config rather than trusting the copy. - A hue field, so a translucent surface has something to be translucent against. Four wide, saturated radial blobs span the shell and fade to their own colour at zero alpha rather than through the
transparentkeyword, which would dip the ramp through an unrelated hue on the way out. Sized to their own corners they left the middle of the window at the flat base colour — the one part of a window nobody can avoid looking at — so they are deliberately wide enough to overlap across the centre. - The MCP server now answers four capabilities, not one. It advertised
toolsand nothing else, so the only way to ask it anything was a tool call. It now implements every server method the 2026-07-28 schema defines:resources/list,resources/readandresources/templates/listexpose the control plane as addressablegitpulse://documents (eight repository facets as RFC 6570 templates, plusgitpulse://server/manifestandgitpulse://server/health);prompts/listandprompts/getship four workflows that resolve against live state and embed it asresourceblocks rather than telling the model to go and fetch it; andcompletion/completecompletesrepo_pathfor both. All four list operations are cursor-paginated. The method set is taken from the published schema, which is also why there is nopingand nologging/setLevel— neither exists in this revision, and inventing them would have been worse than omitting them. gitpulse-hook, so the control plane can refuse an edit before it happens rather than describe it afterwards. Three hooks ship inplugins/gitpulse/hooks/hooks.json.collision-guardescalates aPreToolUseedit to the user when the same path is dirty in another worktree;command-gateroutes the agent'sBashcalls through the MANVI command gate the desktop app already uses, sogit push --forceis denied with the gate's own words instead of the gate applying to the GUI alone;session-briefinjects a bounded repository brief atSessionStart. Each degrades to no decision plus asystemMessagesaying the check did not run rather than to silent approval — a hook that cannot check must never look like one that checked and passed.
Fixed
hiddendid not hide. The UA stylesheet's[hidden] { display: none }loses to any author rule that setsdisplay, so a Tailwindflexutility on the same element silently outranked it — two settings rows kept rendering while marked hidden, which the filter depends on.[hidden]is now enforced at author level regardless of which utilities a row picks up later.- The theme segment could report a choice the app was no longer honouring. It snapshotted the preference at mount, and ⌘-shortcuts and the native View menu change it from outside the modal; reopening now re-reads it. A control that reads as selected while something else is in force is the same failure as a setting that does nothing.
- The terminal stayed a solid slab, because two things under it paint black and neither is reachable from the stylesheet. Second instance of the graph canvas's shape, and the last surface in the app that owns its own paint.
--bg-terminalresolving totransparentmakes xterm paint nothing, so the surface is the plate its mount div already carries — a second translucent fill from the terminal would be exactly the double coverage the panes were just cured of. The token is deliberately not--bg-surface, which has a second runtime reader in the graph's node stroke; a stroke is not a fill. Three things are load-bearing there and none is cosmetic:allowTransparencymust be set beforeopen()and cannot be changed afterwards; the colour must be hex, becausecss.toColorin @xterm/xterm 6.0.0 parses#rgb/#rgba/#rrggbb/#rrggbbaaitself and pushes everything else through a canvas probe that throws when the sampled alpha is not 255 (measured in WebKit:rgba(20, 26, 41, 0.5)samples 128, thetransparentkeyword samples 0, andgetComputedStylereturns exactly those forms); and xterm.css hard-codesbackground-color: #000on its viewport, which is why a transparent theme colour on its own changed nothing visible. - The commit tooltip was the one float in the application with no blur at all, which is why it read as see-through rather than as glass: a filter that never runs looks exactly like one that does until content moves behind it. The float tier was written as
.gp-card.shadow-float, which named the dialogs and missed everything else — the tooltip carriesshadow-pop, and the toasts, the coach mark and the go-to-line popover carryshadow-floatwithout.gp-card. The tier is now the float shadow, derived in a test from what the components actually wear, and those surfaces keep their denserbg-surfacefill rather than the thinner glass one: a dialog card sits on a dimmed scrim, a tooltip sits straight on commit rows. - The sidebar's LOCAL, ORIGIN and TAGS rows wore a filled band that the folder rows beside them did not.
bg-surfaceon a section header was a barely-visible tint against an opaque sidebar and a dark bar against a translucent one — the same coverage arithmetic as the recesses above. Nothing scrolls under those headers (the list is virtualised, not sticky), so the fill was decoration; it is gone, and the two kinds of group header now look alike. - **The Work screen hugged the left edge and stopped a third of the way down the w...
GitPulse v0.0.5
Added
- Fleet: one surface for every open repository, and every recent one. A workspace of two dozen tabs could only be inspected one tab at a time — "is anything unsaved anywhere", "which of these has an agent running", "what is all this costing on disk" were unanswerable without visiting each in turn. Fleet (
Shift+F10, the leftmost chip in the repository strip, or the command palette) puts them on one grid: changes, sync, conflicts, stash, parked operations, worktrees, agent sessions and last activity, plus lines of code, disk usage, dependency audits and coverage on demand. It is deliberately not a sixteenth view — aViewTabis stored on the active repository's session and its pane lives inside{#key currentPath}, so a view would be scoped to the wrong thing and rebuilt on every repository switch. It sits beside the repository pane instead, and the two swap by hiding rather than unmounting, because that subtree holds the live terminal PTY. - Every Fleet cell is a value, not scanned, or could not read — never a reassuring zero. The failure a workspace dashboard invites is reporting a fleet of clean, empty, vulnerability-free repositories that nobody ever scanned. So the three states are distinct in the model, in the markup and in the totals: a repository with no audit shows "not scanned", an audit that ran but could not finish is marked as a floor, a ledger that could not be opened fails its four cells rather than emptying them, and a total says "1.50 GB — counted across 14 of 21, 1 failed, 6 not scanned" rather than a bare number implying the whole workspace. A repository whose sweep failed is reported unknown, never clean — but never at the cost of downgrading one that has real conflicts.
- Expensive scans stay opt-in, on the same posture as automatic coverage and the release check. Storage walks up to 250,000 files behind a 20-second deadline and the dependency audit spawns
npm audit/cargo auditwith a 90-second timeout; neither ever runs from an effect. The cheap tier costs nothing at all — changes, sync and conflicts are already hydrated in memory — and the middle tier is a newcmd_fleet_snapshot, twogitspawns per repository in one rayon-parallel round trip, deliberately narrower thancmd_insights_snapshot(which probes every worktree and cross-scans up to 16 for collisions: correct for one repository on screen, several hundred subprocesses across a workspace). Family sweeps run two at a time for the expensive families and report successes, failures and skips separately. - Scan results are cached in each repository's own ledger, with their age. A new additive
fleet_metricstable records one row per repository, every family carrying its own value and its own timestamp, so "never scanned" isNULLrather than0and a displayed number can always be dated. Families are independent: a storage scan cannot blank last week's audit. Reads go through a read-only, no-create SQLite open, so rendering a row for a repository that is merely in the recents list never writes a.devcouncil/directory into a repository the user has not opened. - Fleet repository removal with accessible action button and keyboard navigation (
Delete/Backspace). Rows in the Fleet dashboard provide a direct removal action via an action button or keyboard shortcut (Delete/Backspace): removing an open repository closes its tab and drops it from the workspace, while removing a recent repository purges it from workspace history and last-closed tabs so stale paths stay clean without lingering. - The package is now installable and connectable as a Claude Code plugin. The Agent Plugins 1.0 package described the server, but no client could find it: Claude Code discovers a package through a
.claude-plugin/marketplace.jsonat the repo root and then reads<source>/.claude-plugin/plugin.jsonand<source>/.mcp.json, none of which existed. Those files now sit alongside the Agent Plugins and Codex manifests in the one canonical package, sharing its singleskills/tree, and the marketplace points at it. Verified end to end:claude plugin installrecords 0.0.5, the component inventory resolves both skills and the MCP server, andclaude mcp listreports the server connected. - GitPulse is now a native Codex plugin, not only an MCP binary that happened to exist.
plugins/gitpulse/.codex-plugin/plugin.jsondeclares the native surface,.agents/plugins/marketplace.jsonmakes it installable from the repository, and the same canonical package is bundled intoContents/Resources/pluginfor Settings discovery. The portable manifest still launchesgitpulse-mcpfrom PATH; this host's Codex MCP registration uses the absolute installed path so desktop, CLI, and IDE launches do not depend on shell initialization. Verified with a fresh Codex process that loadedgitpulse@gitpulse, invokedgitpulse_insights, and received the live branch and change count. npm run mcp:installandnpm run mcp:doctor. The MCP manifests spawn the bare tokengitpulse-mcpoff PATH — correct for a published plugin, but it means the server a client actually connects to is whatever is on PATH, which no build step owned.mcp:installputs it there throughcargo install, so the binary is tracked and refreshable.mcp:doctorcompletes a real handshake and compares the version the server reports against this tree's, keeping absent, unresponsive and stale distinct from matching — the first of those is the one a naive check would report as a silent pass. Deliberately not inci:local: CI has no reason to install the server, and a check that cannot run there must not be made to look like one that passed.- A repository that pins its own toolchain is told how to honour that pin. Go, Swift, .NET and Dart still refuse when their runtime is absent — installing a language runtime is a host-wide change with no bounded, reversible command, and a coverage panel does not get to make that trade for you. But "install Go, then rescan" is a poor answer for a checkout that has already written down which Go it wants. When
mise.toml,.mise/config.tomlor.tool-versionsis present, the refusal names the pin and the one command that honours it, and it names only a manager actually on PATH — a mise-only config never suggests asdf, which cannot read it. GitPulse still does not run it: the command writes outside the repository, so it stays on your side of the line. - Line count, coverage and storage now track the repository instead of the moment a panel was opened. All three were one-shot fetches keyed on the repository path changing and nothing else, so a day of editing left the headline LOC number, the coverage report and the disk usage exactly as they were when the tab was first shown — with nothing on screen saying so. Meanwhile the backend already emitted
repo-changedon every settled write and onlyrepoStorelistened. A new metric layer (src/lib/metrics/freshness.ts) owns freshness for all three: one measurement serves every panel, the watcher revalidates it, and each metric carries its own debounce and minimum interval derived from what its command actually costs — 20 s for LOC, 30 s for coverage, 120 s for the storage walk, so a build writing continuously intotarget/cannot pin a 20-second tree scan. Change count needed none of this:cmd_branch_statswas already refetched byhandleRepoChanged, and re-measuring it here would have run the same git subprocesses twice per change. - A value that could not be refreshed no longer looks refreshed. A failed revalidation keeps the last good report — panels must not blank out on a transient error — but the snapshot carries
valueandstaleindependently, and every panel reads both. Storage shows an "out of date" badge, the Pulse LOC tile degrades to partial, and a truncated or stale reading is never recorded as a point on the line-count trend. - C/C++ coverage, via an out-of-tree instrumented CMake build. This family used to be a flat dead end on the grounds that GitPulse cannot add coverage flags to a project's build files. The premise was right and the conclusion too strong: CMake takes compiler and linker flags on the configure command line, into a separate build directory, so
cmake -S . -B build-gitpulse-coverage -DCMAKE_C_FLAGS=--coverage→cmake --build→ctest --test-dir→gcovr --lcovproduces coverage without touchingCMakeLists.txtor the developer's ownbuild/. gcovr installs into a project-local virtualenv, exactly as pytest already did. Make-only projects stay a dead end and say why:make CFLAGS=--coveragereplaces a project's own flags rather than adding to them. - Storage now reports a reclaim audit, not just sizes. The report published raw numbers and left every judgement to the reader —
gc_recommendedwas a bare boolean, prunable worktree admin was not detected at all, and nothing said how many bytes were actually recoverable. Each row now carries the action that reclaims it, whether the bytes were measured or estimated, and whether a human has to decide. The headline counts measured and safe bytes only: estimates (a repack's saving depends on the content) and items needing review (committed build output, the reflog, a large file that may be someone's dataset) are carried as separate figures rather than folded into a total the repository cannot deliver. New detection:.git/worktrees/<name>left behind by a worktree deleted withrm -rf, which the old report counted as space belonging to a live worktree. - Manvi Control Plane & Operator Panel:
ManviHarnessPane,ManviOpsPanel, andHarnessBadgeintegration for real-time agent session monitoring, task attribution, and operation gating.- Dedicated harness store (
harnessStore.ts) with lease tracking, task status synchronization, and worktree association.
- Durable WAL SQLite Ledger & Redaction:
...
GitPulse v0.0.4
Fixed
-
Storage report accuracy & hardening:
- Premature truncation resolved: Raised per-directory entry limits inside build artifact directories from 4,000 to 100,000, preventing Cargo dependency directories (
target/debug/deps) with > 4,000 files from prematurely tripping scan truncation. - Unix hard link deduplication: Scoped
(st_dev, st_ino)tracking on Unix for files withnlink > 1, ensuring Cargo hard links (deps/libfoo-hash.atolibfoo.a) are counted exactly once, eliminating gigabytes of phantom disk usage. - Monolithic container roll-up: Container build directories (
target,node_modules,.venv) roll nested build outputs (debug/build,.../out) up into the parent scope rather than fragmenting into dozens of child rows. - Source-tree false positive protection: Paths inside
src/no longer match generic build or cache directory names (e.g.src/lib/coverageremains recognized as source code rather than an unignored cache). - Single-pass worktree traversal: Merged large-file collection directly into the worktree walker using
WorktreeWalkContext, eliminating the redundant second walk and halving disk I/O. - Developer and agent caches classified: Recognized
.devcouncil,.gitnexus,.claude,.cursor,.agents,.gemini, and.antigravityunder cache artifacts.
- Premature truncation resolved: Raised per-directory entry limits inside build artifact directories from 4,000 to 100,000, preventing Cargo dependency directories (
-
Windows: cloning a repository failed. The clone destination was resolved with
canonicalize, which on Windows always answers with a verbatim\\?\C:\...path. git refuses that as a working tree (could not create work tree dir ...: Invalid argument), and the prefix leaked into the refusal text users saw. Resolution now produces a spelling external tools accept. -
Windows: cloning into an existing directory resolved onto the source repository. The clone's directory name was derived by splitting the URL on
/alone, so a local Windows path (C:\src\repo) survived whole and the drive-colon split returned a rooted\src\repo— and joining a rooted path discards the destination. Both path separators are now split on, and the derived name is guaranteed to be a single component. -
Windows: external tools resolved to the wrong file. Tool lookup tried the bare program name before any executable suffix, so
npmmatched Node's extension-less shell script instead ofnpm.cmdand failed with "%1 is not a valid Win32 application". Suffixed spellings are now tried first, in the order Windows itself uses.
Changed
- CI runs the Rust suite with
--no-fail-fast. It previously stopped at the first failing test binary, so a failure in the library suite silently skipped every integration suite — 43 binaries that had never run on Windows at all — while reporting a single failure. A check that could not run must not read the same as one that passed.