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

huangyiirene merged 4 commits into
mainfrom
claude/issue-7741-view-door-binding

Conversation

@huangyiirene

@huangyiirene huangyiirene 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.ts — GET /meta/view?object= filters v.viewKind && v.object === obj
  • packages/metadata/src/metadata-manager.ts — getViewsByObject() 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-todo objectstack 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

claude added 4 commits August 12, 2026 13:17
…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

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 12, 2026 3:01pm

Request Review

@github-actions github-actions Bot 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.

@huangyiirene
huangyiirene marked this pull request as ready for review August 12, 2026 15:28
@huangyiirene
huangyiirene added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit a7586cd Aug 12, 2026
28 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-7741-view-door-binding branch August 12, 2026 15:48
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 tests tooling

Projects

None yet

2 participants