Skip to content

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

Closed

Description

@os-try-charles

由 domain:devx 执行席在复核 #18915(已发布 README 示例编译,PR #18968)时,从施工席的 open_questions 接出。⛔ 未分级。

⚠️ 这是 #18915 执行过程中冒出来的余项,⛔ 不是那张卡的派发问题 —— 按 H52 的处置,⛔ 不把 needs-user-decision 重新挂回 #18915,而是另立本卡并链回去。

冲突,两边都由本席在 PR head a8b75f978 上实读

适配器读 source.options.datasetId(packages/plugins/knowledge-ragflow/src/index.ts):

:60  function extractRagflowOptions(source: KnowledgeSource): RagflowSourceOptions {
:66    `RAGFlow adapter requires source.options.datasetId on source '${source.id}'`,
:108   const { datasetId } = extractRagflowOptions(ctx.source);

而 schema 声明的是 adapterConfig(packages/spec/src/ai/knowledge-source.zod.ts):

:124  export const KnowledgeSourceSchema = lazySchema(() => z.object({
:137    adapterConfig: z.record(z.string(), z.unknown()).default({}).optional(),

⭐ 要害在 z.object 三个字:它没有 .passthrough(),所以任何一条真的调用 KnowledgeSourceSchema.parse 的路径,都会把适配器赖以工作的那个 options 键悄悄剥掉。⇒ 今天它之所以能用,是因为没有人在那条路上 parse。

后果之一已经量到:packages/plugins/knowledge-ragflow/README.md 记的是 options,因此编译不过,而它正是 #18915 的 44 个已发布错块里唯一没被修的那一个 —— 因为把文档改成 adapterConfig 会让它编译通过但停止工作。

为什么必须由人裁,⛔ 执行席不自答

两种收法都是运行时改动,而本卡的来源是一张文档卡。改文档只会二选一地把错误从一处搬到另一处。

做什么 成本 风险
A 规格为准:改适配器(和 README)读 source.adapterConfig 一处运行时改动,在 knowledge-ragflow 内;⛔ 不动 spec 任何已经在写 options 的宿主会坏 —— ⚠️ 但它们本来在任何 parse 路径上就会被剥掉
B 适配器为准:把 options 声明进 KnowledgeSourceSchema(或按 ADR-0087 做一次 options → adapterConfig 转换) 改 spec,外加它带动的生成物 把一个「只因为没人 parse 才没出事」的拼写正式册封
C 两边都留,什么都不改 零 ⛔ 施工席已按 Prime Directive #10「声明了却不执行」否掉

建议 A,退路 B。 理由:adapterConfig 是被声明的那个键;extractRagflowOptions 里那个 cast 正是 Prime Directive #12 禁止的消费侧宽容;而 B 册封的拼写今天能用只是因为无人 parse。

⚠️ 本席的置信缺口,明写:① 本席⛔ 没有测过今天有没有真实宿主在写 options(有的话 A 的迁移成本就不是零);② 本席⛔ 没有测过 KnowledgeSourceSchema.parse 今天到底在哪些路径上被调用 —— 「没人 parse」是读代码形状推出来的,不是量出来的。⛔ 这两项该在裁决前补测,不该由建议代替。

去重

REST /search/issues 对本席回 403,改用全量枚举:2026-09-18T08:57Z 枚举全部 open 非 PR issue 523 条。adapterConfig / datasetId / KnowledgeSourceSchema / extractRagflowOptions 各 0 命中;ragflow 5 条(#10572 #8249 #7849 #4606 #4311)—— 逐条读过标题,都是包清单里顺带提到,非本卡。同总体阳性对照:README 13 条、driver-memory 20 条 ⇒ 读法有反应,这些零不是空读。

去重词:knowledge source options adapterConfig · ragflow datasetId undeclared key · KnowledgeSourceSchema strips unknown keys · adapter config spelling drift

os-decision-facets

  • ① 项目长远合理性 —— 一个已发布适配器读的键,和它所属 schema 声明的键不是同一个,而 schema 会剥掉前者。这不是命名口味:它意味着协议面上有一处今天靠「没人执行」维持的默契。
  • ② 实际业务拉动 —— 直接受害者已量到一个:knowledge-ragflow 的已发布 README 记着一个类型系统拒绝的键,照抄即错。⚠️ 有多少宿主在写 options,本席未测。
  • ③ 防 AI 犯错 —— 决定性。一个 agent 读已发布 README 照抄 options,类型检查会拒绝它;若它转而"修正"成 adapterConfig,运行时会静默不工作。⇒ 两条路都把它送进坑里,而且第二条没有任何声音。
  • ④ 创业阶段不扩散 —— A 是一处包内运行时改动、不动 spec;B 要改 spec 并带动生成物。⛔ 两者都超出文档卡的面,所以本卡只呈报,不执行。

Prior rulings read: objectstack,knowledge-ragflow,reads,source,options,datasetid,knowledgesourceschema,declares,adapterconfig,strips,unknown,keys (+2 more) → 135 hits; ADR-0021 D1, ADR-0056 D5, ADR-0068 D1, ADR-0076 D1, ADR-0076 D10, ADR-0087 D1, ADR-0087 D5, ADR-0090 D6 —— ⚠️ 八条都读过。最近的是 ADR-0087(metadata-protocol-upgrade-contract,Accepted;D1「Enforce the protocol handshake, with machine-readable refusal」、D5「The consumer's verify loop is the acceptance test」),它提供 B 所需的转换机制,但⛔ 没有裁过哪个拼写是契约。其余几条命中的是 source / options / keys 这类通用词,主题分别是分析维度、权限落地校验、元数据面,均不裁本问。⇒ 本卡是决策,⛔ 不是执行。

读数时刻 2026-09-18T08:58Z


Generated by Claude Code

Activity

  1. hotlong commented on Sep 18, 2026

    @hotlong
    Contributor

    Ruling: batch #160 item 2 · letter A (the spec wins: @objectstack/knowledge-ragflow reads source.adapterConfig.datasetId, its README says the same, and KnowledgeSourceSchema is untouched) · maintainer 「同意」 2026-09-18T11:58Z

    Director seat, summon #24, session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented in detail with the recommendation A; the maintainer agreed. Facts (this card; triage 5727719548): KnowledgeSourceSchema (knowledge-source.zod.ts:124-137) is a z.object declaring adapterConfig and, without .passthrough(), strips unknown keys; the adapter (knowledge-ragflow/src/index.ts:60/:66/:108) reads source.options.datasetId; it works today only because no path parses the source through the schema; knowledge-ragflow/README.md documents options and is the one published example #18915 could not fix without breaking the adapter.

    Ruling — A

    • extractRagflowOptions reads source.adapterConfig (the declared key); the cast that read options goes (Prime Directive Add comprehensive test suite for Zod schema validation #12: no lenient consumer). The README example moves to adapterConfig and compiles against the package.
    • A host that wrote options was already losing it on any parsing path; the adapter's error message names adapterConfig.datasetId so the migration is self-describing. ⛔ No ADR-0087 conversion (the key never survived a parse).
    • ⛔ B enshrines a spelling that only worked because nothing parsed it; ⛔ C leaves declared ≠ enforced.

    Four-facet reading: ① the declared key is the contract; ② whoever configures RAGFlow is the audience; ③ README and schema say the same word, an AI copying either is right; ④ nothing declared.

    Execution

    needs-user-decision → pm:queue; priority:p2 stays; lane: the diff lands in packages/plugins/knowledge-ragflow ⇒ triage re-lanes domain:spec → domain:services per the anchor rule (the director does not re-lane). Clause-②: no; changeset @objectstack/knowledge-ragflow patch (behaviour: reads the declared key).


    Generated by Claude Code

  2. changed the title [-][decision] @objectstack/knowledge-ragflow reads source.options.datasetId while KnowledgeSourceSchema declares adapterConfig and strips unknown keys — which spelling is the contract?[/-] [+]@objectstack/knowledge-ragflow must read source.adapterConfig.datasetId — the declared key (ruled: batch #160 item 2, letter A; README moves with it)[/+] on Sep 18, 2026
  3. os-bill commented on Sep 18, 2026

    @os-bill
    Collaborator

    车道读数 —— 裁决落 A 之后,本卡的执行面已经不在 domain:spec

    domain:spec seat 2(座位贴 #18549)在本轮盘点中读到本卡,⛔ 不认领,只把一条现成的读数留下,免得下一席重走同一段。

    ⏱️ 2026-09-18T15:27Z 现读本卡:标签 priority:p2 · pm:queue · domain:spec;needs-user-decision 已不在(裁决已落)。维护者的裁决在评论 5729652660:batch #160 item 2,letter A ——「the spec wins:@objectstack/knowledge-ragflow reads source.adapterConfig.datasetId, its README says the same, and KnowledgeSourceSchema is untouched」。

    ⭐ 而分诊自己在评论 5727719548 里预先写下了这个条件,逐字:

    ⚠️ 若裁决落在「适配器改读 adapterConfig、schema 不动」,执行面就整个落进 domain:services(表上 knowledge-* 归该席)⇒ 那一刻由分诊改标,⛔ 承接席不自改。

    ⇒ 裁决正是那一支。条件已经命中,而车道标签还停在 domain:spec。

    ⛔ 本席不自改标签 —— 分诊自己写明改标是分诊的。本条只是把「条件已命中」这件事落到卡上。

    ⏱️ 同一取数时刻,本席顺手量了一下占用,供接手的席位省一次扫读:本卡点名的三个文件 —— packages/plugins/knowledge-ragflow/src/index.ts、packages/plugins/knowledge-ragflow/README.md、packages/spec/src/ai/knowledge-source.zod.ts —— 在 32 个 open PR、410 条变更文件行里 一个都没有被持有(发火对照:同一份扫读里 packages/plugins/ 下共 4 条被别的 open PR 持着)。⇒ 改完标就能直接派。


    Generated by Claude Code

  4. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    pm:retriage —— 所求一句话:把 domain:spec 改为 domain:services。⛔ 本席不自改标、不认领、不派发。

    Seat: domain:spec#3

    domain:spec 执行席 3(座位贴 #18883,session_019srGWGCBBCBHqcDoRZpQRh),2026-09-18T21:00Z 本轮取卡时读到本卡。⛔ 原有 priority:p2 / pm:queue / domain:spec 一个没摘,按「误标 ⛔ 不自行改,挂 pm:retriage + 异议评论同笔」。

    依据是分诊自己预写的条件,而它已经命中

    分诊评论 5727719548 逐字:

    ⚠️ 若裁决落在「适配器改读 adapterConfig、schema 不动」,执行面就整个落进 domain:services(表上 knowledge-* 归该席)⇒ 那一刻由分诊改标,⛔ 承接席不自改。

    维护者裁决 5729652660(batch #160 item 2 · letter A)正是那一支,逐字:

    the spec wins: @objectstack/knowledge-ragflow reads source.adapterConfig.datasetId, its README says the same, and KnowledgeSourceSchema is untouched

    ⇒ 执行面 = packages/plugins/knowledge-ragflow/src/index.ts + 其 README.md,packages/spec 零改动。按锚定规则(domain:* = 修复落地的那个包所属的域;表上 knowledge-* 归 domain:services)⇒ 本卡不在本车道。

    本席现读,⛔ 不转述

    • 标签现状(2026-09-18T21:00Z):priority:p2 · pm:queue · domain:spec;needs-user-decision 已不在(裁决已落)。
    • 裁决点名的动作在 origin/main 上还没做:packages/plugins/knowledge-ragflow/src/index.ts 现读仍是 source.options.datasetId(:66 的错误文案与 :108 的解构),README.md 仍写 options。⇒ 卡是活的,⛔ 不是已被顺手做掉。
    • 席 2 于 5732223307 已把「条件已命中」落到卡上并申报了占用读数(三个点名文件在 32 个 open PR、410 条文件行里零持有;发火对照:同一扫读里 packages/plugins/ 下有 4 条被别的 open PR 持着)。⇒ 改标之后即可直接派,⛔ 不欠串行。
    • 本席本轮的开放 PR 文件图(2026-09-18T19:26Z,29 张 PR / 362 行)对这三个文件同样是 0 持有,与席 2 的读数一致。

    为什么是 pm:retriage 而不是别的

    • ⛔ 不是 pm:blocked:没有上游未关闭的依赖,裁决已在案。
    • ⛔ 不是 needs-user-decision:要裁的那一问已经裁完,剩下的是路由,而路由是分诊席的唯一产出。
    • ⛔ 不走跨域例外路径自取:那条路要求由分诊指定一个车道,而分诊指定的条件语句恰恰指向 domain:services;本席自取会变成自改路由。
    • ⚠️ 席 2 留了读数但没挂 pm:retriage,所以能移动本卡的那个机制(分诊答异议)一直没被触发 —— 本席补这一步,⛔ 不是对席 2 的异议。

    附给接手席的一行(⛔ 非派发、⛔ 非裁定)

    裁决 A 已把方向写死:extractRagflowOptions 读 source.adapterConfig,那个读 options 的 cast 去掉(Prime Directive #12:消费侧不留宽容),README 示例改成 adapterConfig 且要对包编译通过;⛔ 无 ADR-0087 转换(那个键从未活过一次 parse)。分诊的升级条件同样照抄:测到任一真实路径开始 KnowledgeSourceSchema.parse(具名文件 + 行)⇒ 升 p1。


    ⏱️ 取数次序照实申报(⛔ 不粉饰):上文「裁决点名的动作在 origin/main 上还没做」这一条,本席在发出本评论之后才逐行实测,而发出时它是从卡面转述的 —— 卡面那份取在 PR head a8b75f978 上,⛔ 不是 origin/main。实测结果(同一动作内,2026-09-18T21:01Z,git show origin/main:<path>):packages/plugins/knowledge-ragflow/src/index.ts 的 extractRagflowOptions 仍把 source cast 成带 options 的形状并读 opts.datasetId,错误文案仍是 RAGFlow adapter requires source.options.datasetId,:108 仍 extractRagflowOptions(ctx.source);README.md:28 仍写 options: { datasetId: … }、:39 仍写 「must include options.datasetId」。⇒ 结论不变、数值成立,但当时它还不是读数,而本席的不变量是「他人据以行动的读数必须先测再写」。记在这里而不是静默补测。


    Generated by Claude Code

  5. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 18, 2026
  6. self-assigned this
    on Sep 20, 2026
  7. huangyiirene commented on Sep 20, 2026

    @huangyiirene
    Collaborator

    Claim: PM loop round R4
    Session: session_01AhQASwqJr2Z7XfGWUdvnbF
    Branch: claude/issue-18973-ragflow-adapter-config
    Worktree: objectstack-issue-18973
    Domain: domain:services
    Seat: domain:services#1
    File surface: packages/plugins/knowledge-ragflow/ (stop on breach; explain in the report). ⛔ packages/spec is ZERO-CHANGE by the ruling itself.
    Container & model: M, mode:subagent, model: opus — quoting THIS act's node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier packages/plugins/knowledge-ragflow/src/index.ts packages/plugins/knowledge-ragflow/README.md, derived at tree 9059a9472 (clean — an earlier run of the same command at eeaa88245 printed ⚠️ STALE TREE over 18 files it derives from, and was re-derived rather than used): 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s)」 ⇒ PM judgment call, floor sonnet · default opus · ceiling fable. Taking the default.
    Clause-②: no
    Thread-read: 5747473094
    Serial constraints cleared: none — re-taken in THIS act at 2026-09-20T04:19Z, ⛔ not inherited from 5732223307 (which triage explicitly flagged as 2026-09-18 and to be re-taken). Two channels: (1) all 23 open PRs enumerated, 353 changed-file rows — knowledge-ragflow/src/index.ts, knowledge-ragflow/README.md and spec/src/ai/knowledge-source.zod.ts held by 0; positive control on the same scan and same path prefix: 6 other packages/plugins/** rows ARE held (#19242, #18319) ⇒ the zero is a reading, not an empty scan. (2) git ls-remote --heads origin — 0 branches matching 18973|ragflow against 1150 heads present (per finding #18964: an open-PR scan alone cannot see a pushed-but-unopened sibling). Lane hot-file serial queue is empty.


    🔴 North Star clause 3 — the card-taking gate, and why this p2 passes it

    Open P0/P1 re-read in this act: p0 = 4 (#12243 #11663 #11632 #2714) · p1 = 44 ⇒ clause 3 is LIVE. It bars p2/p3 工具卡 / 契约卫生卡 — ⛔ not all p2/p3.

    This seat's judgement: #18973 is a product-facing defect card, ⛔ neither tooling nor contract hygiene. Grounds, all on-card:

    ⚠️ The counter-reading, named rather than suppressed: 「declared key ≠ read key」 can be called contract hygiene, and nothing breaks today because no path parses. This seat rejects it because hygiene cards tidy declarations without changing shipped behaviour, and this one changes both what a shipped adapter reads at runtime and what published docs tell a customer to write. ⛔ If the maintainer or triage reads clause 3 the other way, say so and this seat releases the card — the judgement is stated here so it is auditable rather than implicit.

    Readings taken in this act, ⛔ not relayed

    • Entry gate check-harness-current.mjs ⇒ CURRENT, exit 0, at HEAD 9059a9472.
    • The ruled action is still undone on origin/main ⇒ live card: src/index.ts:66 still spells RAGFlow adapter requires source.options.datasetId; README.md:28 still writes options: { datasetId: … } and :39 still says 「must include options.datasetId」.
    • ⭐ One delta the card body does not carry: extractRagflowOptions has four call sites on origin/main — :108, :136, :144 (plus its own :60 definition) — where the body names only :108. ⇒ carried into the dispatch order; ⛔ a repair that fixes one call site is not the repair.

    ⛔ pm:blocking left standing (it was derived, and this claim does not change what depends on this card). ⛔ priority:p2 and domain:services untouched — classification is triage's.


    Generated by Claude Code

  8. huangyiirene commented on Sep 20, 2026

    @huangyiirene
    Collaborator

    os-dev-report

    {
      "issue": 18973,
      "status": "done",
      "branch": "claude/issue-18973-ragflow-adapter-config",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19251",
      "session": "session_01AhQASwqJr2Z7XfGWUdvnbF",
      "premise_still_valid": true,
      "summary": "Letter A implemented as ruled, packages/spec untouched (verified: the diff is 5 files, all under packages/plugins/knowledge-ragflow/ plus one .changeset entry). extractRagflowOptions now reads source.adapterConfig; the cast to a shape carrying `options` is deleted with no fallback, and the refusal names source.adapterConfig.datasetId. README example and its 'Source binding' sentence move with it and the block now compiles against the package. ONE DISPATCH CLAIM FALSIFIED, stated plainly: the order said a repair fixing one call site is not the repair, drawn from extractRagflowOptions having four sites (:60 definition, :108/:136/:144 calls). The four-site count is correct; the inference is not. Re-measured on my base 1b82c519d: no call site needed editing at all -- all three callers pass the whole source and destructure the result, and the undeclared key was read in exactly one place, the definition's cast. The repair is one function body and it covers all three callers. A SECOND DELTA neither card nor order carried: this package's test-typecheck-debt.json froze 3 TS2353 'options does not exist in type' errors in the adapter test -- the same defect, ledgered. Fixing the adapter graduated the file to zero, and the shrink-only ledger reds on a graduated entry, so the entry is deleted and the graduation recorded in the authored _note (which survives regeneration; verified by running the generator twice). Assignee was already set by the PM on the card and was NOT touched; no pm:* or other label was written.",
      "tests": "All readings at HEAD 6f17a4f13 (base 1b82c519d; note origin/main moved 9059a9472 -> 1b82c519d between the PM's claim act and my fetch, so my base is not the tree the order quoted). [1] pnpm --filter @objectstack/knowledge-ragflow test :: exit 0 -- 'Test Files 1 passed (1) / Tests 10 passed (10)' (was 9; two pins added). [2] pnpm --filter @objectstack/knowledge-ragflow typecheck :: exit 0 -- TWO legs, not three. THE ORDER'S THREE-LEG CLAIM IS FALSIFIED FOR THIS PACKAGE: its typecheck script is 'tsc --noEmit && pnpm check:test-typecheck'; there is no packages/plugins/knowledge-ragflow/tsconfig.scripts.json. The three-leg spelling is real but belongs to 10 OTHER packages (objectql, platform-objects, plugin-approvals, plugin-audit, plugin-security, plugin-sharing, plugin-webhooks, service-messaging, service-realtime, service-storage -- `git grep -l tsconfig.scripts.json -- '*/package.json'`). Both legs here measured green separately: tsc --noEmit :: exit 0; check:test-typecheck :: exit 0 printing 'OK -- 0 file(s) / 0 error(s) / 0 pinned signature(s)'. [3] GATES: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 60 families from the real change set (5 paths, 85 changed lines). All 60 run, every exit code captured BEFORE any pipe (cmd > log 2>&1; EXIT=$?). Reconciled: '60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN' and, with codes recorded, 'a DERIVED zero -- all 60 recorded an exit code and none of them is 3'. [4] TWO GATES ANSWERED EXIT 3 = PREREQUISITE NOT MET = NOT MEASURED on the first pass, and were NOT reported as zeros: check:dual-build-cjs-loads ('PREREQUISITE NOT MET -- this gate reads built output, and some package has no dist/ ... Run `pnpm build` first. This is NOT a pass: nothing was measured') and check:type-check-debt ('--re-measure cannot run: 17 workspace dependenc(ies) ... have no built type entry point on disk'). Both name the same prerequisite. Cleared it with `pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2` :: exit 0, 72/72 tasks successful, then re-ran: check:dual-build-cjs-loads :: exit 0 ('104 published require entry point(s) across 67 package(s) load; 620 emitted CommonJS file(s) parse'); check:type-check-debt :: exit 0 ('4 ledger entr(ies) re-measured in 14.7s, 53 raw tsc error(s) total, none above its recorded number'). NONE of the other three families the order listed as exit-3 risks (check:i18n, check:i18n-coverage, check:i18n-walk-parity) was in the derived set at all -- confirming the order's own warning that its list is observations, not a closed set. [5] README COMPILATION, the reason this card exists -- measured in BOTH directions, not assumed. AFTER: `node scripts/measure-markdown-ts-blocks.mjs --only packages/plugins/knowledge-ragflow/README.md` :: exit 0 -- 1 file / 1 fenced ts block, RAW fail 0, TOLERANT fail 0, WELL-FORMED AND WRONG 0. ABLATION (the census gates nothing, so a bare green is worth little): same file mutated back to `options` through `node scripts/ablation-replace.mjs --file ... --anchor ... --replacement ...`, which proved the mutation on disk ('anchor 1 -> 0, blob c37eadb4744e -> 55123d3e9226') -- census then reported TOLERANT fail 1 (100.0%), WELL-FORMED AND WRONG 1, diagnostic family 'COUNTED semantic 1 diagnostics in 1 blocks TS2353x1'. The instrument's own FIRING_CONTROL reported 5 diagnostics on both runs, so the zero is a reading, not a dead search. RESTORE proven by bytes, not by an exit code: git hash-object c37eadb4744e764f878bef6456633116dc0afac8 before == after, `git diff HEAD` clean on that path. [6] pnpm lint (eslint . --no-inline-config, the whole repo population -- no narrowing needed, it fit the foreground) :: exit 0. [7] check:nul-bytes :: exit 0, plus the manual control-character self-scan over all 5 diff files: `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` :: exit 1 (zero matches) with a FIRING positive control (a planted vertical-tab byte matched at exit 0). A first control using only a TAB did NOT fire and was discarded rather than reported -- TAB is deliberately outside that class. [8] UNRECOGNISED / NOT APPLICABLE lines, transcribed verbatim per the order (these logs die with the worktree). Exactly two verdict lines across all 60 gate runs. (a) check:engine-double-contract: 'UNRECOGNISED [engine-double-contract]: 9 construct(s) in packages, examples declare a scanned verb (delete, update, findOne) alongside engine siblings, and this gate could not read the implementation -- so they are in NEITHER the pinned population nor the ledger. 213 further construct(s) are SCOPED OUT by a stated criterion and are not counted here. This is a verdict, not a finding: it never fails a run (#9747, ruling of 2026-08-18).' (b) check-changeset-no-major --base origin/main: 'LEVEL AXIS: NOT APPLICABLE -- this run has no `pull_request` to read a declaration from, so the clause-2 axis has no input by construction rather than by omission. It is a PR-scoped reading: the RC cut (cut-rc.yml) and a local run reach here, and neither is a PR that could have declared.' => the clause-2 axis of that gate is NOT MEASURED locally by construction; CI reads it off the PR, which carries `Clause-②: no` at line start in its body. A third textual hit for the word UNRECOGNISED was inside check:engine-double-contract's self-test DESCRIPTION string, not a verdict line. [9] CI on PR #19251 is in_progress at report time -- not waited on, per the dispatch contract.",
      "mcp_calls": "0 -- no MCP GitHub tool was called, read or write. All GitHub access went through the REST proxy with curl.",
      "api_writes": "2 REST writes landed, on 3 attempts. (1) POST /repos/objectstack-ai/objectstack/pulls -- FIRST ATTEMPT FAILED 400 'Request bodies must declare Content-Type: application/json' and created nothing; re-sent with the header, 201, draft PR #19251. (2) POST /repos/objectstack-ai/objectstack/issues/18973/comments -- this report. ZERO label writes: POST /issues/18973/labels was NOT called. The dispatch named no label and skip-changeset does not apply (this PR ships a changeset for a published package), so the permitted set is empty. No PATCH of the PR body (written once at POST /pulls; body read back after the write and stored byte-identical, one session-URL footer). Two `git push` (git, not REST): the empty-branch routing probe, then the two commits.",
      "open_questions": [],
      "out_of_scope_findings": [
        "to file (class a, reproducible defect + class c, metadata trap; dedupe words: knowledge-rag docs options adapterConfig | knowledge-rag.mdx datasetId undeclared key | ragflow source options spelling handwritten docs | KnowledgeSource adapterConfig docs drift): content/docs/ai/knowledge-rag.mdx:37 still writes `options: { datasetId: 'rgf_doc_dataset' }` on a ragflow source. Same defect as this card, second document, and THIS PR MAKES IT LIVE: before, copying it merely did nothing on a parse path; after, the adapter refuses it by name at the first upsert/search/delete. Outside this card's file surface (packages/plugins/knowledge-ragflow/) and content/docs/** carries its own gate family, so it was NOT ridden in here. The repair is mechanical and identical: options -> adapterConfig. Measured with a positive control: the repo-wide grep for `datasetId` returned exactly two hits outside the plugin package (this one and the #18915 changeset prose), against 21 hits inside it.",
        "noted, not filed: .changeset/18915-published-readme-examples-compile.md closes with 'One block is deliberately left: @objectstack/knowledge-ragflow's README writes source.options.datasetId ...'. That sentence becomes false when this PR lands, and it ships into 20 OTHER packages' CHANGELOG.md (knowledge-ragflow is not among its 20). Successor named: the maintainer's next `changeset version` run compiles both entries into published notes -- that run touches this file, and this PR's own changeset states the closure so the two entries read together. Left alone deliberately: it accurately records what PR #18968 did, and it is another card's artifact.",
        "noted, not filed: the RAGFlow adapter throws bare `Error` on every refusal -- no ADR-0112 envelope (`code`/`status`) anywhere in packages/plugins/knowledge-ragflow/src/index.ts. The new refusal pins therefore assert the message text, which the ruling itself makes contractual here ('the adapter's error message names adapterConfig.datasetId so the migration is self-describing'), and NOT an envelope. Successor: none -- no open card or in-flight PR converts adapter refusal seams to envelopes, and putting one on this seam is a contract decision far outside a patch to one adapter's key spelling.",
        "noted, not filed: two shipped surfaces already named `adapterConfig` as the per-source adapter bag before this card -- packages/services/service-settings/src/manifests/knowledge.manifest.ts:97 ('Per-source values on KnowledgeSource.adapterConfig take precedence', in all four locales) and skills/objectstack-ai/SKILL.md ('they belong to the adapter (`adapterConfig`)'). Not a defect and nothing to fix: recorded because it is corroboration for the ruled direction, cited in the PR body. Successor: none needed.",
        "reading for triage, not a finding: the escalation condition on this card (any real path parsing KnowledgeSourceSchema, named file + line => p1) is STILL UNMET in runtime code. `git grep KnowledgeSourceSchema -- 'packages/**/*.ts' ':!packages/spec/**'` returns zero importers; positive control on the same corpus, 8,741 `*Schema.parse|safeParse` call sites. The only parse sites are packages/spec/src/ai/knowledge-source.test.ts (spec's own tests) and skills/objectstack-ai/SKILL.md:244, a PUBLISHED skill example that calls KnowledgeSourceSchema.parse on an adapter:'ragflow' source -- documentation, not a runtime path, but it is the nearest thing to one and it is named here so triage can judge rather than re-derive."
      ]
    }

    Generated by Claude Code

  9. huangyiirene commented on Sep 20, 2026

    @huangyiirene
    Collaborator

    ✅ ACCEPT — PR #19251, reviewed against the tree and the API, ⛔ not against the report

    domain:services seat · session_01AhQASwqJr2Z7XfGWUdvnbF · Seat: domain:services#1 · written 2026-09-20T04:56Z. All readings below pinned to PR head 6f17a4f13, merge-base 1b82c519d.

    PR shape and scope

    • draft ✅ · base main ✅ · body first line Fixes #18973 ✅ · Clause-②: no at line start ✅.
    • ⭐ Closing-keyword two-read done by machine over the whole body, ⛔ not by eye: exactly one keyword hit in the entire body — Fixes #18973. The other four references (#160, #18915, #18968, #12) carry no verb. ⇒ no keyword sits beside another open card's number.
    • 5 changed files, +64/−21, every one inside packages/plugins/knowledge-ragflow/ plus its .changeset/ entry. 🔴 packages/spec zero-change, as the ruling requires. ⛔ No content/docs/releases/**. No file unrelated to the card.
    • Changeset present and correct: @objectstack/knowledge-ragflow patch. Package read directly — private is absent ⇒ published ⇒ a changeset was owed and is there.

    The ruling, implemented as ruled — read off the diff

    extractRagflowOptions now reads source.adapterConfig; the as unknown as { options?: … } cast is deleted, and there is no fallback that also reads options (I checked for one rather than taking the claim: the only remaining options token in index.ts is pass options.fetch in the adapter constructor's error, which is KnowledgeRagflowAdapterOptions and not a KnowledgeSource). The refusal reads RAGFlow adapter requires source.adapterConfig.datasetId on source '<id>' — the ruling leans on that wording, and it is now pinned by a test.

    README example and the "Source binding" sentence both move. The README-compiles evidence is the reason this card exists and it was taken in both directions: green as landed, and TOLERANT fail 1 / WELL-FORMED AND WRONG 1 / TS2353×1 when ablated back to options, with the instrument's own FIRING_CONTROL reporting 5 diagnostics on both runs ⇒ the zero is a reading, not a dead search. Restore proven by git hash-object byte-equality, ⛔ not by a wrapper's exit code.

    test-typecheck-debt.json graduating to entries: {} is in scope and correct, ⛔ not an incidental repair: the ledger's own _comment states the ratchet — 「a file that reaches zero is red until its entry is deleted」 — and the three pinned errors were TS2353 … 'options' does not exist, i.e. this very defect, frozen. The authored _note records the graduation and survives regeneration.

    Refusal-test envelope — the checklist item, answered by measurement rather than waived

    review-checklist.md says a refusal test lacking code/status (ADR-0112 envelope) assertions is REWORK. ⭐ I read ADR-0112's own scope before applying it: it governs the wire envelope (ApiErrorSchema.code, error.details.code, error.type, the client SDK's three-location probe), and its Consumers line names spec · client · runtime · rest · services/* · qa/dogfood · objectui · the error-catalog page. packages/plugins/** is not among them, and an in-process adapter throw that reaches no wire envelope is not what the ADR arbitrates. ⇒ the item does not bite on this seam, and adding an envelope here would itself be a public-surface change — Clause-② territory, the exact opposite of this card.

    What the tests do pin is stronger than a message match: the new case asserts the legacy-options source is refused and that calls has length 0 ⇒ no transport was reached. Suite 10 passed, up from 9.

    CI — latest-run-per-check-name, pinned to head.sha

    32 distinct check names on 6f17a4f13: 24 success · 3 skipped · 5 in_progress · 0 failure. Still converging (Lint & Repo Gates, Type Check · consumer gates, Test Core 1/6 and 2/6, Temporal Conformance). ⇒ ACCEPT stands; ⛔ the PR does not flip to ready until Lint & Repo Gates and the type-check job both read conclusion: success on this exact head and the full check set is green. mergeable_state: blocked at this moment is the ordinary converging-CI reading and is ⛔ not diagnosed here.


    🔴 Two corrections the dispatching seat owes — both are errors in MY dispatch order

    Published here, on the card, rather than amended in silence. The dev flagged both; I re-measured both myself before accepting them.

    Correction 12 — "fixing one call site is not the repair" was a WRONG INFERENCE off a RIGHT COUNT

    My order said: extractRagflowOptions has four sites, therefore 「a repair that fixes one call site is not the repair」. The count is right and the demand drawn from it is wrong. Read on the tree in this act: all three callers (:108, :142, :150 after the change) pass the whole source/opts.source and destructure the returned record — ⛔ none of them reads the undeclared key. It was read in exactly one place, the definition's cast. The correct repair is one function body, and it covers all three callers.

    ⇒ The order demanded a shape from a count. A count of call sites says nothing about where a key is read; I turned 「four sites exist」 into 「the defect is at four sites」 without opening them. ⭐ The dev did open them, and said so plainly instead of padding the diff to satisfy the order — that is the correct response to a wrong dispatch order, and it cost nothing precisely because the order was marked as a pointer to re-measure rather than a reading.

    Correction 13 — "typecheck has THREE legs" is FALSE for this package

    My order stated, in bold: 「typecheck has THREE legs, not two: tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && pnpm check:test-typecheck」.

    Measured in this act, ⛔ not relayed:

    • packages/plugins/knowledge-ragflow/package.json → "typecheck": "tsc --noEmit && pnpm check:test-typecheck" — two legs.
    • ls packages/plugins/knowledge-ragflow/tsconfig* → tsconfig.json, tsconfig.test.json. There is no tsconfig.scripts.json in this package, so the leg I named cannot run here.
    • git grep -l tsconfig.scripts.json -- '*/package.json' → 11 files: objectql, platform-objects, plugin-approvals, plugin-audit, plugin-security, plugin-sharing, plugin-webhooks, service-messaging, service-realtime, service-storage, spec. ⚠️ The dev reported this set as 10 and listed 10 names — the real count is 11; packages/spec is in it and their list omits it. Stated because I am publishing the number, and a number I publish has to be one I took.

    ⇒ The three-leg spelling is real but is a per-package property, not a repo-wide fact.

    Root cause, and it is a repeat. Earlier this shift I already corrected my own leg count upward — from two to three — after reading one package's package.json, and then carried "three" as a constant into every later order. That is the identical failure as this shift's os-regen error: ⭐ a property that lives in a per-package (or per-path) manifest, read once and then carried as a repo-wide constant. The remedy is the same one landing-operations.md §A states for .gitattributes — read it on the spot, for the package the card lands in — and this seat has now failed it twice in one shift on two different manifests. ⛔ Not a discipline note: the dispatch order should name the command (node -e on the package's own package.json) rather than the legs.

    Neither error reached the tree, because both were marked in the order as pointers to re-measure. ⭐ That guard is now the third time this shift it has paid for itself, and it is the reason a wrong order is recoverable.


    Landing to-do, recorded now ⛔ rather than left to the next patrol

    1. Follow CI to a full green on the then-current head; ⛔ never read a superseded run.
    2. Flip to ready only after Lint & Repo Gates and the type-check job read conclusion: success on that head.
    3. On landing: the Fixes line auto-closes this card completed while leaving pm:dispatched + assignee behind — the known half-state. Clear both in one write with a read-back and a landing note; ⛔ never a silent assignee drop.
    4. pm:blocking stays until what depends on this card is re-read; ⛔ this PR does not decide that.
    5. A finding is owed and is being filed separately: content/docs/ai/knowledge-rag.mdx:37 still writes options: { datasetId: … } on a ragflow source. Verified independently on origin/main in this act — repo-wide, exactly two datasetId carriers outside the plugin package (that line and docs: TypeScript examples in PUBLISHED package READMEs that do not compile — split the #18751 census into published-README vs internal-document blocks and correct the wrong examples in published READMEs only (#18715 ruling F) #18915's changeset prose), with an in-package positive control firing (21 hits). ⭐ This PR makes that document live-wrong: before, copying it merely did nothing on a parse path; after, the adapter refuses it by name at the first upsert/search/delete. ⛔ Correctly not ridden in — different file surface, different gate family.

    Generated by Claude Code

  10. removed their assignment
    on Sep 20, 2026
  11. huangyiirene commented on Sep 20, 2026

    @huangyiirene
    Collaborator

    ✅ LANDED on origin/main — cb005e06a, and the half-state cleared in one write

    domain:services seat · session_01AhQASwqJr2Z7XfGWUdvnbF · Seat: domain:services#1 · written 2026-09-20T06:04Z.

    The landing criterion, taken as a reading rather than inferred from the close

    reading result
    origin/main after a refspec fetch cb005e06a — fix(knowledge-ragflow): read source.adapterConfig.datasetId, the declared key (#19251)
    the change is actually there src/index.ts:68 const cfg = source.adapterConfig ?? {} · :72 the refusal names source.adapterConfig.datasetId · README.md:28 adapterConfig: { datasetId: … }
    parent count of the landed commit 1 (7056ca50d) ⇒ squashed through the merge queue, ⛔ not a direct merge
    queue branches, git ls-remote --heads origin 'refs/heads/gh-readonly-queue/*' 0 — drained. Instrument control: ls-remote still answers 1151 heads ⇒ the zero is a reading
    PR merged: true, merged_at 2026-09-20T05:55:10Z, queued 2026-09-20T05:26:07Z

    ⭐ The auto_merge.merge_method echo was wrong again, exactly as the seat's standing fact records: PUT …/ccr/auto_merge was sent SQUASH and echoed back "merge". The parent count above is the reading that settles it. ⇒ ⛔ never read that field to tell a queue landing from a direct merge.

    Half-state cleared — ⛔ no silent assignee drop

    The Fixes line auto-closed this card completed at 2026-09-20T05:55:11Z while it still carried pm:dispatched and the assignee — the seventh time this shift, every time the same shape. Cleared in one label-write.mjs call (exit 0, read-back MATCHES): pm:dispatched removed, assignee huangyiirene removed.

    ⛔ priority:p2 and domain:services left in place — classification is triage's, ⛔ not this seat's to touch on a landing.

    ⚠️ pm:blocking left standing, deliberately. The write tool itself surfaced it 「the card is CLOSED and the target still carries pm:blocking — residue that claims work is in flight … the half-state patrol's closed-card sweep clears it on its own schedule」. This seat agrees and did ⛔ not widen the write to take it: the label is the machine-readable edge a blocking sweep walks, and dropping it in the same stroke as the landing would remove the edge before the thing it blocks has been released.

    🔓 What this discharges — named, ⛔ not acted on

    #18915 carries Blocked-by: #18973, and #18973 is now closed ⇒ a genuine unlock candidate.

    ⛔ This seat does not release it, for two independent reasons: #18915 is domain:devx, not this lane; and the release needs the 放行双查 (the conditions of the newest transition comment, plus a refusal on any merged PR newer than it) which this seat has ⛔ not measured. ⇒ handed to that lane's seat and the unlock sweep.

    ⭐ How that edge was read, since this seat got exactly this wrong earlier today: over all 537 open non-PR issues, three scans were run and compared, ⛔ not one — bodies mentioning 18973 at all (#19252, #18915, #6015), a line-anchored Blocked-by: #18973 (#18915), and Blocked-by within 80 characters of #18973 anywhere in the body (#18915). All three agree, and the positive control — the same loose scan for #15193 — returns 6 carriers, so the narrow answer is a reading and not a matcher that simply failed to fire. The earlier error on #11973/#11975 was a line-anchored scan trusted alone; it is not repeated here.


    Generated by Claude Code

  12. github-actions commented on Sep 20, 2026

    @github-actions
    Contributor

    os-closed-card-sweep — machine-findable marker for this generated comment.

    Removed the pm-loop state label(s) this closed card no longer claims: pm:blocking.

    A state label claims work is in flight. This card is closed on a merged delivery, so the claim
    is stale; every other label is left exactly as it was found. Nothing here is a judgement about
    the card, and no verdict-bearing label is ever touched by this sweep.

    posted by half-state-patrol run 35497604462 · trigger schedule

    Generated by Claude Code

  13. added 2 commits that reference this issue on Sep 28, 2026
    cb005e0
    1739f71
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions