@functional-systems/srcx 0.1.0-dev.7 → 0.1.0-dev.9

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 CHANGED
@@ -9,6 +9,20 @@
9
9
 
10
10
  ### Added
11
11
 
12
+ - Opt-in managed `text-embedding-3-large` (3072 dimensions) in separate pinned
13
+ Collections, with paired small/large evaluation and unchanged small identities.
14
+
15
+ - Paired identifier/natural-language/mixed-query diagnostics on eight new tasks,
16
+ with verified candidate-rank coverage and offline stdout-budget analysis.
17
+ - Bounded actual-CLI lexical/semantic/hybrid evaluation on identical managed
18
+ corpora, with reviewed and original labels, per-question regressions, stdout
19
+ token counts, command timings and durable request reservations.
20
+
21
+ - Opt-in LambdaDB managed OpenAI `text-embedding-3-small` Collections, offline
22
+ embedding input previews, and explicit semantic/RRF hybrid search modes.
23
+ - Candidate validation of generated vectors separately from exact source payloads,
24
+ unchanged-record reuse, and a bounded synthetic managed acceptance harness.
25
+
12
26
  - Persistent one-to-one Git branch / Collection Branch mapping, branch-local
13
27
  incremental updates, and branch-selected search, resolve, and read against the
14
28
  last published commit Tag.
@@ -27,6 +41,9 @@
27
41
 
28
42
  ### Fixed
29
43
 
44
+ - Fetch omitted managed vectors from query responses through the same immutable
45
+ Tag, preserving hit order/scores and requiring exact non-vector payload equality.
46
+
30
47
  - Reject an unprepared CLI evaluation run before creating its root/lock so a
31
48
  subsequent offline preparation can use the same path.
32
49
  - Pin branch control before enumerating versions so a concurrent publication
package/DESIGN.md CHANGED
@@ -39,7 +39,7 @@ Confirmed boundaries from the discussion:
39
39
  - Map each Git tag to a LambdaDB Alias targeting its published commit Tag.
40
40
  Multiple Git tags for the same commit share one commit Tag and Snapshot.
41
41
  - Keep search results and subsequent source reads pinned to the selected version.
42
- - Replace a changed file's entire chunk set initially; cache embeddings by input.
42
+ - Replace a changed file's entire chunk set initially; retain unchanged remote records.
43
43
  - Keep core LambdaDB work, GitHub App/webhooks, UI, MCP, and a public SaaS outside
44
44
  the first deliverable. sbrain reference documents remain unchanged.
45
45
  - No remote repository creation, application commit, push, publication, real
@@ -379,9 +379,10 @@ readers on multiple machines do not require local registration state.
379
379
  changed/deleted file, compute obsolete IDs as previous file/chunk IDs minus
380
380
  replacement IDs. A deleted file has an empty replacement set. Obtain previous
381
381
  IDs from the inventory, not mutable query-by-path results.
382
- 6. Apply the pinned per-chunk embedding policy. If embeddings are enabled, batch
383
- eligible cache misses across chunks/files within the provider's token/request
384
- limits. Validate successful vectors before caching.
382
+ 6. Apply the pinned per-chunk embedding policy. The managed preset materializes
383
+ eligible `embeddingText` inputs without calling a provider locally. LambdaDB
384
+ generates vectors during ordinary upsert. Skip unchanged remote records and
385
+ validate generated vectors in the candidate Tag before publication.
385
386
  Bound queue memory; do not load an entire repository's content into RAM.
386
387
  7. Materialize deterministic records and deletion lists as local build artifacts.
387
388
  Produce size-bounded inventory parts and a root manifest template. Check all
@@ -389,8 +390,10 @@ readers on multiple machines do not require local registration state.
389
390
  journal supplies the separate attempt ID when sealing the final manifest.
390
391
 
391
392
  Dry-run executes the same inventory/chunking path but performs no database mutation
392
- or embedding call. For pending embedding misses, report tokens and projected vector
393
- size separately; do not claim a completed vector/records hash. Report every entry,
393
+ or embedding call. Report `managed` eligible chunks and `managedTokens` (estimated
394
+ input tokens for the complete corpus), with `embedded=0` in the local artifact.
395
+ These are not billed usage or incremental upload counts. Record hashes bind the
396
+ submitted payload; server-generated vectors are validated separately. Report every entry,
394
397
  exclusion reason, parse status, byte size, chunk/token counts, embedding eligibility
395
398
  and skip reasons, and change summary. Dry-run plans may show pending embedding work;
396
399
  published chunk records may not silently treat missing required vectors as skips.
@@ -473,7 +476,7 @@ fields are omitted when absent; records of different kinds need not share them.
473
476
  | Record | Additional stored fields |
474
477
  | --- | --- |
475
478
  | File (`kind=file`) | `path`, `blobOid`, `contentHash`, `byteLength`, `sourceText`, `language`, `parseStatus`, `chunkCount` |
476
- | Chunk (`kind=chunk`) | `fileId`, `path`, parent `contentHash`, `language`, `chunkKind`, `startByte`, `endByte`, `startLine`, `endLine`, `ordinal`, `searchText`, `embeddingStatus`; optional `symbol`, `scope`, `signature`, `embeddingSkipReason`, `embeddingInputHash`, `embedding` |
479
+ | Chunk (`kind=chunk`) | `fileId`, `path`, parent `contentHash`, `language`, `chunkKind`, `startByte`, `endByte`, `startLine`, `endLine`, `ordinal`, `searchText`, `embeddingStatus`; optional `symbol`, `scope`, `signature`, `embeddingSkipReason`, `embeddingInputHash`, `embeddingText`, `embedding` |
477
480
  | Corpus root (`kind=manifest`, `role=corpus`, ID `__manifest__`) | `repoId`, `indexId`, full `commitOid`, `commitTime`, `requestedRef`, full `config`, `inventoryHash`, `recordsHash`, `counts`, `buildId`, `attemptId`, `inventoryPartIds` |
478
481
  | Inventory part (`kind=manifest`, `role=inventory`) | `partOrdinal`, `partHash`, ordered `entries` with path, file/chunk IDs, blob/content identities, inclusion/exclusion reasons, parse states, sizes, and embedding coverage |
479
482
  | Repository control (`kind=manifest`, `role=repository`, main ID `__repo__`) | Full source identity, repo/index IDs, immutable preset/config, initialization state |
@@ -487,8 +490,13 @@ content such as function, declaration, documentation, imports, or fallback; it i
487
490
  separate from the file/chunk/manifest discriminator. `counts` includes included and
488
491
  excluded files, chunks, and embedded/skipped chunks with reason totals.
489
492
 
490
- In a completed corpus, `embeddingStatus=embedded` requires a valid vector and
491
- `embeddingInputHash`, and omits the skip reason. `embeddingStatus=skipped` requires
493
+ The managed preset uses `embeddingStatus=managed` to identify its generation policy,
494
+ not local completion. Eligible chunks carry `embeddingText=searchText` and an
495
+ `embeddingInputHash` over the exact input and embedding configuration; ordinary
496
+ upserts omit `embedding`. A completed candidate must contain a nonzero, finite
497
+ 1536-number vector for every eligible chunk. Local artifacts contain no vectors.
498
+ The internal fixture-only client embedder retains `embeddingStatus=embedded`;
499
+ its arbitrary presets cannot be published through the CLI. `embeddingStatus=skipped` requires
492
500
  an explicit policy reason and omits the vector and input hash. The lexical-only
493
501
  preset records `embedding-disabled`; an embedding-enabled preset records its
494
502
  specific exclusion reason. Pending or failed required embedding work stays in the
@@ -549,10 +557,13 @@ baseline, not a proven code-specific ranking solution. An initial enrichment
549
557
  candidate preserves original identifiers while adding camelCase/snake_case terms;
550
558
  pin and evaluate that transformation before adopting it as the preset.
551
559
 
552
- An embedding-enabled preset adds an unmanaged `embedding` vector field with a
553
- concrete model-selected dimension and similarity metric, fixed before Collection
554
- creation. An indexed vector field does not require a vector on every chunk; file
555
- and manifest records also omit it. The `none` preset omits the vector index entirely.
560
+ The opt-in managed preset adds indexed text `embeddingText` and a managed
561
+ `embedding` vector field with provider `openai`, model `text-embedding-3-small`,
562
+ 1536 dimensions and cosine similarity. The source field, dimensions and similarity
563
+ are nested under the vector field's `embedding` configuration; there are no
564
+ top-level dimension/similarity properties. Collection discovery verifies the
565
+ complete resolved schema and pinned preset. Missing `embeddingText` on skipped
566
+ chunks, files and manifests means no vector is generated for those records. The `none` preset omits the vector index entirely.
556
567
  Changing presets creates a separate index; do not silently mutate an existing
557
568
  serving index. Do not write placeholder or test vectors into a real corpus.
558
569
 
@@ -741,13 +752,36 @@ choice and the finer skip rules remain evaluation decisions, not measured result
741
752
 
742
753
  ### Embedding reuse
743
754
 
744
- Cache embeddings by the exact full input and provider/model/revision/dimensions/
745
- preprocessing settings. Do not include commit, line numbers, file IDs, or build IDs
746
- in that input. A line shift can update positions while reusing the same vector;
747
- changed chunk boundaries or a renamed path in the input can legitimately miss.
748
- Cache only successful validated outputs atomically. Query embeddings use the same
749
- compatible contract and have their own cache/budget accounting. Provider aliases
750
- may drift if no immutable model revision exists; record that limitation.
755
+ The managed implementation reuses unchanged records from the immutable baseline:
756
+ compare all submitted payload fields after separately validating and removing only
757
+ the server-generated vector. Do not upload those unchanged records again. Direct
758
+ managed vector writes are forbidden, including copies fetched from an old Tag.
759
+ Changed files receive new chunk IDs and may require re-embedding even if some text
760
+ is identical. Replaying an uncertain write may also incur embedding usage. There
761
+ is no srcx-managed document or query vector cache and no promised server cache.
762
+ The older local cache remains only for internal client-embedder fixtures.
763
+
764
+ The input includes path/symbol context and source text, not commit/line/build IDs.
765
+ The preset pins provider/model/dimensions/similarity and input policy, but the
766
+ provider model name is not an immutable model revision. Validation proves vector
767
+ shape and payload integrity, not deterministic numerical reproduction by OpenAI.
768
+
769
+ Search remains lexical by default. Explicit `--mode semantic` uses managed
770
+ `knn.queryText`; `--mode hybrid` combines the existing lexical query and that kNN
771
+ query using LambdaDB RRF. Both legs enforce `kind=chunk` and the same optional
772
+ path/language filters; kNN uses a prefilter and `k=limit`. The lexical leg keeps
773
+ nonembedded chunks eligible. Query/fetch/list request vectors explicitly so the
774
+ same validation applies to pinned result handles. When a deployment omits managed
775
+ vectors from list responses, fetch the affected IDs from that same immutable Tag
776
+ and require identical non-vector payloads before vector validation. Source reads still use the
777
+ original file bytes, never enriched text. No relevance gain is claimed yet.
778
+
779
+ Managed source text and semantic/hybrid query text are sent through LambdaDB to
780
+ OpenAI and incur embedding usage plus normal LambdaDB operations/storage. Dry-run
781
+ and lexical queries do not request query embeddings. Use explicit opt-in and a
782
+ bounded synthetic acceptance run before evaluating private or larger corpora.
783
+ See [managed embeddings](https://docs.lambdadb.ai/guides/collections/managed-embeddings)
784
+ and [hybrid query](https://docs.lambdadb.ai/guides/search/hybrid) for the API contract.
751
785
 
752
786
  ## 9. Candidate validation, publication, and `versions`
753
787
 
@@ -1005,7 +1039,7 @@ Required meaningful checks:
1005
1039
  - Byte coverage, Unicode/BOM/CRLF/trailing-newline/long-line spans are exact.
1006
1040
  - Parse failures and huge nodes stay bounded and visible in reports.
1007
1041
  - B contains additions/replacements/deletions while A reads remain unchanged.
1008
- - A controlled line-shift fixture updates positions and reuses identical embeddings.
1042
+ - A controlled line-shift fixture updates positions; managed imports may re-embed changed IDs, while the internal client-cache fixture verifies input-based reuse.
1009
1043
  - Different paths remain distinct even with identical bytes; reuse follows input.
1010
1044
  - Fixed-config mismatches fail before writes; fake vectors never enter a real corpus.
1011
1045
  - Explicit obsolete-ID deletion preserves replacement IDs and removes surplus chunks.
package/README.md CHANGED
@@ -21,7 +21,7 @@ srcx --help
21
21
 
22
22
  ## Run locally
23
23
 
24
- Requires Node.js 22.14+ and Git. The initial preset uses lexical search with
24
+ Requires Node.js 22.14+ and Git. The default preset uses lexical search with
25
25
  `embedding=none`; no embedding account or paid API is needed.
26
26
 
27
27
  ```sh
@@ -77,6 +77,59 @@ Chunks cover the exact source bytes, carry one-based line ranges, target 800 tok
77
77
  and stay below 1,500 tokens including path/symbol context. The internal window
78
78
  baseline is available to the test/build API, not as a public storage backend.
79
79
 
80
+ ## Opt into managed embeddings
81
+
82
+ Supported models are `text-embedding-3-small` (1536 dimensions) and
83
+ `text-embedding-3-large` (3072 dimensions), both cosine. Use either name with
84
+ `--embedding`; each has a separate pinned preset and Collection. Existing small
85
+ Collections keep their identity. Changing models requires a separate import.
86
+
87
+ LambdaDB generates vectors for meaningful code, tests and prose. Imports-only
88
+ and structural chunks remain lexical. The CLI uses LambdaDB credentials; it does not need an OpenAI key.
89
+ Source text sent for embedding and semantic/hybrid queries pass through LambdaDB
90
+ to OpenAI and incur usage charges. See [LambdaDB managed embeddings](https://docs.lambdadb.ai/guides/collections/managed-embeddings).
91
+
92
+ ```sh
93
+ # Offline preview: no upload or embedding request.
94
+ srcx import --path /path/to/repo --ref main --dry-run \
95
+ --embedding text-embedding-3-small --output /tmp/srcx-managed-preview
96
+
97
+ # Creates a separate Collection; existing lexical Collections stay usable.
98
+ srcx repo add --path /path/to/repo --embedding text-embedding-3-small
99
+ # Use the exact collection value returned above, especially with multiple presets.
100
+ srcx import --repo <collection> --ref main
101
+ srcx search --repo <collection> --version main --query "retry failed writes" --mode hybrid
102
+ srcx search --repo <collection> --version main --query "retry failed writes" --mode semantic
103
+ ```
104
+
105
+ Connected imports, including `--dry-run --repo`, use the selected repository's
106
+ pinned preset. `--embedding` on import is only for `--dry-run --path`.
107
+ `search` keeps `--mode lexical` as its default; semantic and hybrid require a
108
+ managed Collection. Hybrid uses LambdaDB RRF with identical filters on both legs.
109
+ Existing result handles, lexical config hashes and publication journals stay valid.
110
+
111
+ Local artifacts contain eligible `embeddingText` and input hashes, not vectors.
112
+ `counts.managed` and `counts.managedTokens` estimate the complete corpus's eligible
113
+ chunks and input tokens; `embedded=0` does not mean the server will skip them.
114
+ Publication requires valid generated vectors in the immutable candidate Tag,
115
+ plus the same exact payload/source checks as lexical publication. Unchanged
116
+ records are not uploaded again. Changed file chunks and uncertain write retries
117
+ may be embedded again; no global or query embedding cache is promised.
118
+
119
+ A small synthetic live check is available after building:
120
+
121
+ ```sh
122
+ npm run build
123
+ node --env-file=.env.local scripts/live-managed.mjs
124
+ ```
125
+
126
+ It uploads two synthetic commits, bounds document inputs to 10,000 estimated
127
+ tokens and issues four managed search queries plus lexical checks. It retains a
128
+ Collection, local fixture, artifacts, result handles and `.srcx/live-managed/report.json`.
129
+ Completed reruns preserve the original report and make no service calls; changed
130
+ runtime inputs require a fresh directory. Failed runs retain journals and reject
131
+ automatic replay. These are correctness checks, not retrieval-quality measurements.
132
+
80
133
  ## Evaluate retrieval quality
81
134
 
82
135
  The internal [lexical chunking pilot](https://github.com/lambdadb/srcx/blob/develop/eval/README.md) compares syntax-aware and
@@ -89,6 +142,22 @@ adds a second public repository and verifies real `search` → `read` results wi
89
142
  the unchanged CLI preset. It uses normal repository Collections and separate local
90
143
  state; see its documented effects before opting into the live run.
91
144
 
145
+ The [retrieval mode comparison](https://github.com/lambdadb/srcx/blob/develop/eval/RETRIEVAL-MODES.md) runs lexical, semantic and
146
+ hybrid against the same managed corpora. It retains original and reviewed labels,
147
+ full stdout accounting, command timings and bounded embedding request estimates.
148
+ `npm run eval:modes:prepare` is offline; live execution is explicit and incurs
149
+ managed embedding and LambdaDB usage.
150
+
151
+ The [query-style diagnostic](https://github.com/lambdadb/srcx/blob/develop/eval/QUERY-STYLES.md)
152
+ pairs identifiers, natural-language descriptions and mixed questions on eight new
153
+ tasks. It adds rank-prefix and stdout-budget measurements without changing the
154
+ model or ranking. [Embedding model candidates](https://github.com/lambdadb/srcx/blob/develop/eval/EMBEDDING-MODELS.md)
155
+ separately describe possible follow-up comparisons and integration requirements.
156
+ The [managed model comparison](https://github.com/lambdadb/srcx/blob/develop/eval/MODEL-COMPARISON.md)
157
+ compares small and large on both frozen suites, including lexical controls.
158
+ [Results](https://github.com/lambdadb/srcx/blob/develop/eval/MODEL-COMPARISON-RESULTS.md)
159
+ show mixed gains and regressions; small remains the initial managed choice.
160
+
92
161
  ## Explicit live acceptance run
93
162
 
94
163
  The live harness accepts `LAMBDADB_BASE_URL`, `LAMBDADB_PROJECT_NAME`, and
package/VALIDATION.md CHANGED
@@ -12,6 +12,124 @@ concurrency, or retrieval-quality benchmark. npm publication evidence is recorde
12
12
  separately below. The persistent Git branch changes have a separate synthetic
13
13
  live run recorded next; the earlier live runs do not validate this new path.
14
14
 
15
+ ## Retrieval mode comparison
16
+
17
+ At application/harness commit `de5a23bbc2f0712d134066354b4313420d21fccd`, the
18
+ actual CLI completed 48 searches across the same two managed corpora and fixed
19
+ 16-question suite. All **456 results/handles** and **236 reads** matched pinned
20
+ source and version facts. Revised-label complete evidence was lexical **16/16**,
21
+ semantic **8/16**, and hybrid **13/16**; unchanged original labels scored 14/16,
22
+ 8/16, and 11/16. These are diagnostic coverage measurements, not human answer grades.
23
+
24
+ All three hybrid regressions contained the required evidence in search's top ten,
25
+ but outside the first five reads. Keep lexical as the default pending fresh-task
26
+ ranking evaluation. The run also exposed a query response omitting one managed
27
+ vector; same-Tag fetch hydration now preserves non-vector payload equality and
28
+ hit order/scores. The failed attempt remains separate from the complete fresh run.
29
+
30
+ Node 22/24 passed **67 tests**, typechecking and installed-package CLI checks after
31
+ the fix. The completed live report was revalidated without new service calls, and
32
+ its bytes/timestamp were unchanged. See [full results](eval/RETRIEVAL-MODE-RESULTS.md)
33
+ for per-mode metrics, inspected misses, input/usage accounting, source revisions,
34
+ report hashes and evidence limits; [protocol](eval/RETRIEVAL-MODES.md) documents
35
+ reproduction and effects.
36
+
37
+ ## Query-style supplement
38
+
39
+ At harness commit `65c8e7060366f4df846c9b2bc08270e7337f0df1`, 72 searches tested
40
+ eight new tasks with identifier, natural-language and mixed formulations using
41
+ the same managed small model and the same two immutable publications. All
42
+ **696 result handles and 356 reads** matched source/version facts. Complete
43
+ first-five evidence by lexical/semantic/hybrid was **7/8, 7/8, 7/8** for identifiers,
44
+ **0/8, 3/8, 1/8** for natural descriptions and **5/8, 7/8, 6/8** for mixed queries.
45
+ These are eight paired tasks, not 24 independent judgments. Semantic helped on
46
+ this diagnostic; the original keyword-heavy result must not be generalized.
47
+
48
+ The frozen protocol adds verified candidate coverage at ranks 1/3/5/10, the first
49
+ complete prefix, stdout tokens and offline token-budget prefixes. No model,
50
+ ranking, read-selection or product default changed. The original suite/results
51
+ and incomplete run remain intact. A completed rerun preserved report bytes and
52
+ timestamp without service calls. Node 22.14.0 and 24.15.0 passed **69 tests**;
53
+ typecheck, formatting and frozen-harness CI passed. See [results and cases](eval/QUERY-STYLE-RESULTS.md),
54
+ [protocol](eval/QUERY-STYLES.md), and [embedding model research](eval/EMBEDDING-MODELS.md).
55
+
56
+ ## Managed small versus large comparison
57
+
58
+ At frozen harness `6fa65b541dc50debdfa4f9a6297301b2cb3c2b53`, small (1536 dimensions)
59
+ and large (3072 dimensions) completed 240 searches over identical pinned source/chunk inputs.
60
+ All **2,304 result handles and 1,184 reads** passed source/version validation.
61
+ Style-suite complete top-five evidence was semantic **17/24 → 16/24** and hybrid
62
+ **14/24 → 16/24**; natural semantic improved **3/8 → 4/8**. The separate regression
63
+ set scored semantic **8/16 → 8/16** and hybrid **13/16 → 12/16**. All 40 lexical
64
+ candidate lists matched across models. Small coverage repeated the earlier runs.
65
+
66
+ An initial large attempt stopped twice with an unknown CLI search error; its
67
+ 19 partial rows and two successful diagnostic queries remain excluded. A fresh
68
+ isolated run reused the same publications and completed without changing the
69
+ frozen protocol. All four completed reports revalidated without service calls
70
+ and retained their bytes/timestamps. Keep small as the initial managed choice
71
+ and large opt-in; these familiar diagnostic tasks do not establish a model winner.
72
+
73
+ Node 22.14.0 and 24.15.0 passed **72 tests**, typecheck and installed-package CLI
74
+ checks covering lexical/small/large. Formatting/version checks and frozen-code
75
+ CI passed. [Full results](eval/MODEL-COMPARISON-RESULTS.md) record per-query changes,
76
+ report hashes, original-label scores, all five roots' reserved usage, and limits;
77
+ [protocol](eval/MODEL-COMPARISON.md) records the predeclared comparison.
78
+
79
+ Review follow-up: resume and completed replay now reject saved rows whose category
80
+ is missing or differs from the frozen question, even when the stored summary was
81
+ recomputed to match the corruption. The subprocess regression first reproduced
82
+ the acceptance bug, then passed with the guard; valid replay and rejected reports
83
+ both preserve their bytes. Node 24.15.0 passed **73 tests** after this change.
84
+ The four completed model reports have no category mismatches and remain unchanged.
85
+ Their frozen harness remains the revision recorded above; the new harness requires
86
+ fresh run roots rather than rewriting historical runtime fingerprints.
87
+
88
+ ## Managed OpenAI embedding acceptance
89
+
90
+ The opt-in managed preset passed a separate synthetic live run at application and
91
+ harness commit `177731386f76a13723a83aa8470d4e0715e29424` using Node 24.15.0 and
92
+ the user-supplied development connection. It completed at
93
+ `2026-09-24T20:46:46.305Z` in 111.706 seconds. Only synthetic source was uploaded.
94
+
95
+ - Both commits published validated immutable Tags, with six generated 1536-number
96
+ vectors in each corpus. Mixed file/manifest records correctly omitted vectors.
97
+ - Six filtered searches (lexical, semantic and hybrid for each version) returned
98
+ 14 result handles; every full-file read matched the pinned Git blob byte for byte.
99
+ - The second import skipped unchanged records, removed deleted content, and kept
100
+ the original version's handle readable. Fresh repository discovery preserved the
101
+ managed preset and schema.
102
+ - Ordinary upserts submitted ten eligible chunk inputs across both imports. The
103
+ preflight upper bound was 167 estimated document input tokens; four searches
104
+ requested query embeddings. These counts are not provider-reported billed usage.
105
+
106
+ The first run at `fb5b6d8` correctly remained unpublished: this deployment omitted
107
+ vectors from list responses despite `includeVectors=true`, while fetch/query on
108
+ that same Tag returned them. The transport now fetches those listed IDs from the
109
+ same immutable Tag and requires exact non-vector payload equality before vector
110
+ validation. Regression checks reject missing fetch records and changed payloads.
111
+ The failed run and retry journal remain under `.srcx/live-managed/`; they were not
112
+ relabelled as successful evidence. The successful run has a separate Collection
113
+ and evidence directory, `.srcx/live-managed-verified/`, in the validation worktree.
114
+ Both synthetic Collections are retained for inspection.
115
+
116
+ The successful report SHA-256 is
117
+ `6c96ac8af9b79c85e98c84f1f01851906f0b1b3e4d96b51d9417cb59380ac532`;
118
+ its runtime fingerprint is
119
+ `7d8a8006820ee523a88e80d6614327000ce305cffafe377d18fda5b50ba9c947`.
120
+ A same-runtime rerun made no service calls and preserved the original report bytes.
121
+ The harness rejects changed runtime/destination/Node inputs and incomplete runs
122
+ before new requests; recovery after uncertain writes remains explicit.
123
+
124
+ Node 22.14.0 and 24.15.0 each passed **61 tests**, typechecking, and installed-tarball
125
+ CLI checks for both presets. Formatting, version validation and diff checks passed.
126
+ Local tests cover generated-vector failures, exact payload validation, unchanged
127
+ record reuse, uncertain-write recovery, preset/schema drift, query serialization,
128
+ and credential-free managed previews. Live checks used the built application APIs;
129
+ installed-package CLI checks used the loopback fault-injection store.
130
+ These checks establish integration/source correctness, not semantic relevance gains
131
+ or performance on real repositories. The earlier lexical evaluations remain separate.
132
+
15
133
  ## Default CLI search/read evaluation
16
134
 
17
135
  The follow-up uses actual built CLI subprocesses with the unchanged default
package/dist/build.d.ts CHANGED
@@ -32,9 +32,76 @@ export type Preset = {
32
32
  embedding: {
33
33
  model: string;
34
34
  dimensions: number;
35
+ managed?: true;
36
+ provider?: "openai";
37
+ similarity?: "cosine";
38
+ sourceField?: "embeddingText";
35
39
  } | null;
36
40
  };
37
41
  export declare const PRESET: Preset;
42
+ /** Opt-in managed preset; PRESET stays byte-for-byte compatible with lexical corpora. */
43
+ export declare const MANAGED_PRESET: Preset;
44
+ export declare const MANAGED_LARGE_PRESET: Preset;
45
+ export declare function presetFor(embedding?: string): Preset;
46
+ export declare function supportedPreset(preset: unknown): preset is Preset;
47
+ export declare function indexConfigs(preset?: Preset): {
48
+ readonly kind: {
49
+ readonly type: "keyword";
50
+ };
51
+ readonly path: {
52
+ readonly type: "keyword";
53
+ };
54
+ readonly language: {
55
+ readonly type: "keyword";
56
+ };
57
+ readonly fileId: {
58
+ readonly type: "keyword";
59
+ };
60
+ readonly symbol: {
61
+ readonly type: "keyword";
62
+ };
63
+ readonly searchText: {
64
+ readonly type: "text";
65
+ readonly analyzers: readonly ["standard"];
66
+ };
67
+ } | {
68
+ embeddingText: {
69
+ type: string;
70
+ analyzers: string[];
71
+ };
72
+ embedding: {
73
+ type: string;
74
+ managedEmbedding: boolean;
75
+ embedding: {
76
+ model: string;
77
+ dimensions: number;
78
+ provider?: "openai";
79
+ similarity?: "cosine";
80
+ sourceField?: "embeddingText";
81
+ };
82
+ };
83
+ kind: {
84
+ readonly type: "keyword";
85
+ };
86
+ path: {
87
+ readonly type: "keyword";
88
+ };
89
+ language: {
90
+ readonly type: "keyword";
91
+ };
92
+ fileId: {
93
+ readonly type: "keyword";
94
+ };
95
+ symbol: {
96
+ readonly type: "keyword";
97
+ };
98
+ searchText: {
99
+ readonly type: "text";
100
+ readonly analyzers: readonly ["standard"];
101
+ };
102
+ };
103
+ /** Validate server enrichment separately; all other payload fields remain exact. */
104
+ export declare function recordHash(doc: Doc, preset: Preset): string;
38
105
  export type InventoryItem = {
39
106
  path: string;
40
107
  pathBase64: string;
@@ -50,6 +117,8 @@ export type InventoryItem = {
50
117
  parseStatus?: string;
51
118
  tokens?: number;
52
119
  embedded?: number;
120
+ managed?: number;
121
+ managedTokens?: number;
53
122
  skipped?: number;
54
123
  };
55
124
  export type Build = {
package/dist/build.js CHANGED
@@ -20,6 +20,67 @@ export const PRESET = {
20
20
  maxFileBytes: 1024 * 1024,
21
21
  embedding: null,
22
22
  };
23
+ /** Opt-in managed preset; PRESET stays byte-for-byte compatible with lexical corpora. */
24
+ export const MANAGED_PRESET = {
25
+ ...PRESET,
26
+ embedding: {
27
+ managed: true,
28
+ provider: "openai",
29
+ model: "text-embedding-3-small",
30
+ dimensions: 1536,
31
+ similarity: "cosine",
32
+ sourceField: "embeddingText",
33
+ },
34
+ };
35
+ export const MANAGED_LARGE_PRESET = {
36
+ ...MANAGED_PRESET,
37
+ embedding: {
38
+ ...MANAGED_PRESET.embedding,
39
+ model: "text-embedding-3-large",
40
+ dimensions: 3072,
41
+ },
42
+ };
43
+ export function presetFor(embedding = "none") {
44
+ invariant(["none", "text-embedding-3-small", "text-embedding-3-large"].includes(embedding), "Embedding must be none, text-embedding-3-small or text-embedding-3-large.");
45
+ if (embedding === "none")
46
+ return PRESET;
47
+ return embedding === "text-embedding-3-large"
48
+ ? MANAGED_LARGE_PRESET
49
+ : MANAGED_PRESET;
50
+ }
51
+ export function supportedPreset(preset) {
52
+ return (preset !== undefined &&
53
+ [hash(PRESET), hash(MANAGED_PRESET), hash(MANAGED_LARGE_PRESET)].includes(hash(preset)));
54
+ }
55
+ export function indexConfigs(preset = PRESET) {
56
+ if (!preset.embedding?.managed)
57
+ return INDEX_CONFIGS;
58
+ const { managed: _, ...embedding } = preset.embedding;
59
+ return {
60
+ ...INDEX_CONFIGS,
61
+ embeddingText: { type: "text", analyzers: ["standard"] },
62
+ embedding: { type: "vector", managedEmbedding: true, embedding },
63
+ };
64
+ }
65
+ /** Validate server enrichment separately; all other payload fields remain exact. */
66
+ export function recordHash(doc, preset) {
67
+ if (!preset.embedding?.managed)
68
+ return hash(doc);
69
+ const { embedding, ...source } = doc;
70
+ if (doc.kind === "chunk" && doc.embeddingStatus === "managed") {
71
+ invariant(typeof doc.embeddingText === "string" &&
72
+ doc.embeddingText === doc.searchText &&
73
+ doc.embeddingInputHash ===
74
+ hash([preset.embedding, doc.embeddingText]) &&
75
+ Array.isArray(embedding) &&
76
+ embedding.length === preset.embedding.dimensions &&
77
+ embedding.every((v) => typeof v === "number" && Number.isFinite(v)) &&
78
+ embedding.some((v) => v !== 0), "Missing or invalid managed embedding.");
79
+ }
80
+ else
81
+ invariant(embedding === undefined && doc.embeddingText === undefined, "Unexpected managed embedding on an ineligible record.");
82
+ return hash(source);
83
+ }
23
84
  export async function* records(directory) {
24
85
  const input = createReadStream(join(directory, "records.jsonl"), {
25
86
  encoding: "utf8",
@@ -62,6 +123,7 @@ export async function materialize(args) {
62
123
  await validateBuild(prev);
63
124
  }
64
125
  invariant(!preset.embedding ||
126
+ (preset.embedding.managed && supportedPreset(preset)) ||
65
127
  (args.embedder?.model === preset.embedding.model &&
66
128
  args.embedder.dimensions === preset.embedding.dimensions), "Embedding provider must match the pinned preset.");
67
129
  const commit = args.resolvedCommit ?? (await resolveCommit(args.identity.path, args.ref));
@@ -192,26 +254,36 @@ export async function materialize(args) {
192
254
  const eligible = !["imports", "structural"].includes(span.chunkKind);
193
255
  if (preset.embedding && eligible) {
194
256
  const inputHash = hash([preset.embedding, span.searchText]);
195
- const cachePath = args.cache
196
- ? join(args.cache, inputHash + ".json")
197
- : undefined;
198
- let vector = cachePath
199
- ? await optionalJson(cachePath)
200
- : undefined;
201
- if (!vector) {
202
- vector = await args.embedder.embed(span.searchText);
257
+ if (preset.embedding.managed) {
258
+ doc.embeddingText = span.searchText;
259
+ doc.embeddingInputHash = inputHash;
260
+ doc.embeddingStatus = "managed";
261
+ delete doc.embeddingSkipReason;
262
+ item.managed = (item.managed ?? 0) + 1;
263
+ item.managedTokens = (item.managedTokens ?? 0) + span.tokenCount;
264
+ }
265
+ else {
266
+ const cachePath = args.cache
267
+ ? join(args.cache, inputHash + ".json")
268
+ : undefined;
269
+ let vector = cachePath
270
+ ? await optionalJson(cachePath)
271
+ : undefined;
272
+ if (!vector) {
273
+ vector = await args.embedder.embed(span.searchText);
274
+ invariant(vector.length === preset.embedding.dimensions &&
275
+ vector.every(Number.isFinite), "Invalid embedding output.");
276
+ if (cachePath)
277
+ await atomic(cachePath, vector);
278
+ }
203
279
  invariant(vector.length === preset.embedding.dimensions &&
204
- vector.every(Number.isFinite), "Invalid embedding output.");
205
- if (cachePath)
206
- await atomic(cachePath, vector);
280
+ vector.every(Number.isFinite), "Invalid cached vector.");
281
+ doc.embedding = vector;
282
+ doc.embeddingInputHash = inputHash;
283
+ doc.embeddingStatus = "embedded";
284
+ delete doc.embeddingSkipReason;
285
+ item.embedded++;
207
286
  }
208
- invariant(vector.length === preset.embedding.dimensions &&
209
- vector.every(Number.isFinite), "Invalid cached vector.");
210
- doc.embedding = vector;
211
- doc.embeddingInputHash = inputHash;
212
- doc.embeddingStatus = "embedded";
213
- delete doc.embeddingSkipReason;
214
- item.embedded++;
215
287
  }
216
288
  else {
217
289
  doc.embeddingSkipReason = preset.embedding
@@ -289,6 +361,12 @@ export async function materialize(args) {
289
361
  excluded: items.filter((e) => e.status === "excluded").length,
290
362
  chunks: items.reduce((n, e) => n + (e.chunkIds?.length ?? 0), 0),
291
363
  tokens: items.reduce((n, e) => n + (e.tokens ?? 0), 0),
364
+ ...(preset.embedding?.managed
365
+ ? {
366
+ managed: items.reduce((n, e) => n + (e.managed ?? 0), 0),
367
+ managedTokens: items.reduce((n, e) => n + (e.managedTokens ?? 0), 0),
368
+ }
369
+ : {}),
292
370
  embedded: items.reduce((n, e) => n + (e.embedded ?? 0), 0),
293
371
  skipped: items.reduce((n, e) => n + (e.skipped ?? 0), 0),
294
372
  },
@@ -322,6 +400,11 @@ export async function validateBuild(b) {
322
400
  invariant(!seen.has(doc.id) && b.recordHashes[doc.id] === hash(doc), "Corrupted or unexpected artifact record.");
323
401
  seen.add(doc.id);
324
402
  invariant(doc.configHash === b.configHash && doc.schemaVersion === 1, "Mixed configuration.");
403
+ if (b.preset.embedding?.managed) {
404
+ invariant(doc.embedding === undefined, "Managed artifacts must not contain vectors.");
405
+ if (doc.kind !== "chunk")
406
+ invariant(doc.embeddingText === undefined, "Only eligible chunks can contain embedding input.");
407
+ }
325
408
  if (doc.kind === "file") {
326
409
  finish();
327
410
  activeFile = doc;
@@ -362,7 +445,23 @@ export async function validateBuild(b) {
362
445
  doc.searchText.endsWith(slice.toString("utf8")) &&
363
446
  doc.tokenCount === tokens(doc.searchText) &&
364
447
  Number(doc.tokenCount) <= CHUNKER.maxTokens, "Invalid chunk source/position/token binding.");
365
- if (doc.embeddingStatus === "embedded")
448
+ if (b.preset.embedding?.managed) {
449
+ const eligible = !["imports", "structural"].includes(String(doc.chunkKind));
450
+ invariant(eligible
451
+ ? doc.embeddingStatus === "managed" &&
452
+ doc.embeddingText === doc.searchText &&
453
+ doc.embeddingInputHash ===
454
+ hash([b.preset.embedding, doc.searchText]) &&
455
+ doc.embeddingSkipReason === undefined
456
+ : doc.embeddingStatus === "skipped" &&
457
+ doc.embeddingText === undefined &&
458
+ doc.embeddingInputHash === undefined &&
459
+ doc.embeddingSkipReason ===
460
+ (doc.chunkKind === "imports"
461
+ ? "imports-only"
462
+ : "structural-only"), "Invalid managed embedding input or eligibility.");
463
+ }
464
+ else if (doc.embeddingStatus === "embedded")
366
465
  invariant(b.preset.embedding &&
367
466
  Array.isArray(doc.embedding) &&
368
467
  doc.embedding.length === b.preset.embedding.dimensions &&