@backbencher/brain 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +298 -0
- package/EMBEDDINGS.md +355 -0
- package/LICENSE +21 -0
- package/README.md +415 -2
- package/bin/bb +13 -0
- package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
- package/dist/brain-index-health-Q5APTJQL.js +17 -0
- package/dist/brain-relationship-enrichment-MYR2AM4F.js +558 -0
- package/dist/brain.sql-EIM4WS4D.js +29 -0
- package/dist/chunk-22V37YTK.js +1758 -0
- package/dist/chunk-2BSKD7O3.js +40 -0
- package/dist/chunk-2DE27DVC.js +1314 -0
- package/dist/chunk-2RRBMNBQ.js +322 -0
- package/dist/chunk-2WXYNLBA.js +29 -0
- package/dist/chunk-3CTFIJJB.js +17 -0
- package/dist/chunk-3ZTOHHSW.js +2104 -0
- package/dist/chunk-4H22TLOT.js +543 -0
- package/dist/chunk-5BVAH26D.js +340 -0
- package/dist/chunk-62DHLYW6.js +3427 -0
- package/dist/chunk-7AN7U5IZ.js +371 -0
- package/dist/chunk-7DRPNVIN.js +782 -0
- package/dist/chunk-7MBYT777.js +174 -0
- package/dist/chunk-AHDWGM7Q.js +236 -0
- package/dist/chunk-B5GEAAYF.js +818 -0
- package/dist/chunk-BEIZZ73B.js +1479 -0
- package/dist/chunk-BQ3OVQ3P.js +125 -0
- package/dist/chunk-D2Q4Y47A.js +53 -0
- package/dist/chunk-DIYAGQJL.js +34 -0
- package/dist/chunk-DWKXZ6IF.js +336 -0
- package/dist/chunk-DYM4FQEE.js +90 -0
- package/dist/chunk-ENW4BTBA.js +930 -0
- package/dist/chunk-EWMWK7WN.js +75 -0
- package/dist/chunk-EY5ZZUUA.js +626 -0
- package/dist/chunk-GBI7V7KY.js +69 -0
- package/dist/chunk-GEFFO6O2.js +152 -0
- package/dist/chunk-HJEWO5EV.js +247 -0
- package/dist/chunk-HQSPJDHI.js +613 -0
- package/dist/chunk-KAET47ZL.js +125 -0
- package/dist/chunk-KCDECRJY.js +440 -0
- package/dist/chunk-L5TDYKYK.js +76 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-NO3U3FJQ.js +201 -0
- package/dist/chunk-OWISBAOF.js +61 -0
- package/dist/chunk-QKTRECYS.js +1119 -0
- package/dist/chunk-QLK6ED4Q.js +235 -0
- package/dist/chunk-R2GW4F6R.js +19 -0
- package/dist/chunk-RCMTFSEF.js +87 -0
- package/dist/chunk-RM3PY6TG.js +376 -0
- package/dist/chunk-ROMM6KMC.js +433 -0
- package/dist/chunk-SGGCSHAX.js +12050 -0
- package/dist/chunk-SK6YVHFZ.js +211 -0
- package/dist/chunk-SM7RFQRO.js +214 -0
- package/dist/chunk-UGR4QIGS.js +67 -0
- package/dist/chunk-UQNQZXHY.js +711 -0
- package/dist/chunk-UUNHHX4M.js +13 -0
- package/dist/chunk-VL52PL2T.js +71 -0
- package/dist/chunk-WIG36E67.js +196 -0
- package/dist/chunk-XHK3WWGB.js +7436 -0
- package/dist/chunk-XTQM2IP3.js +2863 -0
- package/dist/chunk-ZE2LG76D.js +1296 -0
- package/dist/chunk-ZETTGPAW.js +6950 -0
- package/dist/cli/index.js +37 -0
- package/dist/config-HZAAYDP3.js +10 -0
- package/dist/content-search-7XIP555Q.js +593 -0
- package/dist/database-76RP44JG.js +9 -0
- package/dist/doctor-ANGETYMN.js +395 -0
- package/dist/edit-engine-CG72LDZD.js +51 -0
- package/dist/engine-BKEOGN7N.js +11 -0
- package/dist/engine-VIW6NSKO.js +18 -0
- package/dist/fingerprint-OBLIMZLP.js +33 -0
- package/dist/framework-intelligence-3LORTPOX.js +860 -0
- package/dist/freshness-3K6VU5OT.js +174 -0
- package/dist/graph-engine-HBSQNPTB.js +21 -0
- package/dist/graph_request-Q6NKB6N2.js +350 -0
- package/dist/impact_request-LYP672WE.js +68 -0
- package/dist/index-space-IUUN5WYV.js +15 -0
- package/dist/index-status-7OVGYSGW.js +34 -0
- package/dist/index.js +159 -0
- package/dist/instance-FO6QZRLH.js +19 -0
- package/dist/language-detection-DFCAU5DZ.js +11 -0
- package/dist/lsp/server.js +4301 -0
- package/dist/mcp/server.js +132 -0
- package/dist/onboard-DIQZV2NW.js +161 -0
- package/dist/override-companion-pass-XR7MHTP7.js +117 -0
- package/dist/parallel-indexer-FK2XKZXY.js +88 -0
- package/dist/pool-worker.js +1153 -0
- package/dist/project-health-6XPENVAA.js +94 -0
- package/dist/project-index-5SDPHITE.js +57 -0
- package/dist/project-registry-ZJ3DVUGO.js +355 -0
- package/dist/real-bash-analyzer-EJAU5LW5.js +2201 -0
- package/dist/real-c-analyzer-HBD3FNNH.js +5942 -0
- package/dist/real-clean-analyzer-4EEZLPV5.js +3470 -0
- package/dist/real-clojure-analyzer-Q55EBOO2.js +2818 -0
- package/dist/real-cpp-analyzer-IVVOD25R.js +4101 -0
- package/dist/real-csharp-analyzer-ZBNLNJZJ.js +3298 -0
- package/dist/real-css-analyzer-6XRQLUN7.js +15 -0
- package/dist/real-dart-analyzer-OLR72EFW.js +3944 -0
- package/dist/real-elixir-analyzer-MQWGAH2I.js +2192 -0
- package/dist/real-erlang-analyzer-7BVPQEHO.js +1731 -0
- package/dist/real-fsharp-analyzer-C2UPFNOA.js +2390 -0
- package/dist/real-go-analyzer-UKGI43YS.js +4126 -0
- package/dist/real-graphql-analyzer-VE2EEFII.js +1442 -0
- package/dist/real-groovy-analyzer-W26VACFW.js +2285 -0
- package/dist/real-haskell-analyzer-5CVVAJRX.js +2533 -0
- package/dist/real-hcl-analyzer-5ZT4U43N.js +725 -0
- package/dist/real-html-analyzer-JWNX3C6I.js +1759 -0
- package/dist/real-java-analyzer-GU42VBGQ.js +4677 -0
- package/dist/real-javascript-analyzer-QA4QOJ6W.js +14 -0
- package/dist/real-json-analyzer-MPA6PRXL.js +1640 -0
- package/dist/real-julia-analyzer-22CWYQMN.js +2085 -0
- package/dist/real-kotlin-analyzer-QDZJLXXU.js +3956 -0
- package/dist/real-lua-analyzer-G7NHLYKS.js +1957 -0
- package/dist/real-makefile-analyzer-OCY3GT5N.js +920 -0
- package/dist/real-markdown-analyzer-TEJK2JXJ.js +1215 -0
- package/dist/real-matlab-analyzer-Q5SWLN7Z.js +480 -0
- package/dist/real-nim-analyzer-YLXSK563.js +3603 -0
- package/dist/real-objective-c-analyzer-D23ACC2R.js +1518 -0
- package/dist/real-perl-analyzer-WWTRHYDT.js +1865 -0
- package/dist/real-php-analyzer-JNKJHTCL.js +9368 -0
- package/dist/real-powershell-analyzer-XRSNQERV.js +2915 -0
- package/dist/real-protobuf-analyzer-3OKUCZ7T.js +867 -0
- package/dist/real-python-analyzer-VXAFWI72.js +4880 -0
- package/dist/real-r-analyzer-ZGAIDVI4.js +2596 -0
- package/dist/real-ruby-analyzer-X2KTLVBH.js +2429 -0
- package/dist/real-rust-analyzer-OFFHPL2A.js +3359 -0
- package/dist/real-scala-analyzer-TSTW27J6.js +3293 -0
- package/dist/real-scss-analyzer-AVWYPSOQ.js +1515 -0
- package/dist/real-solidity-analyzer-UCI2KWUB.js +2608 -0
- package/dist/real-sql-analyzer-F77ZMQXR.js +3840 -0
- package/dist/real-svelte-analyzer-SF7NRUO5.js +2532 -0
- package/dist/real-swift-analyzer-MN5QT3QN.js +2311 -0
- package/dist/real-toml-analyzer-66L7BDCQ.js +929 -0
- package/dist/real-typescript-analyzer-6KEMVDWQ.js +14 -0
- package/dist/real-vb-analyzer-VICVPOP7.js +3145 -0
- package/dist/real-vba-analyzer-HXLTTHKM.js +466 -0
- package/dist/real-vue-analyzer-QXR56CFV.js +2680 -0
- package/dist/real-xml-analyzer-YL465ZJW.js +2405 -0
- package/dist/real-yaml-analyzer-SUBCBNSI.js +3528 -0
- package/dist/real-zig-analyzer-HIQLZMGY.js +2430 -0
- package/dist/regex-search-NJKHMZNO.js +239 -0
- package/dist/rules-ACHPB7QU.js +152 -0
- package/dist/scoped-path-MJYWJZQX.js +11 -0
- package/dist/setup-show-NT5ZHYNE.js +15 -0
- package/dist/setup-wizard-G5YYR5T6.js +24 -0
- package/dist/symbol-search-GZJIM2J2.js +16 -0
- package/dist/taint-KXUHHPIE.js +202 -0
- package/dist/taint-analyzer-N7YLZU3N.js +163 -0
- package/dist/tool-handlers-OJDC35DI.js +72 -0
- package/dist/tool-registry-BPEJNSZG.js +17 -0
- package/dist/tool-validation-Q57YMMBX.js +32 -0
- package/dist/type-inference-QMT2RMLV.js +13 -0
- package/dist/vector-binary-LMY4QGAN.js +19 -0
- package/dist/version-ABP2TUL2.js +9 -0
- package/dist/wasm-NMWO3WXY.js +16 -0
- package/docs/EMBEDDINGS.md +132 -0
- package/docs/README.md +178 -0
- package/docs/data/m10-baseline.json +602 -0
- package/docs/data/m10-benchmark-baseline.json +57 -0
- package/docs/data/m10-benchmark-last-run.json +127 -0
- package/docs/data/m10-final-audit.json +215 -0
- package/docs/data/m12-final-audit.json +138 -0
- package/docs/data/m12-surface-matrix.json +50 -0
- package/docs/data/public-tools.json +1902 -0
- package/docs/indexing/DEAD-ASSETS.md +88 -0
- package/docs/indexing/INDEXER-REVIEW.md +634 -0
- package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
- package/docs/indexing/README.md +427 -0
- package/docs/indexing/discovery-matrix.md +41 -0
- package/docs/indexing/embeddings.md +325 -0
- package/docs/indexing/environment-variables.md +77 -0
- package/docs/indexing/performance.md +323 -0
- package/docs/indexing/resume.md +345 -0
- package/docs/indexing/troubleshooting.md +391 -0
- package/docs/man/bb.1 +190 -0
- package/docs/tools/bb_analyze.md +147 -0
- package/docs/tools/bb_annotate.md +110 -0
- package/docs/tools/bb_annotations.md +106 -0
- package/docs/tools/bb_ask.md +101 -0
- package/docs/tools/bb_cross_language.md +103 -0
- package/docs/tools/bb_doc.md +169 -0
- package/docs/tools/bb_edit.md +185 -0
- package/docs/tools/bb_graph.md +169 -0
- package/docs/tools/bb_health.md +156 -0
- package/docs/tools/bb_impact.md +117 -0
- package/docs/tools/bb_index.md +108 -0
- package/docs/tools/bb_migrate.md +135 -0
- package/docs/tools/bb_patterns.md +140 -0
- package/docs/tools/bb_search.md +150 -0
- package/docs/tools/bb_security.md +100 -0
- package/docs/tools/bb_visualize.md +184 -0
- package/grammars/README.md +57 -0
- package/grammars/THIRD-PARTY-NOTICES.md +50 -0
- package/grammars/tree-sitter-c/LICENSE +21 -0
- package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
- package/grammars/tree-sitter-cpp/LICENSE +21 -0
- package/grammars/tree-sitter-csharp/LICENSE +21 -0
- package/grammars/tree-sitter-css/LICENSE +21 -0
- package/grammars/tree-sitter-css/README.md +21 -0
- package/grammars/tree-sitter-dart/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/README.md +18 -0
- package/grammars/tree-sitter-elixir/LICENSE +176 -0
- package/grammars/tree-sitter-elixir/README.md +11 -0
- package/grammars/tree-sitter-fsharp/LICENSE +21 -0
- package/grammars/tree-sitter-fsharp/README.md +45 -0
- package/grammars/tree-sitter-graphql/LICENSE +21 -0
- package/grammars/tree-sitter-graphql/README.md +106 -0
- package/grammars/tree-sitter-groovy/LICENSE +21 -0
- package/grammars/tree-sitter-groovy/README.md +4 -0
- package/grammars/tree-sitter-haskell/LICENSE +21 -0
- package/grammars/tree-sitter-haskell/README.md +394 -0
- package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
- package/grammars/tree-sitter-hcl/LICENSE +201 -0
- package/grammars/tree-sitter-hcl/README.md +39 -0
- package/grammars/tree-sitter-html/LICENSE +21 -0
- package/grammars/tree-sitter-html/README.md +21 -0
- package/grammars/tree-sitter-julia/LICENSE +21 -0
- package/grammars/tree-sitter-julia/README.md +24 -0
- package/grammars/tree-sitter-kotlin/LICENSE +9 -0
- package/grammars/tree-sitter-lua/LICENSE.md +21 -0
- package/grammars/tree-sitter-lua/README.md +29 -0
- package/grammars/tree-sitter-make/LICENSE +21 -0
- package/grammars/tree-sitter-matlab/LICENSE +19 -0
- package/grammars/tree-sitter-matlab/README.md +99 -0
- package/grammars/tree-sitter-nim/LICENSE +373 -0
- package/grammars/tree-sitter-nim/README.md +46 -0
- package/grammars/tree-sitter-objc/LICENSE +21 -0
- package/grammars/tree-sitter-objc/README.md +20 -0
- package/grammars/tree-sitter-perl/LICENSE +7 -0
- package/grammars/tree-sitter-perl/README.md +100 -0
- package/grammars/tree-sitter-powershell/LICENSE +21 -0
- package/grammars/tree-sitter-powershell/README.md +7 -0
- package/grammars/tree-sitter-proto/LICENSE +21 -0
- package/grammars/tree-sitter-proto/README.md +105 -0
- package/grammars/tree-sitter-r/LICENSE +9 -0
- package/grammars/tree-sitter-r/README.md +66 -0
- package/grammars/tree-sitter-ruby/LICENSE +22 -0
- package/grammars/tree-sitter-scss/LICENSE +21 -0
- package/grammars/tree-sitter-scss/README.md +17 -0
- package/grammars/tree-sitter-solidity/LICENSE +7 -0
- package/grammars/tree-sitter-sql/LICENSE +21 -0
- package/grammars/tree-sitter-typescript/LICENSE +21 -0
- package/grammars/tree-sitter-vba/LICENSE +21 -0
- package/grammars/tree-sitter-vba/README.md +738 -0
- package/grammars/tree-sitter-vbnet/LICENSE +21 -0
- package/grammars/tree-sitter-vbnet/README.md +322 -0
- package/grammars/tree-sitter-xml/LICENSE +19 -0
- package/grammars/tree-sitter-yaml/LICENSE +20 -0
- package/grammars/tree-sitter-yaml/README.md +21 -0
- package/package.json +184 -4
- package/wasm/dart.wasm +0 -0
- package/wasm/tree-sitter-bash.wasm +0 -0
- package/wasm/tree-sitter-c.wasm +0 -0
- package/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/wasm/tree-sitter-clean.wasm +0 -0
- package/wasm/tree-sitter-clojure.wasm +0 -0
- package/wasm/tree-sitter-core_schema.wasm +0 -0
- package/wasm/tree-sitter-cpp.wasm +0 -0
- package/wasm/tree-sitter-css.wasm +0 -0
- package/wasm/tree-sitter-dart.wasm +0 -0
- package/wasm/tree-sitter-elisp.wasm +0 -0
- package/wasm/tree-sitter-elixir.wasm +0 -0
- package/wasm/tree-sitter-elm.wasm +0 -0
- package/wasm/tree-sitter-embedded_template.wasm +0 -0
- package/wasm/tree-sitter-erlang.wasm +0 -0
- package/wasm/tree-sitter-fsharp.wasm +0 -0
- package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
- package/wasm/tree-sitter-go.wasm +0 -0
- package/wasm/tree-sitter-graphql.wasm +0 -0
- package/wasm/tree-sitter-groovy.wasm +0 -0
- package/wasm/tree-sitter-haskell.wasm +0 -0
- package/wasm/tree-sitter-hcl.wasm +0 -0
- package/wasm/tree-sitter-html.wasm +0 -0
- package/wasm/tree-sitter-java.wasm +0 -0
- package/wasm/tree-sitter-javascript.wasm +0 -0
- package/wasm/tree-sitter-json.wasm +0 -0
- package/wasm/tree-sitter-json_schema.wasm +0 -0
- package/wasm/tree-sitter-julia.wasm +0 -0
- package/wasm/tree-sitter-kotlin.wasm +0 -0
- package/wasm/tree-sitter-lua.wasm +0 -0
- package/wasm/tree-sitter-make.wasm +0 -0
- package/wasm/tree-sitter-markdown.wasm +0 -0
- package/wasm/tree-sitter-markdown_inline.wasm +0 -0
- package/wasm/tree-sitter-matlab.wasm +0 -0
- package/wasm/tree-sitter-nim.wasm +0 -0
- package/wasm/tree-sitter-objc.wasm +0 -0
- package/wasm/tree-sitter-ocaml.wasm +0 -0
- package/wasm/tree-sitter-perl.wasm +0 -0
- package/wasm/tree-sitter-php.wasm +0 -0
- package/wasm/tree-sitter-php_only.wasm +0 -0
- package/wasm/tree-sitter-powershell.wasm +0 -0
- package/wasm/tree-sitter-protobuf.wasm +0 -0
- package/wasm/tree-sitter-python.wasm +0 -0
- package/wasm/tree-sitter-ql.wasm +0 -0
- package/wasm/tree-sitter-r.wasm +0 -0
- package/wasm/tree-sitter-rescript.wasm +0 -0
- package/wasm/tree-sitter-ruby.wasm +0 -0
- package/wasm/tree-sitter-rust.wasm +0 -0
- package/wasm/tree-sitter-scala.wasm +0 -0
- package/wasm/tree-sitter-scss.wasm +0 -0
- package/wasm/tree-sitter-solidity.wasm +0 -0
- package/wasm/tree-sitter-sql.wasm +0 -0
- package/wasm/tree-sitter-svelte.wasm +0 -0
- package/wasm/tree-sitter-swift.wasm +0 -0
- package/wasm/tree-sitter-systemrdl.wasm +0 -0
- package/wasm/tree-sitter-terraform.wasm +0 -0
- package/wasm/tree-sitter-tlaplus.wasm +0 -0
- package/wasm/tree-sitter-toml.wasm +0 -0
- package/wasm/tree-sitter-tsx.wasm +0 -0
- package/wasm/tree-sitter-typescript.wasm +0 -0
- package/wasm/tree-sitter-vba.wasm +0 -0
- package/wasm/tree-sitter-vbnet.wasm +0 -0
- package/wasm/tree-sitter-vue.wasm +0 -0
- package/wasm/tree-sitter-xml.wasm +0 -0
- package/wasm/tree-sitter-yaml.wasm +0 -0
- package/wasm/tree-sitter-zig.wasm +0 -0
- package/wasm/tree-sitter.wasm +0 -0
- package/wasm/tsx.wasm +0 -0
- package/wasm/typescript.wasm +0 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Indexing performance — what to expect
|
|
2
|
+
|
|
3
|
+
> Every number on this page is quoted from a recorded evidence file in
|
|
4
|
+
> `validation/`, with the host it was measured on. Almost all of them come from
|
|
5
|
+
> **one** machine: an Intel Core i7-8650U Linux workstation with ~15.5 GiB RAM.
|
|
6
|
+
> They are that machine's numbers. They are **not** SLOs, not capacity claims,
|
|
7
|
+
> and not portable to your hardware. Where no measurement exists, this document
|
|
8
|
+
> says so instead of estimating.
|
|
9
|
+
>
|
|
10
|
+
> Statements about *behaviour* (which phase runs, which files are skipped) are
|
|
11
|
+
> gated against the code by `script/verify-docs-indexing.ts`; statements about
|
|
12
|
+
> *timings* are only as good as the evidence file named beside them.
|
|
13
|
+
|
|
14
|
+
## The short version
|
|
15
|
+
|
|
16
|
+
- **A small project takes seconds. A large one takes minutes to hours. That is
|
|
17
|
+
normal.**
|
|
18
|
+
- **The cost is the number of eligible files**, and secondarily how expensive
|
|
19
|
+
each language's analyzer is — not the number of languages.
|
|
20
|
+
- **The parse half of a re-run is nearly free**, because unchanged files are
|
|
21
|
+
never re-parsed — on *every* mode, bare `bb index` included. On an unchanged
|
|
22
|
+
project a bare re-run parses nothing and reports `derived: skipped-unchanged`.
|
|
23
|
+
When a re-run *does* parse something, it pays for the full derived pass, where
|
|
24
|
+
`--changed` pays for a bounded one. That is what the flag buys.
|
|
25
|
+
- **The two dominant costs are parsing and writing to the database.** Discovery
|
|
26
|
+
and worker startup are measurable but negligible.
|
|
27
|
+
- If you want one number for your own project, run `bb index` once and read
|
|
28
|
+
`bb status`. There is no way to predict it from file count alone.
|
|
29
|
+
|
|
30
|
+
## What actually determines the time
|
|
31
|
+
|
|
32
|
+
### 1. How many files are eligible, not how many exist
|
|
33
|
+
|
|
34
|
+
Discovery skips `.gitignore`d paths, `.bbignore`d paths, built-in skip patterns
|
|
35
|
+
(`node_modules`, `dist`, `build`, `out`, `target`, `coverage`, …), and files
|
|
36
|
+
over 5 MB (`src/ast/parallel-indexer.ts:253-300`, `:850-877`).
|
|
37
|
+
|
|
38
|
+
Two corpora can both contain 250,000 files and differ by two orders of
|
|
39
|
+
magnitude in index time, because only the eligible ones are parsed. Discovery
|
|
40
|
+
itself is cheap — see the bottleneck table below — so **reducing what
|
|
41
|
+
discovery finds is the single most effective speedup you have.**
|
|
42
|
+
|
|
43
|
+
### 2. How expensive each language's analyzer is
|
|
44
|
+
|
|
45
|
+
This is the dominant variable once file count is fixed. Measured full-index
|
|
46
|
+
wall times, one host:
|
|
47
|
+
|
|
48
|
+
| Corpus | Language | Files | Symbols | Wall | Evidence |
|
|
49
|
+
| --- | --- | ---: | ---: | ---: | --- |
|
|
50
|
+
| cargo | Rust | 1,328 | 16,088 | 38.6s | `validation/m11/systems-known-limitations.md:32` |
|
|
51
|
+
| cabal | Haskell | 2,140 | 19,137 | 141.0s | `validation/m11/fp-known-limitations.md:40` |
|
|
52
|
+
| elixir | Elixir + Erlang | 616 | 11,189 | 192.4s | `validation/m11/fp-known-limitations.md:38` |
|
|
53
|
+
| zig | Zig | 2,950 | 164,506 | 211s | `validation/m11/systems-known-limitations.md:33` |
|
|
54
|
+
| julia | Julia | 941 | 27,560 | 168–208s | `validation/m12/corpus-manifest.json` (the julia entry: 941 `*.jl` files, `corpusSymbols` 27,560) + `docs/analyzers/julia.md:2147` (the timing) |
|
|
55
|
+
| grails | Groovy | 7,114 | 64,719 | 494.6s | `reviews/groovy/master-review-2026-09-29.md:436-441` |
|
|
56
|
+
|
|
57
|
+
Note the shape: `cabal` has more files than `elixir` but takes **less** time;
|
|
58
|
+
`elixir` has fewer files than `cargo` but takes **five times** as long. File
|
|
59
|
+
count alone does not predict anything.
|
|
60
|
+
|
|
61
|
+
**There is no measurement in this repository of index time versus *number of
|
|
62
|
+
distinct languages*.** Every measurement above holds language count effectively
|
|
63
|
+
constant within a corpus. If you have a 20-language monorepo, nobody has
|
|
64
|
+
published a number for that shape and this document will not invent one.
|
|
65
|
+
|
|
66
|
+
### 3. Whether the run has anything to do
|
|
67
|
+
|
|
68
|
+
Every run — incremental or not — parses only files whose `mtime` **and** `size`
|
|
69
|
+
differ from what is stored (`src/ast/parallel-indexer.ts:887-894`). On an
|
|
70
|
+
unchanged project that is zero files. The one recorded unchanged-project run —
|
|
71
|
+
rake, 57 files — completed in **85 ms** with `parsedFiles: 0` and `noop: true`
|
|
72
|
+
(`validation/mA/results/probe-index-ux.json`).
|
|
73
|
+
|
|
74
|
+
Verified live by `script/verify-docs-indexing.ts --live`: a second bare
|
|
75
|
+
`bb index` on an unchanged fixture parses nothing and reports
|
|
76
|
+
`derived: skipped-unchanged`.
|
|
77
|
+
|
|
78
|
+
### 4. The derived phases
|
|
79
|
+
|
|
80
|
+
After parsing, the derived phases run over the whole project. Current code
|
|
81
|
+
records **eight**: `relationships`, `enrichment`, `gravity`, `taint`, `health`,
|
|
82
|
+
`integrity`, `vectorBlobs`, `analyze`
|
|
83
|
+
(`src/ast/parallel-indexer.ts:576`, `:5561-5680`). The measurement below predates
|
|
84
|
+
the last three being recorded, so its table has six rows — the five derived-data
|
|
85
|
+
phases plus `integrity`:
|
|
86
|
+
|
|
87
|
+
| Phase | Time |
|
|
88
|
+
| --- | ---: |
|
|
89
|
+
| relationships | 2,299ms |
|
|
90
|
+
| enrichment | 3,091ms |
|
|
91
|
+
| gravity | 409ms |
|
|
92
|
+
| taint | 2,582ms |
|
|
93
|
+
| health | 124ms |
|
|
94
|
+
| integrity | 905ms |
|
|
95
|
+
| **total post-processing** | **9,494ms** |
|
|
96
|
+
|
|
97
|
+
`validation/mA/results/probe-index-ux.json` (element-plus, 2,580 files,
|
|
98
|
+
27,781 symbols, 47,480 relationships, 7 workers on an 8-CPU host).
|
|
99
|
+
|
|
100
|
+
So on that run: parse 141s, dbFlush 169s, post-processing 9.5s. **Parse and DB
|
|
101
|
+
flush dominate; enrichment is secondary.** Note this run's shape differs from a
|
|
102
|
+
cold full index — see the wall-clock caveat at the end of this document before
|
|
103
|
+
using it to reason about first runs.
|
|
104
|
+
|
|
105
|
+
**A phase's time can be a plan rather than work, and on a fresh index the plan is
|
|
106
|
+
chosen blind.** The pipeline runs `ANALYZE` at the END of the derived pass, so a
|
|
107
|
+
fresh index prepares these statements against tables nothing has measured. One
|
|
108
|
+
cold run reported 440.8s for the taint phase; the same statement, same data and
|
|
109
|
+
same 1,530 resolved flows cost **520.2s planned without statistics and 1.7s with
|
|
110
|
+
them** — and a post pass on the same index after the fix reported the phase at
|
|
111
|
+
1.3s. Resolution, enrichment and gravity query tables the run has just written
|
|
112
|
+
too; only the taint statement has been measured and fixed so far
|
|
113
|
+
(`validation/m60/results/taint-plan-stats.json`, probe:
|
|
114
|
+
`script/perf/probe-taint-plan-stats.ts`). Before attributing a slow phase to its
|
|
115
|
+
algorithm, check whether its plan was chosen blind.
|
|
116
|
+
|
|
117
|
+
## Where the time goes
|
|
118
|
+
|
|
119
|
+
Measured bottleneck classification across five repositories, taking the median
|
|
120
|
+
fraction of total wall time each phase consumed
|
|
121
|
+
(`validation/m21/results/X3.json`):
|
|
122
|
+
|
|
123
|
+
| Phase | Median fraction of wall | Verdict |
|
|
124
|
+
| --- | ---: | --- |
|
|
125
|
+
| Parse | **0.74** | confirmed bottleneck |
|
|
126
|
+
| DB flush | **0.345** | confirmed bottleneck |
|
|
127
|
+
| Worker startup (fork probe) | 0.009 | disproven as a bottleneck |
|
|
128
|
+
| Discovery | 0.002 | disproven as a bottleneck |
|
|
129
|
+
|
|
130
|
+
Practical consequences:
|
|
131
|
+
|
|
132
|
+
- **More CPU cores will not rescue a slow index past the worker cap.** Worker
|
|
133
|
+
count is `max(2, min(cpus - 1, 12))` (`src/ast/parallel-indexer.ts:120`), and
|
|
134
|
+
the pool then sizes itself as `min(concurrency, ceil(files / 10))`
|
|
135
|
+
(`src/ast/parallel-indexer.ts:1243`, `:131`). On a 32-core machine you still
|
|
136
|
+
get 12 workers. On a 4-core machine you get 3.
|
|
137
|
+
- **12 workers is not automatically faster than 1.** The one controlled A/B in
|
|
138
|
+
the repo (`validation/m19/results/S3-controls.json:1-60`, 100-file disjoint samples on
|
|
139
|
+
intellij) found 12 workers *faster* on the throughput control (20,705ms vs
|
|
140
|
+
22,767ms) but *slower* on the memory control (39,775ms vs 25,482ms). The
|
|
141
|
+
evidence file states these were disjoint sequential samples, "not a
|
|
142
|
+
same-input benchmark". Treat "add workers" as unproven.
|
|
143
|
+
|
|
144
|
+
## Does throughput decay as the index grows?
|
|
145
|
+
|
|
146
|
+
There is a recorded curve now. It says the per-file rate does not fall because
|
|
147
|
+
later files are more expensive — but the number a run finishes with *is* lower
|
|
148
|
+
than the number the bar showed at its best, for a different reason.
|
|
149
|
+
|
|
150
|
+
`validation/m60/results/index-scaling-curve.json` holds nested pool tiers
|
|
151
|
+
(n50 ⊂ n200 ⊂ n400 ⊂ n800 — prefixes of one deterministic ordering of this
|
|
152
|
+
repository's own TypeScript, materialised by `script/perf/make-pools.sh`, each
|
|
153
|
+
indexed into a fresh data dir by `script/perf/profile-stages.ts`), two real
|
|
154
|
+
corpora indexed through the CLI, and the rendered `files/s` series of two runs.
|
|
155
|
+
It carries two arms — the resolution plan before and after the linear fix — and
|
|
156
|
+
the raw per-tier output is committed beside it.
|
|
157
|
+
|
|
158
|
+
Pool tiers, after-arm, on the Linux workstation (load1 5.1–13.9 across the runs;
|
|
159
|
+
each tier is a superset of the tier above it):
|
|
160
|
+
|
|
161
|
+
| Tier | Files | Relationships | Wall | files/s | parse | dbFlush | derived |
|
|
162
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
163
|
+
| n50 | 47 | 7,968 | 19.8s | 2.4 | 84% | 21% | 7% |
|
|
164
|
+
| n200 | 196 | 63,331 | 76.1s | 2.6 | 80% | 47% | 17% |
|
|
165
|
+
| n400 | 396 | 91,379 | 105.8s | 3.7 | 74% | 54% | 23% |
|
|
166
|
+
| n800 | 796 | 124,114 | 147.0s | 5.4 | 72% | 70% | 26% |
|
|
167
|
+
|
|
168
|
+
Three things to read out of it:
|
|
169
|
+
|
|
170
|
+
- **files/s rises with size here, it does not fall** — 2.4 → 5.4 over 17× the
|
|
171
|
+
files, and the largest tier ran against *more* machine load than the smallest.
|
|
172
|
+
The small tiers are where discovery, worker startup and the first flush are a
|
|
173
|
+
larger fraction of a short run.
|
|
174
|
+
- **Parse and DB flush are the run at these sizes**, and workers overlap them, so
|
|
175
|
+
the two columns are not a partition and do not add up to 100%.
|
|
176
|
+
- **The derived phases grow as a share of the run** (7% → 26% across these tiers,
|
|
177
|
+
41% at the 12k-file corpus below). What does *not* grow is the per-relationship
|
|
178
|
+
cost of resolution: 0.45 candidate rows per stored relationship at 47 files →
|
|
179
|
+
0.57 at 196 files, a 1.25× growth over 4× the files, and resolve milliseconds
|
|
180
|
+
per relationship at 1.03×. Both growth factors are gated
|
|
181
|
+
(`validation/m60/results/scaling-linear.after-linux.json`).
|
|
182
|
+
|
|
183
|
+
The two arms of that curve produce the same *graph* at every tier: the
|
|
184
|
+
relationship-multiset digest is byte-identical between the window plan and the
|
|
185
|
+
linear plan at n50, n200, n400 and n800. What changed is cost, not output.
|
|
186
|
+
|
|
187
|
+
### The rate the bar shows is not a parse rate
|
|
188
|
+
|
|
189
|
+
The `X/s` on the progress line — and in `bb status` while a run is in flight — is
|
|
190
|
+
`processed files / elapsed-since-start`: a running average over the **whole**
|
|
191
|
+
run. It climbs while the pool is saturating, peaks when the last file is stored,
|
|
192
|
+
and then decays through the derived pass, where no file completes and the clock
|
|
193
|
+
keeps running. `script/perf/live-rate-series.ts` captures it. On element-plus
|
|
194
|
+
(2,581 files) the recorded series peaks at **30.1 files/s at 85s**, and the run
|
|
195
|
+
as a whole reports 102.6s — **25.2 files/s**. That 20% gap is the tail, not a
|
|
196
|
+
parse slowdown. On lodash (73 files, 27.4s) the same arithmetic is starker: peak
|
|
197
|
+
5.7 files/s, whole run 2.7 files/s.
|
|
198
|
+
|
|
199
|
+
So a figure like "22 files/s" is a whole-run number: files divided by everything
|
|
200
|
+
the run did, tail included. The parse-only rate is always higher, and the gap is
|
|
201
|
+
as large as the derived pass is.
|
|
202
|
+
|
|
203
|
+
## Measured anchors, full index, from scratch
|
|
204
|
+
|
|
205
|
+
| Corpus | Files | Symbols | Wall | Evidence |
|
|
206
|
+
| --- | ---: | ---: | ---: | --- |
|
|
207
|
+
| rake | 57 | 779 | 1.93s | `validation/m19/results/S1.superseded-e7c9819f4.json:61-65` |
|
|
208
|
+
| lodash | 36 | 1,048 | 3.41s | `validation/m19/results/S1.superseded-e7c9819f4.json:68-72` |
|
|
209
|
+
| element-plus | 2,580 | 27,751 | 51.58s | `validation/m19/results/S1.superseded-e7c9819f4.json:75-79` |
|
|
210
|
+
| paperclip | 676 | 8,054 | 100.9s | `validation/m19/results/S2-paperclip-timings.json:33` |
|
|
211
|
+
|
|
212
|
+
**Do not read these as a scaling curve.** They are three points on three
|
|
213
|
+
different codebases and the relationship is non-monotonic: lodash (36 files)
|
|
214
|
+
took *longer* than rake (57 files). The evidence file says so itself
|
|
215
|
+
(`validation/m19/results/S1.superseded-e7c9819f4.json:104`): *"Wall-time is
|
|
216
|
+
hardware-bound (machine recorded); not portable"* and, for the second quote,
|
|
217
|
+
`docs/m19/BENCHMARKS.md:14-16`.
|
|
218
|
+
|
|
219
|
+
The full per-phase breakdown for one complete run, element-plus:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
total 151805ms | parse 141079ms | dbFlush 169079ms | discovery 646ms | startup 322ms
|
|
223
|
+
mode worker-pool | workers 7 | noop no
|
|
224
|
+
18.1 files/s
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`validation/mA/results/probe-index-ux.json`. (The `dbFlush` figure exceeds the
|
|
228
|
+
parse figure because flushing is overlapped with parsing across workers; the two
|
|
229
|
+
are not additive.)
|
|
230
|
+
|
|
231
|
+
## Very large repositories
|
|
232
|
+
|
|
233
|
+
| Corpus | Indexed files | Symbols | Wall | Status |
|
|
234
|
+
| --- | ---: | ---: | ---: | --- |
|
|
235
|
+
| ASP.NET Core | 10,431 | 132,744 | 161s + 92s + 77s + 68s, four slices | verified, finalization deferred per slice (`validation/m19/results/S2-timings.json:55,109,163,217`) |
|
|
236
|
+
| IntelliJ Community | 69,509 | 1,071,964 | **not claimed** | counts verified; a single fresh 1,000-file batch took 1,216.9s, so a complete cold run was outside the verification window (`validation/m19/results/S3.superseded-e7c9819f4.json:116-131`, `validation/m19/results/B1.json:42-43`) |
|
|
237
|
+
| groovy (grails) | 7,114 | 64,719 | 494.6s | single process, all tracked files, 0 failures |
|
|
238
|
+
| ASP.NET Core | 12,177 | 196,030 | 2,207.7s | one cold run at this repository's HEAD, full derived pass: 898.8s of the 2,207.7s (41%) is post-processing — `taint` alone is 440.8s, larger than the 297.2s resolution phase (`validation/m60/results/index-scaling-curve.json`) |
|
|
239
|
+
|
|
240
|
+
The last row is a different measurement from the four-slice row above it: a
|
|
241
|
+
single cold run, on a later build, with the derived pass included rather than
|
|
242
|
+
deferred per slice. Its shape is the one worth carrying away — **41% of a 37-minute
|
|
243
|
+
index stores no files at all**, so a `files/s` figure for a corpus this size is
|
|
244
|
+
dominated by work that has nothing to do with files.
|
|
245
|
+
|
|
246
|
+
The IntelliJ row is the important one. A complete cold full-index wall time for
|
|
247
|
+
a repo of that size has **not been measured** and this repository does not
|
|
248
|
+
claim one. Anyone who tells you "it'll take N hours" for a 70k-file Java repo is
|
|
249
|
+
extrapolating, not reporting.
|
|
250
|
+
|
|
251
|
+
## How to speed it up
|
|
252
|
+
|
|
253
|
+
In descending order of effect:
|
|
254
|
+
|
|
255
|
+
1. **Use `bb index --changed`, not a bare `bb index`.** Both parse only files
|
|
256
|
+
whose mtime or size changed — that filter is not a flag. What `--changed`
|
|
257
|
+
changes is the derived pass: `bounded` (scoped to touched files) instead of
|
|
258
|
+
`full` (the whole project). On a steady codebase this is close to free.
|
|
259
|
+
2. **Cut discovery with `.bbignore`.** Discovery is cheap in absolute terms
|
|
260
|
+
(0.002 of wall) but it decides how many files the expensive phases must
|
|
261
|
+
handle. Excluding generated code, vendored trees and fixtures with a
|
|
262
|
+
`.bbignore` (same syntax as `.gitignore`, and it overrides `.gitignore` —
|
|
263
|
+
`src/ast/parallel-indexer.ts:889-894`, `:969-971`) is the highest-leverage
|
|
264
|
+
change you can make on a huge repository. Use it to exclude what you have
|
|
265
|
+
excluded, not to exclude source you intend to search.
|
|
266
|
+
3. **Split parse from derived work.** `bb index --skip-post-processing` parses
|
|
267
|
+
and stores and stops; `bb index --post` derives later. The bounded derived
|
|
268
|
+
pass is genuinely cheaper than the full one — one recorded comparison was
|
|
269
|
+
74ms bounded vs 1,940ms full on the same project
|
|
270
|
+
(`validation/m40/results/A2.json:10-24`) — though that measurement is small
|
|
271
|
+
and project-specific.
|
|
272
|
+
4. **Fix a silent serial run.** If the summary says `parsed in-process — the
|
|
273
|
+
worker pool was unavailable`, you are paying for a single-threaded parse and
|
|
274
|
+
every number else looks fine. See
|
|
275
|
+
[`troubleshooting.md`](troubleshooting.md); this is the most common cause of
|
|
276
|
+
"indexing is mysteriously slow".
|
|
277
|
+
5. **Target one file when you only changed one file.** `bb index --file <path>`
|
|
278
|
+
parses one file and scopes the derived pass to it.
|
|
279
|
+
6. **Do not reach for `--force` to make things faster.** It cannot; it is the
|
|
280
|
+
most expensive mode, because it re-parses everything and recomputes every
|
|
281
|
+
derived phase.
|
|
282
|
+
|
|
283
|
+
## Honest limits of this document
|
|
284
|
+
|
|
285
|
+
- **Every timing is one machine's.** Intel i7-8650U, ~15.5 GiB RAM, Linux
|
|
286
|
+
(`validation/m19/results/S1.json:15-20`). Hardware, disk and load change these
|
|
287
|
+
numbers by large factors. Load alone moved this repo's own measured latency
|
|
288
|
+
from p50 1,655ms to p50 2,618ms at roughly double the machine load
|
|
289
|
+
(`validation/m57/results/T3-latency-idle.json:53-58`).
|
|
290
|
+
- **No cold full-index time exists for this repository.** Its index has been
|
|
291
|
+
measured for *counts* (2,681 files / ~168k symbols;
|
|
292
|
+
`docs/m57/issues/t1.md:13`) and for *read-tool latency*, but no `bb index`
|
|
293
|
+
wall clock is recorded anywhere. Don't infer one from the symbol count.
|
|
294
|
+
- **Declared budgets are not measurements.** `docs/M10-BENCHMARKS.md:52-53`
|
|
295
|
+
declares `index cold <= 8s` and `warm single-file reindex <= 1.5s` for a
|
|
296
|
+
~12-file contract fixture — but `docs/data/m10-benchmark-last-run.json`
|
|
297
|
+
records **no** `index_cold` or `index_warm_reindex` row. Those budgets have
|
|
298
|
+
never been measured. Treat them as unverified.
|
|
299
|
+
- **No throughput figure is claimed for the three M19 anchors.**
|
|
300
|
+
`validation/m19/results/S1.json` records milliseconds only. The 18.1
|
|
301
|
+
files-per-second for element-plus comes from a different, second run of that
|
|
302
|
+
corpus on a different host (`validation/mA/results/probe-index-ux.json`); the
|
|
303
|
+
newer figures on this page — 23.9 and 25.2 files/s for element-plus, 5.5 for
|
|
304
|
+
ASP.NET Core — come from `validation/m60/results/index-scaling-curve.json` and
|
|
305
|
+
are one machine's and two builds', not a portable rate.
|
|
306
|
+
- **Per-language analyzer timings are not comparable across languages.**
|
|
307
|
+
`validation/m60/analyzer-budget-register.json:24-30` records that 39 of 41
|
|
308
|
+
lanes carry an unresolved scoring debt and that no complexity artifact in the
|
|
309
|
+
repo records node counts, so per-1k-node normalization **cannot be derived**.
|
|
310
|
+
The analyzer table above is a list of measured wall times, not a league table.
|
|
311
|
+
- **Embedding time as a share of index time is unknown.**
|
|
312
|
+
`docs/m20/PERFORMANCE-DIAGNOSTIC.md:24` explicitly declines to attribute a
|
|
313
|
+
slow run to embeddings or to WASM parsing for lack of evidence. Embeddings are
|
|
314
|
+
drained in the background after the parse, so they usually land outside the
|
|
315
|
+
measured index window — which is exactly why nobody can attribute them.
|
|
316
|
+
- **Nothing here is a promise about your machine.** The only reliable number for
|
|
317
|
+
your project is the one your own `bb status` prints after your own run.
|
|
318
|
+
|
|
319
|
+
## Related
|
|
320
|
+
|
|
321
|
+
- [`README.md`](README.md) — what each flag actually does
|
|
322
|
+
- [`resume.md`](resume.md) — interrupting and resuming
|
|
323
|
+
- [`troubleshooting.md`](troubleshooting.md) — when the run degrades to serial
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Interrupting and resuming an index
|
|
2
|
+
|
|
3
|
+
> Gated by `script/verify-docs-indexing.ts` (see [`README.md`](README.md)). Source
|
|
4
|
+
> of truth: the cooperative-cancellation path in `src/cli/index.ts:259-271`, the
|
|
5
|
+
> live status file in `src/ast/index-status.ts`, the mtime/size skip in
|
|
6
|
+
> `src/ast/parallel-indexer.ts:887-894`, and the cancel terminal state at
|
|
7
|
+
> `src/ast/parallel-indexer.ts:5313-5324`.
|
|
8
|
+
|
|
9
|
+
An interrupted `bb index` does not have to start over. This document covers how
|
|
10
|
+
to interrupt one safely, what survives, how to resume, how to check that the
|
|
11
|
+
resume actually worked, and what to do when it cannot.
|
|
12
|
+
|
|
13
|
+
## How to interrupt safely
|
|
14
|
+
|
|
15
|
+
Press **Ctrl-C**.
|
|
16
|
+
|
|
17
|
+
That is not a raw kill. The CLI installs a `SIGINT` handler and aborts
|
|
18
|
+
cooperatively (`src/cli/index.ts:259-271`):
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
SIGINT received — cancelling index (draining in-flight files; resume with: bb index --resume)...
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- New batches stop being dispatched immediately.
|
|
25
|
+
- Batches already in flight are allowed to finish.
|
|
26
|
+
- The database therefore keeps only **fully stored** files. A file that was
|
|
27
|
+
half-written is not half-indexed — it is simply not in the index yet. This is
|
|
28
|
+
what makes the run resumable at all: the store, not a worklist, is the
|
|
29
|
+
checkpoint (`src/ast/index-status.ts:19-32` states the contract).
|
|
30
|
+
- Heavy post-processing is skipped entirely on a cancelled run. A user who pressed Ctrl-C wants a
|
|
31
|
+
fast stop, and every derived phase rewrites rows the checkpoint assumes are
|
|
32
|
+
untouched.
|
|
33
|
+
- When the process exits, you get:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
⏸ index cancelled — progress saved. Resume with: bb index --resume
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`SIGTERM` is handled identically (`src/cli/index.ts:269`), so `kill <pid>` is
|
|
40
|
+
also safe. What is **not** safe is `kill -9` or a power cut: the process dies
|
|
41
|
+
with no chance to write the terminal status. The stored data is still
|
|
42
|
+
consistent — batches are committed, not held in memory — but the run never
|
|
43
|
+
labels itself, which changes what `--resume` has to go on. See
|
|
44
|
+
[When resume cannot find a checkpoint](#when-resume-cannot-find-a-checkpoint).
|
|
45
|
+
|
|
46
|
+
**Do not `kill -9` an index run.** If you think it is hung, check
|
|
47
|
+
[`troubleshooting.md`](troubleshooting.md) first; a serial in-process run on a
|
|
48
|
+
large project legitimately looks idle for minutes.
|
|
49
|
+
|
|
50
|
+
## What the checkpoint is
|
|
51
|
+
|
|
52
|
+
Two things, and it matters which one you are looking at.
|
|
53
|
+
|
|
54
|
+
**1. The database is the real checkpoint.** Resume is not a snapshot and there
|
|
55
|
+
is no undo log. A resume simply re-runs the incremental filter: for every
|
|
56
|
+
discovered file, if the stored record's `mtime` and `size` both match the file
|
|
57
|
+
on disk, the file is skipped (`src/ast/parallel-indexer.ts:887-894`).
|
|
58
|
+
|
|
59
|
+
That filter is not a resume feature — it runs on **every** mode, bare `bb index`
|
|
60
|
+
included. Resume is cheap because of the store, not because resume re-plays a
|
|
61
|
+
worklist.
|
|
62
|
+
|
|
63
|
+
This is why resume works, and also why it is honest about its limits:
|
|
64
|
+
|
|
65
|
+
- Files already stored are skipped — **no re-parse, no time cost**.
|
|
66
|
+
- Files queued but never parsed are picked up.
|
|
67
|
+
- Files whose mtime *and* size changed are re-parsed, which is correct.
|
|
68
|
+
- A file edited to the same byte size with a preserved mtime is skipped. This is
|
|
69
|
+
the one real blind spot in the scheme, and it is inherent to mtime+size
|
|
70
|
+
comparison, not to resume specifically.
|
|
71
|
+
|
|
72
|
+
**2. The status file is the progress marker.** A small JSON file per project at
|
|
73
|
+
`<data-dir>/status/<projectId>.json` (`src/ast/index-status.ts:177`; the data
|
|
74
|
+
dir is `~/.bb` or `$BB_DATA_DIR`, `src/util/bb-data-dir.ts:11-13`). Shape, per
|
|
75
|
+
the `LiveIndexStatus` interface (`src/ast/index-status.ts:107+`) — the values
|
|
76
|
+
below are illustrative:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"schemaVersion": 2,
|
|
81
|
+
"projectId": "1d3d57103be91b8f9716d70199fe28a3f39245d9",
|
|
82
|
+
"phase": "cancelled",
|
|
83
|
+
"processedFiles": 70,
|
|
84
|
+
"totalFiles": 2348,
|
|
85
|
+
"filesPerSecond": 3.9,
|
|
86
|
+
"currentFile": "src/ast/engine.ts",
|
|
87
|
+
"startedAt": 1759428000000,
|
|
88
|
+
"updatedAt": 1759428019000
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`phase` is the field that matters, and its value set is a closed list —
|
|
93
|
+
`indexing`, `post-processing`, `complete`, `failed`, `cancelled`
|
|
94
|
+
(`src/ast/index-status.ts:84`). There is no `idle`: a project that was never
|
|
95
|
+
indexed has **no status file at all**, which is how "no run yet" is represented.
|
|
96
|
+
|
|
97
|
+
**When it is written:** on every progress tick (throttled to ~100 ms,
|
|
98
|
+
`src/ast/parallel-indexer.ts:142` and the tick site) and once at the end of the
|
|
99
|
+
run with the terminal phase (`src/ast/parallel-indexer.ts:5313-5324`). A
|
|
100
|
+
cancelled run keeps `phase: "cancelled"` and is **never** relabelled `complete`
|
|
101
|
+
— the `if (!stats.cancelled)` guard at `src/ast/parallel-indexer.ts:5329` is what
|
|
102
|
+
guarantees it. That phase is what lets a later resume tell "the user stopped
|
|
103
|
+
this on purpose" apart from "the process died".
|
|
104
|
+
|
|
105
|
+
Writes are temp-file + `rename`, so a reader sees either the whole previous
|
|
106
|
+
checkpoint or the whole new one, never a splice
|
|
107
|
+
(`src/ast/index-status.ts:34-41`).
|
|
108
|
+
|
|
109
|
+
You can read it without the CLI:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
cat ~/.bb/status/<projectId>.json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The project id is the first 8 characters `bb index` prints in
|
|
116
|
+
`indexing <dir> (project 1d3d5710…)`. `bb status` shows the same data, parsed.
|
|
117
|
+
|
|
118
|
+
## How to resume
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
bb index --resume
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The command reads the checkpoint and tells you which branch it took
|
|
125
|
+
(`src/cli/index.ts:216-229`):
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
resuming interrupted index (checkpoint: 70/2348 files done)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
or, when there is nothing to resume — reproduced by running it:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
no in-progress checkpoint; running incremental (only missing/changed files)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**That second line is not an error.** Resume unconditionally runs incrementally
|
|
138
|
+
(`src/cli/index.ts:228`), so with no checkpoint it degrades to exactly the same
|
|
139
|
+
work as `bb index --changed` and exits normally. You never have to guess whether
|
|
140
|
+
`--resume` will work — it always runs, and it always tells you which mode it
|
|
141
|
+
used.
|
|
142
|
+
|
|
143
|
+
**Be careful what `--resume` reports on an up-to-date project.** With nothing
|
|
144
|
+
left to parse it takes the same no-op path a bare re-run does, and reports
|
|
145
|
+
`derived: skipped-unchanged`, not `bounded`. Measured:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
no in-progress checkpoint; running incremental (only missing/changed files)
|
|
149
|
+
✓ nothing to index — already up to date (7 unchanged, 0 skipped, 0.3s)
|
|
150
|
+
breakdown: 0 newly parsed | 7 unchanged | 0 skipped | 0 symbol-loss (parsed 0 symbols) | 0 errors | 0 timeouts
|
|
151
|
+
parsed nothing — no files needed parsing, so no parse mode was used
|
|
152
|
+
derived: skipped-unchanged (no files parsed, so no derived pass ran)
|
|
153
|
+
consistency: every internal relationship is attached to a symbol (external references are attached to an external identity, which is correct)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
When it *does* have files to parse, `bounded` is what it reports:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
derived: bounded (derived data re-processed for touched files (bounded pass))
|
|
160
|
+
bounded: 11 symbols touched
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
A real cancelled-then-resumed pair, from
|
|
164
|
+
`validation/m21/results/R3-cancel-resume.json`:
|
|
165
|
+
|
|
166
|
+
| Line | Value |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| Original run, live status | `{"phase":"cancelled","processedFiles":70,"totalFiles":2348}` |
|
|
169
|
+
| Original run, breakdown | `70 newly parsed \| 0 unchanged \| 0 skipped \| 70 symbol-loss (parsed 0 symbols) \| 0 errors \| 0 timeouts \| 2278 cancelled` |
|
|
170
|
+
| Resume message | `resuming interrupted index (checkpoint: 70/2348 files done)` |
|
|
171
|
+
| Resume progress | `[progress] 2340/2348 files (99%) 118.2/s ETA 0s` |
|
|
172
|
+
|
|
173
|
+
The `[progress]` prefix in that recorded run is from an older renderer. The
|
|
174
|
+
current one draws a fixed-width bar on a TTY and prefixes pipe output with
|
|
175
|
+
`[index]` (`src/cli/index-progress.ts:202`, `:206-239`); do not pattern-match on
|
|
176
|
+
that prefix.
|
|
177
|
+
|
|
178
|
+
## Verify the resume was correct
|
|
179
|
+
|
|
180
|
+
A resume can finish cleanly and still leave derived data stale. This is not a
|
|
181
|
+
hypothetical — it is a measured case, and the counts on screen look healthy
|
|
182
|
+
while the edges are unattached. Check three lines, in this order.
|
|
183
|
+
|
|
184
|
+
### 1. The `breakdown:` line
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
breakdown: 2278 newly parsed | 0 unchanged | 0 skipped | 0 symbol-loss (parsed 0 symbols) | 0 errors | 0 timeouts
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`newly parsed` should account for the files the checkpoint said were outstanding.
|
|
191
|
+
`cancelled` should be gone from the line — a cancelled bucket on a resume means
|
|
192
|
+
the resume itself was interrupted. Note the `symbol-loss` bucket carries a
|
|
193
|
+
parenthetical in the real line; a doc that omits it is quoting from memory.
|
|
194
|
+
|
|
195
|
+
### 2. The `derived:` line
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
derived: bounded (derived data re-processed for touched files (bounded pass))
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`bounded` is what you want. The other values mean something specific:
|
|
202
|
+
|
|
203
|
+
| Value | Meaning |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| `full` | all derived data recomputed |
|
|
206
|
+
| `bounded` | recomputed for what this run touched |
|
|
207
|
+
| `skipped-unchanged` | no files parsed, so no derived pass ran |
|
|
208
|
+
| `deferred` | derived data is stale — run `bb index --post` |
|
|
209
|
+
| `partial` | a derived phase failed; index marked partial, exit 1 |
|
|
210
|
+
|
|
211
|
+
`skipped-unchanged` on a resume is the trap. A resume that finishes the parse
|
|
212
|
+
with nothing left to parse runs no derived pass — and the previous run was
|
|
213
|
+
cancelled *during* relationship resolution, so it left edges unresolved. The
|
|
214
|
+
relationship **count** recovers to the clean value while a set of edges is
|
|
215
|
+
still unattached, and every count on screen reads healthy. This exact sequence is
|
|
216
|
+
described in the code at `src/cli/index.ts:389-405`.
|
|
217
|
+
|
|
218
|
+
### 3. The `consistency:` line
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
consistency: every internal relationship is attached to a symbol (external references are attached to an external identity, which is correct)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Note the wording: the all-clear talks about **internal** relationships.
|
|
225
|
+
External references legitimately have no `target_symbol_id` — that is how an
|
|
226
|
+
edge to something outside the project is represented — so counting them as
|
|
227
|
+
failures would report thousands of phantom problems on a healthy large index.
|
|
228
|
+
|
|
229
|
+
This is a real measurement taken from the store after the run, not an inference
|
|
230
|
+
from the pass label (`src/cli/index.ts:406-449`). If it is non-zero, you have
|
|
231
|
+
the trap above. The code emits this shape
|
|
232
|
+
(`src/cli/index.ts:440-442`; the example count is illustrative, not a
|
|
233
|
+
measurement):
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
consistency: 16 internal relationship(s) are attached to neither a symbol nor an external reference — they are counted above but read as edges by no traversal. Run: bb index --post
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
There is a third form worth knowing:
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
consistency: not measured for this run (pre-dates the unresolved-relationship count)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
That is **not** an all-clear. "No line" and "checked, nothing found" look
|
|
246
|
+
identical on a terminal, so the code names the check as not run
|
|
247
|
+
(`src/cli/index.ts:443-448`). If you see it, run `bb index --post` to get a real
|
|
248
|
+
measurement.
|
|
249
|
+
|
|
250
|
+
Fix it with:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
bb index --post
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
It parses nothing, so it is cheap, and it exits 1 if any phase fails.
|
|
257
|
+
|
|
258
|
+
A fourth check, if you want the whole picture: `bb stats` for committed counts,
|
|
259
|
+
and `bb status` for the previous run's per-phase timings and worker count.
|
|
260
|
+
|
|
261
|
+
## When resume cannot find a checkpoint
|
|
262
|
+
|
|
263
|
+
`--resume` recognizes a checkpoint only when **both** conditions hold
|
|
264
|
+
(`src/cli/index.ts:219-222`):
|
|
265
|
+
|
|
266
|
+
1. the status file's `phase` is `indexing` **or** `cancelled`, and
|
|
267
|
+
2. `updatedAt` is less than **one hour** old.
|
|
268
|
+
|
|
269
|
+
So a checkpoint is *not* found when:
|
|
270
|
+
|
|
271
|
+
| Situation | Why | What to do |
|
|
272
|
+
| --- | --- | --- |
|
|
273
|
+
| The run finished successfully | `phase` is `complete` | Nothing. Your index is whole. |
|
|
274
|
+
| The run errored out | `phase` is `failed` | Read the error. `bb status` shows it. |
|
|
275
|
+
| `kill -9`, power loss, OOM | No terminal phase was ever written, and the last tick may already be >1h old | `bb index --resume` still runs, as `--changed` — the database skip means stored files are not re-parsed regardless. If files were still queued, they get parsed. |
|
|
276
|
+
| More than an hour between the crash and the resume | Freshness window expired | Same as above. The window governs *whether the checkpoint is reported*, not whether resume works. |
|
|
277
|
+
| `BB_DATA_DIR` changed, or you are a different user | The status file is somewhere else | Same as above. The database, not the status file, is what makes resume cheap. |
|
|
278
|
+
| The index lock is held | Another run is active | Wait for it. See the lock message in [`README.md`](README.md#one-index-run-at-a-time). |
|
|
279
|
+
|
|
280
|
+
The important thing: **a missing checkpoint degrades resume, it does not break
|
|
281
|
+
it.** The worst case is that you pay for an incremental pass plus a bounded
|
|
282
|
+
derived pass. On an unchanged project the parse half is zero files — a recorded
|
|
283
|
+
57-file project re-indexed in 85 ms with `noop: true`
|
|
284
|
+
(`validation/mA/results/probe-index-ux.json`) — but the bounded derived pass
|
|
285
|
+
still runs, so "close to free" is about the parse, not the whole run.
|
|
286
|
+
|
|
287
|
+
## Recovering with `--force`, honestly
|
|
288
|
+
|
|
289
|
+
`bb index --force` is the rebuild-everything escape hatch. Before reaching for
|
|
290
|
+
it, know what it costs.
|
|
291
|
+
|
|
292
|
+
**It is the most expensive mode `bb index` has.** It wipes the project's data
|
|
293
|
+
in place and then re-parses every eligible file and recomputes every derived
|
|
294
|
+
phase from zero. On the measured anchors:
|
|
295
|
+
|
|
296
|
+
| Corpus | Files | Symbols | Full index wall time | Evidence |
|
|
297
|
+
| --- | ---: | ---: | ---: | --- |
|
|
298
|
+
| rake | 57 | 779 | 1.9s | `validation/m19/results/S1.superseded-e7c9819f4.json:61-65` |
|
|
299
|
+
| lodash | 36 | 1,048 | 3.4s | `validation/m19/results/S1.superseded-e7c9819f4.json:68-72` |
|
|
300
|
+
| element-plus | 2,580 | 27,751 | 51.6s | `validation/m19/results/S1.superseded-e7c9819f4.json:75-79` |
|
|
301
|
+
| paperclip | 676 | 8,054 | 100.9s | `validation/m19/results/S2-paperclip-timings.json:33` |
|
|
302
|
+
| grails (Groovy) | 7,114 | 64,719 | 494.6s | `reviews/groovy/master-review-2026-09-29.md:436-441` |
|
|
303
|
+
|
|
304
|
+
All of these were measured on one Intel i7-8650U / 15.5 GiB Linux host. They are
|
|
305
|
+
**that machine's** numbers, not yours and not a promise.
|
|
306
|
+
|
|
307
|
+
> The S1 evidence file was superseded when a later run found the corpora absent on
|
|
308
|
+
> its machine: `validation/m19/results/S1.json` now records empty anchors. The
|
|
309
|
+
> measurement above is preserved verbatim in
|
|
310
|
+
> `validation/m19/results/S1.superseded-e7c9819f4.json`, which is what the table
|
|
311
|
+
> cites. See
|
|
312
|
+
[`performance.md`](performance.md).
|
|
313
|
+
|
|
314
|
+
There is also a real functional cost: the wipe is in place, so a concurrent
|
|
315
|
+
reader sees an empty database until the rebuild finishes
|
|
316
|
+
(`src/cli/index.ts:181-184`). Anyone querying the index during a `--force` gets
|
|
317
|
+
partial or empty results.
|
|
318
|
+
|
|
319
|
+
**Use `--force` when:**
|
|
320
|
+
|
|
321
|
+
- `bb doctor` reports `model-mismatch` or `model-mixed` — the fix it names is
|
|
322
|
+
`bb index --force`, because vectors from a different model can never be
|
|
323
|
+
compared against your queries (`src/setup/doctor.ts:159-181`).
|
|
324
|
+
- The index is genuinely corrupt: counts that contradict the file tree,
|
|
325
|
+
`database is locked` that survives stopping every writer, or a schema you no
|
|
326
|
+
longer trust.
|
|
327
|
+
|
|
328
|
+
**Do not use `--force` for:**
|
|
329
|
+
|
|
330
|
+
- A cancelled run. That is `--resume`.
|
|
331
|
+
- Stale derived data. That is `--post`, which parses nothing.
|
|
332
|
+
- A few changed files. That is `--changed` or `--file`.
|
|
333
|
+
|
|
334
|
+
## A recovery order that works
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
bb status # what phase is the index actually in?
|
|
338
|
+
bb index --resume # cheap; degrades to --changed safely
|
|
339
|
+
# then read the run's own report:
|
|
340
|
+
# derived: → must not be deferred
|
|
341
|
+
# consistency: → must be 0 unresolved
|
|
342
|
+
bb index --post # cheap; parses nothing; fixes derived data
|
|
343
|
+
bb doctor # read-only; flags model mismatch + permissions
|
|
344
|
+
bb index --force # last resort; full rebuild
|
|
345
|
+
```
|