fix(rag): make the RAG docs' own examples compile, and load the chatbot config - #1318
Conversation
…ot config QA round three on #1128. Both blocking items are the same shape: guidance that contradicts the code it documents, fixed by class rather than by line number. `chunkText`'s export-list entry was corrected last round; its usage example 439 lines below was not. It passed `maxTokens`/`overlap` — the field-level `ChunkingConfig` names, not `ChunkingOptions` — and fed the returned `TextChunk[]` straight to `embedBatch(string[])`. Corrected here and at `examples/rag-ollama-demo/README.md:452`, the only other survivor. Sweeping every documented call of every RAG export in that file against the real signatures turned up one more of the same class, unreported: the `registerEmbeddingProvider` example read `config.model` and `config.dimensions` off the `EmbeddingProviderConfig` union, whose custom member is an open `{ type: string; [key: string]: unknown }`. So `model` arrives as `unknown` and `dimensions` is not on the union at all — two type errors. The example now narrows both and the prose says why. The identical defect in `docs/content/how-to/rag-advanced.md` is fixed the same way. `examples/rag-openai-chatbot/opensaas.config.ts` could not be loaded at all: it imported `PrismaPg` from `@prisma/adapter-pg`, which is not a dependency of that example, and declared `prismaClientConstructor`, which is not a member of `DatabaseConfig`. #1263 converted the sibling config in the same commit and left this one behind, so story 16's "both RAG examples reseed" was unexecutable. Fixed exactly as c0d443f fixed the sibling — adapter import and constructor removed, `db: { provider: 'postgresql' }` kept — which is also the shape #1315 had already written into this example's README. The package README pointed at `examples/rag-demo`, which does not exist, and credited it with MCP integration and multiple providers, which neither real example has. It now names both real examples and what each actually does. Verified: every code sample extracted verbatim to a scratch project mapped at the built declarations compiles at exit 0, with the harness falsified first — the uncorrected snippet reproduces TS2353 and TS2345 there. The chatbot config loads (`db: postgresql` with ragPlugin's pgvector pack declared), with the ollama sibling as control, and `opensaas generate` now runs it end to end, emitting `pg/vector@1` at `length: 1536`. Gates: lint 0 errors (2 pre-existing warnings), build 11/11 including docs, rag 448 passed / 2 skipped, cli 406 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 136f2b2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Coverage Report for Core Package Coverage (./packages/core)
File CoverageNo changed files found. |
Coverage Report for UI Package Coverage (./packages/ui)
File CoverageNo changed files found. |
Coverage Report for CLI Package Coverage (./packages/cli)
File CoverageNo changed files found. |
Coverage Report for Auth Package Coverage (./packages/auth)
File CoverageNo changed files found. |
Coverage Report for Storage Package Coverage (./packages/storage)
File CoverageNo changed files found. |
Coverage Report for RAG Package Coverage (./packages/rag)
File CoverageNo changed files found. |
Coverage Report for Storage S3 Package Coverage (./packages/storage-s3)
File CoverageNo changed files found. |
Coverage Report for Storage Vercel Package Coverage (./packages/storage-vercel)
File CoverageNo changed files found. |
borisno2
left a comment
There was a problem hiding this comment.
Verdict: REQUEST CHANGES
Posted as a Comment review — the authenticated gh identity (borisno2) is this PR's own author, so GitHub refuses a formal REQUEST_CHANGES event. Treat the verdict above as the review state.
Method: every code sample was extracted and compiled with the repo's own tsc (@typescript/typescript6@6.0.2) against packages/rag/src at this branch's SHA, not against the built dist on main. Both example configs were typechecked against packages/core/src. Sweeps were re-run independently rather than read off the PR body.
Blocking
1. docs/content/how-to/rag-advanced.md:239 and :309 — the swept class survives twice in a file this PR edited
This is the fourth consecutive round in which the reported defect is fixed and a fresh instance of the same class is left behind — and this time it is in the same file, 116 and 186 lines below the line that was fixed.
The PR fixes registerEmbeddingProvider at rag-advanced.md:123 and states the class was swept. It was not. Line 239:
registerEmbeddingProvider('cohere', (config) => new CohereEmbeddingProvider(config))config is the EmbeddingProviderConfig union; CohereEmbeddingProvider's constructor takes CohereConfig ({ type: 'cohere'; apiKey: string; model?: string }). Compiled:
e_cohere.ts(21,77): error TS2345: Argument of type 'EmbeddingProviderConfig'
is not assignable to parameter of type 'CohereConfig'.
Line 309 is identical in shape and fails the same way — HuggingFaceConfig additionally requires dimensions: number, which CustomEmbeddingConfig ({ type: string; [key: string]: unknown }) does not supply:
f_hf.ts(22,87): error TS2345: Argument of type 'EmbeddingProviderConfig'
is not assignable to parameter of type 'HuggingFaceConfig'.
A reader who follows the "Example: Cohere Provider" or "Example: HuggingFace Provider" sections gets precisely the compile failure this PR exists to eliminate. Both need the same narrowing (or a config.type === 'cohere' guard) that was applied at line 123.
The root cause is visible in the PR body: the sweep was scoped to "every documented call of every RAG export in packages/rag/CLAUDE.md", and rag-advanced.md was then patched only at the one line the CLAUDE.md sweep implicated. That is the same line-targeted treatment the PR's own opening paragraph criticises.
Non-blocking (fix here if cheap, otherwise track)
2. .changeset/swift-pandas-listen.md:32 — the "now compiles" snippet does not compile
The abridged registerEmbeddingProvider block omits embedBatch, which is a required member of EmbeddingProvider (embedBatch(texts: string[]): Promise<number[][]> — not optional):
d_changeset_snippet.ts(3,37): error TS2345: ...
Property 'embedBatch' is missing in type
'{ type: string; model: string; dimensions: number; embed(text): Promise<void>; }'
but required in type 'EmbeddingProvider'.
The CLAUDE.md and rag-advanced.md copies both include it; only the changeset's shortened version drops it. This text ships verbatim as the published release note for @opensaas/stack-rag, in a changeset whose subject is "these examples now compile". Same class as the defect being fixed, in the PR's own new prose.
3. packages/rag/CLAUDE.md:430 — the new explanatory sentence is false
config.dimensionsis not on the union at all
It is on two of three members: OllamaEmbeddingConfig.dimensions is a required number (verified — o.dimensions typechecks clean), and CustomEmbeddingConfig's index signature supplies it as well. The access fails because OpenAIEmbeddingConfig lacks it, i.e. it is not on every member — which is also exactly why 'dimensions' in config is the right narrowing. The sentence echoes TypeScript's own diagnostic wording, but as prose it contradicts ollamaEmbeddings({ dimensions }), documented as required in the same file. Suggest: "…config.dimensions is absent from OpenAIEmbeddingConfig, so it is not readable off the union."
4. examples/rag-openai-chatbot/package.json:25,35 — pg / @types/pg are now dead
Removing the pg.Pool / PrismaPg block leaves no consumer of pg anywhere in the example (grepped *.ts/*.tsx — zero imports). The PR body keeps them on parity grounds ("#1263 pruned no dependencies from the sibling"), but rag-ollama-demo declares neither, so retaining them leaves the two examples less aligned, not more. Small, safe, and in scope for the sweep this PR is performing.
Verified — claims that hold
Corrected samples. ChunkingOptions is { chunkSize, chunkOverlap, strategy, separators, tokenLimit } and chunkText returns TextChunk[], so both the CLAUDE.md and ollama-README corrections are right; the chunks.map((chunk) => chunk.text) fix is required because embedBatch takes string[]. Both corrected blocks, plus the corrected registerEmbeddingProvider at CLAUDE.md:407 and rag-advanced.md:123, compile at exit 0.
Sweep counts. Re-grepped independently: chunkText( call sites are rag-advanced ×5 (331/357/381/405/476), reference/rag.md ×3 (667/674/681), packages/rag/README.md ×3 (367/374/381) — all already correct, and all correctly use chunk.text where they feed a provider. No maxTokens/overlap survives in any chunking context. rag-demo: zero live references (the two hits are this PR's own changeset prose and the historical specs/rag-integration.md). sqlite/vss across the RAG surface: zero, bar the legitimate plugin.test.ts assertions that a sqlite datasource is refused. prismaClientConstructor/adapter imports across the RAG surface: zero. All confirmed.
The config fix. DatabaseConfig (packages/core/src/config/types.ts:2530) has no prismaClientConstructor — only provider, idField, extensions, client, schemas, … — and @prisma/adapter-pg was never a declared dependency of this example. The config was genuinely unloadable on both counts. After the fix, both rag-openai-chatbot/opensaas.config.ts and rag-ollama-demo/opensaas.config.ts typecheck clean against packages/core/src, and their db blocks are now byte-identical (db: { provider: 'postgresql' }). Nothing else in the example depends on what was removed: scripts/seed.ts and the db:seed script are intact and reference no adapter.
The prisma.config.ts judgment call — the reasoning holds, and is understated. It is not two examples: all twelve database-backed examples carry a byte-identical stale file (md5 0ab258d0553ccf5cd46d4b7a90e9c6a0 across auth-demo, blog, composable-dashboard, custom-field, file-upload-demo, json-demo, mcp-demo, both RAG examples, starter, starter-auth, tiptap-demo). The current generator emits a completely different shape — definePrismaConfig + defineConfig from @prisma/orm-postgres/config, contract module, output, extension packs, findDatabaseUrl() — so regenerating one would have made a single example the odd one out among twelve, over a pre-existing repo-wide condition unrelated to #1263's leftover. Reverting was the right call. It is a real defect (prisma CLI commands are broken for every example), so it should be a tracked follow-up covering all twelve at once, not left implicit in a PR body.
Description claims. Verified against the examples: neither config has an mcp block and each declares exactly one provider, so dropping "MCP integration" and "multiple embedding providers" is correct. rag-ollama-demo has test:rag → test.ts, which creates sample documents and needs no API key. rag-openai-chatbot has db:seed → scripts/seed.ts, and source citations are real (app/api/chat/route.ts:57 emits sources; components/ChatInterface.tsx:105-109 renders "Sources used:"). reference/rag.md:711's new "native pgvector column" matches @prisma/orm-extension-pgvector + provider: 'postgresql' (ADR-0045).
Housekeeping. No any and no type casting introduced — the only TypeScript change is a deletion. Changeset scope (@opensaas/stack-rag) is right, and minor matches the immediately preceding round's olive-jars-invent.md for the identical class of change; it is certainly not major. (patch would arguably suit a docs-only correction better, but consistency with the series is worth more than re-litigating that here.)
Out of scope, correctly left alone: the eleven @prisma/adapter-better-sqlite3 examples, and docs/content/how-to/migrate.md + packages/cli/src/mcp/lib/documentation-provider.ts, which also still emit prismaClientConstructor — worth confirming spec 9 (#1129) covers the latter two, since they are docs/MCP output rather than example configs.
Fix finding 1 and this is ready; 2–4 are cheap enough to fold into the same push.
Ran a mechanical sweep instead of a targeted patch: extracted all 97 fenced TypeScript blocks from the six markdown files this branch modifies and compiled each one against the built declarations in a scratch project. 67 compile; the other 30 are deliberate fragments (elisions, partial configs, parallel snippets sharing a binding name), each classified with a reason. Fixed, all of the same class the PR exists to eliminate: - rag-advanced.md Cohere and HuggingFace registrations passed the EmbeddingProviderConfig union straight into constructors typed CohereConfig and HuggingFaceConfig (TS2345 each). Both now narrow with the `in` guard used at line 123 — apiKey is absent from OllamaEmbeddingConfig and dimensions from OpenAIEmbeddingConfig, so neither is readable off the union. - The changeset's abridged snippet omitted embedBatch, a required member of EmbeddingProvider. It ships as the published release note. - Three field-config `chunking:` blocks used chunkSize/chunkOverlap. Those are ChunkingOptions keys, for chunkText(); the field option is ChunkingConfig, whose keys are maxTokens/overlap (TS2353). - reference/rag.md passed `dimensions` to openaiEmbeddings(), which has no such option — the model determines it, as the prose directly below already says. - rag-ollama-demo README called ollamaEmbeddings() without the required dimensions, immediately above a note that dimensions must match. Also corrects the CLAUDE.md claim that config.dimensions "is not on the union at all": it is required on OllamaEmbeddingConfig and supplied by the custom member's index signature, but absent from OpenAIEmbeddingConfig, which is why the `in` guard is right. Drops pg and @types/pg from rag-openai-chatbot, which have had no consumer since the PrismaPg block was removed; the sibling example declares neither. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Compile table — all 97 TypeScript blocks in every file this branch modifiesRan the procedure rather than a targeted patch. Harness falsification (done first)
The third control is the load-bearing one: had A second pass adds an ambient prelude that binds real exports via Result97 blocks: 67 compile, 30 are deliberate fragments (each classified below), 0 unexplained. Before this push, 7 blocks failed for real type errors. All 7 are fixed; the table below is the after state. What was fixed
Three of these were not in the review. Also folded in: the Checks
Full table
|
| Block @ line | Compiles | If no, why |
|---|---|---|
| 13 | yes | — |
| 32 | yes | — |
docs/content/how-to/rag-advanced.md — 32 blocks, 17 compile, 15 deliberate fragments
| Block @ line | Compiles | If no, why |
|---|---|---|
| 21 | yes | — |
| 63 | no | fragment: callback params take their types from a surrounding config the snippet omits |
| 102 | no | fragment: depicts the package's INTERNAL registry; Factory/*EmbeddingProvider are not user-importable symbols |
| 120 | yes | — |
| 151 | yes | — |
| 180 | yes | — |
| 196 | yes | — |
| 258 | no | fragment: partial config; the prose elides lists with "// ... lists" |
| 279 | yes | — |
| 352 | yes | — |
| 380 | yes | — |
| 404 | yes | — |
| 428 | yes | — |
| 452 | yes | — |
| 477 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 490 | yes | — |
| 549 | yes | — |
| 581 | yes | — |
| 608 | yes | — |
| 637 | no | fragment: several alternative snippets share one fence and reuse the same binding name |
| 666 | yes | — |
| 702 | no | fragment: callback params take their types from a surrounding config the snippet omits |
| 775 | no | fragment: item is keyed to a generated TypeInfo that docs blocks do not carry |
| 825 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 855 | no | fragment: item is keyed to a generated TypeInfo that docs blocks do not carry |
| 905 | no | fragment: illustrative retry loop; assignment happens in an elided path |
| 938 | no | fragment: catch (error) is unknown under strict; narrowing is elided |
| 976 | yes | — |
| 1025 | no | fragment: illustrative lookup table indexed by a free string |
| 1055 | no | fragment: callback params take their types from a surrounding config the snippet omits |
| 1091 | no | fragment: test-file example; vitest not resolvable from the scratch project |
| 1127 | no | fragment: test-file example; vitest not resolvable from the scratch project |
docs/content/reference/rag.md — 22 blocks, 17 compile, 5 deliberate fragments
| Block @ line | Compiles | If no, why |
|---|---|---|
| 35 | yes | — |
| 84 | yes | — |
| 101 | yes | — |
| 133 | yes | — |
| 153 | yes | — |
| 166 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 195 | yes | — |
| 213 | yes | — |
| 262 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 295 | yes | — |
| 364 | yes | — |
| 390 | no | fragment: openaiEmbeddings({/* ... */}) is a deliberate elision |
| 416 | yes | — |
| 432 | yes | — |
| 494 | yes | — |
| 548 | yes | — |
| 576 | yes | — |
| 599 | no | fragment: callback params take their types from a surrounding config the snippet omits |
| 627 | yes | — |
| 646 | yes | — |
| 662 | no | fragment: several alternative snippets share one fence and reuse the same binding name |
| 689 | yes | — |
examples/rag-ollama-demo/README.md — 9 blocks, 6 compile, 3 deliberate fragments
| Block @ line | Compiles | If no, why |
|---|---|---|
| 150 | no | fragment: partial config; the prose elides lists with "// ... lists" |
| 175 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 196 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 225 | yes | — |
| 369 | yes | — |
| 414 | yes | — |
| 438 | yes | — |
| 446 | yes | — |
| 451 | yes | — |
packages/rag/CLAUDE.md — 17 blocks, 13 compile, 4 deliberate fragments
| Block @ line | Compiles | If no, why |
|---|---|---|
| 69 | yes | — |
| 105 | yes | — |
| 142 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 176 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 186 | yes | — |
| 220 | no | fragment: callback params take their types from a surrounding config the snippet omits |
| 257 | yes | — |
| 312 | yes | — |
| 371 | yes | — |
| 388 | yes | — |
| 403 | yes | — |
| 462 | yes | — |
| 479 | yes | — |
| 507 | yes | — |
| 522 | yes | — |
| 537 | yes | — |
| 551 | no | fragment: test-file example; vitest not resolvable from the scratch project |
packages/rag/README.md — 15 blocks, 12 compile, 3 deliberate fragments
| Block @ line | Compiles | If no, why |
|---|---|---|
| 33 | yes | — |
| 91 | yes | — |
| 108 | yes | — |
| 134 | no | fragment: partial config; the prose elides lists with "// ... lists" |
| 170 | yes | — |
| 208 | yes | — |
| 228 | yes | — |
| 239 | yes | — |
| 252 | no | fragment: contains a // ... elision, so it is not parseable standalone |
| 279 | yes | — |
| 319 | yes | — |
| 345 | yes | — |
| 363 | no | fragment: several alternative snippets share one fence and reuse the same binding name |
| 392 | yes | — |
| 413 | yes | — |
borisno2
left a comment
There was a problem hiding this comment.
Code review — REQUEST CHANGES
Covers commit bf55b228 only ("fix(rag): compile every TS block in the files this PR touches"). Earlier commits were reviewed in previous rounds and are not re-reviewed except where this commit changes their meaning.
(Posted as a Comment review: GitHub refuses a formal Request-changes event from the PR's own author. The verdict is REQUEST CHANGES.)
Everything below was re-derived independently: 97 blocks re-extracted from the six files, compiled against the built .d.ts under --strict with a non-any prelude, and each reported fix falsified by reverting it.
Blockers
B1 — docs/content/how-to/rag-advanced.md:181 documents embedBatch as optional, in the same commit that fixed the changeset for omitting it
The block at line 180 is certified "compiles" in the table. It reads:
// Generate batch embeddings (optional, but recommended)
embedBatch?(texts: string[]): Promise<number[][]>EmbeddingProvider.embedBatch is required (packages/rag/src/providers/types.ts). This commit's own log says the changeset snippet was fixed precisely because embedBatch is "a required member of EmbeddingProvider" — and leaves a block 190 lines above, in a file it edits in the same commit, positively asserting the opposite, with a comment that spells the error out.
It "compiles" only because it declares its own local interface EmbeddingProvider, so the procedure structurally cannot see the contradiction. Falsified: dropping embedBatch from a factory return reproduces TS2345: Property 'embedBatch' is missing in type ... but required in type 'EmbeddingProvider'.
This is the fifth-round pattern exactly — the reported defect fixed, a fresh instance of the same class left standing in the same file.
B2 — Three "deliberate fragments" fail only on real, fixable defects in the taught code
Declaring the prose placeholders the author's own second pass declares (logToSentry, logMetric, EmbeddingProvider — the last of which is an actual export), the only remaining diagnostic in each is a genuine strict-mode defect:
| Block | Diagnostic | Stated reason | Why the reason is wrong |
|---|---|---|---|
rag-advanced.md:905 |
TS2454: Variable 'lastError' is used before being assigned. |
"assignment happens in an elided path" | The assignment is inside the block's own catch. Nothing is elided; TS cannot prove definite assignment. Fix: let lastError: Error | undefined. |
rag-advanced.md:938 |
TS18046: 'error' is of type 'unknown'. |
"catch (error) is unknown under strict; narrowing is elided" |
The narrowing is not elided — the sample literally writes error.message. That is the error. |
rag-advanced.md:1025 |
TS7053: Element implicitly has an 'any' type ... |
"illustrative lookup table indexed by a free string" |
That restates the defect as an excuse. Fix: const COST_PER_1K_TOKENS: Record<string, number>. |
Each is the class the PR exists to eliminate, waved through by a reason that reframes the error as an artifact of excerpting.
B3 — The judgment call at rag-advanced.md:102 does not hold
The stated reason is that the block "depicts the package's INTERNAL registry; Factory/*EmbeddingProvider are not user-importable symbols". Two of three are:
packages/rag/src/providers/index.ts
export * from './types.js' // EmbeddingProvider
export { OpenAIEmbeddingProvider, createOpenAIProvider, ... }
export { OllamaEmbeddingProvider, createOllamaProvider }
Only Factory is invented — the real registry types the map inline as (config: EmbeddingProviderConfig) => EmbeddingProvider.
More seriously, the block does not merely omit internals, it misstates them:
providerFactories.set('openai', (config) => new OpenAIEmbeddingProvider(config))That is TS2345 for exactly the reason this commit fixed the Cohere and HuggingFace registrations 100 lines below. The real code narrows on config.type first, and createEmbeddingProvider's real signature is generic (<TConfig extends EmbeddingProviderConfig>(config: TConfig & BuiltInConfigFor<TConfig>)), not the flat one shown. So the block teaches the defect the same commit removes twice, twenty lines above the registerEmbeddingProvider example that now narrows correctly. Correct it to the real narrowing shape, or cut it to prose.
Follow-ups
F1 — rag-advanced.md:1091 genuinely compiles. Verified clean under --strict from inside packages/rag, where vitest resolves as it does anywhere in this repo. Classifying it a fragment for "vitest not resolvable from the scratch project" excuses a block for the harness's own gap — the one shape "0 unexplained" must not rest on. (CLAUDE.md:551 and rag-advanced.md:1127 stay fragments on other grounds — ./openai.js, @/.opensaas/context — but the reason cited for them is also not the operative one.)
F2 — Nine blocks carry a reason that is false for the block. "contains a // ... elision, so it is not parseable standalone" is given for rag-advanced 477, 825 · reference/rag.md 166, 262 · rag-ollama-demo/README.md 175, 196 · CLAUDE.md 142, 176 · README.md 252. None contains an elision. Every one is a bare object-literal property (fields: {, ragPlugin({, DocumentChunk: list({). The conclusion is right; the reason is boilerplate applied without reading the block — which is how a real failure ends up wearing a plausible label.
F3 — The table is not reproducible: the prelude is unpublished. Two blocks it certifies show what that hides: packages/rag/README.md:392 and :413 call createEmbeddingProvider without importing it (the sibling at reference/rag.md:688 does import it), and pass only because the prelude declares the name ambiently. Every block whose subject is context.db.* is likewise carried by an ambient context, so the compile result asserts nothing about the surface those blocks exist to teach. (I checked that surface separately — nearest(field, vector, { limit, minScore }) returning { item, score } matches packages/core/src/secured/read.ts and vocabulary.ts, and create({ data }) / findMany match real example code — so nothing is wrong today. The procedure just isn't what establishes it.) Publishing the prelude alongside the table would close this.
F4 — Changeset accuracy. Bump is correct (minor, @opensaas/stack-rag). But it ships as the published release note and still describes only round 3's two fixes. None of this commit's five appear: the Cohere/HuggingFace narrowing, the three chunking field-config keys, the openaiEmbeddings dimensions removal, the Ollama dimensions. Its second paragraph still frames the sweep as "the rest of the file".
F5 — The chunking: renames carry their numbers across a unit change. chunkSize: 1000 (characters, ChunkingOptions) became maxTokens: 1000 (tokens, ChunkingConfig); same for 500. The key is now right and the value now means roughly 4× what it did. reference/rag.md shows both spellings ~240 lines apart with nothing saying why they differ.
F6 — rag-advanced.md:775 and :855 use SearchResult[] without importing it; it is exported from @opensaas/stack-rag.
F7 — rag-advanced.md:124 / CLAUDE.md:408 silently default dimensions to 768, while the Cohere and HuggingFace factories added in this same diff correctly throw. dimensions is a schema fact (ADR-0045) — a wrong default surfaces as a pgvector width mismatch on write, not at pnpm generate.
Verified — what holds
The four new failures are real and the fixes are genuine. Compiled each edited block against the built .d.ts with a non-any prelude: all six pass. Reverting each reproduces the reported diagnostic exactly —
TS2353: 'chunkSize' does not exist in type 'ChunkingConfig'
TS2353: 'dimensions' does not exist in type 'Omit<OpenAIEmbeddingConfig, "type">'
TS2741: Property 'dimensions' is missing ... required in 'Omit<OllamaEmbeddingConfig, "type">'
TS2345: Property 'embedBatch' is missing ... required in type 'EmbeddingProvider'
Cohere and HuggingFace compile clean with only the third-party module stubbed; the in guards are the right narrowing and mxbai-embed-large is 1024. Nothing was made to compile in a way that changes what it teaches.
Claim 2 — half right. The two types are real and distinct: ChunkingOptions (chunkSize/chunkOverlap, runtime/chunking.ts) for chunkText(); ChunkingConfig (maxTokens/overlap, config/types.ts) for field and plugin chunking:. There is a third: BuildTimeConfig legitimately uses chunkSize/chunkOverlap again.
But the earlier sweep did not break correct code. git log -S 'chunkSize: 1000' puts the field-config misuse in reference/rag.md and rag-advanced.md at afa865f6 (#741) — not at any commit on this branch. 2c74bf31's only two chunking edits (CLAUDE.md:507, rag-ollama-demo/README.md:452) are both chunkText call sites and both moved in the correct direction. The accurate statement is that the earlier sweep went one direction only. Please don't let the stronger framing reach the changeset.
Current state is right for all three types at every site, checked exhaustively: chunkText × 9 (all chunkSize/chunkOverlap), generateEmbedding({ chunking }) at README.md:427 — which is ChunkingOptions, correctly left alone — and field chunking: × 3 (all maxTokens/overlap).
Other checks. Block census exact: 97 across six files, per-file counts match to the block. No any, no casts in the new text. pg/@types/pg removal is safe — no import, require or reference anywhere in examples/rag-openai-chatbot; pg arrives as a direct dependency of @prisma/orm-postgres; the sibling declares neither; the lockfile drops only the 8.20.0 pin and the pg-protocol@1.14.0 that hung off it, leaving @types/pg@8.20.4 intact for packages/core and packages/auth.
Verdict: REQUEST CHANGES. B1 and B3 are the ones that matter — both are blocks the procedure certified or excused that teach the exact defect this commit removes elsewhere. B2 is three more of the same, mislabelled. The mechanical sweep is a real improvement over targeted patching and its arithmetic checks out; what it does not yet do is audit its own excuses, and that is where this round's residue is.
The previous round's table certified rag-advanced.md:181 as compiling while it documented `embedBatch` as "(optional, but recommended)". It is required on `EmbeddingProvider` — the same commit fixed the changeset snippet for exactly that omission. It passed because the block declares its own local `interface EmbeddingProvider`, so the compile check structurally could not see it. That was a hole in the procedure, so the procedure is now committed and extended rather than re-run by hand: scripts/check-doc-typescript-blocks.mjs, with its prelude, the file list and the fragment classifications beside it. Three checks per block: - Compile, against this branch's own built declarations under `strict`. - Imports. Prelude bindings are `typeof import(...)` of the real export, so a nested excerpt still gets the shipped signature; each block is then compiled a second time without them, and a block carrying imports of its own that still needs a prelude name is failed for not importing it. That is what surfaced README.md:392 and :413 calling `createEmbeddingProvider` unimported. - Shadowing. A block redeclaring an exported type is checked for structural equivalence with the real one, both directions. 1 of 97 blocks redeclares one, and it was wrong. Six falsification controls, all firing: a reverted `embedBatch?`, an injected type error, a bad `dimensions` (proving the RAG declarations resolve rather than degrading to `any`), a removed import, a fragment reason on a block that compiles, and an unresolvable prelude. The last needed the preludes copied in as `.ts` — `skipLibCheck` was skipping them as `.d.ts`, which is how the previous prelude's `Context` import silently resolved to nothing. Blocks fixed: - rag-advanced.md:181 documented `embedBatch` as optional. Required. - rag-advanced.md:102 invented a `Factory` type and constructed built-in providers off the un-narrowed union — TS2345, the defect corrected twenty lines below it. Replaced with the registry's real signature plus prose on why narrowing is needed and how `createEmbeddingProvider` closes the same gap. - Three blocks classified "deliberate fragment" for defects that were neither deliberate nor elided: `lastError` read before assignment (and cast with `as Error`), `.message` off `unknown`, and a cost table indexed by a free string. - Blocks using `createEmbeddingProvider`, `getContext`, `text`, `SearchResult` or `EmbeddingProvider` without importing them now import them. - Multi-vector search typed `nearest()`'s result as `SearchResult`; that is what `semanticSearch()` and `findSimilar()` return. `nearest()` gives `NearestMatch` from `@opensaas/stack-core`. Every fragment reason is rewritten from the block's actual diagnostic. Nine said the block "contains a `// ...` elision" and none did — all nine are bare object-literal properties. rag-advanced.md:1091 was excused for vitest not resolving; the scratch project now sits under packages/rag, where it does, and the block compiles. The `chunking:` renames carried their numbers across a unit change: `chunkSize: 1000` characters became `maxTokens: 1000` tokens, roughly 4x the text. At the ~4-characters-per-token ratio the same page states, those are now 250/50 and 125, and the guide says which unit a field's `chunking:` is in. Three types spell these options — `ChunkingOptions` and `BuildTimeConfig` in characters, `ChunkingConfig` in tokens — and every site is checked against the right one. Correcting the previous round's claim: the earlier sweep did not break correct code. The field-config misuse dates to afa865f (#741); that sweep's two chunking edits were both `chunkText` call sites moved in the correct direction. It went one direction only. 97 blocks: 70 compile, 27 classified fragments, 1 redeclares an exported type. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Round 4 — the procedure now audits its own excuses, and it is committedEvery point was addressed. The headline finding was right, and it was a defect The check is now a file, not a description
Three checks per block:
A fragment entry excuses a block from compiling standalone. It never excuses a Shadowing: how many, and what the check found1 of 97 blocks redeclares an exported type — That the count is 1 is itself the answer to "how many shadow": the check Falsification — seven controls, all firing
The last two did not fire on the first attempt, and fixing that turned up a BlockersB1 — B2 — three "deliberate fragments" that were real defects. All three fixed,
B3 — Follow-upsF1 — F2 — nine false reasons. All nine rewritten from the block's actual F3 — the prelude. Committed, and split so the convenience it provides cannot F4 — the changeset. Rewritten to name this commit's five fixes and this F5 — the unit change. Corrected at the ~4-characters-per-token ratio the F6 — F7 — the The retracted claimWithdrawn, and the stronger framing reached neither the changeset nor this Checks
Not in this PRAs agreed: the wall-clock concurrency flake (#1320) and the stale Compile table — all 97 blocks97 blocks: 70 compile, 27 classified fragments, 1 redeclares an exported
|
| Block @ line | Compiles | Redeclares | If no, why (the block's actual diagnostic) |
|---|---|---|---|
| 14 | no | — | provider is a bare name; the snippet shows only the chunkText -> embedBatch call pair. |
| 33 | yes | — | — |
docs/content/how-to/rag-advanced.md — 32 blocks, 23 compile, 9 fragments, 1 redeclare an exported type
| Block @ line | Compiles | Redeclares | If no, why (the block's actual diagnostic) |
|---|---|---|---|
| 22 | yes | — | — |
| 64 | no | — | pseudo-code for the hook the plugin injects: sourceField, fieldName, listName, provider and writeUnderSudo are closure bindings the plugin holds, and the hook's parameters are typed by the list it is injected into. |
| 106 | yes | — | — |
| 128 | yes | — | — |
| 159 | yes | — | — |
| 190 | yes | EmbeddingProvider | — |
| 206 | no | — | imports cohere-ai, a third-party SDK this repo does not install. |
| 268 | no | — | imports @/lib/providers/cohere, the file the reader wrote in the block above, and passes a config() whose lists the prose elides. |
| 289 | no | — | imports @huggingface/inference, a third-party SDK this repo does not install. |
| 362 | yes | — | — |
| 390 | yes | — | — |
| 414 | yes | — | — |
| 438 | yes | — | — |
| 462 | yes | — | — |
| 492 | no | — | a bare lists: object-literal property, not a statement. |
| 505 | yes | — | — |
| 564 | yes | — | — |
| 596 | yes | — | — |
| 623 | yes | — | — |
| 652 | no | — | two alternative snippets share one fence and both bind matches. |
| 681 | yes | — | — |
| 717 | yes | — | — |
| 790 | no | — | imports @huggingface/inference, a third-party SDK this repo does not install. |
| 845 | no | — | a bare fields: object-literal property, not a statement. |
| 875 | no | — | context.db.Article rows are keyed to the app's generated TypeInfo; against the un-parameterised StackContext the row is OrmRow, which carries no id. |
| 929 | yes | — | — |
| 964 | yes | — | — |
| 1004 | yes | — | — |
| 1053 | yes | — | — |
| 1085 | yes | — | — |
| 1121 | yes | — | — |
| 1157 | yes | — | — |
docs/content/reference/rag.md — 22 blocks, 18 compile, 4 fragments, 0 redeclare an exported type
| Block @ line | Compiles | Redeclares | If no, why (the block's actual diagnostic) |
|---|---|---|---|
| 36 | yes | — | — |
| 85 | yes | — | — |
| 102 | yes | — | — |
| 135 | yes | — | — |
| 156 | yes | — | — |
| 169 | no | — | a bare fields: object-literal property, not a statement. |
| 199 | yes | — | — |
| 217 | yes | — | — |
| 266 | no | — | a bare fields: object-literal property, not a statement. |
| 299 | yes | — | — |
| 368 | yes | — | — |
| 396 | no | — | openaiEmbeddings({/* ... */}) and ollamaEmbeddings({/* ... */}) are written as elisions, so the required apiKey and dimensions are absent by design. |
| 422 | yes | — | — |
| 438 | yes | — | — |
| 500 | yes | — | — |
| 554 | yes | — | — |
| 582 | yes | — | — |
| 605 | yes | — | — |
| 633 | yes | — | — |
| 652 | yes | — | — |
| 669 | no | — | three alternative snippets share one fence and each binds chunks. |
| 696 | yes | — | — |
examples/rag-ollama-demo/README.md — 9 blocks, 5 compile, 4 fragments, 0 redeclare an exported type
| Block @ line | Compiles | Redeclares | If no, why (the block's actual diagnostic) |
|---|---|---|---|
| 151 | no | — | a config() whose lists the prose elides. |
| 176 | no | — | a bare fields: object-literal property, not a statement. |
| 198 | no | — | a bare fields: object-literal property, not a statement. |
| 228 | yes | — | — |
| 372 | yes | — | — |
| 417 | yes | — | — |
| 441 | no | — | provider, text1, text2 and text3 are bare names the prose supplies. |
| 449 | yes | — | — |
| 454 | yes | — | — |
packages/rag/CLAUDE.md — 17 blocks, 11 compile, 6 fragments, 0 redeclare an exported type
| Block @ line | Compiles | Redeclares | If no, why (the block's actual diagnostic) |
|---|---|---|---|
| 70 | yes | — | — |
| 106 | yes | — | — |
| 143 | no | — | a bare fields: object-literal property, not a statement. |
| 177 | no | — | a bare fields: object-literal property, not a statement. |
| 187 | yes | — | — |
| 221 | yes | — | — |
| 258 | yes | — | — |
| 313 | yes | — | — |
| 372 | yes | — | — |
| 391 | yes | — | — |
| 406 | yes | — | — |
| 465 | yes | — | — |
| 482 | yes | — | — |
| 510 | no | — | provider is a bare name the prose supplies. |
| 525 | no | — | query — the caller's search string — is a bare name the prose supplies. |
| 540 | no | — | id is a bare name the prose supplies, and the row is keyed to the app's generated TypeInfo, so article.contentEmbedding is unknown here. |
| 554 | no | — | imports ./openai.js by relative path from inside the package's own test tree. |
packages/rag/README.md — 15 blocks, 12 compile, 3 fragments, 0 redeclare an exported type
| Block @ line | Compiles | Redeclares | If no, why (the block's actual diagnostic) |
|---|---|---|---|
| 34 | yes | — | — |
| 92 | yes | — | — |
| 109 | yes | — | — |
| 136 | no | — | a config() whose lists the prose elides. |
| 172 | yes | — | — |
| 210 | yes | — | — |
| 231 | yes | — | — |
| 242 | yes | — | — |
| 255 | no | — | a bare fields: object-literal property, not a statement. |
| 283 | yes | — | — |
| 323 | yes | — | — |
| 349 | yes | — | — |
| 368 | no | — | three alternative snippets share one fence and each binds chunks. |
| 397 | yes | — | — |
| 419 | yes | — | — |
Code review —
|
…it claim
The harness that verified these blocks is repo tooling with no relationship to
@opensaas/stack-rag's published surface. It needs its own CI wiring and its own
correctness argument, so it moves to its own pull request and the script, its
fixtures, the package.json entry and the .gitignore line come out of here. This
PR is documentation and examples only.
The changeset stated `ChunkingOptions` is "in characters" unconditionally. It is
not: under `strategy: 'token-aware'`, `chunkText` hands `chunkOverlap` to
`tokenAwareChunk`, which multiplies it by ~4 characters per token — and does the
same to `chunkSize` when no `tokenLimit` is given. The advanced guide's own
`token-aware` block said so inline, 33 lines above the new prose that
contradicted it. Both the changeset and that prose now state the unit per
strategy. Also moved into the changeset: the accurate replacement for the
retracted claim, which the round-3 comment said was already there and was not.
Re-verifying the blocks with the harness's shadowing check repaired — it was
seeing 4 of ~262 exported type names, because barrels re-export with
`export type { X } from './y.js'` and those symbols carry only SymbolFlags.Alias
— turned up two more documented types disagreeing with the shipped ones:
- The "Rate Limiting" recipe had the reader hand-write a `RateLimiter` class.
`@opensaas/stack-rag/runtime` exports one already, under the same name and a
different shape, and the reference lists it as a public export. The recipe is
now the shipped class.
- The reference's `SearchResult` sketch omitted the `= unknown` default the
package declares, making the bare `SearchResult` it uses elsewhere an arity
error the package does not have.
97 blocks: 70 compile, 27 classified fragments, 2 redeclare an exported type and
now agree with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… they redeclare References #1301, which asked for this tool. It does not close it: the check runs over the nine files named in scripts/doc-blocks/files.txt, not all of docs/content/, and the four `@opensaas/stack-core/context` imports that issue reports sit in context-api.md, which is not on the list. scripts/check-doc-typescript-blocks.mjs extracts every fenced TypeScript block from the listed files and compiles each one under `strict` against this branch's own built declarations, with @opensaas/* mapped to packages/*/dist. A block that cannot stand alone carries a reason in scripts/doc-blocks/fragments.json, and the reason is treated as a claim: an entry whose block compiles is STALE, an entry matching no block is ORPHAN. The check landed inside #1318 as supporting evidence for a docs sweep. It is repo tooling with no relationship to any published package, it needs CI wiring, and its central check was defeated, so it belongs here on its own. The shadowing check saw 15 of 236 exported type names — 4 of them on a tree where packages/auth/dist had not been built, which is the run the review reported. `collectExportedTypeNames` filtered `getExportsOfModule` by Interface|TypeAlias|Class, and every barrel in this repo re-exports with `export type { X } from './y.js'`, whose symbols carry only SymbolFlags.Alias. `SearchResult`, `ChunkingConfig`, `NearestMatch` and `StackContext` were among the ~220 missed, so a block could document any of them as the opposite of what ships and be certified as compiling. Exports are now resolved through the alias before the flag test. Two further ways the check reported a number that was not about the docs: - The shadow probe filtered diagnostics to probe.ts, so a redeclaration nested in a function never reached module scope, the probe's two assignability checks compared against an error type, and the block was counted as agreeing with a type it was never compared to. Diagnostics on the appended re-export now fail the probe, and a block that exports the name itself is not given a duplicate one. - fragments.json was checked for staleness one way only. A key orphaned by any edit above its block was ignored, so the file could only grow. The summary line also counted every non-clean block as a classified fragment, which reported 28 classifications against 27 entries whenever one block failed. It now counts entries. A missing packages/*/dist aborts with exit 2 instead of blaming a doc block for an unresolved import — that is the state the review's own run was in. Wired into the test workflow beside check:adr-duplicates and check:prisma-error-codes, with the package build it needs. It is skipped for PRs into main, where the fixtures do not describe the documents. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Split, per the review's recommendation — the harness is now #1324Agreed on all three counts: the script is repo tooling with no relationship to Moved to #1324 ( This PR is now documentation and examples only. Nine files: Every documentation and example fix from the three commits is kept. The changeset is kept and rewritten to describe only what ships — the paragraph naming F2 — the unit rule was false, and is now stated per strategyYou are right, and it is the same class again. Both now say the same thing:
The prose at The "now in the changeset" nitFair catch, and it is put there rather than talked around. The changeset now closes with:
Re-verification after the strip, and two more defects it foundRe-run with a scratch harness, falsified first — six controls, all firing: the Running it with B1 fixed — the map goes from 15 names to 236 on a fully built tree, and to 4 when
Final: 97 blocks — 70 compile, 27 classified fragments, 2 redeclare an exported type and now agree with it. Both changed the block line numbers, and re-keying Checks
Left as follow-upsF1 (a field's 🤖 Generated with Claude Code |
The check's fixtures are keyed `file:line`, and #1318 (de80685) and #1332 (3138401) rewrote the documents it reads. Merging origin/prisma-8 shifted every line in rag-advanced.md and packages/rag/CLAUDE.md, and brought in .changeset/swift-pandas-listen.md, which the check had been skipping because the file did not yet exist on this branch. The orphan arm did its job: 14 of the 27 keys matched no block and were reported, so no shifted key silently excused a different block. The 13 that still matched were re-read against their current diagnostics rather than assumed, and each still describes the block it lands on. Every entry here is re-derived from the block's actual diagnostic. Two classifications crossed over in rag-advanced.md and would have been wrong had the keys been moved mechanically: the block now at 523 is one entry of a `lists:` object, not the `lists:` property its old key described, and the block now at 880 is the `lists:` property, not the `fields:` one. Three more reasons named things that are no longer true — the plugin hook at 66 closes over `writePluginOwnedField` and `embeddingWriter`, not `writeUnderSudo`; the `stored.vector` failure at CLAUDE.md:546 is a `{}` narrowed from `unknown`, not `unknown` itself; and the `NearestMatch` mismatch at 910 is stated as the diagnostic reports it. The block redeclaring `EmbeddingProvider` with an optional `embedBatch` no longer fails: #1318 made it required, so it now agrees with the shipped type and needs no entry. 97 blocks: 70 compile, 27 classified fragments, 2 redeclare an exported type and are checked against it. No failures, none stale, none orphaned. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The fixtures were sound; the checker was not. Each of these let it report a clean run over something it had not checked. B1 — a type-parameter default escaped the shadowing check. The alias branch ended `(?:<[^=]*>)?\s*=`, and `[^=]*` cannot cross the `=` in `type SearchResult<T = unknown>`. #1318 made exactly that edit, which silently removed the block from the check. The parameter list now tolerates a default and one level of nesting. The tally moves 2 -> 3; no fixture was re-keyed. B2 — a generic export was an unfixable failure reported as a comparison. The probe emitted a bare `declare const documented: Documented`, so a block declaring `type Row = { id: string }` against the exported `Row<C, R, K>` got TS2314 twice, compared nothing, could not be excused, and was still counted as "checked against it". The probe now picks a type-argument arity both declarations accept and fills it with fresh opaque parameters; where no such arity exists, or the parameters carry constraints it cannot fill, the name is reported as NOT COMPARED and left out of the tally. A comparable generic is still compared in both directions. A redeclaration that never reaches module scope still fails, and is no longer counted as compared either. B3 — a listed file that did not exist was dropped along with its fixture keys, for exit 0 and no signal. This PR was the proof: a changeset was listed and missing for the branch's whole life. Only `.changeset/*` is now exempt, and that exemption covers its keys too; any other unreadable listed path fails by name with its keys reported as orphans. The orphan arm also stops ignoring keys that name an unlisted file, or carry no `file:line` shape at all. Also: `tsx` fences are reported as unchecked rather than given a misleading `TS1161`; the prelude guard names the block it aborted on; and the `main` gate gains the counterpart the sibling test steps have — it cannot run there, so it now says so in the job log on every PR into main instead of skipping in silence. Flipping it is tracked on #1301, named at the gate. `Known limits` covers all of the above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixes the QA round-three defects on #1128 (N5, N6, N7). Both blocking items are the same shape — guidance that contradicts the code it documents — so each was fixed by class and the whole file swept, not just the line QA named.
N5 (blocking) —
chunkText's documented call did not compilepackages/rag/CLAUDE.md:57had its signature corrected last round; the file's own usage example 439 lines below did not. It passedmaxTokens/overlap, which are the field-levelChunkingConfigmember names, notchunkText'sChunkingOptions, and fed the returnedTextChunk[]straight toembedBatch(string[]).Fixed there and at
examples/rag-ollama-demo/README.md:452. Sweep result: every otherchunkTextcall site in the repo (rag-advanced.md×5,reference/rag.md×3,packages/rag/README.md×3) already used the correct names — those two were the only survivors.The sweep found one more of the same class that QA did not report. Compiling every documented call of every RAG export in
packages/rag/CLAUDE.mdturned up theregisterEmbeddingProviderexample readingconfig.modelandconfig.dimensionsstraight off theEmbeddingProviderConfigunion, whose custom member is an open{ type: string; [key: string]: unknown }:The example now narrows both, and the prose says why. The identical defect in
docs/content/how-to/rag-advanced.md:123is fixed the same way.Every other documented RAG export call —
semanticSearch,findSimilar,generateEmbedding,batchProcess,searchable,embedding,ragPlugin, theStoredEmbeddingliteral — checks out against source; nothing else changed.N6 (blocking) — the chatbot example's config could not load
examples/rag-openai-chatbot/opensaas.config.tsimportedPrismaPgfrom@prisma/adapter-pg, not a dependency of that example, and declaredprismaClientConstructor, not a member ofDatabaseConfig. PR #1263 converted the sibling config in the same commit and left this one behind, so story 16's "both RAG examples reseed" was unexecutable — an incomplete sweep within this PRD, not a spec-9 deferral.Fixed exactly as
c0d443fbfixed the sibling: adapter import and constructor removed,db: { provider: 'postgresql' }kept. That is also the shape #1315 had already written into this example's README, so file and README now agree.How it was confirmed. Reproduced the failure first, with the sibling as control:
After the fix it loads, with
ragPlugindeclaring the pgvector pack exactly as the sibling does:Pushed further than "loads":
opensaas generatenow runs end to end on this example, seeding the pgvector space and emittingpg/vector@1atlength: 1536— the 1536-dimension analogue of the vector(768) #1263 verified for the sibling. The generated artifacts are not committed: both examples share an identically staleprisma.config.ts, so regenerating only this one would have made the siblings diverge over a pre-existing shared condition rather than #1263's leftover.Left alone as genuinely spec 9's (#1129): the other examples importing
@prisma/adapter-better-sqlite3.pg/@types/pgstay declared — #1263 pruned no dependencies from the sibling.N7 (low) — broken path
packages/rag/README.mdpointed atexamples/rag-demo, which does not exist, and credited it with MCP integration and multiple providers — neither of which either real example has (verified: nomcpblock and a single provider in both configs). It now namesexamples/rag-ollama-demoandexamples/rag-openai-chatbotand says what each actually demonstrates.Sweep result: every other path and anchor in the RAG surface resolves (
./CLAUDE.md,#provisioning-pgvector,generation-failure.ts,packages/cli/src/commands/{db,dev}.ts). One further stale claim turned up and is fixed:docs/content/reference/rag.md:711still described the ollama demo as using "SQLite VSS", which #1263 removed from the example itself. Sweeping the whole RAG surface forsqlite/vssleaves only legitimate test assertions that a sqlite datasource is refused, and forprismaClientConstructor/adapter imports, zero survivors.Verification
Every code sample was extracted verbatim to a scratch project outside the repo, with
@opensaas/*mapped to the built type declarations, and compiled. Harness falsified first: the uncorrected snippet reproduces QA's exactTS2353+TS2345there, while the corrected control compiles clean — so the diagnostics belong to the snippets, not the probe. Final formatted text of every changed block recompiles at exit 0.Gates:
pnpm lint0 errors (2 pre-existing warnings) ·pnpm build11/11 including the docs build · rag 448 passed / 2 skipped (20 files) · cli 406 passed (42 files) — all matching QA's recorded baselines.pnpm manypkg fixandpnpm formatclean. Changeset included (minor,@opensaas/stack-rag).Not addressed, correctly out of scope: #1316, #1317, and the tracked set including #1265, #1271, #1272, #1310, #1311.
🤖 Generated with Claude Code