You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
finding(docs): docs/ARCHITECTURE.md teaches a six-method DataSource interface that no package exports and ObjectView would reject — the examples type-check only because two anys launder the value #8160
Filed by the domain:devx @ objectui execution seat (PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-06T21:36Z) from the census objectui#7856 card 1 took while bringing docs/*.md into the doc-snippet walk (PR #8158, head f011733bf). This is a proposal that PR deliberately did NOT act on — the gate structurally cannot see it, and repairing it means deciding what the page should teach, which is not a typed-binding job.
What the page says
docs/ARCHITECTURE.md, section "Custom Data Source Interface" (the interface DataSource fence, ~line 240 on f011733bf), documents a six-method interface (find, findOne, create, update, delete, getObjectSchema-less) and says third-party systems implement it to plug into the console. Examples 1–3 on the same page pass such an object to DataSourceProvider and read it back with useDataSource() into ObjectView.
What the tree says (measured on f011733bf, through the gate's own compileSnippets)
Annotate a stand-in with the page's verbatim interface and pass it to ObjectView: TS2741: Property 'getObjectSchema' is missing in type 'DataSource' but required in type 'DataSource(OPT)'.
@object-ui/types exports a much larger DataSource; @object-ui/app-shell holds a near-identical private copy and does not export it. No package exports the shape the page documents.
The page's examples compile only because DataSourceProviderProps.dataSource is declared any and useDataSource() returns any — the value is laundered between the provider and the renderer, so the page's central claim is unchecked end to end by construction. PR feat(gate): bring the root docs/*.md pages into the doc-snippet walk #8158's stand-ins record this honestly in a fence comment ("the annotation records where the value goes rather than checking it").
Why it is a finding and not a repair rider
The correct fix is a decision: either the page teaches the real exported DataSource from @object-ui/types (and says which methods a minimal adapter must implement), or a minimal-adapter contract is exported and the page names it. Both change what a reader is told to build. Adjacent cards on the any root cause: objectui#7912 (SchemaRendererProvider's dataSource prop typed any) and objectui#7483 (ObjectView's props surface typed any). This card is the documentation face of the same seam and should be routed together with them; it does not duplicate either.
Verification recipe
git fetch origin main
git show origin/main:docs/ARCHITECTURE.md | sed -n '/^interface DataSource/,/^}/p'
git grep -n "export interface DataSource\b" origin/main -- packages/types/src packages/app-shell/src
Triage: lands in docs/ARCHITECTURE.md ("Custom Data Source Interface" + Examples 1–3); rationale: class (a) — a named, executed probe (TS2741: Property 'getObjectSchema' is missing) shows the documented six-method interface is rejected by ObjectView, and no package exports that shape. Graded p2 (above its pair objectui#8161) because two anys launder the value, so the failure arrives late and unattributable rather than immediately and self-explaining. Default direction: teach the real exported DataSource; exporting a new minimal-adapter contract is the fallback and would need review.
Claim: PM loop round R46 — domain:devx @ objectui execution seat.
Session: session_01FhBNJcLRZLe8M87VcUgpKr
Branch: claude/issue-8160-architecture-md-datasource-and-use-client
Worktree: /home/user/objectui-issue-8160
Domain: domain:devx
File surface: docs/ARCHITECTURE.md only (the "Custom Data Source Interface" section, Examples 1–3, and "Example 2: Next.js Integration"). ⛔ Not packages/*/src/** — the any root cause belongs to #7912 / #7483.
Dispatch: one os-dev subagent of this session (mode:subagent, model: opus); the dispatch note with the brief follows on this card. Labels pm:queue → pm:dispatched and assignee set in one write, read back at 2026-09-07T14:16Z.\n\nFold-or-serial, answered before either card was claimed (both want docs/ARCHITECTURE.md): ONE dev, ONE worktree, ONE PR carrying both — which is what triage recommended on each card ("同一页、同一受众,建议一个 PR 一起改,⛔ 别分两次评审"). ⛔ This is NOT a family fold: the defect forms differ (one is which interface the page should teach, the other is a missing App Router directive), so gate ① of the folding rule does not pass and neither card inherits the other's acceptance. What it IS: the same-file serial collapsed into a single dispatch, so the two never race the page. Each defect keeps its OWN named acceptance criterion and the PR body must show both, separately. ⛔ Excluded from this dispatch and named so: the any root cause under #7912 and #7483 (this is the documentation face of that seam, not its repair), and any other defect on the page that neither card measured.
Dispatched — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-07T14:18Z. Two of three slots in flight (with #7995; disjoint surfaces).
Dev. One os-dev subagent of this session on branch claude/issue-8160-architecture-md-datasource-and-use-client, worktree /home/user/objectui-issue-8160, base origin/main at 411a132c0 or newer. Race re-reads at 14:16Z: the newest Claim: is 5571942201 on this card and 5571942832 on #8161, both naming that branch.
Fold-or-serial, answered before either card was claimed. ONE dev, ONE worktree, ONE PR carrying both — triage's recommendation on each card. ⛔ Not a family fold: the defect forms differ, gate ① of the folding rule does not pass, and neither card inherits the other's evidence. Each keeps its own acceptance criterion and the PR body shows both separately. The PR's first line is Fixes #8160 with Fixes #8161 on its own line, so both close on the merge.
Route applied (this card): triage's 甲 — teach the REAL exported DataSource from @object-ui/types and say which methods a minimal adapter must implement. ⛔ 乙 (export a new minimal-adapter contract) is a public-surface expansion and is NOT dispatched; if no self-consistent minimal subset can be taught, the dev STOPS and reports rather than inventing one.
Facts the seat read on 411a132c0: the page's interface fence declares find / findOne / create / update / delete / getMetadata? while the real DataSource (packages/types/src/data.ts) requires six members including getObjectSchema — the page teaches an optional method the interface does not require and omits one it does, which is the TS2741 the card measured. The example-implementation fence below it ends in // ... implement other methods, the same elision shape #5174's batch 37 repaired in the root README with an ambient declare class … implements DataSource; the dev decides under the four axes between that treatment and a members table, and ⛔ no elision survives as the page's teaching of what to implement.
⚠️ A premise of #8161 is falsified and the brief says so: that card's control for its zero-reading is use client being non-empty in packages/app-shell/src — it is zero there on today's main, so the control is dead and the zero is unbacked as written. A live control exists in apps/site/app/** (75 files repo-wide carry the directive). The dev re-derives the zero with a control that fires and reports both.
⛔ Excluded and named: the any root cause under #7912 and #7483 — this page is the documentation face of that seam, not its repair. 'use client' is also uncheckable by any gate (each fence compiles as an isolated module), and the PR must say so rather than implying coverage.
{
"issues": [
8160,
8161
],
"status": "done",
"branch": "claude/issue-8160-architecture-md-datasource-and-use-client",
"pr": "https://github.com/objectstack-ai/objectui/pull/8358",
"base_sha": "411a132c01c9f53e9a6762af6cea49d67e7b0ce2",
"final_head": "4a5ff64ef5e522d2d276d8ca3bcc1533097527d2",
"notation": "Square brackets stand in for the compiler's angle brackets throughout this report (DataSource[any] for the generic instantiation). GitHub's body sanitizer eats short tag-shaped fragments, including inside backticks.",
"premise_still_valid": {
"8160": true,
"8160_note": "Fully confirmed. The page declared a local six-method DataSource with an optional getMetadata and no getObjectSchema; the exported DataSource in packages/types/src/data.ts has exactly 6 required members (find, findOne, create, update, delete, getObjectSchema) and declares no getMetadata anywhere in packages/types/src. ObjectViewProps.dataSource is typed DataSource and is required. The card's TS2741 reproduced verbatim.",
"8161": "defect TRUE, card's control FALSE",
"8161_note": "The zero on docs/ARCHITECTURE.md is real (exit=1, 0 lines). The card's stated control (packages/app-shell/src) is dead: it returns the SAME reading as the subject (exit=1, 0 lines), so it could never distinguish a working grep from a broken one. Re-derived against a live control instead. The PM's falsification is confirmed exactly as stated."
},
"c8160": {
"probe_before": "compileSnippets() from the gate's own harness, page interface read verbatim off disk, stand-in handed to ObjectView, giving TS2741: Property 'getObjectSchema' is missing in type 'DataSource' but required in type 'DataSource[any]'. Harness controls green in the same run: resolution landed on packages/types/dist/index.d.ts (a built artifact, not source), sentinel TS2305, positive 0 diagnostics.",
"interface_after": "Route 甲 as dispatched. The page now imports the exported type rather than re-declaring it: 'import type { DataSource } from @object-ui/types', then 'type MinimalDataSource = Pick[DataSource, find|findOne|create|update|delete|getObjectSchema]' which compiler-checks the six NAMES, then 'declare const minimal: MinimalDataSource; export const adapter: DataSource = minimal' which proves the six SUFFICE and goes red the moment a seventh member becomes required. Route 乙 not needed: a self-consistent minimal subset does exist (the six required members), so no new public contract was invented.",
"pick_negative_control": "Swapping the page's old getMetadata back into the Pick yields 2 diagnostics (TS2344: 'getMetadata' is not assignable to keyof DataSource[any]; TS2741: getObjectSchema missing), while the as-published spelling yields 0. So the Pick is load-bearing, not silently any.",
"elision_decision": "Elision removed by ANNOTATION, not by an ambient declare class. The example fence is now a fully written 'export const myDataSource: DataSource = { ...six members... }'. Four axes: (1) 实际业务需求 — measured on the consuming prop (ObjectViewProps.dataSource: DataSource, required); the reader's real failure mode is a missing member, and an annotated literal is both complete and copy-paste-able where a declare class hands back nothing runnable. A hand-written members table was rejected on the same axis for a sharper reason: a copied signature table is exactly the drift hazard this gate's own header records against itself (objectui#7448). (2) 项目长远合理性 — contract-first; the page had become a SECOND declaration of a contract @object-ui/types already owns, and the repair deletes it rather than syncing it. No signature is restated anywhere on the page now, so no maintenance coupling remains to forget. (3) 防 AI 写代码犯错 — the deciding axis: the elision was a hole an agent fills by guessing, which is the batch-error shape this axis names; the annotation converts it into a loud compile-time refusal (an incomplete adapter stops the block compiling and check:doc-snippets fails naming this file). Contract tightening over consumer tolerance, declaration enforced rather than merely written. The ambient declare class scores nearly as well here and loses only on axis 1. (4) 创业阶段不扩散需求 — nothing exported, no surface growth; MinimalDataSource is a local alias inside a documentation block, not a published type. Route 乙 declined. Immediate retirement with no dual spelling: the wrong interface is deleted in this PR, not deprecated alongside the right one. No axis conflicts, so nothing is escalated.",
"probe_after": "Same harness, what the page NOW teaches read verbatim off disk, same ObjectView call: parseFailures 0, semanticallyJudged 1, ZERO DIAGNOSTICS. Acceptance criterion met."
},
"c8161": {
"dead_control": "git grep -n 'use client' origin/main -- packages/app-shell/src gives exit=1, 0 lines. Identical to the subject's own reading, so it distinguishes nothing. Confirmed dead exactly as the PM re-ran it. Root cause found, and it is not an accident: 'use client' occurs zero times in the BUILT output too (app-shell/dist 0, providers/dist 0, plugin-view/dist 0, react/dist 0) — the packages ship no client banner at all, so that tree could never have fired.",
"live_control": "git grep -ln 'use client' origin/main -- apps/site/app gives exit=0, 8 files (incl. apps/site/app/components/InteractiveDemo.tsx, apps/site/app/playground/page.tsx). Repo-wide: exit=0, 75 files. Same grep invocation as the subject, so it genuinely bounds the instrument.",
"zero_rederived": "git grep -n 'use client' origin/main -- docs/ARCHITECTURE.md gives exit=1, 0 lines, now backed by a control that fires. All four exit codes captured by redirect-then-capture, never through a pipe (a trailing 'head' closes the read end and hands the producer a SIGPIPE-flavoured 0).",
"directive_placement_and_why": "BOTH fences of Example 2 (app/layout.tsx and app/[object]/page.tsx), plus one prose paragraph above the pair rather than a comment inside each fence. Each fence needs it for an INDEPENDENT reason: the page fence calls the useDataSource() hook; the layout fence renders ThemeProvider and AppShell, whose React context cannot be created on the server. Marking only the page would leave the layout broken; marking only the layout does not make a child module a client module (the directive is per-module, not inherited). The prose states the underlying rule — every module under app/ is a server component by default, and the @object-ui/* packages carry no client banner of their own, so the boundary must be declared in the consumer's files — so the reader can generalise instead of pattern-matching a line. Example 3 (Embedded Widget) deliberately does NOT get it: its fence declares no app/ file path and the directive is App-Router-specific.",
"gate_coverage_disclaimer": "'use client' is NOT gate-checkable and this PR does not imply that it is. Each fence compiles as an isolated module, so the directive is merely a string-expression statement to the TypeScript program; nothing in check:doc-snippets or any other gate validates a React Server Components boundary. #8161's whole evidence is the grep re-derivation plus the dist census. Stated explicitly in the PR body as well."
},
"gate_totals": {
"documents": "229 scanned / 229 covered / 0 ungated — matches the dispatch, identical before and after",
"blocks": "790 covered — 632 to compile, 158 declared fragments; flat across this change (two fences replaced by two fences)",
"semantic_phase": "632 of 632 judged, 0 failed — before AND after",
"discrepancy_vs_dispatch": "The dispatch carried 631 of 631 from the last landing; today's origin/main (411a132c0) reads 632 of 632. It reads 632 identically BEFORE this change, so one block joined the corpus between that landing and today: the corpus moved under the dispatch note, not under this diff. No regression in either direction."
},
"positive_control": {
"method": "On the COMMITTED tree. trap 'restore' EXIT INT TERM with an absolute REPO_ROOT path; restore spelled 'git checkout HEAD -- path' (never bare), so it cannot pull the mutation back out of a polluted index.",
"mutation_proof_on_disk": "Proven BEFORE any gate result was read: old anchor count went 1 to 0, injected-text count 1, and git hash-object 9d0e755c906e824d3b334b488120862bcf739942 differs from the HEAD blob 304a349efe11c085e8ef7ff54f691ec50b0828be. The empty-hash case is handled as FAILURE, not as 'nothing to compare'.",
"red": "MUTATED_EXIT=1, and the gate NAMES the file: docs/ARCHITECTURE.md:287:3 TS2344 ('getObjectSchemaX' is not assignable to keyof DataSource[any]. Did you mean 'getObjectSchema'?) and docs/ARCHITECTURE.md:295:14 TS2741 (getObjectSchema missing in MinimalDataSource). Semantic phase 632 of 632, 1 failed.",
"green": "RESTORED_EXIT=0, semantic phase 632 of 632, 0 failed — same run.",
"restore_proof": "restored blob 304a349efe11c085e8ef7ff54f691ec50b0828be EQUALS the HEAD blob; git diff HEAD emitted 0 lines; injected-text count back to 0. Verified by observing state, not by reading a cleanup step's exit code.",
"verdict": "The repaired fence is genuinely load-bearing: breaking one member name in it turns the gate red naming this file, and unmodified it is green in the same run."
},
"gates": {
"pnpm check:doc-snippets": 0,
"pnpm check:doc-examples": 0,
"pnpm check:doc-fences": 0,
"node scripts/check-doc-links.mjs": 0,
"pnpm check:doc-types": 0,
"pnpm exec vitest run scripts/__tests__/": "0 — 120 files, 3575 tests passed",
"pnpm lint:root": "0 — 32 warnings, 0 errors, all pre-existing",
"pnpm check:control-bytes": "0 — 6638 tracked text files",
"node scripts/check-changeset-presence.mjs": "0 — no changeset owed (1 file changed, 0 of it published source of a released package)",
"node scripts/check-governed-queue-guard.mjs --test docs/ARCHITECTURE.md": "0 — NOT GOVERNED, none of the 5 governed surfaces matched",
"build_closure": "0 — the doc gate's own --build-filter (34 packages) was built first via turbo run build --concurrency=2, 35/35 tasks successful. Without it the gate exits 2 ('could not run'), which is not a verdict about any document.",
"control_byte_self_scan": "grep -naP over the changed file exits 1 with no output (no control bytes), independent of the gate.",
"method": "Every exit code captured by redirect-then-capture (command redirected to a file, then EXIT captured, then tail) — never read after a pipe."
},
"files_changed": [
"docs/ARCHITECTURE.md"
],
"mcp_calls": "0 — all GitHub reads and writes went through repo-scoped REST; no MCP GitHub call was made this run.",
"filed": [],
"observations_for_pm_to_triage": [
"The @object-ui/* packages ship NO 'use client' banner in their built output (app-shell/dist 0, providers/dist 0, plugin-view/dist 0, react/dist 0). This is what killed #8161's stated control, and it is a real consumer-facing constraint: every App Router consumer must wrap each ObjectUI import in their own client file. Whether the packages SHOULD ship banners is a product decision (under 创业阶段不扩散需求 it is a capability claim, not an obvious fix), so this is observation-class rather than a concrete defect. NOT FILED, and the reason is mechanical rather than a judgement call: observation-class findings need the 'finding' label, and this dispatch forbids me writing labels. Handing it to PM to file. The docs now teach the constraint correctly either way, so nothing is left unhandled for the reader."
],
"deviations": [
"Gate total re-measured at 632 of 632, not the dispatch's 631 of 631. Re-measured on the UNCHANGED tree first, so it is not this PR's movement — reported rather than silently matched to the dispatch note.",
"pnpm lint:root passes --ignore-pattern 'docs/**', so it never reads the changed file. Its green is declared as NOT coverage of this diff rather than quietly counted as such.",
"objectui has no scripts/pm/os-verify-lock.sh (only scripts/pm/check-half-states.mjs), so there was no shared verification lock to take in this repo. Heavy work still ran under the resource discipline: NODE_OPTIONS=--max-old-space-size=4096 and turbo --concurrency=2.",
"PR left DRAFT as dispatched (REST cannot un-draft). Noted for the record: the governed-surface guard returns NOT GOVERNED for docs/ARCHITECTURE.md, so the draft state here is the seat convention, not a governed-surface hold — the PR is on the ordinary route and a human may take it forward normally."
],
"open_questions": []
}
LANDED: PR #8358 merged at 2026-09-07T14:57:31Z as 04597f3a5; content probe on re-fetched origin/main passed.
What the page teaches now. It imports the exported DataSource instead of declaring a rival copy, names the six required members through a Pick that fails on a member the interface does not have, and proves those six SUFFICE with export const adapter: DataSource = minimal — a line that goes red the moment a seventh member becomes required. The example implementation is annotated : DataSource and writes all six, so the // ... implement other methods elision is gone rather than relocated.
Route 甲 as triage graded it. Route 乙 (exporting a new minimal-adapter contract) stayed unneeded: a self-consistent minimal subset does exist, so no public surface was invented.
Verified independently by this seat:getMetadata — the optional method the page used to teach — occurs in zero files under packages/types/src, and ObjectViewProps.dataSource is DataSource, required (ObjectView.tsx:268). The card's TS2741 reproduced on the base and reads zero on the head.
⛔ Untouched, as scoped: the any seam under #7912 and #7483. This page was its documentation face, not its repair. New card from the same run: #8362 (no @object-ui package ships a 'use client' banner).
Closed via Fixes; pm:dispatched stripped in the same stroke. — domain:devx @ objectui seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, 14:59Z.
Activity
os-zhuang commentedon Sep 7, 2026
分诊 · 准入通过 (a) 类
标签:
domain:devx·documentation·bug·pm:queue·priority:p2· typeBugfinding已摘。写前状态复核:open · 无 assignee · 无 PR。✅准入判定 —— 失败探针已具名且已跑过
⇒ 这不是"读起来可疑",是用门禁自己的
compileSnippets跑出来的一条失败读数。(a) 命中,⛔ 无需再论证。⇒ 业务上:第三方按这一页实现适配器,会得到一个
ObjectView要求的方法它没有的对象。⭐ 最重的一点:为什么没有任何东西拦住它
页面的例子能编译通过,只因为
DataSourceProviderProps.dataSource声明为any、useDataSource()返回any⇒ 值在 provider 与渲染器之间被洗白了。⇒ 卡片那句话我原样背书:「the page's central claim is unchecked end to end by construction」 —— 这一页的核心主张,按构造就是不可能被检查的。
⇒ 而且这决定了失败的时机:类型层不报、运行期直到
ObjectView真去调getObjectSchema才炸。p2 的理由(高于配对的 #8161 一档)
undefined is not a function,⛔ 不指向文档any把类型层的信号全洗掉了⇒ 同一页、同一次普查、同一批读者,但"坏得晚且安静"要比"坏得早且响亮"高一档。 这是我本轮维持的分界。
为什么是
pm:queue而不是决策箱卡片给了两条路,⚠️ 但它们地位不对等:
@object-ui/types真正导出的DataSource,并说清最小适配器要实现哪几个方法 ⇒ 纯文档订正,今天就能做;⇒ 按甲派单。⛔ 只有当实施者发现"没有一个自洽的最小子集可讲"时,乙才浮上来 —— 那时再作为决策提出。
边界
any的根因。卡片正确地把它指给了 finding(react): SchemaRendererProvider's dataSource prop and SchemaRendererContextType are typed any — a string passed where the host adapter belongs raises nothing #7912(SchemaRendererProvider.dataSource: any)和 finding(app-shell):ObjectView's entire props surface is typedany— a misspelled prop on a public route-level view raises nothing #7483(ObjectView整个 props 面any),并说明本卡是同一接缝的文档面、⛔ 非重复。⇒ 本卡只改文档;根因归那两张。docs/ARCHITECTURE.md's Next.js example has no'use client'—app/layout.tsxandapp/[object]/page.tsxare App Router server components as written and fail at runtime for a reader who copies them #8161 配对:同一页、同一受众,建议一个 PR 一起改,⛔ 别分两次评审。Triage: lands in
docs/ARCHITECTURE.md("Custom Data Source Interface" + Examples 1–3); rationale: class (a) — a named, executed probe (TS2741: Property 'getObjectSchema' is missing) shows the documented six-method interface is rejected byObjectView, and no package exports that shape. Graded p2 (above its pair objectui#8161) because twoanys launder the value, so the failure arrives late and unattributable rather than immediately and self-explaining. Default direction: teach the real exportedDataSource; exporting a new minimal-adapter contract is the fallback and would need review.Generated by Claude Code
claude commentedon Sep 7, 2026
Claim: PM loop round R46 —
domain:devx @ objectuiexecution seat.Session:
session_01FhBNJcLRZLe8M87VcUgpKrBranch:
claude/issue-8160-architecture-md-datasource-and-use-clientWorktree:
/home/user/objectui-issue-8160Domain:
domain:devxFile surface:
docs/ARCHITECTURE.mdonly (the "Custom Data Source Interface" section, Examples 1–3, and "Example 2: Next.js Integration"). ⛔ Notpackages/*/src/**— theanyroot cause belongs to #7912 / #7483.Dispatch: one
os-devsubagent of this session (mode:subagent,model: opus); the dispatch note with the brief follows on this card. Labelspm:queue→pm:dispatchedand assignee set in one write, read back at 2026-09-07T14:16Z.\n\nFold-or-serial, answered before either card was claimed (both wantdocs/ARCHITECTURE.md): ONE dev, ONE worktree, ONE PR carrying both — which is what triage recommended on each card ("同一页、同一受众,建议一个 PR 一起改,⛔ 别分两次评审"). ⛔ This is NOT a family fold: the defect forms differ (one is which interface the page should teach, the other is a missing App Router directive), so gate ① of the folding rule does not pass and neither card inherits the other's acceptance. What it IS: the same-file serial collapsed into a single dispatch, so the two never race the page. Each defect keeps its OWN named acceptance criterion and the PR body must show both, separately. ⛔ Excluded from this dispatch and named so: theanyroot cause under #7912 and #7483 (this is the documentation face of that seam, not its repair), and any other defect on the page that neither card measured.Generated by Claude Code
claude commentedon Sep 7, 2026
Dispatched —
domain:devx @ objectuiexecution seat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-07T14:18Z. Two of three slots in flight (with #7995; disjoint surfaces).Dev. One
os-devsubagent of this session on branchclaude/issue-8160-architecture-md-datasource-and-use-client, worktree/home/user/objectui-issue-8160, baseorigin/mainat411a132c0or newer. Race re-reads at 14:16Z: the newestClaim:is5571942201on this card and5571942832on #8161, both naming that branch.Fold-or-serial, answered before either card was claimed. ONE dev, ONE worktree, ONE PR carrying both — triage's recommendation on each card. ⛔ Not a family fold: the defect forms differ, gate ① of the folding rule does not pass, and neither card inherits the other's evidence. Each keeps its own acceptance criterion and the PR body shows both separately. The PR's first line is
Fixes #8160withFixes #8161on its own line, so both close on the merge.Route applied (this card): triage's 甲 — teach the REAL exported
DataSourcefrom@object-ui/typesand say which methods a minimal adapter must implement. ⛔ 乙 (export a new minimal-adapter contract) is a public-surface expansion and is NOT dispatched; if no self-consistent minimal subset can be taught, the dev STOPS and reports rather than inventing one.Facts the seat read on
411a132c0: the page's interface fence declaresfind/findOne/create/update/delete/getMetadata?while the realDataSource(packages/types/src/data.ts) requires six members includinggetObjectSchema— the page teaches an optional method the interface does not require and omits one it does, which is theTS2741the card measured. The example-implementation fence below it ends in// ... implement other methods, the same elision shape #5174's batch 37 repaired in the root README with an ambientdeclare class … implements DataSource; the dev decides under the four axes between that treatment and a members table, and ⛔ no elision survives as the page's teaching of what to implement.use clientbeing non-empty inpackages/app-shell/src— it is zero there on today's main, so the control is dead and the zero is unbacked as written. A live control exists inapps/site/app/**(75 files repo-wide carry the directive). The dev re-derives the zero with a control that fires and reports both.⛔ Excluded and named: the
anyroot cause under #7912 and #7483 — this page is the documentation face of that seam, not its repair.'use client'is also uncheckable by any gate (each fence compiles as an isolated module), and the PR must say so rather than implying coverage.Generated by Claude Code
claude commentedon Sep 7, 2026
os-dev-report
{ "issues": [ 8160, 8161 ], "status": "done", "branch": "claude/issue-8160-architecture-md-datasource-and-use-client", "pr": "https://github.com/objectstack-ai/objectui/pull/8358", "base_sha": "411a132c01c9f53e9a6762af6cea49d67e7b0ce2", "final_head": "4a5ff64ef5e522d2d276d8ca3bcc1533097527d2", "notation": "Square brackets stand in for the compiler's angle brackets throughout this report (DataSource[any] for the generic instantiation). GitHub's body sanitizer eats short tag-shaped fragments, including inside backticks.", "premise_still_valid": { "8160": true, "8160_note": "Fully confirmed. The page declared a local six-method DataSource with an optional getMetadata and no getObjectSchema; the exported DataSource in packages/types/src/data.ts has exactly 6 required members (find, findOne, create, update, delete, getObjectSchema) and declares no getMetadata anywhere in packages/types/src. ObjectViewProps.dataSource is typed DataSource and is required. The card's TS2741 reproduced verbatim.", "8161": "defect TRUE, card's control FALSE", "8161_note": "The zero on docs/ARCHITECTURE.md is real (exit=1, 0 lines). The card's stated control (packages/app-shell/src) is dead: it returns the SAME reading as the subject (exit=1, 0 lines), so it could never distinguish a working grep from a broken one. Re-derived against a live control instead. The PM's falsification is confirmed exactly as stated." }, "c8160": { "probe_before": "compileSnippets() from the gate's own harness, page interface read verbatim off disk, stand-in handed to ObjectView, giving TS2741: Property 'getObjectSchema' is missing in type 'DataSource' but required in type 'DataSource[any]'. Harness controls green in the same run: resolution landed on packages/types/dist/index.d.ts (a built artifact, not source), sentinel TS2305, positive 0 diagnostics.", "interface_after": "Route 甲 as dispatched. The page now imports the exported type rather than re-declaring it: 'import type { DataSource } from @object-ui/types', then 'type MinimalDataSource = Pick[DataSource, find|findOne|create|update|delete|getObjectSchema]' which compiler-checks the six NAMES, then 'declare const minimal: MinimalDataSource; export const adapter: DataSource = minimal' which proves the six SUFFICE and goes red the moment a seventh member becomes required. Route 乙 not needed: a self-consistent minimal subset does exist (the six required members), so no new public contract was invented.", "pick_negative_control": "Swapping the page's old getMetadata back into the Pick yields 2 diagnostics (TS2344: 'getMetadata' is not assignable to keyof DataSource[any]; TS2741: getObjectSchema missing), while the as-published spelling yields 0. So the Pick is load-bearing, not silently any.", "elision_decision": "Elision removed by ANNOTATION, not by an ambient declare class. The example fence is now a fully written 'export const myDataSource: DataSource = { ...six members... }'. Four axes: (1) 实际业务需求 — measured on the consuming prop (ObjectViewProps.dataSource: DataSource, required); the reader's real failure mode is a missing member, and an annotated literal is both complete and copy-paste-able where a declare class hands back nothing runnable. A hand-written members table was rejected on the same axis for a sharper reason: a copied signature table is exactly the drift hazard this gate's own header records against itself (objectui#7448). (2) 项目长远合理性 — contract-first; the page had become a SECOND declaration of a contract @object-ui/types already owns, and the repair deletes it rather than syncing it. No signature is restated anywhere on the page now, so no maintenance coupling remains to forget. (3) 防 AI 写代码犯错 — the deciding axis: the elision was a hole an agent fills by guessing, which is the batch-error shape this axis names; the annotation converts it into a loud compile-time refusal (an incomplete adapter stops the block compiling and check:doc-snippets fails naming this file). Contract tightening over consumer tolerance, declaration enforced rather than merely written. The ambient declare class scores nearly as well here and loses only on axis 1. (4) 创业阶段不扩散需求 — nothing exported, no surface growth; MinimalDataSource is a local alias inside a documentation block, not a published type. Route 乙 declined. Immediate retirement with no dual spelling: the wrong interface is deleted in this PR, not deprecated alongside the right one. No axis conflicts, so nothing is escalated.", "probe_after": "Same harness, what the page NOW teaches read verbatim off disk, same ObjectView call: parseFailures 0, semanticallyJudged 1, ZERO DIAGNOSTICS. Acceptance criterion met." }, "c8161": { "dead_control": "git grep -n 'use client' origin/main -- packages/app-shell/src gives exit=1, 0 lines. Identical to the subject's own reading, so it distinguishes nothing. Confirmed dead exactly as the PM re-ran it. Root cause found, and it is not an accident: 'use client' occurs zero times in the BUILT output too (app-shell/dist 0, providers/dist 0, plugin-view/dist 0, react/dist 0) — the packages ship no client banner at all, so that tree could never have fired.", "live_control": "git grep -ln 'use client' origin/main -- apps/site/app gives exit=0, 8 files (incl. apps/site/app/components/InteractiveDemo.tsx, apps/site/app/playground/page.tsx). Repo-wide: exit=0, 75 files. Same grep invocation as the subject, so it genuinely bounds the instrument.", "zero_rederived": "git grep -n 'use client' origin/main -- docs/ARCHITECTURE.md gives exit=1, 0 lines, now backed by a control that fires. All four exit codes captured by redirect-then-capture, never through a pipe (a trailing 'head' closes the read end and hands the producer a SIGPIPE-flavoured 0).", "directive_placement_and_why": "BOTH fences of Example 2 (app/layout.tsx and app/[object]/page.tsx), plus one prose paragraph above the pair rather than a comment inside each fence. Each fence needs it for an INDEPENDENT reason: the page fence calls the useDataSource() hook; the layout fence renders ThemeProvider and AppShell, whose React context cannot be created on the server. Marking only the page would leave the layout broken; marking only the layout does not make a child module a client module (the directive is per-module, not inherited). The prose states the underlying rule — every module under app/ is a server component by default, and the @object-ui/* packages carry no client banner of their own, so the boundary must be declared in the consumer's files — so the reader can generalise instead of pattern-matching a line. Example 3 (Embedded Widget) deliberately does NOT get it: its fence declares no app/ file path and the directive is App-Router-specific.", "gate_coverage_disclaimer": "'use client' is NOT gate-checkable and this PR does not imply that it is. Each fence compiles as an isolated module, so the directive is merely a string-expression statement to the TypeScript program; nothing in check:doc-snippets or any other gate validates a React Server Components boundary. #8161's whole evidence is the grep re-derivation plus the dist census. Stated explicitly in the PR body as well." }, "gate_totals": { "documents": "229 scanned / 229 covered / 0 ungated — matches the dispatch, identical before and after", "blocks": "790 covered — 632 to compile, 158 declared fragments; flat across this change (two fences replaced by two fences)", "semantic_phase": "632 of 632 judged, 0 failed — before AND after", "discrepancy_vs_dispatch": "The dispatch carried 631 of 631 from the last landing; today's origin/main (411a132c0) reads 632 of 632. It reads 632 identically BEFORE this change, so one block joined the corpus between that landing and today: the corpus moved under the dispatch note, not under this diff. No regression in either direction." }, "positive_control": { "method": "On the COMMITTED tree. trap 'restore' EXIT INT TERM with an absolute REPO_ROOT path; restore spelled 'git checkout HEAD -- path' (never bare), so it cannot pull the mutation back out of a polluted index.", "mutation_proof_on_disk": "Proven BEFORE any gate result was read: old anchor count went 1 to 0, injected-text count 1, and git hash-object 9d0e755c906e824d3b334b488120862bcf739942 differs from the HEAD blob 304a349efe11c085e8ef7ff54f691ec50b0828be. The empty-hash case is handled as FAILURE, not as 'nothing to compare'.", "red": "MUTATED_EXIT=1, and the gate NAMES the file: docs/ARCHITECTURE.md:287:3 TS2344 ('getObjectSchemaX' is not assignable to keyof DataSource[any]. Did you mean 'getObjectSchema'?) and docs/ARCHITECTURE.md:295:14 TS2741 (getObjectSchema missing in MinimalDataSource). Semantic phase 632 of 632, 1 failed.", "green": "RESTORED_EXIT=0, semantic phase 632 of 632, 0 failed — same run.", "restore_proof": "restored blob 304a349efe11c085e8ef7ff54f691ec50b0828be EQUALS the HEAD blob; git diff HEAD emitted 0 lines; injected-text count back to 0. Verified by observing state, not by reading a cleanup step's exit code.", "verdict": "The repaired fence is genuinely load-bearing: breaking one member name in it turns the gate red naming this file, and unmodified it is green in the same run." }, "gates": { "pnpm check:doc-snippets": 0, "pnpm check:doc-examples": 0, "pnpm check:doc-fences": 0, "node scripts/check-doc-links.mjs": 0, "pnpm check:doc-types": 0, "pnpm exec vitest run scripts/__tests__/": "0 — 120 files, 3575 tests passed", "pnpm lint:root": "0 — 32 warnings, 0 errors, all pre-existing", "pnpm check:control-bytes": "0 — 6638 tracked text files", "node scripts/check-changeset-presence.mjs": "0 — no changeset owed (1 file changed, 0 of it published source of a released package)", "node scripts/check-governed-queue-guard.mjs --test docs/ARCHITECTURE.md": "0 — NOT GOVERNED, none of the 5 governed surfaces matched", "build_closure": "0 — the doc gate's own --build-filter (34 packages) was built first via turbo run build --concurrency=2, 35/35 tasks successful. Without it the gate exits 2 ('could not run'), which is not a verdict about any document.", "control_byte_self_scan": "grep -naP over the changed file exits 1 with no output (no control bytes), independent of the gate.", "method": "Every exit code captured by redirect-then-capture (command redirected to a file, then EXIT captured, then tail) — never read after a pipe." }, "files_changed": [ "docs/ARCHITECTURE.md" ], "mcp_calls": "0 — all GitHub reads and writes went through repo-scoped REST; no MCP GitHub call was made this run.", "filed": [], "observations_for_pm_to_triage": [ "The @object-ui/* packages ship NO 'use client' banner in their built output (app-shell/dist 0, providers/dist 0, plugin-view/dist 0, react/dist 0). This is what killed #8161's stated control, and it is a real consumer-facing constraint: every App Router consumer must wrap each ObjectUI import in their own client file. Whether the packages SHOULD ship banners is a product decision (under 创业阶段不扩散需求 it is a capability claim, not an obvious fix), so this is observation-class rather than a concrete defect. NOT FILED, and the reason is mechanical rather than a judgement call: observation-class findings need the 'finding' label, and this dispatch forbids me writing labels. Handing it to PM to file. The docs now teach the constraint correctly either way, so nothing is left unhandled for the reader." ], "deviations": [ "Gate total re-measured at 632 of 632, not the dispatch's 631 of 631. Re-measured on the UNCHANGED tree first, so it is not this PR's movement — reported rather than silently matched to the dispatch note.", "pnpm lint:root passes --ignore-pattern 'docs/**', so it never reads the changed file. Its green is declared as NOT coverage of this diff rather than quietly counted as such.", "objectui has no scripts/pm/os-verify-lock.sh (only scripts/pm/check-half-states.mjs), so there was no shared verification lock to take in this repo. Heavy work still ran under the resource discipline: NODE_OPTIONS=--max-old-space-size=4096 and turbo --concurrency=2.", "PR left DRAFT as dispatched (REST cannot un-draft). Noted for the record: the governed-surface guard returns NOT GOVERNED for docs/ARCHITECTURE.md, so the draft state here is the seat convention, not a governed-surface hold — the PR is on the ordinary route and a human may take it forward normally." ], "open_questions": [] }Generated by Claude Code
claude commentedon Sep 7, 2026
LANDED: PR #8358 merged at 2026-09-07T14:57:31Z as
04597f3a5; content probe on re-fetchedorigin/mainpassed.What the page teaches now. It imports the exported
DataSourceinstead of declaring a rival copy, names the six required members through aPickthat fails on a member the interface does not have, and proves those six SUFFICE withexport const adapter: DataSource = minimal— a line that goes red the moment a seventh member becomes required. The example implementation is annotated: DataSourceand writes all six, so the// ... implement other methodselision is gone rather than relocated.Route 甲 as triage graded it. Route 乙 (exporting a new minimal-adapter contract) stayed unneeded: a self-consistent minimal subset does exist, so no public surface was invented.
Verified independently by this seat:
getMetadata— the optional method the page used to teach — occurs in zero files underpackages/types/src, andObjectViewProps.dataSourceisDataSource, required (ObjectView.tsx:268). The card'sTS2741reproduced on the base and reads zero on the head.⛔ Untouched, as scoped: the
anyseam under #7912 and #7483. This page was its documentation face, not its repair. New card from the same run: #8362 (no@object-uipackage ships a'use client'banner).Closed via
Fixes;pm:dispatchedstripped in the same stroke. —domain:devx @ objectuiseat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, 14:59Z.Generated by Claude Code