Skip to content

GitPulse v1.1.0

Choose a tag to compare

@github-actions github-actions released this 14 Sep 16:52

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 the a key 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 --version answers with its build and every subcommand it
    serves. No host sends it; mcp:doctor needs 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; bypass is 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 as b.., 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
    stale chip 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 to glib 0.22.9. On 2026-09-13 the tree
    passed cargo audit --deny warnings across 550 Rust dependencies and
    npm audit across 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 .git directory or by carrying a registered
    worktrees/ entry whose gitdir names 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 no pipe2, 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 300 git spawns).
  • The repository status publish gate compared a hand-written list of eleven
    fields and silently ignored warnings. 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
    spawn gitpulse-hook for the collision guard, the command gate and the
    session brief — so a validated, fully tested plugin shipped with two gates
    permanently disabled and mcp:doctor reported ok throughout. 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 macOS set_title ignores None
    outright, so nothing cleared the button. GitPulse now sends the empty title
    that actually clears it — already what None meant 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, and cargo-audit went 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 as root, so it answered for a path this process had never resolved.
    It resolves first, and returns unchecked carrying 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:local runs every gate CI runs, and no longer stops at the first
    failure. It was a single && chain — the one shape ci.yml deliberately
    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-fast and --locked that 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 of ci.yml and coverage.yml on
    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's slide holds an inline overflow:hidden and
    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.