Skip to content

docs(data-modeling): virtual objects — rows from your server, shown in any view (plugin-driver recipe) - #27349

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-6ml89f
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-6ml89f

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread

Fixes #27278
Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 新增文档页「虚拟对象」:三分法(计算型 / 不可落地 / 实时 ⇒ 虚拟;慢变参考数据 ⇒ 真对象 + 定时拉取)、插件 driver 完整配方(driver 骨架、插件、数据源、外部对象、current_user 锁公式、beforeUpdate 守卫、甘特视图、权限集)、driver 实际收到的调用、共享模型规则、driver 形状 pin 测试模板、五个业务例;从 external-datasources、drivers 与模块首页链入。
  • 为什么改: object-gantt can no longer write back to a same-origin endpoint after #23082 — a measured producer of the retired view-level api source (read + write) #27130 这类客户要「行来自自家服务器、任何视图可看可改」,平台零协议改动已能做到,但没有一页可照抄的配方;AI 作者也照这页写。
  • 风险与回滚: 纯文档(外加文档审计范围清单自动生成的一行),revert 即回滚;页内整套栈与 driver 骨架已在真实 kernel 中启动跑通(见 Tests)。
  • 先看: content/docs/data-modeling/virtual-objects.mdx 的「Credentials」段(实测:插件 driver 的数据源上 credentialsRef 不被解析)与「Choose the sharing model on purpose」提示框;其次第一个 driver 骨架代码块。
  • 两分钟验证: pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:skill-examples,预期 ✅ 272 prose examples type-check,页内两个 os:check 代码块在其中(变异后报 virtual-objects.mdx:140 与 :288)。
  • 需要你做的: 定夺插件 driver 的凭据通道(Acceptance notes 第 1 条);页面目前只写实测事实,并按插件约定让宿主经插件选项传入。

What changes

Tests

Docs only; nothing published changes. Every code block on the page was run before it was written down:

  • The whole recipe boots. A throwaway copy of the reference test's boot harness (packages/runtime, same plugin set as plugin-driver-external-object.spike.test.ts) with the page's driver skeleton, plugin and defineStack block (identical but for comments) behind an in-memory transport: 6/6 passed under the verify lock (VERDICT command-exit 0). Measured: list read with the view's filter, sort and top=50 reaches the remote as filter [archived eq false], sort start_date asc, limit 50, then a count with the same filter; the formula reads locked: false for the planner and true for the administrator; a PATCH on a locked slot answers 409 RESOURCE_CONFLICT / declaredCode: PLAN_LOCKED; the administrator's PATCH answers 403 PERMISSION_DENIED / declaredCode: NOT_PLANNER with no remote write; the planner's PATCH makes three by-id reads and one patch carrying exactly start_date, end_date, plan_note; an outsider's list read is 403; a filter the driver refuses (>=) answers 500 INTERNAL_ERROR. The probe files were deleted; the tree carries no new test.
  • The shape-pin template passes against the skeleton: 5/5 (vitest run, identical text to the page's block except the import path).
  • The reference test still holds on this base: plugin-driver-external-object.spike.test.ts 12/12 (VERDICT command-exit 0).
  • The os:check blocks are really compiled: with transactionsUnsupported: 'yes' and sharingModel: 'public_rw' injected, check:skill-examples exits 1 naming virtual-objects.mdx:140:17 and :288:7; restored by git checkout HEAD, blob hash equal to HEAD.

Acceptance notes

  1. credentialsRef is not resolved for a plugin driver (measured, seam level). DatasourceConnectionService.attemptConnect returns already-registered for a driver a plugin registered before it reaches credential resolution. Probe on 6d7634a9: pre-registered driver + credentialsRef ⇒ already-registered with no secret store and with a resolver (resolver calls 0); not pre-registered ⇒ failed-credentials / connected (resolver calls 1). So the fail-closed sentence in external-datasources §4 does not hold for a plugin driver, and the epic's standing rule "credentials only through credentialsRef" has no channel on Tier A today. The page states the fact and points to the plugin convention (the host passes options to the plugin it composes); it does not claim a platform channel. Not filed: the reach is a seam probe, not a public door.
  2. "DataSync" in the card is spelled as what exists. DataSyncConfig is retired (never executed); a sync is defined on its target as a mapping with connectorSource driven by a job pull. The counter-example links /docs/automation/jobs#pulling-a-mapping and says there is no separate data-sync type.
  3. Mounting through defineStack({ plugins }) is not measured under objectstack serve. The boot probe composes with kernel.use, as the reference test does; the plugins sentence follows the documented plugin convention (plugins/development step 6), and the "register in init" rule rests on reading ObjectQLPlugin.start (adopts driver.* services) and AppPlugin.start (connects declared datasources).
  4. Owner-column route (the second way out of the sharing rule) is named and marked as not covered by the reference test.
  5. object-gantt can no longer write back to a same-origin endpoint after #23082 — a measured producer of the retired view-level api source (read + write) #27130 is not answered here; per the card the triage seat replies after this lands.
  6. The card's objectql: virtual-object correctness floor — applyDriverQueryInMemory helper, per-datasource row cap with a loud refusal, supports honoured for plugin drivers, refusal-envelope mapping for driver failures (epic #27261 P1) #27277 item "Driver shape pin test template in the guide (P1-2)" is carried by the template section; objectql: virtual-object correctness floor — applyDriverQueryInMemory helper, per-datasource row cap with a loud refusal, supports honoured for plugin drivers, refusal-envelope mapping for driver failures (epic #27261 P1) #27277's helper, row cap and refusal envelopes are described only as planned.

Verification

Final head beadd67a. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 53 families for this diff; all 53 run, all exit 0; --ran reconciles 53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN.

Docs gates (selected verdict lines)
  • check:doc-anchors: 803 internal fragment links across 476 source files all resolve.
  • check:docs-audit-scope: green after the regeneration.
  • node scripts/docs-audit/check-affected-docs.mjs: exit 0; affected-docs.mjs origin/main: 0 docs (no packages/** change).
  • check-section-landing-index: 9 section index blocks enumerate their meta.json pages (red before the index card was added, green after).
  • check:skill-examples: 272 prose examples type-check (needs @objectstack/client-react built with declarations first, otherwise it refuses with exit 3).
  • check:doc-formula-expressions, check:doc-security-posture, check:doc-authoring, check:doc-frontmatter, check:docs-single-h1, check:nul-bytes: exit 0.

🤖 Generated with Claude Code

https://claude.ai/code/session_017ihL2c5z3q4h8inrn5kwqV

…a plugin driver

A new guide page: the three-way rule (computed / must-not-land / real-time
=> virtual; slow reference data => a real object fed by a scheduled pull),
the plugin-driver recipe as the reference test boots it (driver skeleton,
plugin, datasource, external object, current_user lock formula,
beforeUpdate guard, gantt view, permission set), what a driver receives,
the sharing-model rule, the credentialsRef reading for plugin drivers, a
driver shape-pin test template and five business examples. Linked from the
external-datasources and drivers pages; nav and the docs-audit scope
artifact updated.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2c5z3q4h8inrn5kwqV
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 29, 2026
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation labels Oct 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
ACCEPT · PR #27349 @ beadd67a80300d7882e0fb7c764c8f63902d0ed2 · 2026-10-29T02:24Z

维护者速读

席位意见:ACCEPT。页面只陈述 main 上实测的行为:引擎兜底(#27277)写作「计划中」,凭据一节如实写明插件 driver 不解析 credentialsRef。若 #27277 的裁决改为不做引擎兜底,本页那一句欠一次跟进修改,由 #27277 承接。

席位核对

  1. Shape: read by the seat: one new page (541 lines) plus 4 cross-links and the handwritten-docs registry row. The recipe mirrors packages/runtime/src/plugin-driver-external-object.spike.test.ts (merged in test: spike a virtual object on a plugin driver through steps a to g #27330); the dev booted the page's own skeleton in that harness (6/6) and its shape-pin template against the skeleton (5/5), then deleted the probes.
  2. Premise measured: the page claims nothing that has not landed: the objectql: virtual-object correctness floor — applyDriverQueryInMemory helper, per-datasource row cap with a loud refusal, supports honoured for plugin drivers, refusal-envelope mapping for driver failures (epic #27261 P1) #27277 floor and its refusal codes are "planned"; the default-sharing 403 links access: an external object on the default sharing model refuses every member edit (403) because the ownership checks read owner_id / created_by, which no remote row carries — os validate says nothing (epic #27261, found by the P0 spike) #27332 without promising a fix; DataSync is taught as mapping + job pull because DataSyncConfig is retired.
  3. Gate check-runs on beadd67a: 46 runs — 31 success, 15 skipped, 0 failed or pending; mergeable_state: clean. Dev local: 53 families run, all exit 0; check-affected-docs exit 0; check:skill-examples type-checks both blocks (reverse-verified with injected errors).
  4. Accepted deviations: branch claude/project-thread-6ml89f (platform exception, in the claim); pr_create landed one minute before head beadd67a was pushed (the body names beadd67a).
  5. Out of scope: the plugin-driver credentialsRef reading is carried to the open decision [Decision] Ontology over external systems: an API-backed external datasource authenticates through a connector instance's credential (one Connect per vendor for actions and records), its own auth block, or static credentialsRef only (epic #27261) #27298 (comment 6485908058), not filed. objectql: virtual-object correctness floor — applyDriverQueryInMemory helper, per-datasource row cap with a loud refusal, supports honoured for plugin drivers, refusal-envelope mapping for driver failures (epic #27261 P1) #27277's "shape pin template" item is done here. Clause-②: no · Metadata-protocol: none · Skills-docs: updated here — content/docs/data-modeling/virtual-objects.mdx.
  6. Landing: not governed (check-governed-merges --pr 27349: 0 of 6 paths, 555 lines ≤ 3000) → seat lands: pr_ready + automerge_enable through the relay. After it lands, the triage seat answers object-gantt can no longer write back to a same-origin endpoint after #23082 — a measured producer of the retired view-level api source (read + write) #27130 (card task).

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 04:20
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 01516dc Oct 29, 2026
47 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-6ml89f branch October 29, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants