docs(readme): make the published README TypeScript examples compile - #18968
Merged
os-try-charles merged 2 commits intoSep 18, 2026
Merged
Conversation
Splits PR #18751's census by publication (a block is published when its document is inside its package's `files[]`) and corrects the published half: 43 of 44 syntactically-valid-and-wrong blocks across 20 package READMEs. Internal documents (ADVANCED_FEATURES.md, PHASE2_IMPLEMENTATION.md, V3_MIGRATION_GUIDE.md, ARCHITECTURE.md, …) are untouched, and no gate, ratchet or CI wiring is added — #18715 ruling F. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude <noreply@anthropic.com>
…blished-readme-examples-compile
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 154 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Sep 18, 2026
os-try-charles
marked this pull request as ready for review
September 18, 2026 09:17
os-try-charles
deleted the
claude/issue-18915-published-readme-examples-compile
branch
September 18, 2026 09:41
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 28, 2026
…ared key (objectstack-ai#19251) Fixes objectstack-ai#18973 Clause-②: no Ruled on-card: batch objectstack-ai#160 item 2, **letter A**, maintainer 「同意」 2026-09-18 (`issuecomment-5729652660`) — "the spec wins: `@objectstack/knowledge-ragflow` reads `source.adapterConfig.datasetId`, its README says the same, and `KnowledgeSourceSchema` is untouched". `packages/spec` is untouched here, as ruled. ## What was wrong `extractRagflowOptions` cast the source to a shape it does not have and read `options.datasetId`: ```ts // RECORD_OF_UNKNOWN stands for the record-of-string-to-unknown generic; the // angle-bracket spelling is avoided because this platform rewrites such // fragments in a body, inside a fence as readily as outside one. const opts = ((source as unknown as { options?: RECORD_OF_UNKNOWN }).options ?? {}) as RECORD_OF_UNKNOWN; ``` `KnowledgeSourceSchema` declares `adapterConfig` for adapter-specific configuration and is a plain `z.object` with no `.passthrough()`, so any path that parses a source drops `options` before an adapter sees it. The adapter worked only because nothing parses a source today. The published README documented the undeclared spelling, which made it the one block of objectstack-ai#18915's 44 that could not be repaired: correcting the word alone would have compiled and stopped working. ## What changed - **`src/index.ts`** — the cast is gone. `extractRagflowOptions` reads `source.adapterConfig`, a declared, already-typed property, so no cast is needed at all. There is no fallback that also reads `options` (Prime Directive objectstack-ai#12 — no lenient consumer). The refusal now names the declared key: `RAGFlow adapter requires source.adapterConfig.datasetId on source 'SOURCE_ID'` (the source's own id interpolated), so a host on the old spelling is told what to write instead of retrieving nothing. - **`README.md`** — the example and the "Source binding" sentence move to `adapterConfig`, and the block now compiles against the package (evidence below). - **`src/__tests__/ragflow-adapter.test.ts`** — the three `KnowledgeSource` literals move with it; the refusal test now pins the message text (the ruling relies on it naming the key, so the wording is contractual here), and a new test pins that a source carrying only the legacy `options` spelling is refused and reaches no transport at all. - **`test-typecheck-debt.json`** — emptied. See below; this was not an incidental repair. - **Changeset** — `@objectstack/knowledge-ragflow` patch, behaviour: reads the declared key, with the `FROM` → `TO` mapping in the body. ## Four call sites, one repair — and a correction to the dispatch order The card body names one call site (`:108`). There are four sites on `origin/main` — the definition at `:60` and calls at `:108`, `:136`, `:144` — and the dispatch order carried that as "a repair that fixes one call site is not the repair". Measured on this branch's base: **no call site needed editing.** All three calls pass the whole `source` and destructure the result; the undeclared key was read in exactly one place, the definition's cast. The four-site count is correct and the inference drawn from it is not: the repair is one function body, and it covers all three callers. `grep -n "options" src/index.ts` after the change returns one line — `pass options.fetch` in the adapter constructor's own error, which is about `KnowledgeRagflowAdapterOptions` and not about a `KnowledgeSource`. ## The type-debt ledger was this same defect, frozen `packages/plugins/knowledge-ragflow/test-typecheck-debt.json` pinned 3 errors in one file, all `TS2353: Object literal may only specify known properties, and 'options' does not exist in type '…'`. Those three errors *were* this card: the test wrote the undeclared key because the adapter read it. With the adapter on `adapterConfig` the file graduates to zero, and the ledger is shrink-only — a graduated file is red until its entry is deleted. `check:test-typecheck` said so in as many words, and the entry is now gone. The file is kept with an empty `entries` map (it is the per-file ledger the gate reads for this package), and its authored `_note` records the graduation; the note survives regeneration, verified by running the generator twice. ## Two shipped surfaces already said `adapterConfig` The adapter was the outlier, not the schema. Both of these ship today: - `packages/services/service-settings/src/manifests/knowledge.manifest.ts:97` — "Per-source values on **KnowledgeSource.adapterConfig** take precedence", in all four translated locales. - `skills/objectstack-ai/SKILL.md` — "they belong to the adapter (`adapterConfig`) or application code", above an example that calls `KnowledgeSourceSchema.parse({ … adapter: 'ragflow' … })`. Neither is touched by this PR; they are cited because they make the ruled direction the one that leaves the repo self-consistent. ## Evidence **The README block compiles, and the measurement can fail.** `measure-markdown-ts-blocks` is a census, not a gate, so a bare green from it is worth little — it was run in both directions, at `6f17a4f13`: | run | result | |---|---| | README as landed here | 1 file / 1 TS block, **RAW fail 0, TOLERANT fail 0, WELL-FORMED AND WRONG 0** | | README ablated back to `options` (via `scripts/ablation-replace.mjs`, anchor hit 1→0, blob `c37eadb4744e` → `55123d3e9226`) | **TOLERANT fail 1 (100%), WELL-FORMED AND WRONG 1, `TS2353 x1`** | The instrument's own `FIRING_CONTROL` reported 5 diagnostics on both runs, so the zero is a reading and not a dead search. Restore was proven byte-identical by `git hash-object` (`c37eadb4744e…` before and after) with `git diff HEAD` clean, not by the wrapper's exit code. **Gates.** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived **60 families** from this change set; all 60 were run and all recorded **exit 0**, reconciled with `--ran`: `60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN` — a derived zero, since every family recorded its code. Two answered **exit 3 (PREREQUISITE NOT MET — not a pass)** on the first pass, `check:dual-build-cjs-loads` and `check:type-check-debt`; both state the same prerequisite, a built workspace. It was cleared (`turbo run build --filter='./packages/*' --filter='./packages/*/*'`, 72/72 successful) and both re-run green. **Per-package.** `pnpm --filter @objectstack/knowledge-ragflow test` → 10 passed (was 9). `pnpm --filter @objectstack/knowledge-ragflow typecheck` → exit 0 across both of its legs (`tsc --noEmit`, then `check:test-typecheck`, which reports 0 files / 0 errors / 0 pinned signatures). `pnpm lint` over the whole repo → exit 0. ## Acceptance notes Noted here, not fixed, not filed by this PR: - **`content/docs/ai/knowledge-rag.mdx:37` still writes `options: { datasetId: 'rgf_doc_dataset' }`** on a `ragflow` source. It is the same defect in a second, hand-written document, and after this PR it is a live one: an author copying it now gets a source the adapter refuses by name. It is outside this card's file surface (`packages/plugins/knowledge-ragflow/`), and `content/docs/**` brings its own gate family, so it is reported to the dispatching seat to file rather than ridden in here. - **`.changeset/18915-published-readme-examples-compile.md` closes with "One block is deliberately left"**, describing this README. That changeset belongs to PR objectstack-ai#18968 and covers 20 other packages; its text becomes stale when both land. Left alone deliberately — it accurately records what that PR did — and the closure is stated in this PR's own changeset instead. - **This adapter throws bare `Error`s, with no ADR-0112 envelope** (`code` / `status`) on any refusal, so the new refusal test pins the message text rather than an envelope. Introducing an envelope on this seam is a contract decision well outside a patch to one adapter's key spelling. - Triage's escalation condition on the card (a real path parsing `KnowledgeSourceSchema` ⇒ p1) is **still unmet in runtime code**: `git grep KnowledgeSourceSchema` outside `packages/spec` returns zero importers in `packages/**/*.ts` (positive control: 8,741 `*Schema.parse|safeParse` call sites repo-wide). The only parse call sites are `packages/spec`'s own tests and a published-skill example. --- _Generated by [Claude Code](https://claude.ai/code/session_01AhQASwqJr2Z7XfGWUdvnbF)_ --------- Co-authored-by: claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #18915
Clause-②: no
Executes maintainer decision batch #156 item 2 — ruling F on #18715. No gate, no ratchet, no CI wiring is added: this is the user-facing half of PR #18751's census, corrected.
Act 1 — the split, re-derived
PR #18751's instrument re-run on a fresh
origin/main(node scripts/measure-markdown-ts-blocks.mjs --json, workspace built first, all three controls behaving: GREEN clean, FIRING reports TS2341, UNPUBLISHED_SUBPATH reports a counted TS2307 on each of its three specifiers).The card's numbers reproduce exactly:
Split by publication — a block is published when its document is inside its package's
files[]:The split is unambiguous in this repo: every non-private package's
files[]is["dist","README.md","CHANGELOG.md"](only@objectstack/speclists more), so the published package-root Markdown is exactlyREADME.md, and every other package-root document —ADVANCED_FEATURES.md,PHASE2_IMPLEMENTATION.md,V3_MIGRATION_GUIDE.md,ARCHITECTURE.md,DEVELOPMENT_PLAN.md,PLUGIN_STANDARDS.md,REST_API_PLUGIN.md,ZOD_SCHEMA_AUDIT_REPORT.md,STACKBLITZ.md,CLIENT_SPEC_COMPLIANCE.md,ROADMAP.md, plus the privatepackages/qa/*READMEs — is internal.Confirmed against the packer rather than asserted from
package.json, with a control from the same population that must read the other way:No count in the split is zero, so no zero needed pairing.
packages/spec/liveness/README.md(PR #18938's surface) measures as published —livenessis afiles[]entry, andnpm packputsliveness/README.mdandliveness/state-counts.mdin the tarball. It is nonetheless not in this card's population: the census population is Markdown at a package root (a directory carrying apackage.json),packages/spec/livenessis not one, and the file therefore contributes none of the 76/58. No overlap with #18938, and nothing of theirs is touched here.Act 2 — the published corrections
43 of the 44 published syntactically-valid-and-wrong blocks now compile; 20 READMEs changed. Re-measured on the same instrument:
The 11 remaining published tolerant failures are 10 syntax-only blocks — bare type-signature fragments in
service-automation,trigger-record-change,trigger-scheduleandservice-job, theneeds-explicit-partial-tagclass the instrument's own forward convention describes — plus the one block below. Syntax-only blocks are outside act 2's mandate, which is the syntactically-valid-and-wrong set.What was wrong, by class:
@objectstack/client-react's hooks takefields/orderBy/limit/where; the README still wroteselect/sort/top/filters, and readPaginatedResult.valuewhere the member isrecords. AlsotimeouttotimeoutMs(service-job),attemptstomaxAttempts(service-queue),filtertowhere(IDataEngine.find).ObjectKernel.use()is async and resolves to the kernel, sokernel.use(a).use(b)does not type-check at all; andObjectKernelConfighas nopluginsmember.implements Pluginwith no import — which silently bound to the DOM'sPlugin— and three omitted the requiredinit.PluginContext.getServiceis declared with a type parameter that has no default, so every example that read a service back left itunknown.@objectstack/driver-memory's default export is a legacyonEnableobject thatkernel.use()refuses on both the type and the boot path — the quick start now registers throughDriverPlugin, and the "Key Exports" row that called it a drop-in plugin is corrected with it. Its persistence adapters take an options bag and hang underpersistence.adapter.defineStackhas nodriverkey.@objectstack/rest'sRestServertakes the hostIHttpServeras its first argument andregisterRoutes()takes none;RouteManageris constructed on a server.ObjectSchema.parse()returns the value — the{ success, data }envelope belongs tosafeParse.useMutationhas noonMutateand no mutation context, so the "Optimistic Updates" example was rebuilt on the options it does have.--strictin React and handler examples, annotated.Two of the 44 (
packages/cli,packages/mcp) were measurement artefacts worth stating plainly:objects: Object.values(objects)over an elided./src/objectsbarrel. The forgiven TS2307 leaves the namespaceany, andObject.valuesthen infers its type parameter from the union-shaped contextual type, producing a mismatch a reader's own resolvable barrel would not produce. Both now name the objects they import, which is typed and clearer either way.Beyond the counted blocks, the same defect class was corrected in three further
client-reactblocks (Master-Detail, Search with Debounce, and the Type Safety comment) that the census does not flag only because they import nothing and so type-check asany. Leavingdata.valueandselect:standing one section below a corrected copy of themselves was not defensible; this is called out because it is work outside the measured set.The one block deliberately left, and why
packages/plugins/knowledge-ragflow/README.mdwritessource.options.datasetId. That is what the shipped adapter reads (extractRagflowOptionscasts the source to a shape carrying an optionaloptionsrecord, and its error text namessource.options.datasetId), and it is not whatKnowledgeSourceSchemadeclares — the declared key isadapterConfig, and the schema is a plainz.object, so a parse would stripoptionsoutright.Correcting the document to
adapterConfigwould make it compile and stop working. Correcting the adapter is a runtime change, out of this card's scope, and picks a winner between two live spellings. Contract-first says the defect is upstream, so the block is left as it stands and the conflict is reported for the maintainer instead of being papered over in a docs PR.That is why this PR says
Part of #18915and notFixes.Changeset — measured for this diff, not inherited
The house
skip-changesetargument for docs cards is "no package'sfiles[]reachescontent/docs/**". It inverts here.README.mdis listed infiles[]for every one of the 20 packages touched, so the bytes this PR changes are inside the published tarball — measured above withnpm pack --dry-runand a same-population control that reads the other way.AGENTS.md:
skip-changeset"is for a diff that publishes nothing from any released package". This diff publishes changed bytes from twenty released packages, and those bytes are what an upgrading agent reads. So this PR carries apatchchangeset naming all twenty, and ⛔ noskip-changesetlabel.Scope
packages/*/README.mdonly, plus the changeset. ⛔ No internal document, ⛔ noCHANGELOG.md, ⛔ nocontent/docs/**, ⛔ no runtime code, ⛔ no gate or CI wiring.packages/**/README.md); it is the companion artefact the measurement above obliges, and it is named here rather than slipped in.Acceptance notes
packages/client/README.mddocumentsdata.find()'s legacy vocabulary (select/filters/sort/top). Unlike theclient-reactcase this compiles —QueryOptionsstill accepts it — so it is out of this card's set, butfinditself carries@deprecatedanddata.query()is the canonical call. Noted, not filed.check:undeclared-dep-importsfamily is not affected: nopackage.jsonmoved.Verification
Tree:
a8b75f978(origin/mainmerged in, workspace rebuilt,pnpm install --frozen-lockfileafter the lockfile moved). Every number below is from that tree.The census, final run.
node scripts/measure-markdown-ts-blocks.mjs --json, exit 0, all three controls behaving (green=clean firing=fires unpublished-subpath=fires):284 rather than 282 because the
observabilitywiring block, which redeclaredmetricsfour times in one fence, is now three fences — one per deployment, which is how a reader picks between them.Gate families, derived in-worktree from this tree with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no stale-tree warning after the merge): 63 derived, 63 run, all exit 0, reconciled back through--ranwith each command's exit code captured before any pipe — "63 derived familt(ies) accounted for — 63 run, 0 NOT-MEASURED (a DERIVED zero — all 63 recorded an exit code and none of them is 3)".check:pm-dispatch-gatesis not among the derived families for this change set. That reconciliation answers one link only; it is not a complete account of CI.Tests. The diff changes no TypeScript, so no package's
tscprogram or vitest source set moves. Two suites do read a README this PR edits, found by grepping every test file inpackages/forREADME.md(19 hits, triaged by the path each one reads), and both were run:The first one parses
packages/client/README.mdwith the TypeScript parser and validates the manifest it finds against the install contract — it is the pin that a README edit in that package could break.Lint. Zero files in this diff are in eslint's population, measured from eslint's own config rather than assumed, with a control from the same tree that must read the other way:
eslint.config.mjsscopes every block to{ts,tsx,mts,cts,js,jsx,mjs,cjs}, so no configuration in this diff can move an untouched file's verdict either.Control bytes.
grep -naPfor the C0 range over every changed path: no hits; a fixture carrying one byte in that range hits, so the scan is live.pnpm check:nul-bytesis among the 63 green gates.Authored by Claude Code, session
session_017ef78bLdybu3AffehKkhfk.Generated by Claude Code