Repository navigation
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 branches or generating code without requiring a full
repository switch. -
Scroll cues now surface overflow on clipped panels. A new overlay component (
ScrollCue)
and DOM observer (observeOverflow) add edge chevrons and gradient fade cues to scrollers whose
overflow was otherwise silent, includingBranchList,FileViewer,DiffFileRail,TerminalPanel,
RepoTabBar, andVirtualList. The graph layout's horizontal gutter overflow calculation now shares
the same canonicalresolveOverflowHintlogic. -
Soft pill dividers and high-contrast section borders unify panel styling. Replaced harsh full-width hairline
borders with centered pill dividers (.gp-separator,.gp-menu-sep) and solid high-contrast borders
(.gp-section-edge) across modals, panels, and dropdowns to improve visual hierarchy and contrast in both
dark mode and forced-colors accessibility modes.
Fixed
-
A branch whose ahead/behind git never filled in would have read as merged.
list_branchesparsed
the%(ahead-behind:)field leniently, turning an empty or malformed value into(0, 0)beside a
named base — the wire shape for "every commit is already in the base". Unreachable for branch refs,
which are always commit-ish, but the safety of that lenient path was an argument rather than a check.
Both listings now share one strict parser: an unparsable field leaves the row uncompared instead,
andbranch_refs_always_yield_two_countspins the assumption. The lenient parser and its private
tokeniser are gone. -
An annotated tag reported its own object id where a commit id was expected.
list_tagsread
%(objectname), which for an annotated tag is the tag object, not the commit it points at, so the id
shown in the tag tooltip matched nothing on the graph. It now peels through%(*objectname), as the
graph's own decoration listing already did. -
The storage trend sparkline ignored the chosen accent and always drew the same purple. It
readvar(--accent, #8b5cf6), and--accentis defined nowhere — the token is--accent-color.
A misspelt custom property is invisible in a way a misspelt class is not: CSS resolves to the
literal after the comma and paints a plausible colour, so the line looked designed rather than
broken. A sweep of everyvar()and every scripted token read acrosssrc/found this as the
only unresolved reference of 51;src/lib/ui/cssTokens.contract.test.tsnow holds both surfaces
to tokens something actually defines. -
Every row in the coverage file list read the same, because the paths were truncated from the
wrong end. The list is flat and the paths share a prefix, so right-truncating the whole path
produced rows identical up to the ellipsis (rust-port/crates/devmap-e…) and clipped away the
file name, which is the only part that distinguishes them. The name now comes first with the
directory dimmed after it, so truncation eats the half that repeats, and the full path is on the
row's tooltip — the same shapeDiffFileRailalready used. -
The work-in-progress indicator and its details dropdown rendered too transparently over underlying content.
Sitting on the translucent repository tab bar, the button carried no border, no background fill, and
only a 10% amber hover state, making it read as ghosted or see-through over the glass chrome. It now
wears the shared.gp-btnpill geometry in both states — an outlined surface pill with subtle emerald
indicator when all clear, and a defined, tinted amber pill (!bg-amber-500/20light,dark:!bg-amber-500/25)
with WCAG-accessible contrast text (text-amber-700 dark:text-amber-300) and active state when work
is in progress. Furthermore, its details popover panel previously used.gp-cardwithoutshadow-float,
causing it to miss the macOS float blur tier (compositing with 0px blur) and 50% opacity directly over
underlying code and diffs. It now correctly carriesshadow-floatfor proper float-tier backdrop blur
and a densebg-surface/95background withborder border-border/80so content behind it does not
bleed through.
Internal
- Two Rust tests asserted POSIX behaviour on every platform, and the Windows suite had never run
to reach them.the_user_shell_starts_as_a_login_shellexpected-l, whichlogin_flag
deliberately never returns on Windows; it is now#[cfg(unix)], the gate its sibling
login_flag_covers_known_shells_onlyalready carried.
a_slow_call_does_not_delay_the_answer_to_a_fast_onemade its slow call withrepo_path: "/tmp",
which is not an absolute path on Windows — the call was rejected before doing any work
(duration_ms: 0) and answered instantly, so it beat the "fast" request and failed the test for
the exact opposite of the condition it exists to catch. The slow call now scans a directory that
is absolute on every platform, with Unix keeping/tmpverbatim because the timing was tuned
against it. - Windows clippy failed on unix-only test and bench code, and had been hidden behind an earlier
failing step.-D warningspromotesunused_importsanddead_codeto errors, and six items
reachable only from#[cfg(unix)]code were declared ungated: two imports in theanalyzer::deps
tests, two in theprocguardtests, theRegistration::slottest accessor, and the whole
preamble ofbenches/process_spawn.rs(whosenot(unix)main deliberately measures nothing).
Each is now gated to the platform that uses it. CI steps also stopped at the first failure, so
this sat invisible behind a Vitest failure on the same leg and each fix revealed the next one a
push later; the independent checks now run under!cancelled()so one run reports all of them —
the reasoning--no-fail-fastalready applied to cargo, one level up. - CI was red on
mainfor two checks that could not run, on platforms where nothing was wrong.
pre-push-hook-contractexecuted.githooks/pre-pushdirectly, and Windows cannot exec a file
by its shebang —spawnSync ENOENTfailed a case that had never started. Git reaches hooks
through a shell there (theshbundled with Git for Windows), so the case now invokesbashby
name with a relative path, which is both how git runs the hook and the one spelling that works on
all three platforms; without any bash it is skipped rather than passed.
spawn_resolution_finds_rust_toolchain_in_cargo_binasserted thatrustupresolves to a fixture
in a temporary~/.cargo/bin, but that directory is the last GUI-launch fallback, behind real
system directories the test cannot sandbox. A macOS runner with a Homebrewrustupresolved
/opt/homebrew/bin/rustup— the right answer on that machine — and failed a test that meant to
ask something else. The regression is now asserted where the host cannot reach it (the directory
is ingui_launch_fallback_dirs) plus one uniquely-named probe proving resolution walks that far.
Neither production path changed; the fallback ordering is deliberate and still stands.