Commit Graph
2516 Commits
Author SHA1 Message Date
Martin Vogel 0b736b2f6f fix(test): retire the last stub-era ingest probes; unfork the cypher guard
Three test families leaned on ingest_traces being a discard-stub:
- the daemon-runtime usability probe sent traces:[] and grepped for the
  stub's traces_received; it now sends a valid minimal ingest for the
  seeded project (the daemon requires the project argument) and checks
  the honest pairs_unmatched report
- cypher_wide_return_projection_bounded forked the sanitized runner for
  crash isolation; a fork that then does real store setup is
  byte-fragile on macOS — a 1.5KB schema addition got the child killed
  before the query ran (bisected: any equal growth flips it, even with
  a 1-column query). The parse-time bound is the contract; assert it
  in-process. A regression still fails loudly, and the test now runs
  on Windows too instead of skipping

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 23:19:30 +02:00
Martin Vogel 7f2c563210 fix(test): cancel-probe tools updated to the real ingest_traces contract
Both predispatch-cancel tests probed the follow-up request with
ingest_traces traces:[] and expected the stub's 'accepted'; empty
traces are a tool error under the real contract. The outer-scope test
now probes with a valid ingest (unresolved names store nothing); the
raw-index test — where no project exists by design — probes with the
project-free list_projects. Clean means what it always meant: the next
request runs unpoisoned by the cancel.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 22:51:35 +02:00
Martin Vogel ce2e406839 feat(ui): observed vs possible — runtime markers in flows and traces
Trace hops and flow steps that actually fired render '▶ observed ×N'
with the run label and date; a fully-observed trace says so in one
line, and the footer states the honesty rule: unmarked hops are
possible, not dead — runs only cover what they exercised. The wiki
gains the observed entry (first-class, with its caps), and F5's gap
narrows to what is still missing (automatic capture).

Live-smoke fix on the C side: resolve_store hands handlers a
query-only connection, so ingest writes silently no-op'd against real
project stores — ingest_traces now takes the dedicated read-write
connection (open_store_for_write, né open_adr_store_for_write), whose
open also creates the observed tables in pre-existing databases.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 21:46:24 +02:00
Martin Vogel baf0df8e91 feat(mcp): ingest_traces stores observed runtime calls for real
The stub counted the traces array, answered 'accepted', and discarded
everything. Now: traces are call paths of qualified names (adjacent =
caller→callee; bare {caller, callee} pairs still accepted), stored in
observed_calls/observed_paths — SEPARATE from the graph tables and
keyed by qualified name, because node ids are reassigned on every
reindex and id-keyed observation would silently rot. Counts accumulate
per run label; whole runs age out together past the 200k-pair cap
(oldest first, deterministic under the one-second timestamp resolution
via rowid tiebreaks — eviction is never a timing lottery). The response
is honest about resolution: only pairs whose both endpoints are indexed
get stored, and up to ten unresolved names come back so producers can
fix their naming instead of silently losing data.

Atlas joins observation at read time: trace and flow-detail hops that
actually ran gain observed {count, label, last_seen}; absence adds
nothing — an unobserved hop is merely possible, never 'dead'.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 21:31:49 +02:00
Martin Vogel 409dce4d81 fix(test): size mkdtemp templates for the Windows %TEMP% expansion
cbm_mkdtemp copies the expanded path back into the caller's template;
on Windows that is %TEMP%\..., far longer than the literal. The two
literal-sized arrays (symbol-history, why) were stack overflows there —
caught by the real-Windows leg as an fopen failure. CBM_SZ_256 buffers,
like every other call site.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 20:48:13 +02:00
Martin Vogel 136685f1a5 fix(test-infra): make container legs work from git worktrees
A worktree's .git is a pointer file into the primary repo's .git
directory, which lives outside the ..:/src mount — git inside the
container fails and every git-based contract (the line-ending guard)
dies with 'matched no files'. run.sh now detects the worktree case and
exports the primary git dir; the test-running services mount it read-
only at the same absolute path so the pointer resolves. Plain checkouts
keep a harmless no-op default mount.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 20:15:38 +02:00
Martin Vogel 848770323c chore: ladder.sh is executable like every script beside it
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 20:11:11 +02:00
Martin Vogel 3ce8338276 style: clang-format the impact and symbol-history additions
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 20:11:03 +02:00
Martin Vogel b808eec514 fix(ui): dispatch /api/symbol-history before the /api/symbol* wildcard
The wildcard swallowed the longer path, answering symbol-history
requests with the symbol bundle's 'missing id or qn' error. Caught
live; route order now states the constraint.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 18:58:55 +02:00
Martin Vogel 11bdc08147 feat(ui): why is this here — per-symbol git history with linked evidence
The most-deferred developer question in the literature gets its evidence
surface: /api/symbol-history runs a bounded git log -L over the symbol's
line range (follows renames, 12 commits, on demand — one subprocess,
never automatic), and file history now carries the forge base URL
(git@/ssh remotes normalized to https). The symbol page grows a 'Why is
this here?' card: trace button, commit list with hashes and #123 refs
linked to the forge, honest empty/capped states, and a footnote that
titles are not fetched. The existing recent-commits list linkifies
through the same component. F7's gap narrows accordingly.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 18:51:39 +02:00
Martin Vogel 617a4b1495 feat(ui): change impact — who can reach this symbol, and what to retest
/api/impact answers the strongest-demand developer question in the
information-needs literature: if I change this, what breaks and what
must I retest. A reverse CALLS adjacency joins the flow cache (built in
the same edge pass); a distance-labelled BFS from any symbol reports
reachers per hop, a region rollup, and — separately — the test
functions that reach the symbol, nearest first: the tests to run.
Honest caps (depth 10, 50k visits, both flagged) and a stated basis:
static CALLS only, so every count is a floor.

The symbol page grows an 'If you change this' card: headline sentence,
cumulative hop tiers, region chips, run-these-first test list with an
explicit warning when no test reaches the symbol, and the floor
footnote. Question family F3 flips to answered (7/8/3); the
blast-radius wiki entry now names its caps.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 14:29:51 +02:00
Martin Vogel ed8a45c9d9 feat(ui): the question index — what Atlas answers, honestly
Twenty years of developer-information-needs research reduces to 18
question families; the Overview now opens with Atlas's scorecard against
them: 6 answered, 9 partial, 3 not yet — counts derived, never
hard-coded. Each row names the question, where to look, and for partial
and missing families exactly what's missing; rows with a home navigate
there. Status is glyph + word, color redundant. Chrome en+zh; the
question text itself awaits the content-i18n pass.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 13:38:20 +02:00
Martin Vogel 9c9e353795 feat(ui): wiki hover previews, tooltip delay setting, tooltip breakdowns
- every metric chip previews its wiki entry on dwell: term, tier, the
  single-source sentence, click-for-more; pointer can enter the card,
  Esc dismisses just the card, no auto-timeout; portaled + clamped
- tooltip delay is a display setting (150-800ms, persisted), read at
  hover-start so changes apply to the next hover
- galaxy tooltips show the arithmetic already in the scene payload:
  fan-in/out and lines per node; symbols, files, cohesion per region
- test setup restores jsdom's localStorage under Node's experimental
  global, so storage persistence tests assert for real instead of
  silently no-opping

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 13:30:50 +02:00
Martin Vogel b7f227617a fix(ui): chart-standard compliance sweep — honest axes, real knee, bound bins
Evidence-audit fixes across the dashboard and friends:
- sparklines plot real time (indexed_at), break across gaps, and carry a
  printed first→last delta; no line under 4 snapshots (a 2-point line is
  a trend claim)
- the hero head cut is a Kneedle-style knee (unit square, smoothed, max
  distance below the chord, clamped 3..15) — largest-relative-drop
  collapsed to 'top 1' on power-law data
- hero rows show both axes (commits and complexity), caption carries the
  denominator; 'Complexity mix' (né distribution) prints per-bin counts,
  N, and its flag threshold
- Modules bars state their reference; Overview regions get a cohesion
  strip with median tick; the Control gauge prints its cut-points with
  non-color glyphs
- complexity bins live in one module (lib/complexity.ts); a test binds
  the takeaway sentence's claims to the bin labels so they cannot drift

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 13:14:36 +02:00
Martin Vogel cfebb1b2f9 feat(ui): #wiki=<slug> deep link — wiki entries addressable by URL
The doc_url target: the hash opens the entry's panel over whatever view
the rest of the URL names, and hashchange keeps it live.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 12:26:56 +02:00
Martin Vogel 3f3f1ac486 feat(ui): the metric wiki — single-source entries, WikiPanel, term chips
Every number Atlas shows gets a wiki entry: one tooltip-sized sentence,
why it matters, how it is computed with named caps, what it deliberately
does not cover, where it appears, and its paired counter-metric. Four
'refused' entries document metrics Atlas will not show (Maintainability
Index, Halstead, coverage gates, quality grades) and why.

Entries live in one typed module (word-budget-enforced by test) and are
rendered by a docked WikiPanel with a navigation stack — terms inside the
panel are chips too, so concepts link without nested tooltips. Dotted-
underline MetricChips wire the Dashboard and Symbol surfaces in; section
headings ship en+zh.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 12:23:50 +02:00
Martin Vogel f450f9b13e feat(daemon): CBM_STARTUP_TIMEOUT_MS overrides the startup budget
A fixed 30 s startup budget races whatever else the host is doing;
under a parallel test wave a cold daemon loses through no fault of
its own and every client gives up with 'could not start within
30000 ms'. The env override (clamped to 1 s..10 min, malformed
values fall back to the default) lets loaded hosts widen the budget
without changing the default behavior anywhere else. Applied to the
MCP/CLI client bootstrap configs, the upgrade drain request, and
the client cohort-acquire deadline.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-27 01:45:32 +02:00
Martin Vogel e14624d681 feat(ui): semantic zoom, flow-hop confidence, honest caps, tri-state filter
- Semantic zoom on the region scene: zooming INTO a region blob
  opens it, guarded by the anti-annoyance set from the research
  round - user wheel input within 600 ms only (programmatic fly-tos
  never trigger), approach direction only, 1.5 s grace so backing
  out doesn't bounce back in. The HUD hint says so.
- Flow steps carry the resolver confidence of their parent edge;
  hops under 75% show a small warm approx marker with the number -
  a misresolved hop now looks different from a certain one.
- Dashboard lists declare their caps honestly: 'top N of M' where
  the payload names the population (callables, churned files),
  plain 'top N' where it doesn't - never an invented M.
- FilterPanel All|None buttons became one tri-state master checkbox
  (checked / unchecked / mixed dash, aria-checked, keyboard-native).

vitest 71/71, tsc clean, C suites 96/0, lint green.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-26 23:01:11 +02:00
Martin Vogel c36a9b81ff feat(ui): galaxy orientation triad — halos, view marker, framed camera
- Landmark halos: the top-12 hubs carry a soft additive corona
  (single Points layer, one sprite) so panning always has stable
  anchors - the CodeCity landmark idea, kept sparse on purpose.
- Minimap gains a view marker: a ring at the camera's orbit target,
  reported throttled (200 ms, movement-gated) from inside the
  canvas, projected with the same bounds as the region dots.
- Constrained camera: orbit min/max distance follow the scene's
  actual radius instead of fixed constants; scenes auto-frame on
  load (the fixed default camera landed inside larger clouds) while
  deep-linked nodes keep their focus fly-to.
- Minimap moved above the Prompt button (they overlapped).

vitest 71/71 incl. minimapProject bounds tests.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-26 22:34:51 +02:00
Martin Vogel 470a802142 feat(ui): zh chrome — new tabs and project switcher translated
The Atlas program added seven tabs of hardcoded English on top of an
en/zh UI. This translates the shell (tab labels, switcher strings)
so zh users get a coherent frame; panel-content i18n is a dedicated
follow-up pass, recorded.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-26 22:03:30 +02:00
Martin Vogel bdf977a264 fix(ui): question-first symbol page, cross-region dots de-noised
- The Why view now sits directly under the symbol header, before the
  flat caller/callee columns: the condition question is the primary
  answer, the lists are reference (question-first IA per the
  research synthesis).
- Cross-seam dots on connection rows invert into noise on utilities
  called from everywhere; past half the rows the list now says it
  once in the header ('79 of 83 cross regions') instead of dotting
  nearly every row.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-26 21:55:36 +02:00
Martin Vogel f13f7f845e feat(ui): Why-view depth — dispatch chip, guard table, flow-step guards
- /api/why entries carry candidates from the CALLS edge; the tree
  shows a '1 of N targets' diamond chip on dynamic dispatch -
  genuine resolver uncertainty made visible.
- Decision-table pivot on the trigger tree (>=3 guarded callers):
  rows = callers, columns = distinct conditions, cells = when /
  unless / none. The experimentally-winning format for
  condition-combination lookup (Huysmans 2011).
- /api/flow&guards=1: flow-detail steps gain per-step guard chips,
  computed from each step's parent call site - journeys read as
  'then X, when ok && *cursor' instead of bare arrows.
- Number formatting unified to en-US (mixed browser locales
  rendered 1.053 next to 1,053).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-26 21:50:58 +02:00
Martin Vogel 2df92078f9 feat(atlas): the Why view — trigger trees with syntactic guard chains
The condition→action abstraction (Pennington 1987: the mental model
with the highest error rates, and the one code text serves worst),
served on demand. Design per the research synthesis: thin caller-only
spine (thin slicing), closed question menu, guard text ON the edge
in words (information scent), glyphs always paired with text
(Reacher's 22% missed-icon failures), complexity-gated (Scanlan),
declared elision, both directions (Green), honest footer.

- /api/why?id|qn&dir=up|down: callers (or callees) with the
  SYNTACTIC guard chain around each call site — enclosing
  if/else/switch-case/loop/ternary/catch conditions, outermost
  first, else-arm negation detected — extracted by parsing the few
  caller files live with the embedded tree-sitter grammars from the
  call-site line the CALLS edges already carry. Zero indexing cost;
  works on every existing index. Per-request parse cache; 2 MB file
  cap; unparseable sources reported as guards_unavailable, never
  guessed.
- /api/trace&guards=1: A-to-B paths gain per-hop guard chips -
  'reachable in 5 hops' becomes 'reachable when ...' (the exact gap
  Reacher's users hit: they hand-derived conditionals).
- Symbol page: 'When does this run?' tree - guard chips (amber
  unless / blue when), loop marker, unguarded honestly labeled,
  per-caller upward expansion, direction toggle, single-line answer
  when every call site is unguarded, footer stating guards are
  syntactic and dispatch/events are invisible.
- Trace results in Flows render the same chips per hop.

Tests: nested-if chain outermost-first + else-arm negation + loop
flag + downward direction + callsite helper on a real temp-dir C
file (C); /api/why route wiring; formatGuard/chain + the
complexity gate (vitest). Trap fixed en route: the extension table
keys WITH the dot ('.c'), not 'c'.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-26 21:05:56 +02:00
Martin Vogel 67ba264002 fix(ui): portal the switcher panel out of the header stacking context
The header's backdrop-filter creates a CSS stacking context, which
paints its entire subtree as ONE atomic layer. A child's z-index
cannot escape it: the body-level dim overlay (z-20) therefore sat
above the dropdown panel (z-30-inside-the-header) and dimmed the
panel together with the page - uniform darkening, zero separation.

Both the backdrop and the panel now portal to <body>, where the
paint order is explicit: backdrop z-20 dims everything, panel z-30
floats above it, anchored to the trigger button's rect (closes on
resize; Esc and backdrop-click close; click-away listener dropped -
the backdrop IS the click-away surface).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 18:02:49 +02:00
Martin Vogel 8b467e7e57 fix(ui): project switcher — searchable, properly elevated
Review feedback: the dropdown blended into the page and offered no
way to search among many projects.

- Type-to-filter input at the top, autofocused on open; Enter picks
  the first match, Esc closes.
- The panel is now a real layer: solid surface, stronger border,
  deep shadow, and a viewport backdrop that dims the page. The
  backdrop is portaled to <body> because the header's
  backdrop-filter makes it the containing block for fixed children,
  which silently clamped the first version to the header bar.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 17:55:00 +02:00
Martin Vogel d634ee89d4 fix(ui): header IA — project switcher first, no dead tabs
Review feedback: the top bar mixed project-scoped tabs with
app-level ones and left the project tabs visible-but-dead until a
project was chosen elsewhere.

- The project is now a first-class switcher directly after the
  wordmark (dropdown: all indexed projects + a manage-and-index
  row); picking one lands on its Overview.
- Project tabs (Overview...Dashboard) render only when a project is
  selected - a control that cannot work does not render, same trust
  rule as everywhere else. A divider separates context from views.
- App-level destinations sit quietly on the right: search (only
  with a project) and Control. The redundant right-side project
  chip is gone; the Projects tab is reachable through the switcher
  and remains the no-project landing page.

UI-only change; vitest 66/66, tsc clean.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 17:26:03 +02:00
Martin Vogel e44ef0a917 feat(atlas): the handout — a shareable codebase document from the graph
GET /api/handout renders a self-contained, print-friendly HTML
document that explains the project to a newcomer: the region map
(cohesion as words, hubs), boundary spanners with a why-you-care
line, the main entry-to-effect journeys, and the churn x complexity
table. Linked from the Overview header ('Handout').

Honesty properties, tested: every graph-derived string is
HTML-escaped; no host paths leak into the artifact; caps are
declared inline ('20 of 33 regions shown', '10 of 60 journeys');
a project with no readable git history says so instead of skipping
silently; every fact is deterministic - the header states that no
model wrote any of it.

Exposing the same document as an MCP resource is recorded as a
follow-up (touches the MCP protocol surface; wants its own review).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 16:45:20 +02:00
Martin Vogel d8f95946c2 feat(atlas): working-set blast radius on the Changes tab
/api/blast buckets a file list by region (names included, 200-file
cap declared as truncated, every file accounted for: bucketed +
unmapped == files). The Changes tab feeds it the impacted symbols'
files (falling back to raw changed files) and renders the review
signal git cannot compute: 'your change reaches N files across K
regions' with per-region chips - disambiguated with the same
hub-suffix rule the galaxy uses, so two 'tests' communities don't
render as twins - and a seam warning at K>=3 suggesting a split or
citing the region names in the prompt.

Tests: blast bucketing + accounting + empty-input refusal (C),
/api/blast route wiring.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 16:31:56 +02:00
Martin Vogel a306569596 feat(atlas): honest lists — overflow shape, seam dots, boundary spanners
The D6 trust sweep: every capped or scoped surface says what it hid
and what it means.

- Symbol connections: when the page cuts the list, the hidden
  remainder is grouped BY FILE ('src/auth/session.c x 12') next to
  the Show-more button - a bare '+N more' hides the answer to 'who
  calls this' on exactly the nodes where it matters.
- Connection rows carry the neighbor's region; a row whose edge
  crosses an architectural seam gets a quiet accent dot.
- The Data-flows panel no longer renders on projects with no
  DATA_FLOWS edges at all: a section that can only ever say 'none'
  is noise, not honesty.
- /api/bridges: boundary spanners ranked by DISTINCT foreign regions
  a node calls into (cross-call count as tiebreak), test files
  excluded and inbound direction ignored - orchestrators like main
  and cbm_daemon_host_run surface; utility sinks and test harnesses
  (which the naive both-directions metric ranked first) do not.
  Overview shows the top 5 with a why-you-care line.
- Staleness banner was built and then REMOVED: no server signal
  means 'files changed since indexing' today, and detect_changes
  (a git-base diff) would have shown the wrong number. Recorded as
  a follow-up instead of shipped with wrong semantics.

Tests: bridges need >=2 foreign regions (a dense gamma community
mirrors the beta fixture pattern), overflow tail accounts for
exactly what the page cut, per-row regions + data-flow presence
flag, /api/bridges route wiring.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 16:13:43 +02:00
Martin Vogel a4286e8420 feat(ui): flow merging, treemap bars, persistent galaxy (Field Guide D5)
- The galaxy stays mounted once visited: hidden with invisible (never
  display:none, which zeroes a canvas), WebGL context and layout kept,
  frameloop paused via the scene's active prop - switching tabs no
  longer drops and refetches the whole 3D scene.
- Flows list merges journeys by entry point: one row per entry
  (richest first) with an 'x N journeys from <entry> - show all'
  expander, so ten main() walks stop drowning the list.
- Modules gains a map|bars toggle (persisted): the same children as a
  sorted horizontal-bar list - region-colored, symbol counts - for
  when proportion matters less than ranking.
- Filter chips strike through when disabled (excluded, not missing).
- One-time in-place hint on the region scene; dismissing persists,
  leaving without dismissing does not.

Tests: groupFlowsByEntry ordering/merge (vitest); suite green with
the keep-mounted change.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 14:32:36 +02:00
Martin Vogel 03995b7461 feat(atlas): galaxy orientation — search scent, minimap, region cards
The D4 orientation package, mechanics adapted from the two reference
UIs studied in the screened snapshots:

- Search scent propagates up the hierarchy: a 'find a symbol' input on
  the region sidebar lights matching regions in the 3D scene, pins hit
  counts on the region rows and the minimap, and reports the total
  with an outside-regions remainder. Served by /api/scent - a
  case-insensitive name match bucketed per region via the cached
  file->region map (File/Folder nodes excluded so a file never
  double-counts its own symbols; sub-2-char queries refused).
- 2D minimap panel (bottom right, collapsible): every region as a dot
  on a fixed top-down projection, sized by sqrt(members), colored by
  region, open region ringed, scent counts above the dots, click to
  open, and a Home button that fits the current scene.
- Region hover card: members/files, cohesion as a word (tightly /
  moderately / loosely connected) with the score, and the hub.
- Node tooltip on graphite tokens (last pre-D1 hardcoded surface) and
  in/out call degree - layout nodes now carry out_calls, which the
  degree pass already computed and discarded.
- Honest scope note in the region detail panel: '+N callers outside
  this region' whenever the full-graph in-degree exceeds the loaded
  window.

Tests: scent bucketing + LIKE-escape refusal (C), /api/scent route
wiring, minimap projection + cohesion words (vitest), region-list
queries disambiguated from minimap titles.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 14:27:19 +02:00
Martin Vogel b54eb3d0c1 feat(atlas): answer history, ownership, and A-to-B reachability
The two questions the research says developers ask most and tooling
answers worst: why is this code the way it is (rationale via change
history and ownership), and does A reach B (LaToza reachability,
asked ~9x/day). Plus the user-requested data-flow lens.

- /api/trace: shortest A-to-B path over CALLS or DATA_FLOWS (BFS,
  depth-capped at 12), endpoints by id or qualified name; the flow
  cache gains a DATA_FLOWS CSR beside the CALLS one.
- Symbol bundle: file_history (recent commits, commits/year, top
  author share from a bounded git log at metrics time, cached and
  honest - available:false when no history was parsed) and
  data_in/data_out with the stored edge detail.
- Flows tab: trace bar with symbol pickers and a control/data
  toggle; the result path is clickable hop by hop.
- Symbol tab: 'Recent changes to this file' and 'Data flows'
  panels with honest empty states.
- Tests: trace reachability both modes + endpoint errors, symbol
  bundle data-flow/history shape, /api/trace route wiring, and a
  fetchTrace client test.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 10:32:50 +02:00
Martin Vogel 46cf438dce feat(ui): findings feed Overview + hero Dashboard (Field Guide D2)
The metrics screens now answer questions instead of displaying data
(dashboard canon: Few, Grafana, CodeScene — the Field Guide carries the
citations):

Overview
- Four opinionated slots on top: Needs attention (knee-point count over
  churn×complexity), Direction (deltas vs the previous index), Riskiest
  area (named, with its why), and an untoned Trust strip (index stamp,
  missed files, unmapped symbols).
- Surprising couplings become finding sentences with evidence, an
  'inspect' next click, and an 'intended' dismissal whose key buckets by
  order of magnitude — a dismissed finding resurfaces only when it grows
  10x (regression re-alert, CodeScene's goals idea in one line of code).
- Hubs, entry points and boundaries demote to a collapsed Reference
  disclosure; inventory facts move to an untoned footer.

Dashboard
- Hero: the churn×complexity list with the knee-point head highlighted, a
  cost sentence ('N files — X% of the codebase, Y% of all commits this
  year'), raw commits shown per row, mute-as-intended dispositions, and
  rows opening the file's folder in Modules.
- Quality signals (resolution, dead, tested, documented) keep tones and
  trends; the complexity histogram now states its takeaway and names the
  tail; averages and equal-weight inventory cards are gone — facts live
  in the footer.
- Metrics payload gains raw commits per risky entry, churn totals and the
  distinct-file count to make the cost sentence computable.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 09:59:42 +02:00
Martin Vogel 3bc9d973e6 feat(ui): the Atlas graphite design system + trust repairs (Field Guide D1)
Visual language, rebuilt on the researched rules (Primer/Carbon/Material/
Radix/Linear; details in the Atlas Field Guide):

- Graphite surfaces with fixed step jobs (#0D0F12 canvas → #293038
  selected) replace the saturated navy; elevation is a lighter surface
  step, not a shadow; hover/selection are surface washes.
- One accent (sky #4FA8E0) reserved for interactivity; semantic
  good/warn/crit separate; the type floor is 12px (labels), body 13-14px,
  system font stacks (sans UI, mono identifiers only — zero vendored
  bytes), tabular numerals, radius 6px.
- Region colors move to the Okabe-Ito color-blind-safe palette, assigned
  server-side to the ten largest regions with a neutral tail — the code
  graph is the only colorful thing on screen.

Trust repairs, because every wrong-looking item costs belief in every
right one:
- builtins filtered out of hub lists (a 'len' hub reads as a bug);
- a product-code lens hides test regions by default everywhere regions
  appear, one honest toggle away ('18 test regions hidden — show');
- flow labels carry file context when the bare name is ambiguous
  ('datastructures.__init__ → …' instead of '__init__ → …').

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 09:50:25 +02:00
Martin Vogel b516eb20eb fix(ui): Projects enrichment — pass include_details, cancel on unmount, cache schemas
The lazy-schema fix was ineffective: the UI never sent include_details,
so list_projects returned names only, every card lacked totals, and the
>1M-node skip compared against undefined — each Projects visit still ran
a ~30 s kernel schema scan, and revisits stacked uncancelled sweeps that
kept the store busy (the felt slowness).

Now: list_projects(include_details:true) carries totals and indexed_at;
the enrichment loop is generation-guarded (unmount or refresh abandons
it), schemas are cached for the session, auto-fetch is capped at 20
projects of ≤200k nodes, and larger stores keep a card with totals and
no chips. After a Projects visit the daemon log shows only sub-second
requests.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 09:16:34 +02:00
Martin Vogel 71a80ed151 fix(ui): render the Projects page immediately; fetch schemas lazily
Opening the /rpc allowlist revealed a pre-existing cost: the Projects tab
awaited get_graph_schema for EVERY project before rendering anything, and
a schema scan walks every node and edge — ~30 s on a 13 GB kernel index.
(Before #1663's fix those calls failed instantly, which is why the page
ever felt fast.)

list_projects already reports per-project node/edge totals, so the page
now renders from those immediately; per-label chips arrive lazily, one
schema at a time, patching each card as it lands — and projects above a
million nodes skip the schema scan entirely, keeping a card with totals
and no chips rather than burning half a minute of server time per load.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 09:08:34 +02:00
Martin Vogel 16b90077db feat(ui): the Atlas Dashboard — graph-derived metrics with drill-down
The numbers view of a project, with the code behind every number one
click away and no composite health score — the cards are the score:

- /api/metrics (cached per project+indexed_at): complexity and function-
  length histograms and top lists straight from pass_complexity's stored
  cyclomatic/cognitive/lines numbers, edge certainty (CALLS vs
  CALL_REFERENCE vs USAGE), dead-code candidates (same exclusions as the
  galaxy classifier), TESTS linkage, SIMILAR_TO duplication, missed
  files, documented-exports coverage.
- Per-file churn from one bounded git log pass (1 year, 10k commits, the
  pass_githistory bounds) computed at metrics time — ZERO cost added to
  indexing — plus churn × complexity, the classic where-the-bugs-live
  list.
- A per-index history sidecar (JSONL in the cache dir, one snapshot per
  indexed_at) feeds trend sparklines across reindexes.
- Dashboard tab: stat cards with warn tones, histograms, top lists
  linking to symbol pages; churn honestly marked unavailable when the
  project has no readable git history.

The wired-routes test covers /api/metrics; the ui suite pins totals,
certainty, histogram completeness and history presence on a fixture.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 01:09:25 +02:00
Martin Vogel ae5988a102 docs: describe CBM Atlas in the README
The UI section now names the two interfaces (MCP for the agent, Atlas
for the human) and documents the screens: Overview, Modules, region-LOD
Galaxy, Flows, Changes, Symbol pages, search and the prompt composer,
plus deep-linking.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 00:54:50 +02:00
Martin Vogel 8bcb308e28 feat(ui): the CBM Atlas screens — Overview, Modules, Symbol, Flows, Changes, search, prompt composer
The human side of the Atlas program:

- Overview: the first read of a project — header numbers, region cards
  with provenance and cohesion, hubs by fan-in, entry points, surprising
  couplings with enumerated reasons, suggested questions with a why, and
  the cross-package boundary table. Assembled client-side from the
  read-only /rpc surface (get_architecture format:json) + the regions
  payload; no new server code.
- Modules: a squarified treemap (in-house, no new dependency) of any
  folder via /api/tree — sized by symbols, colored by dominant region,
  hatched where the indexer missed files, breadcrumbed drill-down, file
  panel listing symbols via search_graph.
- Symbol: one page per node via /api/symbol — docstring, flags, region,
  editor/GitHub links, code on demand, callers/callees with true totals
  and per-type confidence badges (resolved / reference / unproven),
  tests, co-change partners, near-clones.
- Flows: the /api/flows journeys grouped across/within regions, each a
  clickable indented call tree with truncation stated, mermaid export.
- Changes: detect_changes on the working tree grouped by risk, blast
  radius citable into the prompt in one click.
- ⌘K: symbols (search_graph) + code text (search_code) in one box.
- Prompt composer: a basket of cited symbols/regions/flows/questions that
  renders a prompt naming exact qualified names, file:line ranges and the
  MCP calls that reproduce the context — the bridge back to the agent.

Routing carries every screen in the URL (tab, project, path, sym, flow,
node, region) so all of it deep-links. Fixed en route: the /api/flow
route pattern '/api/flow?*' never matched (req->path has no query
string) — now exact, pinned by a wired-routes test that distinguishes a
matched route's 400 from the static handler's 404.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 00:53:47 +02:00
Martin Vogel d91101ba10 fix(ui): flows never start inside test files
On Django the flow list was all setUpTestData/test_* entries: test
functions are natural call roots (no callers, many callees) so they
out-scored real entries. A test file is a journey into the code, not of
it — candidates in test paths score zero unless the indexer explicitly
flagged them as entry points.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 00:40:56 +02:00
Martin Vogel 61c4afe82f feat(ui): CBM Atlas data services — folder tree, symbol bundle, flows
Three read-only services the Atlas screens consume, as pure JSON producers
over an open store (handlers stay thin; tests hit the functions directly):

- GET /api/tree?project&path — aggregated children of one folder: files
  and symbol counts per child, dominant region (majority vote), missed-
  coverage counts from the #963 shadow project, child cap reported rather
  than silent. Feeds the Modules treemap at any project size without
  shipping nodes.
- GET /api/symbol?project&id|qn — the symbol page in one response: node
  with docstring and entry/test/export flags, region membership, callers
  and callees with TRUE totals, per-edge-type counts and pagination
  (CALLS before CALL_REFERENCE before USAGE), inbound TESTS, co-change
  partners of the file (FILE_CHANGES_WITH), and near-clones (SIMILAR_TO).
- GET /api/flows?project, /api/flow?project&id — named entry→terminal call
  journeys: entries scored from stored flags and call shape, bounded DFS
  over CALLS (depth 6, 4 children per step, 40 steps), deduplicated by
  entry/terminal pair, ranked sink-first, truncation counted; cached per
  (project, indexed_at) like the region level. The busiest callee leads
  each step so the trunk of the journey reads first.

The region cache gains an O(1) file→region lookup (hash-backed) shared by
tree, symbol and flows. Fixed en route: yyjson borrows object keys added
via add_*(), so the per-type count keys are now copied into the document
instead of dangling once their sqlite statement is finalized.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-24 00:35:21 +02:00
Martin Vogel bbbc015304 feat(ui): CBM Atlas region scene, deep links, sidebar selection, uncapped connections
The graph tab becomes CBM Atlas and gains the region level as its default
for large projects:

- Projects above 5,000 nodes open on the REGION SCENE: one body per Leiden
  call community (folder groups as fallback), weighted arcs between them,
  a region list on the left and a region panel (provenance, cohesion, top
  callables) on the right. Double-click or 'Open region' loads that
  region's full-detail layout (scope=region:N) with a '< Regions'
  breadcrumb; 'Load full galaxy' keeps the classic whole-graph view one
  click away. Small projects skip the region hop entirely.
- Deep links (#564): ?node= and ?region= join ?tab=&project= — a selected
  node or opened region survives reload and back/forward, and can be
  shared.
- #1197: clicking a symbol row in the sidebar (tree or search) selects the
  node itself — scene highlight AND detail panel; selecting a directory
  clears a stale panel instead of leaving the previous node in it.
- #1198: connection lists page with 'Show N more' instead of a silent cap
  of 25.
- Rename: the UI announces itself as CBM Atlas (header + title).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-23 22:22:56 +02:00
Martin Vogel 499eeba51b feat(ui): region level of detail for the galaxy (level=regions, scope=region:N)
The galaxy loaded 'the first max_nodes nodes' of a project, which above
~10k nodes meant freezing the tab (#498 family) or truncating the graph.
Give it a real coarsest level instead: one body per REGION.

Regions are sets of files. Files are assigned by majority vote of their
callables' Leiden communities over the CALLS graph (seeded, deterministic);
files no kept community explains fall back to folder groups (first two
path components), and a final 'misc' bucket catches the rest. Every node
inherits its file's region, all edge types aggregate into region-to-region
weights, and each region carries its name's provenance ('call community:
202 files, 38% under tests/db_functions'), cohesion, member/file counts
and its top callables by degree.

  GET /api/layout?project=X&level=regions   -> region bodies + weighted arcs
  GET /api/layout?project=X&scope=region:N  -> full detail, that region only

Positions come from a golden-angle sphere seed refined by the shared
anchored optimizer (extracted to layout_internal.h alongside the new
cbm_layout_from_nodes core, so the region scope reuses the exact layout
path). Results are cached per (project, indexed_at): on a 48k-node index
the region scene computes in ~1.6s cold, <1ms cached; a 4.8k-node region
scope lays out in ~0.5s.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-23 22:14:28 +02:00
Martin Vogel dab11a7d31 feat(ui): open the /rpc allowlist to the read-only query surface
The graph UI could call only list_projects and get_code_snippet through
/rpc; every other tool answered 403 'UI RPC method is not allowed'. That
starved the browser of schema, search, tracing, architecture and impact
data the server already computes, and broke the Projects tab, which needs
get_graph_schema (#1663).

Allow the read-only tools (get_graph_schema, search_graph, search_code,
trace_path/trace_call_path, get_architecture, query_graph, detect_changes,
check_index_coverage, index_status) and manage_adr in its read modes only
(default get, sections). A duplicated or non-string mode refuses, so a
write cannot ride in behind a read. Mutating tools keep their 403; the
loopback-only boundary is unchanged.

Fixes #1663.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-23 21:55:07 +02:00
Martin Vogel 010569fa6c Merge pull request #1678 from musichen/fix/pi-extension-tool-schemas
fix(pi): emit parameters and execute in the generated pi adapter
2026-08-21 10:44:22 +02:00
Alexander Musichen 104c9a7262 Merge branch 'main' into fix/pi-extension-tool-schemas 2026-08-21 04:26:56 +02:00
Alex Musichen 2ae84e7f73 test(pi): pin result wrap, error throw, and abort in generated adapter
Maintainer asked for in-tree coverage of {content, details} wrapping and
how tool errors are surfaced, plus a cheap abort-signal line. String-shape
only; no live Pi process.

Signed-off-by: Alex Musichen <alex.musichen@gmail.com>
2026-08-21 04:11:51 +02:00
Martin Vogel 65c7cf10f3 Merge pull request #1775 from DeusData/distill/install-plan-fixture-cleanup
test(cli): clean up before failing the install-plan receipt fixture
2026-08-21 02:58:52 +02:00
Martin Vogel 35ddda9f51 Merge pull request #1774 from DeusData/distill/nomic-blob-generator
fix(build): stop blob regeneration from reverting the ELF stack hardening
2026-08-21 02:15:24 +02:00
Martin Vogel 7b6363d0b6 Merge pull request #1772 from DeusData/fix/startup-coordination-lingering-lock
fix(cli): wait out a held startup transition instead of failing at 10s
2026-08-21 01:44:12 +02:00