Skip to content

fix(knowledge-ragflow): read source.adapterConfig.datasetId, the declared key - #19251

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-18973-ragflow-adapter-config
Sep 20, 2026
Merged

huangyiirene merged 2 commits into
mainfrom
claude/issue-18973-ragflow-adapter-config

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #18973

Clause-②: no

Ruled on-card: batch #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:

// 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 #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 Add comprehensive test suite for Zod schema validation #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 FROMTO 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 c37eadb4744e55123d3e9226) 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 docs(readme): make the published README TypeScript examples compile #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 Errors, 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

…ared key

`extractRagflowOptions` cast the source to a shape carrying `options` and
read `options.datasetId`. `KnowledgeSourceSchema` declares `adapterConfig`
and is a plain `z.object`, so any path that parses a source drops
`options` entirely — the adapter worked only because no path parses. The
cast is removed, the adapter reads the declared key, and the refusal
names `adapterConfig.datasetId` so a host on the old spelling is told
what to write. The published README example moves with it.

Ruled: batch #160 item 2, letter A (maintainer 2026-09-18).

Claude-Session: https://claude.ai/code/session_01AhQASwqJr2Z7XfGWUdvnbF
Co-authored-by: Claude <noreply@anthropic.com>
…duated

The three TS2353 errors this package's test-typecheck ledger froze were
one defect: the test wrote `options` on a `KnowledgeSource` literal, a
key `KnowledgeSourceSchema` does not declare, because the adapter read
that spelling. With the adapter on `adapterConfig` the literals moved
with it and tsc reports none, so the entry is deleted — the ledger is
shrink-only and a graduated file is red until its entry goes. Adds the
changeset for the behaviour change.

Claude-Session: https://claude.ai/code/session_01AhQASwqJr2Z7XfGWUdvnbF
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Sep 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 2 changed file(s) yielded no anchor (packages/plugins/knowledge-ragflow/README.md, packages/plugins/knowledge-ragflow/test-typecheck-debt.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/knowledge-ragflow/README.md, packages/plugins/knowledge-ragflow/test-typecheck-debt.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 1b82c519df9db93891514b814b4d8e16e42d5d5cpackageMentionDocs.

Which tree this was computed on

This run read content/docs from a0af214409ba7dd2192eb022aa6a2b7b64c40c14 — the merge of head 6f17a4f13276ab6798b94eb3064d1f06df6987dc into base 1b82c519df9db93891514b814b4d8e16e42d5d5c, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a0af214409ba7dd2192eb022aa6a2b7b64c40c14 && git checkout a0af214409ba7dd2192eb022aa6a2b7b64c40c14
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1b82c519df9db93891514b814b4d8e16e42d5d5c 6f17a4f13276ab6798b94eb3064d1f06df6987dc && git checkout -B drift-repro 1b82c519df9db93891514b814b4d8e16e42d5d5c && git merge --no-ff 6f17a4f13276ab6798b94eb3064d1f06df6987dc

node scripts/docs-audit/affected-docs.mjs --json 1b82c519df9db93891514b814b4d8e16e42d5d5c

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@huangyiirene
huangyiirene marked this pull request as ready for review September 20, 2026 05:25
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 20, 2026
Merged via the queue into main with commit cb005e0 Sep 20, 2026
36 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-18973-ragflow-adapter-config branch September 20, 2026 05:55
os-try-charles pushed a commit that referenced this pull request Sep 20, 2026
…lared options key

`content/docs/ai/knowledge-rag.mdx` configured its `ragflow` knowledge source with
`options: { datasetId }`. `KnowledgeSourceSchema` declares `adapterConfig` and is a plain
`z.object` with no `.passthrough()`, so the key is dropped before any adapter sees it.
Since #19251 (card #18973, ruling batch #160 item 2, letter A) `extractRagflowOptions`
reads `source.adapterConfig` with no fallback, so a source copied from this page is
refused by name at the first upsert / search / delete:

    RAGFlow adapter requires source.adapterConfig.datasetId on source '<id>'

One line, one page. No spec change and no ADR-0087 conversion: `options` was never a key
`KnowledgeSourceSchema` accepted, so nothing an author could declare is removed.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@objectstack/knowledge-ragflow must read source.adapterConfig.datasetId — the declared key (ruled: batch #160 item 2, letter A; README moves with it)

2 participants