Skip to content

spec: inline view arms of the runtime write door require the object binding - #8089

Merged
huangyiirene merged 4 commits into
mainfrom
claude/issue-7741-view-door-binding
Aug 12, 2026
Merged

spec: inline view arms of the runtime write door require the object binding#8089
huangyiirene merged 4 commits into
mainfrom
claude/issue-7741-view-door-binding

Conversation

@huangyiirene

@huangyiirenehuangyiirene commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7741

What

Direction B, per the maintainer ruling of 2026-08-12 on the issue (comment 5261731941): the inline (flattened-overlay) arms of the view wire union now require the object binding at the runtime write door. A flat body like { name, type: 'grid', columns: [...], data: {...} } — which QA run #7695 measured being accepted, published, badged _diagnostics.valid: true, and then served by nothing — is refused at save time with located guidance that reuses defineView's existing wrap prescription.

The binding is a measured pair: object + viewKind

The ruling requires the inline arm to carry the object binding; the PM dispatch defined "binding" as whatever linkage the read paths need to actually serve the view, measured — no more. Measured on this tree, every object-bound read path filters on both fields, in two call sites with the same predicate:

  • packages/rest/src/rest-server.tsGET /meta/view?object= filters v.viewKind && v.object === obj
  • packages/metadata/src/metadata-manager.tsgetViewsByObject() filters v.viewKind && v.object === object

Requiring object alone would refuse the card's repro and then instruct the author into a second dead row — bound by object, still invisible to the switcher for want of viewKind. So both fields are required on the two overlay arms, and only those two: the other identity fields (label, isDefault, order, ...) stay optional because no read path filters on them.

How

Why platform writes keep working:saveMetaItem normalizes before validating, and viewIdentityPatch (#2555) inherits viewKind/object/label from the registry entry the overlay shadows — an expanded ViewItem always carries both — so console pin/sort/hide PUTs on real views arrive at the schema already bound. The body that arrives unbound is the baseline-less one: exactly the dead row this closes.

Draft = active, per the ruling (「draft 与 active 同样适用 …… 不预留」): the pin lives at the schema layer, which both save modes resolve through the same getMetadataTypeSchema('view') entry.

#7736 not foreclosed: the sibling (container body stored but never expanded) is untouched — the container arm's shape and acceptance are byte-identical, pinned by tests; the write door still has no expansion step, which stays #7736's own card.

Tests

  • New packages/spec/src/ui/view-inline-object-binding.test.ts: the card's exact repro body refused through getMetadataTypeSchema('view') with the located guidance asserted (offending shape, serving-filter reason, wrap remedy, paths); half-bindings refused; bound inline body, ViewItem record, and container accepted byte-identically; the post-normalize personalization PUT accepted; JSON-Schema required face pinned in both io directions.
  • Reverse verification (direction predicted first, then run, fix committed first): restoring the two fields to optional turns the repro GREEN through the same door — 8 refusal-side pins go red, all 5 acceptance-side pins stay green. Plain acceptance direction, no inversion.
  • Fixture triage across the union's consumption radius, each fixture re-judged individually: unbound platform-shape fixtures re-spelled to the bound (post-normalize) form in spec, metadata-protocol, rest, objectql, and runtime tests; two deliberate inversions pinned as such — objectql's "plain name-stamp when the registry has no entry to inherit from" (that save IS the dead row; now asserts INVALID_METADATA + 422 + guidance + nothing stored) and metadata-protocol's diagnostics lean-overlay pin (an unbound stored row is badged invalid now). The sweep(spec): 2-item "validation diagnostics don't reach the real defect" sweep — one claim, one PR, per-item checklist (#6391 #5389) #7025 / A bad key inside a form field's publicPicker reports the CONTAINER branch's diagnostic — the author is told to fix viewKind/config, not the key they typed #7510 "acceptance face did not move" corpora keep both generations: bound spellings in ACCEPTED, unbound originals pinned as REFUSED under a [#7741] marker.

Verification

  • Pre-merge (branch base 7dc106738): @objectstack/spec 10116 tests green + typecheck; metadata-protocol 1094, metadata 603, rest 1555, platform-objects 347, objectql 3356, runtime 2171 — all green after triage; example apps app-crm / app-showcase / app-todoobjectstack validate exit 0.
  • Post-merge with origin/main @ f46e987e9 (merged in this branch): @objectstack/spec full suite 384 files / 10165 tests green; check:generated all 13 artifacts current (api-surface regenerated byte-identical; the overlay members deliberately mint no reference/manifest entries per spec/ui: ViewMetadataSchema 的 union 无判别式且容器成员未导出——消费方做失败诊断只能按成员序索引嵌套 errors #6391, so no content/docs/references/** diff exists to ride). Post-merge consumer re-run COMPLETE and green: metadata-protocol, rest, objectql, runtime, metadata, platform-objects all exit 0.
  • Gates (all pass): check:adr-anchors, check:changeset-gate-self-tests, check:cross-package-test-inputs, check:doc-formula-expressions, check:docs-audit-scope, check:i18n, check:merge-driver, check:release-body, check:spec-parsed-alias, check:nul-bytes.
  • Changeset: @objectstack/spec minor.

Residue (deliberate, reported)

  • A stored pre-existing unbound overlay row now reads back valid: false from the diagnostics badge instead of the false valid: true receipt — that is the ruling's point, but operators with legacy dead rows will see the badge flip.
  • An overlay PUT whose shadowed baseline itself lacks the binding (e.g. a bare aggregated-container row) is refused because inheritance has nothing to supply; per the ruling, no carve-out is reserved without measured evidence (the console's measured flows PUT against expanded dotted view names like showcase_task.default, which always carry the pair).

Generated by Claude Code

…inding (#7741)
Direction B per the maintainer ruling of 2026-08-12: the two flattened
overlay members of ViewMetadataSchema now require object + viewKind —
the exact pair the object-bound read paths filter on (GET
/meta/view?object= in rest-server.ts and getViewsByObject() in
metadata-manager.ts both match v.viewKind && v.object === obj) — so an
inline config that could never be served is refused at the door, draft
and active alike, with located guidance that reuses defineView's
existing wrap prescription instead of forking a second copy.
Personalization PUTs are unaffected: normalizeViewMetadata inherits
viewKind/object/label from the shadowed registry entry (#2555) before
validation, so a console pin/sort/hide PUT on a real view arrives
bound. The body this refuses is the baseline-less one — the dead row
QA run #7695 measured being stored and badged valid:true.
Union membership (#6391) is preserved: four arms, same order, same
JSON-Schema anyOf face; the arms' required set is the only move.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
Fixture triage across the wire union's consumption radius: every consumer
test writing an inline view body through saveMetaItem now carries the
object + viewKind binding the arms require — the post-normalize shape a
real shadowing write always had. Two deliberate inversions ride along:
objectql's 'plain name-stamp with no entry to inherit from' pin (that
save IS the #7741 dead row and now 422s with the located guidance,
asserting code INVALID_METADATA + status 422), and the diagnostics
lean-overlay pin (an unbound stored row is badged invalid now).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 12, 2026 3:01pm

Request Review

@github-actionsgithub-actionsBot added size/l documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/permissions/system-context.mdx(via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentationImprovements or additions to documentationprotocol:uisize/lteststooling

Projects

None yet

2 participants

@huangyiirene@claude