diff --git a/docs/decisions.md b/docs/decisions.md index eac28679..ddff626d 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -517,6 +517,7 @@ result files when the documentation was cleaned up on 2026-10-17. | d528 | 2026-10-22 | DECISION | traverse | K26-1 (PR 197, G1): the engine half of the frontier confirmer, in `lci-maps` only and unreachable until K26-2 wires a key. After the gate and the beam, and before the reranker frontier, the kept hops whose only witnesses are `grep.live` text proposals with a site are taken in hop-value order, cut at the call's `top_k`, and sent in one call to a `FrontierConfirmer` (`lci-maps/src/traverse/confirm.rs`). `Confirmed` (the definition at the site is the source node): the hop keeps its value, its witness becomes `Lsp` and its confidence rises to at least 90. `Refuted` (an answered place held by another symbol): the hop is dropped and still corroborates at 0. `Unknown` (no answer, an error, the deadline, the call cap, or an answer only a file can hold, such as an import or re-export line or a symbol the index lacks): unchanged, fail-open, and a walk never ends on the confirmer. Each proposal is at most one `definition` request; the engine clamps the confirmer's reported calls to the pop's remaining external calls and charges them there. Totals `confirm_*` and `confirm_ms`, the expand event's `confirm` field and `start.confirm` are added; a handle records the setting only when it is on (a default handle's bytes and hash are unchanged) and refuses a resume under the other value. `LspLive::confirmer` asks `definition` at each site. `relabel` changes the witness, the offer and the confidences, not the claims, so corroboration grouping still reads a text group; whether a confirmed group should stop counting as text is a K26-2 decision, as are the byte-column versus UTF-16 column of a site after non-ASCII text, the places kind in the confirmer's identity, and sampling (src, holder) pairs of refutations before the refuted share is trusted. No key, no default, no golden line. | bf7e832, 642912c (PR 197; merge a1fe140) | | d529 | 2026-10-22 | DECISION | lsp | K12a-1: `index_status` (and `lci status`) gain `servers[]` (per adapter, sorted by provider: provider, languages, languages_in_index, configured, enabled, command, probe {probed\|not_probed}, breaker_open_ms (ms left in an open breaker window, null when none), root, session) and `languages_without_server`; a report built from the manager's memo and sessions, no probe run and no server started. `analyzers` and `analyzers_running` unchanged. No key. `Manager::probe_state` reads the `probe_once` success memo; it is filled only by priming and the `lsp.live` warm start (`/ready` probes the adapter directly and does not count), and a failed probe is not remembered, so it reads not_probed; an open breaker window shows as `breaker_open_ms` (`Manager::breaker_remaining`, a read) instead of a third probe state. `root` is always "." until K12a-2. | 0e16d61, b377635 (PR 206; merge 0da50ed) | | d531 | 2026-10-22 | DECISION | lsp | K11b-3 Sol review (merge after fixes), coordinator decisions. (1) HIGH: under `places = "scip"` a tree with no SCIP index paid for LSP with 0 cards (18 of 18 calls, d509), so the default becomes `[run_profile.lsp] places = "auto"` (values "auto", "scip", "outline"): per walk, SCIP places when the SCIP graph loaded for the workspace has at least one node, else the server's outline places; "scip" and "outline" keep their exact behaviour when set explicitly, and an indexed tree walks byte for byte as under "scip". The rust places pair that kept "scip" (d516 rule) was a SCIP-present comparison and does not apply to the no-index fallback. (2) HIGH: `deadline_ms` is not a wall-time promise: the version probe (bounded by its own readiness timeout), the seed search and the tree barrier are credited back by design (d470); documented, not changed (config.example.toml, README and architecture say `deadline_ms` bounds the walk, not setup). (3) LOW: stale lines in architecture (the breaker is on since K11b-3; a walk has one restart per walk: the pre-seed barrier plus the backend's barrier, which restarts only if the stamp changed between them). One of the two allowed Sol re-checks: merge. Sanity 17 added: auto is per workspace, so a rust tree with a SCIP index plus python or typescript files takes SCIP places and those languages get 0 cards while their servers warm; named in the PR and the row, with a per-language auto as a follow-up gated on a mixed-tree measurement (confirmed by d553). | private run record (K11b plan file, Sol review); PR 205 | +| d533 | 2026-10-22 | DECISION | lsp | K12a-2 (N4, PR 212): `[run_profile.lsp] roots = "workspace"` (default, today) \| `"manifest"` (opt-in): a server starts at the directory of the topmost manifest its adapter names (`stamp_manifests`), found in the directories of the index's files of its languages and their ancestors (one directory read each, no recursive walk: the index's file list holds source files only and so lists no manifest). The workspace is kept, with the reason in `servers[].root_reason` (`no_manifest`, `several_roots:`, `files_outside_root:`, `no_file_list`, `unreadable`, and `not_served` for a disabled, unconfigured or unserving server whose root is not worked out, reported under `manifest` only), for no manifest, sibling manifests at the same depth, or a root that would leave indexed files of the server's languages outside it. A session fixes its root at start; `Manager::server_root` and `Manager::session_root`; one session per server per workspace either way, session key, stamp and barrier unchanged. The file list reaches the `lsp.live` managers too: the walk's per-call manager (`per_call_manager`) and the CLI's shared manager read the same index file list (`Manager::indexed_source`, `ManifestFiles::new`), so gopls, pyright and typescript-language-server start at the nested root in a walk as in the service. One limit: a one-shot `lci traverse` under `sessions = "per_call"` has no file list, so its servers start at the workspace with `no_file_list` (the service always has the list). `servers[].root` (K12a-1's `.`) now shows the live session's root, else the root a start would choose now (worked out only for an enabled, configured server whose languages the index holds). Default unchanged: the run-profile golden gains the `run_profile.lsp.roots = "workspace"` line and its hash moves; the default path's answers do not. Premise check (stdio tap on the real `rootUri`, small fixtures): pyright gains only when a name resolves through a nested project's own settings (`extraPaths` in its `pyproject.toml`: workspace root 0 of 7, manifest root 5 of 5; a plain src-layout import resolves from either root); typescript-language-server finds the nearest `tsconfig.json` itself and gains nothing; one stray top-level source file sends `manifest` back to the workspace (`files_outside_root:1`). The claim is that pyright-only gain; gopls, csharp-ls and cost are not measured, and the default flip stays gated on the lsp-cost rule in the plan. Fable review: merge after fixes, all docs. No Sol review (opt-in key, default unchanged). | fb78823 (PR 212 merge); private run record (K12a-2 premise check) | | d534 | 2026-10-22 | DECISION | traverse | K20c-1 (PR 207): the SCIP-edge arbitration engine in lci-maps only, unreachable until K20c-2 wires a key. An Arbitration names an authority map, subordinate (map, family) pairs and the judged symbol kinds; per expansion, before fusion, a subordinate edge of a listed family to a listed-kind symbol stands if the authority answered it (confirmed) and is removed if the authority answered that family with other destinations (overruled); nothing is removed when the authority did not answer the family, when its call was cut at `EXPAND_LIMIT`, or when it named only non-judged destinations (a File fallback). Counts appear on subordinate calls, the expand and end events and `TraversalResult` only when an arbitration is attached; they are per call (a resume restarts them). No byte changes without an arbitration. K20c-2 carries the handle check (S3) and registered-maps-only (S4). | 8490ef8, 4403207 (PR 207; merge ef6d175) | | d535 | 2026-10-22 | DECISION | lsp | K12b plan (Fable planner), coordinator decisions: (1) the section is `[tsgo]`, the provider `typescript-go`, the environment variable `LCI_TSGO_PATH`; (2) typescript and tsgo both enabled is a `Config::validate` error (explicit over first-wins), navigation prefers the first enabled adapter of a language; the default stays typescript-language-server and `init` does not write `[tsgo]`; (3) tsgo (npm `@typescript/native-preview`, Apache-2.0 to be verified from its LICENSE at install; downloads were approved on 2026-10-21) is installed by the coordinator at K12b-2 time outside the repository, its path only in local config; (4) the tsgo against typescript lsp-cost pair shares the quiet window with the K7c cost pass, and the 3-file tree counts for "works" only. K12b-1 starts from main 74e9310 and touches none of K12a-2's or K39a's hunks. | private run record (K12b plan file) | | d536 | 2026-10-22 | DECISION | plan | K39a re-aimed at main 74e9310 (planner), coordinator answers: (1) a `traverse_matrix` cell `lsp_default` as its own commit (0b) before the first move, pinning the default `lsp.live` plan; (2) `SEEDS_REPO_MAP` stays in the re-export list if anything reads it; (3) the resolved places choice on `Built` is left to K20c-2; (4) a stale comment stays byte-identical in the registration move and is fixed in K39b's docs pass. Commits land one at a time (implementer per commit, coordinator commits between). | private run record (K39a plan file) | @@ -546,3 +547,5 @@ result files when the documentation was cleaned up on 2026-10-17. | d560 | 2026-10-23 | DECISION | lsp | N118 (PR 221): the status gains a top-level `sync` key (a sibling of `analyzers`, present only while a workspace's sync runs, so byte-equal with sync off or no session) that reports `Manager::sync_status`: backend, the new `failed` flag (neither watcher could start; previously only a `warn!`), and the counters events, batches, forwarded, walks and rescans; a silent Windows stop (N102) shows as events not growing. A sync whose opening task dies is marked failed (review fix). Navigation status test under Watch and Off, sync unit test with an injectable fallback opener; mutation-checked. | 765bd60, fc16725 (PR 221; merge 838867d) | | d561 | 2026-10-23 | FINDING | process | Sanity 19: the K11b-4 flip rests on identical cards and a 62-129x cost gap (d553, d555) and holds; its residuals are that python and go have never run on a stock install (shim, d540), per-walk auto was never exercised in a run (d539, d543), and the burst and memory cost of kept servers on a large tree is unmeasured (N119). K20c-2 and K26-2 are layers with no value measurement, as d396 allows. Prior art (Sourcegraph, Serena, mcp-language-server, Codanna, aider) keeps servers resident with watchers and prefers precise over search, but none arbitrates SCIP against a server or confirms a grep frontier live. Picks: K11b-5 merges only after its pre-registered smoke and no-shim lsp-cost; per-language auto moves ahead of K12a-2 and K12b; N118 and the N122 fix join the closing list; K12a-2, K12b-2, K31, K39b move to cycle 2 unless started by 2026-10-24 00:00Z; the one measurement that can reverse a default is the N119 write-storm run on a large tree under the stock default. The cut is the owner's; asked 2026-10-23. | private run record (sanity 19 review) | | d562 | 2026-10-23 | FINDING | lsp | K11b-6 per-language places = "auto" (branch feat/k11b-6-per-language-auto, not merged): its pre-registered release-build measurement failed (mixed tree: 0 python lsp.live cards, R1a/R1b; small rust and deadline misses that the diagnosis reads as window noise, every card difference sitting on a deadline- or cap-hit call). The gate itself works: the rust index covers only .rs, python files are outlined and minted as sym:python:, and py01 discovers the two call_site.py members (hops 96 vs 94), but none is popped or listed: Contains edges carry prior 0.5 against 1.0 for calls and references, the 20-pop budget goes to the rust seeds' neighbours and answers keep 8 walk cards. No change inside the slice can surface them, so per-language auto moves to cycle 2 together with mixed-tree surfacing (Contains prior, budget, top_k), measured with a pre-registered python-seeded mixed question. A redo under a rule written after the failure is not taken. M2 closes without it. | private run record (K11b-6 measurement and diagnosis) | +| d563 | 2026-10-23 | FINDING | process | Sanity 20 (M2 closed, d561 and d562): the direction holds; K11b-6's miss is a walk-ranking and seed finding, not a places one (the gate is verified; hard-coded hop priors, 20 pops, 8 walk cards; vectorless mixed arms), and its remedy is cycle 2 iteration with vectors present, a python-seeded mixed question and a discovered-nodes rule, seed-side before priors; prior art (aider's language-agnostic PageRank, Serena's explicit per-language routing, Sourcegraph's per-language SCIP) surfaces nothing by priors. The finish line is unchanged (d422); what remained was docs fold 4, K39b, K12a-2 narrowed to its measured pyright gain (0 of 7 against 5 of 5, opt-in, tsserver no gain) and the cycle 1 report. Recommended cut: K12b-2 and K31 to cycle 2, K12b-1 parked on its branch, K39b and K12a-2 kept; the K26 and K20c value windows need the owner's placement. Forecast at the time: close about 2026-10-24 02:00Z without K12b and K31. Process: two brittle pre-registrations in a row (K11b-6's positive rules could not fire under the question set; K11b-5's absolute-hash precondition failed literally under a merge of a default-off key, d558). Adopted change: every pre-registered positive rule carries a reachability line naming the baseline artefact that shows its measured quantity's input exists and that the budget leaves room; a precondition compares golden text against the baseline commit ("empty, or only default-off keys named here"), never an absolute hash; a reviewer other than the planner signs both before the quiet window. | private run record (sanity 20 review) | +| d564 | 2026-10-23 | DECISION | traverse | K39b (PR 223): `run_with_trait_seeds` in `src/app/traverse/mod.rs` is split into private phases, move-only: `prepare` returns `Prepared` (the resolved call: limits as `Cow`, steer, selection, walk_off, store, loaded, observer, rescorer), `WalkSetup` (snapshot, handler, actions, priors, tie_break, identity, preset) and `Ledger` (budget, notes, warnings); the tree barrier stays in the orchestrator because the map context ties the restarts' lifetime to the call; `seeds` returns `Seeded` (found, `maps::Built`, arbitration; `find_seeds`, `build`, the arbitration and confirm checks, the trait-seed hoist); `run_first_stage` returns `Staged` (stage, exact report, stage time); the setup credit stays in the orchestrator at the same sequence point (`plan_maps` is taken, not cloned); `walk` returns `WalkOutcome` (the no-seeds answer or `Walked`: result, effective policy, enabled maps, restored nodes, notes, warnings); `finish` builds the handle, trace, map notes and response. Public paths and signatures and the report JSON are unchanged; the 18-cell `traverse_matrix` was byte-identical after each of the five commits (the fifth is the docs pass: the stale "below" comments in `mod.rs` and `maps.rs` and the module table in `architecture.md`). Fable review by normalised statement diff (495 old statements against 493 new, every residual diff glue or rustfmt reflow; the 30 note and deadline-credit sites in the same sequence; 13 `.clone()` sites unchanged; no new `pub` or `#[allow]`; the three note arms the matrix does not pin read verbatim): MERGE, no kill rule tripped. No Full gate locally (narrow, d520) and no Sol review (no behaviour change). K39 closed. | PR 223; merge 6efaf47; private run record (K39b review) | diff --git a/docs/development/roadmap.md b/docs/development/roadmap.md index a03944a4..7fb3318f 100644 --- a/docs/development/roadmap.md +++ b/docs/development/roadmap.md @@ -48,7 +48,7 @@ that document and is not a roadmap id. ## Now and next -On main (838867d): PRs up to 221 (212 is the K12a-2 draft, not merged), the latest being N122 (218, d558), K11b-5 (219, d557), K26-2 (220, d559) and N118 (221, d560), after K12a-1 (206, d529), K20c-1 (207, d534), N87 (208), N25 (209), N48 and N116 (210), K11b-3 (205, d550), N20 (a) (211), the Live Acceptance workflow made manual-only (213, d538), K39a (214, d554), K11b-4 (215, d555), K20c-2 (216, d556) and the docs PR (217, d529 to d556), after K7c (203, d544, d545), the previous docs PRs (201, 204), N115 (202), K26-1 (197), the K5b Serena arm (198), the CodeQL advanced setup (199, d521), K11b-1 and K11b-2 (200), N94 (193), K39a commit 0 (194), the N18 low batch (195), N21 (196), K12c, N104 + N106, K18a, N107, ASYNC-2 B, K11d-2a (d509, d510, d512), N112, N113 and B2 (N109, N110, d514), plus this docs PR. K22a-3 is measured and closed as a finding (d519). The K11b post-merge window found the stock `sync = "off"` default wrong for non-rust servers (d553) and K11b-4 flipped it (d555); K11b-5 made the readiness probe per adapter so python and go join `lsp.live` on a stock install (d557, d540). Now: M2 closed 2026-10-23 without K11b-6 (its release-build measurement failed, so per-language `places = "auto"` moves to cycle 2 with mixed-tree surfacing, d562). Open lanes: K39b (in progress), K12a-2 (a draft PR held until its premise check, which is running: do pyright and tsserver find nested manifests themselves) and K12b (the tsgo adapter; the tsgo facts check is pending and nothing merges before it and the lsp-cost pair, d537). Sanity 19 (d561) proposes a cut (K12a-2, K12b-2, K31 and K39b to cycle 2 unless started by 2026-10-24 00:00Z); the cut is the owner's and awaits his answer (asked 2026-10-23). The K20c rust measurement (the arbitration stays off until it passes) and the K26 value measurement open cycle 2. ASYNC-2 C stays parked. Only docs PRs and the coordinator rewrite this line; a feature PR leaves it alone so parallel PRs do not conflict (d476). The live state is the coordinator's run checkpoint. +On main (6efaf47): PRs up to 223, the latest being K39b (223, d564), K12a-2 (212, d533), the docs PR (222, d557 to d562), N118 (221, d560), K26-2 (220, d559), K11b-5 (219, d557) and N122 (218, d558), after K12a-1 (206, d529), K20c-1 (207, d534), N87 (208), N25 (209), N48 and N116 (210), K11b-3 (205, d550), N20 (a) (211), the Live Acceptance workflow made manual-only (213, d538), K39a (214, d554), K11b-4 (215, d555), K20c-2 (216, d556) and the docs PR (217, d529 to d556), after K7c (203, d544, d545), the previous docs PRs (201, 204), N115 (202), K26-1 (197), the K5b Serena arm (198), the CodeQL advanced setup (199, d521), K11b-1 and K11b-2 (200), N94 (193), K39a commit 0 (194), the N18 low batch (195), N21 (196), K12c, N104 + N106, K18a, N107, ASYNC-2 B, K11d-2a (d509, d510, d512), N112, N113 and B2 (N109, N110, d514), plus this docs PR. K22a-3 is measured and closed as a finding (d519). The K11b post-merge window found the stock `sync = "off"` default wrong for non-rust servers (d553) and K11b-4 flipped it (d555); K11b-5 made the readiness probe per adapter so python and go join `lsp.live` on a stock install (d557, d540). Now: M2 closed 2026-10-23 without K11b-6 (its release-build measurement failed, so per-language `places = "auto"` moves to cycle 2 with mixed-tree surfacing, d562). Cycle 1 implementation is complete: K39b (d564) and K12a-2 (d533, opt-in, the narrow pyright claim) merged, and sanity 20 (d563) recommends K12b-2 and K31 move to cycle 2 with K12b-1 parked on its branch (the tsgo adapter; the tsgo facts check is pending and nothing merges before it and the lsp-cost pair, d537). The cut is the owner's and awaits his answer (asked 2026-10-23), as does the placement of the K20c rust and K26 value windows (each needs a release quiet window with no cargo; the arbitration stays off until the K20c measurement passes), which sanity 20 proposes for cycle 2. The cycle 1 report goes to the owner next; per the autonomy rule the coordinator then waits for his word. ASYNC-2 C stays parked. Only docs PRs and the coordinator rewrite this line; a feature PR leaves it alone so parallel PRs do not conflict (d476). The live state is the coordinator's run checkpoint. ## Cycle 1 @@ -72,7 +72,7 @@ an arm works: no slice claims a gain over rg or LSP before the paired screening | K10a | Done (K10a-1 d442, K10a-2 d449): place table without SCIP, `[run_profile.lsp] places` (`"scip"` default or `"outline"`, d442): `OutlinePlaces` over the LSP outline, ids from the index else minted with the file's language; the `LostEdge` gap closes under `outline`; `lsp.live` stays labelled `SiteDrift` because card sites are the index's. Rust members the index lacks take the ids of the `sym.defs` extractor's declarations (`chunk::extract_for_path`, not the bundle store, which `traverse::run` cannot reach; `CrateNames::read_paths`), so Rust ids equal the index's. Left open: placing a Symbol seed whose file was never outlined (module-path inversion, with K21a, d442), outlining destination files so `holder` names a reference in a never-outlined file | `lsp.live` runs with `scip.mode = off`; a shifted-line placement check: after lines are inserted above a symbol its place follows (closes the freshness `LostEdge` gap under `places = outline`; `lsp.live` stays labelled `SiteDrift`) | M | - | | K7c | Done (K7c-1 d544, K7c-2 d545; PR 203): document lifecycle without a key. A position request sends `didChange` with the whole text to a server that advertised change sync (else a second `didOpen`, as before) and waits for the server's diagnostics; a cancelled caller leaves its pending wait to the next one (N111); typescript-language-server opens documents; a session holds at most 64 open documents (the least recently used is closed with `didClose`). The 3 s `head_start` was already gone (ASYNC-2 B, d512). Measured after the merge: the real typescript-language-server answered from the new text after a same-size, same-mtime edit through one `didChange`; lsp-cost for typescript and python is within noise of d512 (shared+sync warm 2.1 to 4.1 ms per expansion), no new diagnostics-wait timeouts | Fake LSP event log; the value check is a before and after of the lsp-cost arms on the same arm text (K7c has no key, so K11d's `open` arms do not exist) | M | N2 | | K11e | Done (d474, first commit of the K11e + N103 PR; N103's `lsp_start_timeout_seconds` is the second): LSP down-window breaker, `[run_profile.lsp] breaker` (30 s per provider, shared by the process's managers; default off then, on since K11b-3): a failed version probe, or a server start that failed without timing out (spawn failure, exit, protocol break), opens the window, and the next probe or start of that provider is refused at once with a note that names it; a started server is still asked, a timed-out start opens nothing, `/ready` still probes. Turned on by default in K11b-3 | Fake LSP tests: a dead server costs one probe per window, not one per call | S | - | -| K39 | Speed-up: split `run_with_trait_seeds` (src/app/traverse/mod.rs, about 1,176 lines and 141 branch tokens on main at 7684817) into a few phases that return typed values (for example prepared, registered maps, execution, finalisation; each carries only what the next phase needs), keeping the public signature and the report JSON. Why: it is the file where parallel lanes collide (39 commits since 2026-10-08, 4 of the last 20 catch-up merges edited it on both sides), and every new map and `[run_profile]` key lands in that one function. Risk is temporal coupling (setup order, resource lifetimes, freshness stamps, deadline accounting, continuation semantics), not length. Starts only after K11e and N103 land and no open lane edits that file (otherwise the split is the conflict). Owner request via the maintainability audit, d487. K39a done (commit 0 in PR 194, d502: the pinned matrix `tests/cases/traverse_matrix.rs`, goldens under `tests/golden/traverse-matrix/`, extended by the review with trait seeds, registration order and off notes; commits 1 to 3 and 0b in PR 214, d554): the map registry lives in `src/app/traverse/maps.rs` (`select`, `build`), move-only, the matrix 18 of 18 after every commit, so every new map and `[run_profile]` key now lands there. K39b ready: split prepare, seeds and first stage, walk and response in `mod.rs`, same matrix, same kill rules, plus the docs pass (stale comments, the module table in architecture) | Move-only commits; before the first move pin a byte-identical traverse output matrix (fresh vs resumed handle, an opt-in map off vs on, LSP down vs ready, trace and record output) that every commit reproduces; then the Full gate | M (split before picking if it grows) | - | +| K39 | Speed-up: split `run_with_trait_seeds` (src/app/traverse/mod.rs, about 1,176 lines and 141 branch tokens on main at 7684817) into a few phases that return typed values (for example prepared, registered maps, execution, finalisation; each carries only what the next phase needs), keeping the public signature and the report JSON. Why: it is the file where parallel lanes collide (39 commits since 2026-10-08, 4 of the last 20 catch-up merges edited it on both sides), and every new map and `[run_profile]` key lands in that one function. Risk is temporal coupling (setup order, resource lifetimes, freshness stamps, deadline accounting, continuation semantics), not length. Starts only after K11e and N103 land and no open lane edits that file (otherwise the split is the conflict). Owner request via the maintainability audit, d487. K39a done (commit 0 in PR 194, d502: the pinned matrix `tests/cases/traverse_matrix.rs`, goldens under `tests/golden/traverse-matrix/`, extended by the review with trait seeds, registration order and off notes; commits 1 to 3 and 0b in PR 214, d554): the map registry lives in `src/app/traverse/maps.rs` (`select`, `build`), move-only, the matrix 18 of 18 after every commit, so every new map and `[run_profile]` key now lands there. K39b done (PR 223, d564): `run_with_trait_seeds` is private phases (prepare, seeds, first stage, walk, finish) with the tree barrier and the setup credit kept in the orchestrator, move-only, signatures and report JSON unchanged, the matrix byte-identical after each of five commits, Fable review by normalised statement diff, plus the docs pass (stale comments, the module table in architecture). K39 closed | Move-only commits; before the first move pin a byte-identical traverse output matrix (fresh vs resumed handle, an opt-in map off vs on, LSP down vs ready, trace and record output) that every commit reproduces; then the Full gate | M (split before picking if it grows) | - | | K11d | K11d-2a done (d509, d510; frozen pass on release d8c812f, after-pass on a8755dd): rust with SCIP, `base` hits the 20 s deadline on every call and `shared` warms to p50 79 ms per expansion (p95 799); on python, go, typescript, c and cpp the 3 s `head_start` per expansion before ASYNC-2 B became 1.6 to 52.7 ms after it (`shared+sync`); no non-rust adapter gets an `lsp.live` card under `places = "scip"` without a SCIP index, and every call does under `places = "outline"` (warm 2.2 to 26.2 ms per expansion). K11d-2b (the `open` arms) is replaced by K7c's before and after on the same arm text, done for typescript and python (d545). Harness landed (PRs 177, 189); K11d-2 only (K11d-1 landed, d438: `cargo xtask maps lsp-cost` and the numbers on main): after SHARED (d454) the arms `base` and `per_call+sync` write `sessions = "per_call"` explicitly, and a real-rust-analyzer re-run of `--arms base,shared` records the default's cost; the `documents = open` arms (`open`, `open+shared`, `open+cache`) do not exist because K7c added no key; the typescript `outline_cache` interaction d424 deferred to K7c is still unmeasured (typescript opens documents since d544). Still open from K11d-1: N8 (the analyzer state at each dark call needs K7b-3a), and the `shared+calls` arm of K9b | Report under `test-results/lsp-cost/` | S | N8 (live diagnosis) | | K20b | Done (K20b-1, d471): `[run_profile.scip] mode` defaults to `cache`; an index that cannot be parsed is parsed once per file stamp (size and modified time) and later calls say it was not read again; recipes with no index refuse with the hint to run `rust-analyzer scip`. The golden moved by the `scip.mode` line and the hash only; results stamped under an unset mode carry a new profile hash (K11d-1's numbers, d438, carry the old one). The label and the arbitration are K20c (`[run_profile.scip] arbitration`, off by default) | Its own tests: SCIP absent, the remembered failure (traverse unit, MCP, recipes) | S | - | | K20c | Arbitration of SCIP edges: `role` in `maps[]`, SCIP edges subordinate and labelled, LSP wins a disagreement, confirmed and overruled counts, the navigation.rs ceiling check. After K9b and K11b (no authority exists before them). K20c-1 done (d534, PR 207; the engine, lci-maps only) and K20c-2 done (d556, PR 216): `[run_profile.scip] arbitration` (default `false`), the wiring for `scip.impact`, `scip.implements` and `structure.contains` (functions and methods only; `graph.scip` is never arbitrated), `role` under `map_freshness`, the counts in the trace, `per_map`, `totals` and a note, and the handle check. Default off until the pre-registered rust measurement (false-overrule rate at most 5 %, the edit pass, no card or deadline loss; the coordinator's lane, with a Sol review for the flip; rust only, d556) | Its own tests: SCIP present but subordinate, a disagreement | S | - | @@ -89,7 +89,7 @@ an arm works: no slice claims a gain over rg or LSP before the paired screening | **M4-core** | **Maps, servers, safety, mining (unconditional)** | | | | | K26 | K26-1 done (d528; PR 197): the frontier confirmer in `lci-maps`, unreachable until K26-2; K26-2 done (d559; PR 220): the `walk.confirm` key (default `false`), the app wiring in `maps::build`, the golden and docs; the value measurement (cost and quality per call) opens cycle 2. `grep.live` proposes, `lsp.live` confirms the top-k frontier (`walk.confirm`); no Codanna dependency | Cost and quality per call against the walk without confirmation | M | G1 | | K9c | Type-hierarchy edges. paused(measurement), moved to cycle 2: the K24c control shows SCIP `implements` edges already bring traits to the regex-rg level (d459); LSP type-hierarchy edges matter only under `scip.mode = off` with `places = outline`, which no cycle 1 run measures | Ceiling | S | - | -| K12a | Partly done: K12a-1 done (d529; PR 206): `index_status` and `lci status` gain `servers[]` (per adapter: languages, languages in the index, configured, enabled, command, probe `probed`/`not_probed`, `breaker_open_ms`, root, session) and `languages_without_server`, a report from the manager's memory with no probe run and no server started. Remaining: K12a-2, root discovery per server (`[run_profile.lsp] roots = "workspace" \| "manifest"`, default `"workspace"`; a server starts at the directory of the topmost manifest its adapter names; several sibling manifests fall back to the workspace), a draft PR (212) held until its premise check runs in a measurement window: do pyright and tsserver find nested manifests themselves, and does a server rooted above its manifest resolve imports (d537); it re-merges main and re-blesses the golden after K20c-2 and K26-2 | Live acceptance per server | S | N4 | +| K12a | Done: K12a-1 (d529; PR 206): `index_status` and `lci status` gain `servers[]` (per adapter: languages, languages in the index, configured, enabled, command, probe `probed`/`not_probed`, `breaker_open_ms`, root, `root_reason`, session) and `languages_without_server`, a report from the manager's memory with no probe run and no server started. K12a-2 (d533; PR 212), opt-in: `[run_profile.lsp] roots = "workspace"` (default, today's root) \| `"manifest"`, a server starts at the directory of the topmost manifest its adapter names among the directories of the index's files of its languages; no manifest, sibling manifests or an indexed file outside the root keep the workspace, with the reason in `servers[].root_reason`; a one-shot `lci traverse` under `per_call` has no file list (`no_file_list`). Premise check (private run record): pyright gains only when a name resolves through a nested project's own settings (`extraPaths`: workspace root 0 of 7, manifest root 5 of 5); typescript-language-server finds the nested `tsconfig.json` itself and gains nothing; one stray top-level file returns the root to the workspace. The claim is that narrow pyright gain; gopls, csharp-ls and cost are unmeasured, the default flip stays gated on the lsp-cost rule, and one session per sibling module is not built. The per-server live acceptance was not run: only pyright and typescript-language-server were exercised against real servers, in the premise check | Live acceptance per server | S | N4 | | K12c | Done (PR 183): marksman adapter for prose | Live acceptance | S | N95 | | K18a | Built (d485, merged in PR 185): `seeds.repo_map`, an opt-in start-node map under `[run_profile.seeds] repo_map` (default off; K18a-1): personalized PageRank over the SCIP graph (calls and containment, `implements` edges left out, damping 0.85, at most 30 iterations), with the jump on the top 64 `seeds.bm25` matches, so a function the question's matched functions call becomes a seed even when its own name holds none of the question's words; its top `seed_k` nodes go after the hybrid and `seeds.bm25` ones. No bm25 match, no seeds (no global hubs). Open: the ceiling run (`maps bench --set traits --set where-to --max-depth 0 --include-seeds`, waves `k18a-rm`, `k18a-bm25-rm`, plus the depth-1 walk `k18a-walk` against the K24c control; report the reorder rate against bm25's top 10); park K18a-2 (the SCIP-free graph) when the reorder rate is under 30 % and where-to does not move | Ceiling | M | G2 | | K19 | Built (d483): `grep.structural`, an opt-in map under `[run_profile.grep] structural` (default off): ripgrep names the files that hold a symbol's name as a whole word, an ast-grep pattern over each file's tree (the chunker's own grammars) keeps the `impl`, `extends` and `implements` headers (`Implements` edges) and the calls (`CallsOrRefs` edges). `ast-grep-core =0.40.1` is the last release on tree-sitter 0.25: it moves with K15a. Open: the ceiling run (`maps bench --set traits --max-depth 1`, waves `k19-ctl`, `k19-on`, `k19-only`; A = the K24c control arms, B = A + the map, C = the map as the only implements source; no gain claim, d422 (11); `maps bench` turns the key on in the arm's derived overlay when `--maps` names the map) | Ceiling on structural queries | M | - |