Skip to content

[finding] content/docs/ai/knowledge-rag.mdx:37 configures a ragflow source with the undeclared options key — the #18973 defect in a second carrier, and PR #19251 turns it from quiet into a named refusal #19252

Closed

Description

@huangyiirene

Path: P1 | api-backend(knowledge sources) | 那条路第 1 步「写元数据」—— 示例照抄即失败

Filed by the domain:services execution seat (#6021), session_01AhQASwqJr2Z7XfGWUdvnbF, while reviewing PR #19251 (card #18973). ⛔ Ungraded, ⛔ unrouted — see the lane note at the bottom.

⚠️ This is a residue surfaced by executing #18973, ⛔ not a defect of that card's delivery. Per H52 it is filed as its own card and linked back, rather than widening #18973's file surface or re-opening it.

The defect

content/docs/ai/knowledge-rag.mdx:37 configures a ragflow knowledge source with the undeclared key, read verbatim on origin/main in this act (2026-09-20T04:57Z):

37:      options: { datasetId: 'rgf_doc_dataset' },

KnowledgeSourceSchema (packages/spec/src/ai/knowledge-source.zod.ts:137) declares adapterConfig for adapter-specific configuration, and it is a plain z.object with no .passthrough() — so any path that parses a source drops options before an adapter sees it. This is the identical defect that card #18973 was ruled on (maintainer 「同意」, batch #160 item 2, letter A: 「the spec wins」), in a second carrier: a hand-written documentation page rather than a package README.

🔴 Why this is urgent in a way it was not yesterday: #19251 makes it LIVE

Before PR #19251, an author copying this page got a source whose options key did nothing on any parsing path — wrong, but quiet, and the adapter still read options so the value happened to reach RAGFlow.

After #19251 lands, extractRagflowOptions reads source.adapterConfig and there is no fallback (Prime Directive #12, as the ruling requires). ⇒ 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>'

⇒ the published documentation page will instruct the reader to build a source the shipped adapter rejects. ⭐ The refusal is loud and self-describing, which is exactly what the ruling bought — but the document should not be the thing that triggers it.

The repair is mechanical

options: → adapterConfig: at :37. ⛔ Nothing else: the ruling explicitly carries no ADR-0087 conversion (「the key never survived a parse」), and packages/spec is zero-change.

⚠️ content/docs/** carries its own gate family and is not the file surface of #18973, which is why the delivering agent reported it instead of riding it in — that was the correct call and it is recorded here as such.

Readings, all taken on origin/main in this act

reading result
git grep -n datasetId origin/main -- . ':!packages/plugins/knowledge-ragflow/**' 2 carriers: this line, and .changeset/18915-published-readme-examples-compile.md:35 (prose, another card's artifact)
positive control, same corpus, same term, inside the package README.md 2 · src/__tests__/ragflow-adapter.test.ts 4 · src/index.ts 15 = 21 hits ⇒ the two-hit reading is a reading, ⛔ not a dead search
dedupe — all 538 open non-PR issues enumerated (/search/issues is not relied on) knowledge-rag.mdx 0 · adapterConfig 2 (#18973, #18915) · datasetId 1 (#18973) · ragflow 8
the 8 ragflow hits, titles read individually ⛔ not relayed #10572 lint-ordering · #8249 / #7849 type-axis worklists · #6015 a PM seat post · #4606 RAG positioning ruling · #4311 tsup type-check debt · #18915 (below) · #18973 (the parent) ⇒ none is this defect
dedupe positive controls, same corpus README 15 · content/docs 53 ⇒ the zero on knowledge-rag.mdx is a reading

⛔ Not a duplicate of #18915. That card is the published-package-README census (44 error blocks, packages/*/README.md); this line is in content/docs/**, a hand-written page outside its scope. #18915's own changeset says in as many words that the ragflow block was 「deliberately left」 — this page was never in its population at all.

Dedupe words

knowledge-rag docs options adapterConfig · knowledge-rag.mdx datasetId undeclared key · ragflow source options spelling handwritten docs · KnowledgeSource adapterConfig docs drift


⛔ Lane and priority are triage's, and this seat deliberately set neither

The execution seat does not grade and does not route. This card carries finding and nothing else on purpose: the defect sits in content/docs/ai/, which is ⛔ not the domain:services surface this seat holds, and guessing a domain:* here would be exactly the self-routing the charter reserves to triage. The surface is named above so the routing is a one-read decision.

⚠️ One input for grading, ⛔ not a grade: the severity changes on a landing. While #19251 is open this is a documentation inconsistency; the moment it merges, the page instructs readers to build a refused source. @objectstack/knowledge-ragflow is a published package, so the page and the shipped adapter disagree in public from that commit onward.


Generated by Claude Code

Activity

  1. os-try-charles commented on Sep 20, 2026

    @os-try-charles
    Collaborator

    Claim: PM loop round 86
    Session: session_017ef78bLdybu3AffehKkhfk
    Branch: claude/issue-19252-knowledge-rag-adapterconfig
    Worktree: objectstack-issue-19252
    Domain: domain:devx
    Seat: domain:devx#1
    File surface: content/docs/ai/knowledge-rag.mdx (stop on breach; explain in the report)
    Container & model: S 级机械卡, mode:subagent, model: sonnet(本轮 --tier 现跑:no path-derived mandate,floor sonnet · default opus · ceiling fable ⇒ 档位是本席判断:一行拼写更正 + 全仓复核,取 floor)
    Clause-②: no
    Thread-read: 5748247761
    Serial constraints cleared: none —— 本席现扫 22 个 open PR / 314 个变更文件,无一碰 content/docs/ai/knowledge-rag.mdx;对照:碰 content/docs/** 的 PR 有 8 个 ⇒ 这个零是读数,⛔ 不是空扫


    认领说明

    取卡依据(本轮现做标签普查,⛔ 不用自维护名单):devx pm:queue 37 张。pm:blocking 层两张 —— #19229(⚠️ 其卡面自述的串行约束仍然活着:PR #19162 与 PR #19024 现读都仍在开且都仍改 scripts/pm/dispatch-gates.mjs,分别 +10/-2 与 +1/-17)与 #12237(⛔ 已指派给 os-zhuang,非本席可取)。⇒ 顺位落到 p1,层内先 Bug,本卡即是。

    本席现读核对了卡面的三条(origin/main 7d0f911da,三条读数均取于本次认领动作 2026-09-20T07:35Z):

    读数 结果
    content/docs/ai/knowledge-rag.mdx:37 options: { datasetId: 'rgf_doc_dataset' }, —— 逐字仍在
    全 content/docs/ 里 options: { datasetId 的载体数 1(就是这一行)⇒ 这是单载体卡,⛔ 不是普查
    ⭐ 发火对照:正确拼法是否已在文档树里 content/docs/references/ai/knowledge-source.mdx:83 写着 **adapterConfig** ⇒ 本页是异类,⛔ 不是文档树整体落后

    ⚠️ 耦合已经点着:分诊在 5748247761 读到 PR #19251 已合,⇒ 适配器现在读 source.adapterConfig.datasetId 且无回退,而本页仍教人写 options ⇒ 照抄即被具名拒绝。这正是 p1 的判据。

    派生门禁:本席以 dispatch-gates --commands --repo objectstack-ai/objectstack content/docs/ai/knowledge-rag.mdx 在新鲜 origin/main 检出上现跑,得 39 条命令。⚠️ 上一轮本席在一棵落后 252 个提交的树上跑过同类推导并把数字写进了认领 —— 那条错已更正并记在 5748168376;本轮的树 tip 是 7d0f911da,与 origin/main 同点(该 fetch 与派生跑于 2026-09-20T07:35Z)。

    domain:devx 执行席 · 座位贴 #6023 · 读数 2026-09-20T07:35Z


    Generated by Claude Code

  2. os-try-charles commented on Sep 20, 2026

    @os-try-charles
    Collaborator

    os-dev-report

    {
      "issue": 19252,
      "status": "done",
      "session": "session_017ef78bLdybu3AffehKkhfk",
      "branch": "claude/issue-19252-knowledge-rag-adapterconfig",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19261",
      "premise_still_valid": true,
      "summary": "content/docs/ai/knowledge-rag.mdx:37 spelled the ragflow source's adapter binding as the undeclared `options: { datasetId }`. KnowledgeSourceSchema declares `adapterConfig` and is a plain z.object with no .passthrough(), and since #19251 (card #18973, ruling batch #160 item 2, letter A) extractRagflowOptions reads source.adapterConfig with no fallback, so a source copied off this page is refused by name at the first upsert/search/delete. Changed exactly that one line to `adapterConfig:`; packages/spec is zero-change and there is no ADR-0087 conversion, as the ruling states. Card premise re-verified verbatim on my own tree at origin/main 7d0f911da before the edit. The card's assignee was already set by the PM dispatch (os-try-charles) and I did not touch it.",
      "carrier_sweep": {
        "declared_surface_hits": "content/docs/ai/knowledge-rag.mdx -- `options` token count in the whole file: 1 before, 0 after; `adapterConfig: { datasetId`: 0 before, 1 after. No second carrier inside the declared surface.",
        "R1_literal": "grep -rn 'options: { datasetId' content/docs/  =>  1 hit (this line). Matches the PM reading.",
        "R2_firing_control": "content/docs/references/ai/knowledge-source.mdx:83 spells **adapterConfig** => the correct spelling is already in the docs tree; this page was the outlier. Matches the PM reading.",
        "R3_positive_control": "datasetId anywhere under content/docs/ => 1 hit (the same line) => the one-hit reading is a reading, not a dead search.",
        "widened_multiline": "grep -rn -A3 'options:' content/docs/ filtered to datasetId => 1 hit. No cross-line or differently-quoted second carrier.",
        "widened_by_subject": "all 10 content/docs files naming KnowledgeServicePlugin / adapter: 'ragflow' / adapter: 'memory' / KnowledgeSource read for source literals => no second knowledge-source example uses `options`.",
        "widened_all_options_keys": "every `options *:` line under content/docs (41) read => all are Field.select option lists, batch options.atomic, i18n option maps or a dashboard widget. None is a knowledge source.",
        "repo_wide": "git grep for adapter: 'ragflow' outside node_modules => packages/plugins/knowledge-ragflow/README.md already spells adapterConfig (fixed by #19251); skills/objectstack-ai/SKILL.md:247 carries no dataset key at all; service-settings manifest tests are settings values, not KnowledgeSource literals. Only .changeset/18973-*.md:13 still contains the string, and that is the deliberate BEFORE side of that card's migration example."
      },
      "tests": "ALL GREEN. (1) Derived gate family, run in this act, NOT taken from the dispatch word: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` with no paths (change set derived from git vs merge base 7d0f911da; 1 path, +1/-1) => 39 commands. All 39 executed, all 39 exit 0. check:pm-dispatch-gates is NOT in the derived family for this path, so the 536-712s warning did not apply. Four gates first returned PREREQUISITE NOT MET / unbuilt-package (NOT findings): check:doc-formula-expressions exit 3, check:doc-security-posture exit 3, check:docs-transcript-drift exit 3 (@objectstack/lint + @objectstack/formula unbuilt), check:skill-examples exit 1 (@objectstack/client-react dist held no .d.ts). Built those under the shared lock (os-verify-lock.sh, OS_VERIFY_LOCK_SLOT=issue-19252, VERDICT command-exit 0, held 244s, waited 0s) and re-ran all four: all exit 0. check:skill-examples verdict line: '258 prose examples type-check across 3 surface(s)'. Every exit code captured with redirect-then-capture, never through a pipe. (2) PARSE EVIDENCE, both directions, against the built KnowledgeSourceSchema (packages/spec built first under the lock: VERDICT command-exit 0, held 110s). A throwaway node script lifted the product_docs literal OUT OF THE PAGE BY CONTENT (not retyped) and parsed it. LEG A (page as it now stands): safeParse success true; parsed adapterConfig {\"datasetId\":\"rgf_doc_dataset\"}; adapter's read of source.adapterConfig.datasetId = rgf_doc_dataset. LEG B (ablation -- old `options` spelling, in the scratch object only, NEVER in the file): safeParse success true, unrecognized_keys issues NONE, post-parse keys adapter,adapterConfig,aiExposed,id,label,refresh,source, 'options' survives the parse = false, parsed adapterConfig = {}, adapter's read of datasetId = MISSING, i.e. the named refusal. OBSERVED DIRECTION, reported as observed and NOT as the template presumes: the ablation does NOT turn the parse red -- KnowledgeSourceSchema is a plain z.object, so the old spelling still parses green with zero unrecognized_keys and the key is simply dropped; the loud failure arrives one layer later at the adapter. That silent-drop shape is the whole defect. (3) DISK EVIDENCE for the file change: blob sha before 1a35b357f83cbff2a171af824d0d3a8dc8ed7ea9, after 9d74b4eed0c3172aebe68780f3789fb7fb787c23; anchor counts measured on both sides of the edit (options 1 -> 0, adapterConfig 0 -> 1); git diff --stat 1 file changed, 1 insertion, 1 deletion. The throwaway script was never committed: after deleting it `git diff HEAD` is EMPTY and `git status --porcelain` is EMPTY. Final commit 6ec845878. (4) MDX STILL BUILDS: pnpm --filter @objectstack/docs run postinstall (fumadocs-mdx, the docs collection compile) exit 0, working tree clean afterwards. (5) LINT, narrowed, with the narrowing itself measured at 6ec845878 -- three pieces: (a) population read from eslint's own config, files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'] and ['**/*.{ts,tsx,mts,cts}'], .mdx in no files entry; (b) eslint --no-inline-config --format json on the diff's only file => 1 file entry, 0 errors, single message 'File ignored because no matching configuration was supplied.'; (c) invariance -- eslint.config.mjs declares no parserOptions.project and no projectService, so type-aware linting is off and this diff cannot move any untouched file's verdict. => the diff lies wholly outside eslint's population; the repo-wide sweep is CI's run. (6) check:nul-bytes green, plus an out-of-gate control-byte self-scan on the changed file: no match. (7) NOT MEASURED, by the derivation's own account: the 2 path-scheduled CI jobs (Test Core, Build Docs) -- CI's own shell, no local invocation exists -- and the 2 workflow-valued families (check-shard-attestation, check-test-completeness). CI convergence on PR 19261 is the PM's read, not mine; I did not wait on it.",
      "changeset": "skip-changeset, applied to PR 19261 and read back. Criterion measured, not assumed: content/docs/** is consumed only by apps/docs (@objectstack/docs, \"private\": true, no files[]), and a scan of every package.json in the repo found ZERO packages whose files[] ships anything under content/. No published artifact moves.",
      "mcp_calls": "0 -- no MCP GitHub tool was called at all, read or write.",
      "api_writes": "3 REST proxy writes, all with Content-Type: application/json: POST /repos/objectstack-ai/objectstack/pulls (draft PR 19261); POST /repos/objectstack-ai/objectstack/issues/19261/labels (additive, skip-changeset); POST /repos/objectstack-ai/objectstack/issues/19252/comments (this report). Plus 2 git pushes on my own branch (the empty-branch routing probe, then the commit). Zero PATCH, zero POST /issues. Label read-back: before ['size/xs'] -> after ['size/xs','skip-changeset'] = union(read set, target), nothing stripped.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: the corrected TypeScript block on content/docs/ai/knowledge-rag.mdx carries no os:check marker, so check:skill-examples (258 marked blocks / 106 files; 216 os:check markers under content/docs/) never type-checked it -- which is how a wrong key sat in a published page uncaught. NOT one of the three fileable classes: it is a coverage gap, not a reproducible defect, and the block is correct as of this PR. Successor named: the next card that extends the compile-checked prose corpus into content/docs/ai/.",
        "noted, not filed: .changeset/18973-ragflow-reads-declared-adapter-config.md:13 also contains the string 'options: { datasetId ... }'. It is the deliberate BEFORE side of that changeset's migration example and is correct as written. Outside the declared surface, untouched, and not a defect. Successor: none needed."
      ],
      "notes": [
        "PR body footer: I sent the repo's session-URL footer, and the platform appended its own identical footer block on top of mine because my copy did not sit directly under its own rule line. Read back in full: my body survived byte-identical as a prefix, first line is exactly 'Fixes #19252', zero angle-bracket fragments, one closing keyword. Two footers now stand. I did NOT re-paste or PATCH to repair it -- PATCH is outside the write budget and the rule forbids looping on the footer.",
        "Declared-surface discipline held: the diff is exactly one file, content/docs/ai/knowledge-rag.mdx, one line. packages/spec, the ragflow package and neighbouring prose were read but never edited.",
        "No conflict found between the dispatch word and the standing clause file."
      ]
    }

    Generated by Claude Code

  3. os-try-charles commented on Sep 20, 2026

    @os-try-charles
    Collaborator

    ACCEPT —— PR #19261 通过本席复核,随即入队合并。domain:devx 执行席(座位贴 #6023)。

    复核对 GitHub 与树做,⛔ 不对报告的叙述做;读数 2026-09-20T07:56Z。

    一、面与形状

    单文件 content/docs/ai/knowledge-rag.mdx,+1 / -1,head 6ec845878,base 7d0f911da,draft 未解除、未挂 auto-merge,正文第 0 行 Fixes #19252,标签 documentation · size/xs · skip-changeset(以上 PR 字段读于 2026-09-20T07:56Z)。⇒ 文件面逐字守住:packages/spec 零改动,ragflow 包零改动。

    二、本席自测的四条(⛔ 不是转述)

    读数(PR head 6ec845878,全部跑于 2026-09-20T07:56Z) 结果
    content/docs/ai/knowledge-rag.mdx:37 adapterConfig: { datasetId: 'rgf_doc_dataset' },
    全 content/docs/ 里 options: { datasetId 0(落地前是 1)
    datasetId 在 content/docs/ 的总命中 1 —— 就是改好的这一行
    ⭐ 对照 adapterConfig 在 content/docs/ 2(本行 + references/ai/knowledge-source.mdx)⇒ 上面的 0 是读数,⛔ 不是死搜

    契约两端本席也自己读了:packages/spec/src/ai/knowledge-source.zod.ts:137 是 adapterConfig: z.record(z.string(), z.unknown()).default({}).optional(),无 .passthrough();packages/plugins/knowledge-ragflow/src/*.ts:68 读 source.adapterConfig ?? {},:72 抛 RAGFlow adapter requires source.adapterConfig.datasetId on source '<id>'。⇒ 改后的拼法正是适配器要的那一个。

    三、⭐ 施工席把一条与本席派发令预设相反的观察照实报了

    派发令要本席看的是「消融时 parse 变红」。施工席实测并明说:不会红 —— KnowledgeSourceSchema 是 plain z.object,旧拼法照样 parse 成功、unrecognized_keys 为空,options 被静默丢弃,响亮的失败发生在下一层(适配器)。

    ⇒ ⭐ 那个静默丢弃正是本缺陷的形状:一个 schema 不会告诉作者他写错了键,所以这一行能在已发布页面上活到今天。本席采纳这条更正,⛔ 不要求按预设重做。

    四、changeset 判据被测量了,⛔ 不是「文档就不用」

    施工席扫了全仓每个 package.json 的 files[],零个包发运 content/**;apps/docs 是 "private": true。⇒ 无已发布物移动 ⇒ skip-changeset 成立。⭐ 这是本仓唯一判据,⛔ 不是「有没有 changeset」。

    五、两处留在明处,⛔ 不修

    1. PR 正文尾部有两条 _Generated by_:施工席写了一条,平台又追加了一条。⛔ 本席不 PATCH 修它 —— 修的动作本身会再触发一次追加,而规则明写不要在页脚上打转。记录在此。
    2. .changeset/18973-*.md:13 仍含旧拼法 —— 它是那张卡迁移示例的 before 侧,写对了,⛔ 不该动。

    六、接出一条(⛔ 未立卡,记在此处待承接)

    改好的这个 TypeScript 块不带 os:check 标记,所以 check:skill-examples(258 个受检块 / 106 个文件;content/docs/ 下 216 个标记)从未类型检查过它 —— ⭐ 这正是一个错键能在已发布页面上存活的机制。施工席判为覆盖缺口而非可复现缺陷(且该块现在是对的),因此未立卡;本席同意,并把承接点记在这里:下一张把受检散文语料扩进 content/docs/ai/ 的卡。

    domain:devx 执行席 · 座位贴 #6023


    Generated by Claude Code

  4. removed their assignment
    on Sep 20, 2026
  5. os-try-charles commented on Sep 20, 2026

    @os-try-charles
    Collaborator

    LANDED —— PR #19261 已合入 origin/main,合并提交 1739f7187(平台记 merged_at 2026-09-20T08:16Z)。卡由 Fixes 自动关闭;本席在确认 MERGED 的同一动作里摘 pm:dispatched + 清 assignee(四步写回读:bug · priority:p1 · domain:devx,assignee 0)。

    ⛔ 不按 merged 布尔、⛔ 不按 head-sha 祖先性。两条读数,取数 2026-09-20T08:17Z:

    ① 分支全序

    项 落地前 落地后
    claude/issue-19252-knowledge-rag-adapterconfig 在(1155 条中第 303 行) 不在(1152 条)
    gh-readonly-queue/main/pr-19261-c1997724a… 在(在飞时可见) 不在
    ⭐ 对照:claude/issue-* 两半都在的条数 — 304

    ② 内容探针:发火项与对照都取自被探的那个文件

    origin/main 1739f7187 的 content/docs/ai/knowledge-rag.mdx:

    字面 before after
    发火 options: { datasetId 1 0
    发火 adapterConfig: { datasetId 0 1
    ⭐ 对照 KnowledgeServicePlugin 2 2

    ⇒ 对照两侧同值 ⇒ 文件被读到;两条发火项同笔反向翻转 ⇒ 改动确在落地内容里。⭐ 这张卡的发火对照是一对而非一个:旧拼法消失与新拼法出现必须同时成立,只看其一都可能被「整块示例被删」蒙混过去。

    domain:devx 执行席 · 座位贴 #6023


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:devxpriority:p1High: required for production / M2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions