@functional-systems/srcx 0.1.0-dev.6 → 0.1.0-dev.8
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 +9 -2
- package/DESIGN.md +55 -21
- package/README.md +55 -1
- package/VALIDATION.md +72 -0
- package/dist/build.d.ts +68 -0
- package/dist/build.js +106 -19
- package/dist/build.js.map +1 -1
- package/dist/cli.js +20 -7
- package/dist/cli.js.map +1 -1
- package/dist/publish.js +11 -5
- package/dist/publish.js.map +1 -1
- package/dist/remote.js +30 -3
- package/dist/remote.js.map +1 -1
- package/dist/repository.d.ts +4 -1
- package/dist/repository.js +15 -11
- package/dist/repository.js.map +1 -1
- package/dist/search.d.ts +7 -2
- package/dist/search.js +24 -4
- package/dist/search.js.map +1 -1
- package/package.json +5 -3
- package/src/build.ts +138 -22
- package/src/cli.ts +59 -10
- package/src/publish.ts +18 -6
- package/src/remote.ts +44 -2
- package/src/repository.ts +20 -11
- package/src/search.ts +37 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Default CLI search/read evaluation across two pinned public repositories, with
|
|
6
|
+
alternative answer sets, exact source verification, and complete stdout accounting.
|
|
5
7
|
- Internal reproducible lexical chunking evaluation with pinned source labels,
|
|
6
8
|
equal path-only enrichment, isolated live indexes, and token-budget metrics.
|
|
7
9
|
|
|
8
|
-
## Unreleased
|
|
9
|
-
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- Opt-in LambdaDB managed OpenAI `text-embedding-3-small` Collections, offline
|
|
13
|
+
embedding input previews, and explicit semantic/RRF hybrid search modes.
|
|
14
|
+
- Candidate validation of generated vectors separately from exact source payloads,
|
|
15
|
+
unchanged-record reuse, and a bounded synthetic managed acceptance harness.
|
|
16
|
+
|
|
12
17
|
- Persistent one-to-one Git branch / Collection Branch mapping, branch-local
|
|
13
18
|
incremental updates, and branch-selected search, resolve, and read against the
|
|
14
19
|
last published commit Tag.
|
|
@@ -27,6 +32,8 @@
|
|
|
27
32
|
|
|
28
33
|
### Fixed
|
|
29
34
|
|
|
35
|
+
- Reject an unprepared CLI evaluation run before creating its root/lock so a
|
|
36
|
+
subsequent offline preparation can use the same path.
|
|
30
37
|
- Pin branch control before enumerating versions so a concurrent publication
|
|
31
38
|
cannot advance the selector beyond the version list being read.
|
|
32
39
|
- Reject live checkpoint reuse across different code/harness inputs and preserve
|
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;
|
|
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.
|
|
383
|
-
eligible
|
|
384
|
-
|
|
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.
|
|
393
|
-
|
|
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
|
-
|
|
491
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
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
|
|
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
|
|
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,55 @@ 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
|
+
LambdaDB can generate OpenAI `text-embedding-3-small` vectors (1536 dimensions,
|
|
83
|
+
cosine) for meaningful code, tests and prose. Imports-only and structural chunks
|
|
84
|
+
remain lexical. The CLI uses LambdaDB credentials; it does not need an OpenAI key.
|
|
85
|
+
Source text sent for embedding and semantic/hybrid queries pass through LambdaDB
|
|
86
|
+
to OpenAI and incur usage charges. See [LambdaDB managed embeddings](https://docs.lambdadb.ai/guides/collections/managed-embeddings).
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
# Offline preview: no upload or embedding request.
|
|
90
|
+
srcx import --path /path/to/repo --ref main --dry-run \
|
|
91
|
+
--embedding text-embedding-3-small --output /tmp/srcx-managed-preview
|
|
92
|
+
|
|
93
|
+
# Creates a separate Collection; existing lexical Collections stay usable.
|
|
94
|
+
srcx repo add --path /path/to/repo --embedding text-embedding-3-small
|
|
95
|
+
# Use the exact collection value returned above, especially with two presets.
|
|
96
|
+
srcx import --repo <collection> --ref main
|
|
97
|
+
srcx search --repo <collection> --version main --query "retry failed writes" --mode hybrid
|
|
98
|
+
srcx search --repo <collection> --version main --query "retry failed writes" --mode semantic
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Connected imports, including `--dry-run --repo`, use the selected repository's
|
|
102
|
+
pinned preset. `--embedding` on import is only for `--dry-run --path`.
|
|
103
|
+
`search` keeps `--mode lexical` as its default; semantic and hybrid require a
|
|
104
|
+
managed Collection. Hybrid uses LambdaDB RRF with identical filters on both legs.
|
|
105
|
+
Existing result handles, lexical config hashes and publication journals stay valid.
|
|
106
|
+
|
|
107
|
+
Local artifacts contain eligible `embeddingText` and input hashes, not vectors.
|
|
108
|
+
`counts.managed` and `counts.managedTokens` estimate the complete corpus's eligible
|
|
109
|
+
chunks and input tokens; `embedded=0` does not mean the server will skip them.
|
|
110
|
+
Publication requires valid generated vectors in the immutable candidate Tag,
|
|
111
|
+
plus the same exact payload/source checks as lexical publication. Unchanged
|
|
112
|
+
records are not uploaded again. Changed file chunks and uncertain write retries
|
|
113
|
+
may be embedded again; no global or query embedding cache is promised.
|
|
114
|
+
|
|
115
|
+
A small synthetic live check is available after building:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
npm run build
|
|
119
|
+
node --env-file=.env.local scripts/live-managed.mjs
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
It uploads two synthetic commits, bounds document inputs to 10,000 estimated
|
|
123
|
+
tokens and issues four managed search queries plus lexical checks. It retains a
|
|
124
|
+
Collection, local fixture, artifacts, result handles and `.srcx/live-managed/report.json`.
|
|
125
|
+
Completed reruns preserve the original report and make no service calls; changed
|
|
126
|
+
runtime inputs require a fresh directory. Failed runs retain journals and reject
|
|
127
|
+
automatic replay. These are correctness checks, not retrieval-quality measurements.
|
|
128
|
+
|
|
80
129
|
## Evaluate retrieval quality
|
|
81
130
|
|
|
82
131
|
The internal [lexical chunking pilot](https://github.com/lambdadb/srcx/blob/develop/eval/README.md) compares syntax-aware and
|
|
@@ -84,6 +133,11 @@ window chunks on a fixed public corpus and 16 pinned investigation queries.
|
|
|
84
133
|
`npm run eval:prepare` builds and validates the comparison offline. Live evaluation
|
|
85
134
|
is an explicit separate operation and retains isolated evaluation Collections.
|
|
86
135
|
|
|
136
|
+
The [default CLI workflow evaluation](https://github.com/lambdadb/srcx/blob/develop/eval/CLI-WORKFLOW.md)
|
|
137
|
+
adds a second public repository and verifies real `search` → `read` results with
|
|
138
|
+
the unchanged CLI preset. It uses normal repository Collections and separate local
|
|
139
|
+
state; see its documented effects before opting into the live run.
|
|
140
|
+
|
|
87
141
|
## Explicit live acceptance run
|
|
88
142
|
|
|
89
143
|
The live harness accepts `LAMBDADB_BASE_URL`, `LAMBDADB_PROJECT_NAME`, and
|
package/VALIDATION.md
CHANGED
|
@@ -12,6 +12,78 @@ 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
|
+
## Managed OpenAI embedding acceptance
|
|
16
|
+
|
|
17
|
+
The opt-in managed preset passed a separate synthetic live run at application and
|
|
18
|
+
harness commit `177731386f76a13723a83aa8470d4e0715e29424` using Node 24.15.0 and
|
|
19
|
+
the user-supplied development connection. It completed at
|
|
20
|
+
`2026-09-24T20:46:46.305Z` in 111.706 seconds. Only synthetic source was uploaded.
|
|
21
|
+
|
|
22
|
+
- Both commits published validated immutable Tags, with six generated 1536-number
|
|
23
|
+
vectors in each corpus. Mixed file/manifest records correctly omitted vectors.
|
|
24
|
+
- Six filtered searches (lexical, semantic and hybrid for each version) returned
|
|
25
|
+
14 result handles; every full-file read matched the pinned Git blob byte for byte.
|
|
26
|
+
- The second import skipped unchanged records, removed deleted content, and kept
|
|
27
|
+
the original version's handle readable. Fresh repository discovery preserved the
|
|
28
|
+
managed preset and schema.
|
|
29
|
+
- Ordinary upserts submitted ten eligible chunk inputs across both imports. The
|
|
30
|
+
preflight upper bound was 167 estimated document input tokens; four searches
|
|
31
|
+
requested query embeddings. These counts are not provider-reported billed usage.
|
|
32
|
+
|
|
33
|
+
The first run at `fb5b6d8` correctly remained unpublished: this deployment omitted
|
|
34
|
+
vectors from list responses despite `includeVectors=true`, while fetch/query on
|
|
35
|
+
that same Tag returned them. The transport now fetches those listed IDs from the
|
|
36
|
+
same immutable Tag and requires exact non-vector payload equality before vector
|
|
37
|
+
validation. Regression checks reject missing fetch records and changed payloads.
|
|
38
|
+
The failed run and retry journal remain under `.srcx/live-managed/`; they were not
|
|
39
|
+
relabelled as successful evidence. The successful run has a separate Collection
|
|
40
|
+
and evidence directory, `.srcx/live-managed-verified/`, in the validation worktree.
|
|
41
|
+
Both synthetic Collections are retained for inspection.
|
|
42
|
+
|
|
43
|
+
The successful report SHA-256 is
|
|
44
|
+
`6c96ac8af9b79c85e98c84f1f01851906f0b1b3e4d96b51d9417cb59380ac532`;
|
|
45
|
+
its runtime fingerprint is
|
|
46
|
+
`7d8a8006820ee523a88e80d6614327000ce305cffafe377d18fda5b50ba9c947`.
|
|
47
|
+
A same-runtime rerun made no service calls and preserved the original report bytes.
|
|
48
|
+
The harness rejects changed runtime/destination/Node inputs and incomplete runs
|
|
49
|
+
before new requests; recovery after uncertain writes remains explicit.
|
|
50
|
+
|
|
51
|
+
Node 22.14.0 and 24.15.0 each passed **61 tests**, typechecking, and installed-tarball
|
|
52
|
+
CLI checks for both presets. Formatting, version validation and diff checks passed.
|
|
53
|
+
Local tests cover generated-vector failures, exact payload validation, unchanged
|
|
54
|
+
record reuse, uncertain-write recovery, preset/schema drift, query serialization,
|
|
55
|
+
and credential-free managed previews. Live checks used the built application APIs;
|
|
56
|
+
installed-package CLI checks used the loopback fault-injection store.
|
|
57
|
+
These checks establish integration/source correctness, not semantic relevance gains
|
|
58
|
+
or performance on real repositories. The earlier lexical evaluations remain separate.
|
|
59
|
+
|
|
60
|
+
## Default CLI search/read evaluation
|
|
61
|
+
|
|
62
|
+
The follow-up uses actual built CLI subprocesses with the unchanged default
|
|
63
|
+
syntax/path-scope-symbol preset across pinned public `srcx` and `lambdadb-cli`
|
|
64
|
+
commits. At harness commit `57b63e1cdbf7ec722b95bb0f40ac0ef5039af3e8`, both
|
|
65
|
+
publications validated and all **136 search results/handles** and **76 read
|
|
66
|
+
responses** matched their pinned source, version and citation. No embeddings
|
|
67
|
+
were used. The run completed at `2026-09-24T20:01:58.865Z`.
|
|
68
|
+
|
|
69
|
+
The frozen 16-question suite recovered complete labeled evidence for **14/16**.
|
|
70
|
+
The two remaining cases reflect a one-byte blank-line boundary and an omitted
|
|
71
|
+
alternative document/code answer, not demonstrated source-integrity failures.
|
|
72
|
+
Mean full CLI stdout was **7,383 tokens** per query. This fixed-five-read protocol
|
|
73
|
+
is distinct from the earlier full-chunk/token-budget comparison; neither measures
|
|
74
|
+
general task success. Labels remain assistant-authored without independent human
|
|
75
|
+
review. See [eval/CLI-RESULTS.md](https://github.com/lambdadb/srcx/blob/develop/eval/CLI-RESULTS.md)
|
|
76
|
+
for per-repository scores, inspected misses and evidence boundaries, and
|
|
77
|
+
[eval/CLI-WORKFLOW.md](https://github.com/lambdadb/srcx/blob/develop/eval/CLI-WORKFLOW.md)
|
|
78
|
+
for reproduction and normal Collection effects.
|
|
79
|
+
|
|
80
|
+
Local Node 24.15.0 passed **54 tests**, typechecking, formatting/version checks and
|
|
81
|
+
the installed-package CLI contract. Node 22/24 CI passed at the harness commit.
|
|
82
|
+
Live execution used built CLI subprocesses; package validation used a loopback
|
|
83
|
+
fixture. A completed rerun preserved the original report bytes and timestamp.
|
|
84
|
+
Raw evidence and recovery journals remain under `.srcx/cli-eval/` in the validation
|
|
85
|
+
worktree; the older pilot's original report remains unchanged.
|
|
86
|
+
|
|
15
87
|
## Retrieval evaluation harness
|
|
16
88
|
|
|
17
89
|
The internal pilot adds five checks for unique source-range scoring, token-budget
|
package/dist/build.d.ts
CHANGED
|
@@ -32,9 +32,75 @@ 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 function presetFor(embedding?: string): Preset;
|
|
45
|
+
export declare function supportedPreset(preset: unknown): preset is Preset;
|
|
46
|
+
export declare function indexConfigs(preset?: Preset): {
|
|
47
|
+
readonly kind: {
|
|
48
|
+
readonly type: "keyword";
|
|
49
|
+
};
|
|
50
|
+
readonly path: {
|
|
51
|
+
readonly type: "keyword";
|
|
52
|
+
};
|
|
53
|
+
readonly language: {
|
|
54
|
+
readonly type: "keyword";
|
|
55
|
+
};
|
|
56
|
+
readonly fileId: {
|
|
57
|
+
readonly type: "keyword";
|
|
58
|
+
};
|
|
59
|
+
readonly symbol: {
|
|
60
|
+
readonly type: "keyword";
|
|
61
|
+
};
|
|
62
|
+
readonly searchText: {
|
|
63
|
+
readonly type: "text";
|
|
64
|
+
readonly analyzers: readonly ["standard"];
|
|
65
|
+
};
|
|
66
|
+
} | {
|
|
67
|
+
embeddingText: {
|
|
68
|
+
type: string;
|
|
69
|
+
analyzers: string[];
|
|
70
|
+
};
|
|
71
|
+
embedding: {
|
|
72
|
+
type: string;
|
|
73
|
+
managedEmbedding: boolean;
|
|
74
|
+
embedding: {
|
|
75
|
+
model: string;
|
|
76
|
+
dimensions: number;
|
|
77
|
+
provider?: "openai";
|
|
78
|
+
similarity?: "cosine";
|
|
79
|
+
sourceField?: "embeddingText";
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
kind: {
|
|
83
|
+
readonly type: "keyword";
|
|
84
|
+
};
|
|
85
|
+
path: {
|
|
86
|
+
readonly type: "keyword";
|
|
87
|
+
};
|
|
88
|
+
language: {
|
|
89
|
+
readonly type: "keyword";
|
|
90
|
+
};
|
|
91
|
+
fileId: {
|
|
92
|
+
readonly type: "keyword";
|
|
93
|
+
};
|
|
94
|
+
symbol: {
|
|
95
|
+
readonly type: "keyword";
|
|
96
|
+
};
|
|
97
|
+
searchText: {
|
|
98
|
+
readonly type: "text";
|
|
99
|
+
readonly analyzers: readonly ["standard"];
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
/** Validate server enrichment separately; all other payload fields remain exact. */
|
|
103
|
+
export declare function recordHash(doc: Doc, preset: Preset): string;
|
|
38
104
|
export type InventoryItem = {
|
|
39
105
|
path: string;
|
|
40
106
|
pathBase64: string;
|
|
@@ -50,6 +116,8 @@ export type InventoryItem = {
|
|
|
50
116
|
parseStatus?: string;
|
|
51
117
|
tokens?: number;
|
|
52
118
|
embedded?: number;
|
|
119
|
+
managed?: number;
|
|
120
|
+
managedTokens?: number;
|
|
53
121
|
skipped?: number;
|
|
54
122
|
};
|
|
55
123
|
export type Build = {
|
package/dist/build.js
CHANGED
|
@@ -20,6 +20,55 @@ 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 function presetFor(embedding = "none") {
|
|
36
|
+
invariant(["none", "text-embedding-3-small"].includes(embedding), "Embedding must be none or text-embedding-3-small.");
|
|
37
|
+
return embedding === "none" ? PRESET : MANAGED_PRESET;
|
|
38
|
+
}
|
|
39
|
+
export function supportedPreset(preset) {
|
|
40
|
+
return (preset !== undefined &&
|
|
41
|
+
[hash(PRESET), hash(MANAGED_PRESET)].includes(hash(preset)));
|
|
42
|
+
}
|
|
43
|
+
export function indexConfigs(preset = PRESET) {
|
|
44
|
+
if (!preset.embedding?.managed)
|
|
45
|
+
return INDEX_CONFIGS;
|
|
46
|
+
const { managed: _, ...embedding } = preset.embedding;
|
|
47
|
+
return {
|
|
48
|
+
...INDEX_CONFIGS,
|
|
49
|
+
embeddingText: { type: "text", analyzers: ["standard"] },
|
|
50
|
+
embedding: { type: "vector", managedEmbedding: true, embedding },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Validate server enrichment separately; all other payload fields remain exact. */
|
|
54
|
+
export function recordHash(doc, preset) {
|
|
55
|
+
if (!preset.embedding?.managed)
|
|
56
|
+
return hash(doc);
|
|
57
|
+
const { embedding, ...source } = doc;
|
|
58
|
+
if (doc.kind === "chunk" && doc.embeddingStatus === "managed") {
|
|
59
|
+
invariant(typeof doc.embeddingText === "string" &&
|
|
60
|
+
doc.embeddingText === doc.searchText &&
|
|
61
|
+
doc.embeddingInputHash ===
|
|
62
|
+
hash([preset.embedding, doc.embeddingText]) &&
|
|
63
|
+
Array.isArray(embedding) &&
|
|
64
|
+
embedding.length === preset.embedding.dimensions &&
|
|
65
|
+
embedding.every((v) => typeof v === "number" && Number.isFinite(v)) &&
|
|
66
|
+
embedding.some((v) => v !== 0), "Missing or invalid managed embedding.");
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
invariant(embedding === undefined && doc.embeddingText === undefined, "Unexpected managed embedding on an ineligible record.");
|
|
70
|
+
return hash(source);
|
|
71
|
+
}
|
|
23
72
|
export async function* records(directory) {
|
|
24
73
|
const input = createReadStream(join(directory, "records.jsonl"), {
|
|
25
74
|
encoding: "utf8",
|
|
@@ -62,6 +111,7 @@ export async function materialize(args) {
|
|
|
62
111
|
await validateBuild(prev);
|
|
63
112
|
}
|
|
64
113
|
invariant(!preset.embedding ||
|
|
114
|
+
(preset.embedding.managed && supportedPreset(preset)) ||
|
|
65
115
|
(args.embedder?.model === preset.embedding.model &&
|
|
66
116
|
args.embedder.dimensions === preset.embedding.dimensions), "Embedding provider must match the pinned preset.");
|
|
67
117
|
const commit = args.resolvedCommit ?? (await resolveCommit(args.identity.path, args.ref));
|
|
@@ -192,26 +242,36 @@ export async function materialize(args) {
|
|
|
192
242
|
const eligible = !["imports", "structural"].includes(span.chunkKind);
|
|
193
243
|
if (preset.embedding && eligible) {
|
|
194
244
|
const inputHash = hash([preset.embedding, span.searchText]);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
245
|
+
if (preset.embedding.managed) {
|
|
246
|
+
doc.embeddingText = span.searchText;
|
|
247
|
+
doc.embeddingInputHash = inputHash;
|
|
248
|
+
doc.embeddingStatus = "managed";
|
|
249
|
+
delete doc.embeddingSkipReason;
|
|
250
|
+
item.managed = (item.managed ?? 0) + 1;
|
|
251
|
+
item.managedTokens = (item.managedTokens ?? 0) + span.tokenCount;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const cachePath = args.cache
|
|
255
|
+
? join(args.cache, inputHash + ".json")
|
|
256
|
+
: undefined;
|
|
257
|
+
let vector = cachePath
|
|
258
|
+
? await optionalJson(cachePath)
|
|
259
|
+
: undefined;
|
|
260
|
+
if (!vector) {
|
|
261
|
+
vector = await args.embedder.embed(span.searchText);
|
|
262
|
+
invariant(vector.length === preset.embedding.dimensions &&
|
|
263
|
+
vector.every(Number.isFinite), "Invalid embedding output.");
|
|
264
|
+
if (cachePath)
|
|
265
|
+
await atomic(cachePath, vector);
|
|
266
|
+
}
|
|
203
267
|
invariant(vector.length === preset.embedding.dimensions &&
|
|
204
|
-
vector.every(Number.isFinite), "Invalid
|
|
205
|
-
|
|
206
|
-
|
|
268
|
+
vector.every(Number.isFinite), "Invalid cached vector.");
|
|
269
|
+
doc.embedding = vector;
|
|
270
|
+
doc.embeddingInputHash = inputHash;
|
|
271
|
+
doc.embeddingStatus = "embedded";
|
|
272
|
+
delete doc.embeddingSkipReason;
|
|
273
|
+
item.embedded++;
|
|
207
274
|
}
|
|
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
275
|
}
|
|
216
276
|
else {
|
|
217
277
|
doc.embeddingSkipReason = preset.embedding
|
|
@@ -289,6 +349,12 @@ export async function materialize(args) {
|
|
|
289
349
|
excluded: items.filter((e) => e.status === "excluded").length,
|
|
290
350
|
chunks: items.reduce((n, e) => n + (e.chunkIds?.length ?? 0), 0),
|
|
291
351
|
tokens: items.reduce((n, e) => n + (e.tokens ?? 0), 0),
|
|
352
|
+
...(preset.embedding?.managed
|
|
353
|
+
? {
|
|
354
|
+
managed: items.reduce((n, e) => n + (e.managed ?? 0), 0),
|
|
355
|
+
managedTokens: items.reduce((n, e) => n + (e.managedTokens ?? 0), 0),
|
|
356
|
+
}
|
|
357
|
+
: {}),
|
|
292
358
|
embedded: items.reduce((n, e) => n + (e.embedded ?? 0), 0),
|
|
293
359
|
skipped: items.reduce((n, e) => n + (e.skipped ?? 0), 0),
|
|
294
360
|
},
|
|
@@ -322,6 +388,11 @@ export async function validateBuild(b) {
|
|
|
322
388
|
invariant(!seen.has(doc.id) && b.recordHashes[doc.id] === hash(doc), "Corrupted or unexpected artifact record.");
|
|
323
389
|
seen.add(doc.id);
|
|
324
390
|
invariant(doc.configHash === b.configHash && doc.schemaVersion === 1, "Mixed configuration.");
|
|
391
|
+
if (b.preset.embedding?.managed) {
|
|
392
|
+
invariant(doc.embedding === undefined, "Managed artifacts must not contain vectors.");
|
|
393
|
+
if (doc.kind !== "chunk")
|
|
394
|
+
invariant(doc.embeddingText === undefined, "Only eligible chunks can contain embedding input.");
|
|
395
|
+
}
|
|
325
396
|
if (doc.kind === "file") {
|
|
326
397
|
finish();
|
|
327
398
|
activeFile = doc;
|
|
@@ -362,7 +433,23 @@ export async function validateBuild(b) {
|
|
|
362
433
|
doc.searchText.endsWith(slice.toString("utf8")) &&
|
|
363
434
|
doc.tokenCount === tokens(doc.searchText) &&
|
|
364
435
|
Number(doc.tokenCount) <= CHUNKER.maxTokens, "Invalid chunk source/position/token binding.");
|
|
365
|
-
if (
|
|
436
|
+
if (b.preset.embedding?.managed) {
|
|
437
|
+
const eligible = !["imports", "structural"].includes(String(doc.chunkKind));
|
|
438
|
+
invariant(eligible
|
|
439
|
+
? doc.embeddingStatus === "managed" &&
|
|
440
|
+
doc.embeddingText === doc.searchText &&
|
|
441
|
+
doc.embeddingInputHash ===
|
|
442
|
+
hash([b.preset.embedding, doc.searchText]) &&
|
|
443
|
+
doc.embeddingSkipReason === undefined
|
|
444
|
+
: doc.embeddingStatus === "skipped" &&
|
|
445
|
+
doc.embeddingText === undefined &&
|
|
446
|
+
doc.embeddingInputHash === undefined &&
|
|
447
|
+
doc.embeddingSkipReason ===
|
|
448
|
+
(doc.chunkKind === "imports"
|
|
449
|
+
? "imports-only"
|
|
450
|
+
: "structural-only"), "Invalid managed embedding input or eligibility.");
|
|
451
|
+
}
|
|
452
|
+
else if (doc.embeddingStatus === "embedded")
|
|
366
453
|
invariant(b.preset.embedding &&
|
|
367
454
|
Array.isArray(doc.embedding) &&
|
|
368
455
|
doc.embedding.length === b.preset.embedding.dimensions &&
|