Repository navigation
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 the last visible column reset every column back on, silently
discarding the other five choices. The toggle now refuses, and the menu marks
that one row closed. - A prepared agent run was lost if the launch that followed it failed; the
attempt is now published as soon as the store accepts it, and can be retried
or recovered from the task's Agent tab. - The due-date and owner rows of the context menu were offered as enabled while
writing nothing. - The editor offered to let Manvi draft a task while Apple Intelligence was the
selected engine. - Concurrent Git commands could return a truncated answer that looked like a
complete short one. macOS has nopipe2, so a pipe is created and then
marked close-on-exec in two separate steps; a process started on another
thread in between inherits the half-built pipe and keeps that copy for its
whole life. A stolen write end means the pipe never reaches EOF, so the read
burns its grace window and hands up a prefix. Diffs failed to load, branch
and tag labels went missing from the graph, and commit details came back
empty — twenty-one of the forty-three events in one diagnostics report were
this single bug. Child creation is now serialized across both spawn seams:
measured on eight threads, 147 of 25,415 children (0.58%) inherited a foreign
descriptor before the fix and 0 of 12,944 after, at a cost of one spawn
syscall behind the lock (321µs median, 614µs worst over 300gitspawns). - The repository status publish gate compared a hand-written list of eleven
fields and silently ignoredwarnings. A repository whose churn had stopped
being partial compared equal and never republished, so the explorer kept
showing "counts may understate reality" for numbers that had since parsed
cleanly. The gate now derives its comparison from every field the wire
carries, treats an omitted and an empty list as the same fact, and compares
array values element-wise instead of by identity. - The plugin package installed only its MCP server, while its own manifests
spawngitpulse-hookfor the collision guard, the command gate and the
session brief — so a validated, fully tested plugin shipped with two gates
permanently disabled andmcp:doctorreportedokthroughout. The two
failures are not symmetric: a missing server surfaces as a server that will
not connect, but a hook that cannot start is a non-blocking error the host
swallows, making it indistinguishable from a gate that ran and found nothing.
Both binaries are now installed and given separate verdicts, and a derived
contract test reads the executables out of the shipped manifests. - An untrusted checkout was reported as
[invalid_worktree]— the code said the
opposite of the message it carried. Resolution runs Git, so a trust refusal
happens before a single fact about the worktree has been established; the
trust test now runs ahead of the arms that are verdicts about something
actually examined. A path that is not a repository at all still reports
invalid_worktree. - The status popover's panel had been thinned to .55 alpha during the glass
pass, which put its accent tokens at 2.1–2.5:1 against a white or black
desktop. The panel floats over an arbitrary wallpaper, so its opacity is the
only thing decoupling the text from it, and .8 is the lowest value clearing
4.5:1 at both extremes. No glass is lost: a fifth of the desktop still shows
through, under the hue field, the sheen and the tint. - Three developer-facing guards were measuring the wrong thing, each now fixed
with a test that fails against the pre-fix code: the custom-property sweep
matched only the first declaration on a line, so three of the status
popover's four hue channels read as undefined; the status browser fixture
carried its own copy of the glass ladder and neither appearance arm, so it
validated a surface that never ships; and the vendored macOS ports warned
only in release builds, where a debug run could not see it. - The code-intelligence browser fixtures had drifted from the wire shape they
claim to speak, so three harnesses were exercising failure paths instead of
the behaviour they name, and a field-name contract check could not see it.
The palette harness had been failing, unrun, through a release because every
caller hand-listed which harnesses to run and none named it; runs are now
derived from the registry. - Menu bar status icon: the glyph is drawn at menu-bar weight. The pulse was
stroked at 2px in a 36px template shown at 18pt, which measured 1pt on screen —
thinner than the count printed beside it and than every neighbouring icon, with
over half the mark left as anti-aliasing fringe. It is now a 3px stroke, the
~1.5–2pt weight macOS menu-bar glyphs carry. - Menu bar status icon: the count beside the glyph can be cleared again.
Turning "Show counts beside the icon" off, closing the last repository or
opening a bare one stopped updating the count but left the last value on screen
for the rest of the session:tray-icon's macOSset_titleignoresNone
outright, so nothing cleared the button. GitPulse now sends the empty title
that actually clears it — already whatNonemeant on Linux, and ignored on
Windows, so no behaviour changes there.
Security
These narrow what GitPulse does on your behalf; no surface is widened. Opening
a repository is an explicit decision, and ordinary file saves no longer follow
links out of the entry they target.
- Opening a checkout requires an explicit Trust and Open decision before
GitPulse runs Git against it. Path inspection is filesystem-only: a canceled
or failed approval starts no watchers, status hydration, dependency scans, or
code indexing. Trust permits that checkout's Git hooks, helpers, and project
tools — including code in submodules — to run with your account's
permissions. It is an execution decision, not an OS sandbox, and not a claim
that the project is safe. - Remembered grants live in the application configuration directory, outside
repository policy and task files, and bind the canonical checkout, private
Git directory, common Git directory, and their filesystem identities.
Replacing a checkout or redirecting a gitfile requires fresh approval. - Linked worktrees are approved separately. Shared Git-directory discovery is
filesystem-only, and removing a worktree requires trust for the target as
well as the parent, because Git may scan the target internally. - Native Git, background analysis, DevMap, PTY startup, and MCP reads all
enforce trust, and MCP cannot grant it. A refusal is an explicit error or an
unavailable result, never a successful empty scan. - Global tool probes run from a neutral filesystem root. Installing a tool from
local source requires approval for the checkout used to build it. - Revoke repository trust on the repository tab closes the tab and blocks
subsequent operations. Terminals and agent tasks already running keep the
authority they were given; stop those separately. - Filesystems that cannot supply a stable creation identity are refused with an
explanation rather than trusted silently. Trust decisions are local to this
installation and are not portable project metadata. - Editor and documentation saves replace file entries atomically under pinned,
symlink-free parent directories, so saving one hard link no longer truncates
the inode shared by its other names. External symlinks and special files are
refused; nested paths, empty files, internal symlinks, and executable
permissions still work, under the existing 8 MiB budget. - Platform metadata preservation — macOS metadata copying, Linux ownership and
extended attributes, Windows replacement — now fails the save rather than
silently dropping metadata. Linux saves clear set-id and file-capability
privileges as ordinary content writes do, with metadata work bounded to 512
attributes and 1 MiB of values. - A save that loses a race to a concurrent change fails with an error naming the
retained recovery content. This is not an OS sandbox against a process already
running with your rights. - The working directory a child process is spawned in is pinned by descriptor at
admission, and the child is re-anchored against that pin. The directory the
trust gate admitted could previously be substituted between the check and the
child starting. The re-anchor costs +123% per spawn, so a privately held path
skips it. - Dependency scanners no longer load repository-controlled code while scanning.
Composer ran the project's own plugins and scripts, a Go scan let the module
pick its toolchain, andcargo-auditwent through the repository's alias
table. Each is now invoked so that scanning a tree cannot execute it. - The reserved harness policy endpoint forwarded the caller's string to the
sidecar asroot, so it answered for a path this process had never resolved.
It resolves first, and returnsuncheckedcarrying the resolution failure.
Documentation
- Installation and first-run each have a single owner under
docs/. The README
had grown to 433 lines answering every question at once, and the wiki answered
several of them again in its own words, so "how do I install this" had two
implementations that could disagree. There is now a documentation index, a
canonical installation guide, and a first-repository walkthrough; the README
keeps what only it can do, and the wiki pages link to those owners instead of
restating them. - Corrected while moving: release availability, signing and quarantine,
repository trust, optional tools, network boundaries, and the difference
between a feature being implemented and being qualified. A version in a
checkout is not a published release, and the docs now say so where readers act
on it. - The architecture-docs contract resolves every local link in the tracked
documentation and fails naming the file and the href, so a moved guide cannot
leave a dangling link behind. It carries a vacuity guard: too few links
resolved means the walk broke, not that the tree is clean.
Development
npm run ci:localruns every gate CI runs, and no longer stops at the first
failure. It was a single&&chain — the one shapeci.ymldeliberately
refuses, where "the first failure hides all of them, so a red build reports
one problem per push and each fix reveals the next". The local gate had the
defect the remote one was hardened against, so a machine that could have
reported four problems at once reported them one push at a time.- The WKWebView sweep is part of that run on macOS. CI runs the browser
regressions under Chrome on Linux and under WKWebView on macOS, but only the
Chrome half was in the local chain — so the engine that produced the only
CI-only failure of this release was the one engine nobody could run before
pushing. A derived contract test now fails when a workflow gains a gate the
local runner does not have, and when the runner claims a gate CI no longer has. - A gate that cannot run on this host is reported as skipped and named in the
summary rather than counted as a pass, and the Rust step passes the
--no-fail-fastand--lockedthat CI passes, so one failing test binary no
longer ends the run. - The pre-push hook refuses a release tag whose commit has not passed CI. The
release gate requires a successful push run ofci.ymlandcoverage.ymlon
the exact tagged commit, and neither workflow triggers on tags, so a tag pushed
too early spent three platform builds before being refused. The hook now runs
the gate's own check, which fails closed when GitHub cannot be reached. - The status harness waits for the popover's transitions to finish rather than
for a fixed delay. Svelte'sslideholds an inlineoverflow:hiddenand
unmounts an outroing node from its finish callback, so a probe measuring
mid-transition read the transition instead of the rule — a failure only a host
slow enough to still be animating ever saw. It waits on the animations'
presence, which is what Svelte's cleanup removes, and reports its own headroom
with the verdict.