Skip to content

fix(spec)!: the view write door refuses an unknown top-level key - #26851

Merged
objectstack-fleet[bot] merged 10 commits into
mainfrom
claude/project-thread-694qho
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 10 commits into
mainfrom
claude/project-thread-694qho

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Fixes #26711

Clause-②: no (narrowing) — the view write door refuses a body carrying an undeclared top-level key on a view item record or an inline list or form view, which it saved before without the key. Nothing is widened.
Metadata-protocol: narrowed (a saved view body may no longer carry a top-level key its shape does not declare)
Skills-docs: none — no page under skills/** or content/docs teaches that the view write door kept an unknown top-level key; the authoring surfaces (defineView / defineViewItem) were already closed

维护者速读

  • 改了什么:view 写入口(ViewMetadataSchema 的 view item 记录、内联 list 覆盖、内联 form 覆盖三个成员)从 .strip() 改为封闭,未知顶层键返回 422 unrecognized_keys 并点名;container 本来就是封闭的。
  • 为什么改:自 spec(ui)+metadata save: judge a flattened view overlay's top-level options.KIND with the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051 起保存的是解析后的 body,未知键(拼错的、已退役的如 striped)会得到 200 但被静默丢弃,与 object / page / dashboard 写入口不一致。
  • 风险与回滚:收窄型变更,带 unrecognized_keys 的旧请求现在会被拒;已测语料(示例 59 个 view、showcase 启动后 36 个已服务 view 的回写)零误伤;唯一受影响的平台写入方是 UI 适配器 createView 给 envelope 加顶层 data,本 PR 已修。回滚 = revert 本 PR。
  • 先看:packages/spec/src/ui/view.zod.ts(closeWireMember 与三个成员),再看 view-wire-unknown-key-refusal.test.ts。
  • 两分钟验证:pnpm --filter @objectstack/spec exec vitest run --project local src/ui/view-wire-unknown-key-refusal.test.ts,以及 pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.save-union-issues.test.ts。
  • 需要你做的:进入队列前需要一次 contract 级评审(收窄型协议变更)。

What changes

ViewMetadataSchema judges every view body saved through PUT /api/v1/meta/view/:name (draft or publish), the MCP metadata tools and Studio. It is a union of four members. The defineView container was already closed; the other three stripped their top level:

  • the view item record { name, object, viewKind, config } (ViewItemWireSchema's arms),
  • the inline list view overlay (ListViewOverlayWireSchema),
  • the inline form view overlay (FormViewOverlayWireSchema).

Since the save stores the parsed body (#20051), a key one of them did not declare answered 200 and was not stored. All three are now closed with the same strictObject / closedObject machinery the other doors use, so the save answers 422 INVALID_METADATA with an unrecognized_keys issue that names the key and the shape: Unrecognized key(s) on this inline list view: `striped`.

  • Console decorations are kept. The preprocess still removes the console's row ids on filter[] / sort[] before the check; the save door removes the read decorations (_diagnostics, _draft) and the server-stamped provenance (_packageId, _packageVersion, _provenance) before it validates, as it did before. Every key the console writes and reads back (VIEW_CONSOLE_ROUND_TRIP_KEYS: isDefault, isPinned, sortOrder, visibility, columnState, _isOverride) is declared on the member it is written on.
  • Retired keys get what their retirement gives them. Keys with a live 18.x tombstone (filterableFields, pageName, tabs, owner, hidden) raise their own prescription, lifted to the union message as before. striped, bordered and virtualScroll retired in 17.x, and their kits have aged out under the ADR-0087 amendment of 2026-10-26 (spec: retire tombstones, conversions and migration entries older than one protocol major; a strict unknown-key refusal with an upgrade-guide link takes over (maintainer ruling D2 = A) #25767 ruling D2 = A), so they are named and pointed at the protocol upgrade guide, which is the channel that amendment prescribes. I did not re-add tombstones for them, since that would contradict the ruling.
  • Alias spellings point at the declared key. objectName gets Did you mean `objectName` → `object`? and a top-level id gets name.
  • One platform writer fixed. ObjectStackAdapter.createView (@object-ui/data-objectstack) added a top-level data to every spec. On a ViewItem envelope data belongs inside config, so that create would now be refused. It now adds data only to a flat list view (patch changeset for @object-ui/data-objectstack under the root .changeset, which is the only changeset directory the release reads). The console's own create flow goes through createRuntimeMetadata / viewEnvelope and was not affected.
  • Reverse pins moved. STILL_STRIP in metadata-type-schemas.test.ts is now empty: view was its last entry, and its "end state" note is kept as history with the premise that changed. The authoring lint's view-divergence pin now asserts the registry schema refuses the key, instead of the old strip finding. The strictness ledger row for view.zod.ts is updated and its counts regenerated.

Tests

  • packages/spec/src/ui/view-wire-unknown-key-refusal.test.ts (new, 17 tests) covers each closed member:
    • a clean body saves;
    • console row ids are still removed;
    • a junk key is refused by name with the member's surface;
    • the container is still closed;
    • striped gets the upgrade-guide sentence;
    • filterableFields gets its live tombstone prescription;
    • objectName and id get their alias guidance.
  • packages/metadata-protocol/src/protocol.save-union-issues.test.ts exercises the real saveMetaItem path:
    • a draft or publish save with a junk key, or with striped, on an inline list view answers 422 INVALID_METADATA and writes 0 rows, and the message names the key;
    • the same holds for a junk key on a view item record;
    • control: a clean draft stores one row;
    • read path: a stored row carrying a junk key is served with _diagnostics.valid: false naming the key.
  • ui/packages/data-objectstack/src/createView.envelopeData-26711.test.ts: createView on an envelope sends no top-level data and the platform schema accepts the body. The control shows that body with the old data is refused, and a flat list view still gets its default data.
  • Re-judged fixtures that froze the strip. Each one now pins the refusal, or moves the key to where it is declared:
    • view-authoring-wire-split, view-filter-rule-wire-id, view-metadata-schema, view-overlay-viewkind-arm, view-strictness-batch18, view-union-diagnostics, view.test;
    • metadata-type-schemas, metadata-authoring-lint;
    • protocol.graft-folded-form-sections;
    • CI round at a3aeacbf: @objectstack/objectql protocol-meta and protocol-publish-rollback (objectName is now refused naming object, and extra is gone from the diff fixture); spec scripts/strictness-ledger.test (the view.zod.ts strip count 4 → 2, with a history line); and view-list-filterable-fields-retirement (an EXCLUDED entry for this PR's own refusal pin).

Acceptance notes

Verification

  • Premise re-measured on main ef7067fd (the QA ran at 3fcafcd3). At schema level, the list overlay, the view item record and the form overlay each accepted a junk key and striped, and stripped them. The container refused.
  • Live showcase boot on this branch:
    • PUT /api/v1/meta/view/showcase_task.qa_rk_view?mode=draft with a clean body: 200.
    • The same body with zzz_junk_key or striped: 422 INVALID_METADATA, and the issue names the key.
    • The stored draft keeps only the clean keys.
    • Round-trip of 36 served views (GET, then PUT back unchanged as a draft): 36 accepted, 0 refused.
  • CI round at a3aeacbf (Test Core 1/8 and 7/8 on 66637aea were this PR's): before the main merge, a locked wide run passed 17 of 17 suites (spec --project repo, objectql, rest, runtime, metadata-fs, metadata-core, cli unit, hono-server, client, audit, mcp, i18n, automation, http-conformance, security, cloud-connection, client-react). On the merged tree (5b87033b), @objectstack/metadata-protocol passed 270 files / 21672 tests, @objectstack/objectql passed 475 / 8843, and spec check:generated exits 0.
  • Locked test runs on this branch, after the fixture re-judging (logs below):
    • spec, metadata-protocol, lint and metadata;
    • the 19 ui test files that reference the view schema exports;
    • spec and metadata-protocol typecheck;
    • check:test-typecheck, check:generated and check:strictness-ledger.
  • Derived gates: all 89 accounted for, 0 not measured. Dogfood on the final (merged) head: 314 files, 2609 tests green.
Test and gate logs

Merged head, under scripts/pm/os-verify-lock.sh (shared box):

spec          pnpm --filter @objectstack/spec test                 Test Files 735 passed (735) · Tests 22045 passed | 1 todo
protocol      pnpm --filter @objectstack/metadata-protocol test    Test Files 269 passed | 4 skipped · Tests 21628 passed | 26 skipped
ui view       19 ui test files on the view schema exports          Test Files 19 passed · Tests 591 passed
adapter       pnpm --filter @object-ui/data-objectstack test       Test Files 101 passed · Tests 1205 passed
dogfood       pnpm --filter '@objectstack/dogfood^...' build; pnpm --filter @objectstack/dogfood test
                                                                   Test Files 314 passed | 3 skipped (317) · Tests 2609 passed | 20 skipped
generated     pnpm --filter @objectstack/spec run check:generated  exit 0 (after the merge)

Pre-merge head (same diff), also locked: spec and metadata-protocol typecheck exit 0, @objectstack/lint test exit 0, @objectstack/metadata test exit 0, pnpm lint exit 0.

Derived gates (node scripts/pm/dispatch-gates.mjs --commands, 89 commands, each run with its exit code recorded):

Run reconciliation — 89 derived, 89 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 89 derived famil(ies) accounted for — 89 run, 0 NOT-MEASURED (a DERIVED zero — all 89 recorded an exit code and none of them is 3).

Six of the 89 first answered exit 3 (prerequisite not met): five spec gates that read dist (the test-file edits had moved the source hash) and check-plugin-teardown-shape --self-test (needs an unshallow clone). They were re-run after pnpm --filter @objectstack/spec build and git fetch --unshallow, and all exit 0.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the maintainer digest the dispatch asks for).
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGufziYCUHZQE3MXucvWe9

…he fixtures that froze the strip

Claude-Session: https://claude.ai/code/session_01WGufziYCUHZQE3MXucvWe9
Co-authored-by: Claude <noreply@anthropic.com>
…iew.zod.ts strip sites 4 → 2 in the strictness ledger

Claude-Session: https://claude.ai/code/session_01WGufziYCUHZQE3MXucvWe9
Co-authored-by: Claude <noreply@anthropic.com>
…r moves

STILL_STRIP empties now that the registered view schema is closed; the
authoring lint's view divergence pin asserts the refusal instead of the
old strip finding; the save-path fixtures that froze a dropped undeclared
key now pin its refusal by name.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufziYCUHZQE3MXucvWe9
…em envelope

The view write door now refuses a key the envelope does not declare, and
data is declared only inside config there. A flat list view keeps its
default data.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufziYCUHZQE3MXucvWe9
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

10 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 55 of 207 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 147 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 e78d7057d547e7d57b64e551432b018f2453e322 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4d94c7e46b1eac043e43f20cb63b272a84efa079 — the merge of head 5b87033b5ad58bab6669a375530d0aff85d199db into base e78d7057d547e7d57b64e551432b018f2453e322, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4d94c7e46b1eac043e43f20cb63b272a84efa079 && git checkout 4d94c7e46b1eac043e43f20cb63b272a84efa079
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e78d7057d547e7d57b64e551432b018f2453e322 5b87033b5ad58bab6669a375530d0aff85d199db && git checkout -B drift-repro e78d7057d547e7d57b64e551432b018f2453e322 && git merge --no-ff 5b87033b5ad58bab6669a375530d0aff85d199db

node scripts/docs-audit/affected-docs.mjs --json e78d7057d547e7d57b64e551432b018f2453e322

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Test UI (12/16) is red on 66637ae at ui/packages/core/src/actions/__tests__/actionKeys.pin.test.ts ("missing disabledReason"). This PR does not touch that file or the action keys. The red comes from main after #26786, and the fix is #26859, which is queued. I will merge main into this branch once #26859 lands, rather than port it here.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 66637aea7e4813ce7072768442ba2cb6d7e2f295
Local-runs: none
Scope: packages/spec/src/ui/view.zod.ts + packages/spec/src/shared/strict-object.ts, packages/spec/src/shared/suggestions.zod.ts, packages/spec/src/kernel/metadata-protection.zod.ts, packages/metadata-protocol/src/protocol.ts, docs/adr/0087-metadata-protocol-upgrade-contract.md, ui/packages/data-objectstack/src/index.ts, ui/packages/app-shell/src/views/objectViewTabs.ts, ui/packages/app-shell/src/views/runtime-metadata-persistence.ts, ui/packages/app-shell/src/views/objectViewViewCrud.tsx, ui/apps/console/src/pages/developer/PublicFormsPage.tsx

Reviewed against card #26711 (body, triage 6444389072, claim 6447314891, os-dev-report 6450470105, claim amendment 6450527026), PR #26851 (body, 19-file list, git diff origin/main...pr26851 at 288cb535...66637aea) and the head's check-runs. The head merges main; only the PR's own hunks were judged. Nothing was built, run or re-run.

① Derived judgments

The narrowing itself: right. ViewItemWireSchema's two arms go from z.object(...).strip() to strictObject(VIEW_ITEM_WIRE_SURFACE, ...); ListViewOverlayWireSchema and FormViewOverlayWireSchema go from .extend(...).strip() / .safeExtend(...).strip() to closeWireMember(...). closeWireMember takes member.strict(), clones its def with strictObjectError(options, def.shape) and re-declares it through closedObject, which is the same terminal-refusal constructor strictObject uses (strict-object.ts lines 505-545). Building the error map from the member's own shape, not the authoring shape it extends, is the right call: an extension inherits the parent's refusal, whose candidate list would miss isPinned, columnState and the other overlay-only keys. The three members declared the same keys before and after; the only accept-set change is that an undeclared top-level key is refused with unrecognized_keys instead of being dropped. The container member was already closed and is untouched. Nested blocks keep their posture (columnState: ViewColumnStateSchema still strips inside, by design, and the ledger row names it as one of the two remaining strip sites).

Every key a shipped writer or the console sends is still accepted: right, measured on the writers. I read every non-test view write in ui/:

  • objectViewViewCrud.tsx save: buildViewConfigSaveBody (objectViewTabs.ts 541-558) emits viewEnvelope (name, object, viewKind, label, config) plus VIEW_ROW_STATE_KEYS, which is derived from the spec's own VIEW_CONSOLE_ROUND_TRIP_KEYS filtered to the viewItem member. Nothing undeclared.
  • objectViewViewCrud.tsx / ObjectDataPage.tsx create: createRuntimeMetadata('view', env.name, env) with the same envelope. Nothing undeclared.
  • Toolbar patches through dataSource.updateView: setDefaultViewPatches sends { isDefault }, the rename sends { label }, the pin sends { isPinned }; the adapter's mergeViewPatch adds only name and object. All declared on viewItem and listOverlay.
  • PublicFormsPage.tsx (361, 439) echoes a served item back with a rewritten config. The served item's _diagnostics / _draft are removed by stripReadDecorations and _packageId / _packageVersion / _provenance by DERIVED_PROVENANCE_KEYS, both in protocol.ts before validation (lines 20026 and the saveMetaItem persists the caller's _packageId/_provenance verbatim, and non-object boot hydration reads it back as truth — a Studio GET-then-PUT can brick a tenant's own app with NOT_OVERRIDABLE forever #16702 strip one beat later); the rest of the ADR-0010 envelope (_lock, _lockReason, _lockSource, _lockDocsUrl) is declared by MetadataProtectionFields, which both viewItemArmShape and flattenedViewOverlayFields spread in. Nothing undeclared.
  • The spec(ui)+objectui: declare the console's round-trip keys on the stored view overlay (#20051 stage ii, ruling 甲) #20456 census note in view.zod.ts says the toolbar save once wrote objectName back and the console once read a top-level id; viewIdentity.ts records that every write now stamps name and none writes id / _id, and no write path I read stamps objectName. The PR's alias entries (objectName to object, id to name) therefore point at a declared key without any live writer sending the alias. Right.
  • @object-ui/data-objectstack createView was the one platform writer that broke (top-level data on an envelope); the PR fixes it (isEnvelope branch) and pins both directions. No ui/ non-test caller of createView exists, so the fix protects the adapter's public API, not a console flow. Right, and in scope under the seat's amendment 6450527026.

STILL_STRIP now empty: right. view was its last entry; the preprocess stripViewConsoleDecorations never removed top-level keys, only filter[].id / sort[].id (the isRow branch of stripRowDecorations, lines 500-534), and that strip is kept and pinned ("the console's row ids are still removed before the parse, not refused"). Nothing the list stripped is sent by a real writer.

Live tombstones keep their prescriptions: right. filterableFields, pageName, tabs, owner, hidden are still retiredKey() calls in the shape at the head (lines 3009, 3053, 3212, 5292-5293, 5949-5950), and strictObjectError excludes tombstones from the candidate list via acceptsNothing while leaving them in the shape, so each still raises its own invalid_type / never prescription at its path. Pinned (filterableFields on the inline list view, matching the "was removed in @objectstack/spec 18" message).

striped / bordered / virtualScroll get the upgrade-guide sentence, not re-added tombstones: right under the 2026-10-26 addendum. The ADR at the head (status line and the "Addendum (2026-10-26)") says a kit lives one major after its retirement and is then removed, and a removed key "is refused as a strict unknown key whose refusal names the protocol upgrade guide". These three retired at 17 (migrations/entries/retired-keys/17.ui__ListView__{striped,bordered,virtualScroll}.ts exist at both refs) and their tombstones are already absent from view.zod.ts at origin/main, so re-adding them would contradict the ruling. strictUnknownKeyError (suggestions.zod.ts line 440) appends RETIRED_KEY_UPGRADE_GUIDE_SENTENCE to every unknown-key refusal, so the sentence arrives on the closed members with no per-key work; pinned on listOverlay and viewItem.

The moved pins measure consequences, not bending. Each reversed assertion now checks the refusal's shape, not just success: false: unrecognized_keys with the key named and the member's surface (view-authoring-wire-split, view-metadata-schema, view-strictness-batch18, view-union-diagnostics, view-overlay-viewkind-arm), the keys array (view.test viewType), the live saveMetaItem path with 422 / INVALID_METADATA / zero rows plus a clean control row (protocol.save-union-issues, protocol.graft-folded-form-sections refusedUndeclared), the read path badging a stored junk key _diagnostics.valid: false, and the authoring-lint pin that now asserts the registry schema refuses bogusViewKey with a clean control. Two fixture edits move a key to where it is declared rather than deleting the case: the kanban "no columns" fixture puts groupByField under kanban and still carries no top-level columns, and the row-id strip pins move from a form overlay (which never declared filter / sort) to a list overlay while the form rows are kept as invalid_union refusals. The strictness ledger row explains the 4 to 2 count honestly (the two arms were counted sites; the overlays never were) and names the two remaining strip sites.

Metadata-protocol consumer-side strips: right. protocol.ts already strips read decorations and derived provenance before the schema gate, so the closed members see the same document the store receives. No new lenient alias or ?? fallback was added anywhere in the diff.

② Semver level

  • @objectstack/spec: minor, with Clause-②: no (narrowing) and Metadata-protocol: narrowed on both the PR body and the changeset, a BREAKING banner, Before / Now / Unchanged / Who sees it / The remedy. Right: a protocol kind (narrowed) takes at least minor on the package the diff moves; the protocol's major is not the npm major (ADR-0087), so minor is the correct level.
  • ADR-0087 marker: not-required (no-migration-prescription). Right: no key of any schema is added, removed or renamed; the save stored the parsed body since spec(ui)+metadata save: judge a flattened view overlay's top-level options.KIND with the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051, so no stored row can carry what the strip dropped, and nothing exists for os migrate meta to rewrite. The "remedy" paragraph names where a misplaced key belongs, which is author guidance, not a FROM-to-TO rewrite of a declared surface. Lint & Repo Gates (which carries check:adr-0087-registration) is green on this head.
  • @object-ui/data-objectstack: patch, Clause-②: no, Metadata-protocol: none. The level and both declaration lines are right: the adapter changes what it sends, not what any author can write. The entry's location is wrong, and it is a must-fix: it sits at ui/.changeset/26711-create-view-envelope-data.md, a directory no release reads. Measured at origin/main: ui/.changeset/ has no config.json, every file in it arrived with the objectui subtree import (fd40831, 2026-10-26) and none has been added since; no script under scripts/ and no workflow under .github/ reads ui/.changeset; the release lane runs changeset version from the repository root, whose .changeset/config.json already carries @object-ui/data-objectstack (and every other @object-ui/* package) in its fixed group; and the precedent is root entries (.changeset/25769-ownership-queue.md, 25883, 25918, 25922, 25923, 25992 all name @object-ui/*, and feat(spec,rest,cli): a doc book's title and section titles are translatable — books.<name> in the translation bundle #26819 released @object-ui/i18n through .changeset/26690-translation-books.md). So as committed, the adapter's behaviour change ships in the tarball with no CHANGELOG.md line an upgrading agent can grep: the fixed group bumps the package, but the note is never compiled. Post-Task Checklist step 3 ("a bug fix in a released package takes a patch changeset, never none") is not met for this package. Fix: move the file, unchanged, to .changeset/26711-create-view-envelope-data.md (its frontmatter, body and both declaration lines are already right), and delete the ui/.changeset copy. Folding the package into the spec changeset would also work but is worse: that entry is minor with a BREAKING banner, and the adapter's note is a patch with Metadata-protocol: none. Check Changeset is green on this head only because the root spec changeset satisfies it; it does not see the ui/ file either way.
  • Generated spec artifacts: none changed in the diff. json-schema.manifest/ records no additionalProperties, the authorable surface records keys (none moved), and Type Check · source gates (the gate that runs check:generated) is green on this head. Consistent.

③ Boundary flags

  • Dev flags in the PR body: "That part is inferred from the same parse and was not driven through the console" (a legacy row's next console save being refused). Answered: the inference is pinned at the layer below the console (refusedUndeclared on the real saveMetaItem, and the read-path _diagnostics pin), and the console's own write bodies were read above and carry no undeclared key. No escalation needed.
  • open_questions: [], out_of_scope_findings: [] in os-dev-report 6450470105. Nothing left unanswered.
  • The createView fix sits outside claim 6447314891's file surface; claim amendment 6450527026 by the owning seat widens the surface to ui/packages/data-objectstack createView, its test and one ui/.changeset entry, under the rule the claim already set for example and platform-objects views ("fixed, not tolerated"). The diff touches exactly those three paths under ui/. In scope.
  • Governed surfaces: none in the file list (Governed Surface Queue Guard green). The diff touches a published schema, which is what makes this record owed at CONTRACT_REVIEW_TIER.
  • PR body truth against the diff: every claim checked holds (the three members, the console decorations, the live tombstones, the aged-out trio, the aliases, the createView fix, STILL_STRIP, the ledger, platform-objects ships no view-type item, the console create flow is unaffected), except one sentence whose attribution is wrong:
    • Stale sentence (under "What changes", first bullet): "The preprocess still removes the read decorations (_diagnostics, _draft), the server-stamped provenance and the console's row ids on filter[] / sort[] before the check."
    • Replacement: "The preprocess still removes the console's row ids on filter[] / sort[] before the check; the save door removes the read decorations (_diagnostics, _draft) and the server-stamped provenance (_packageId, _packageVersion, _provenance) before it validates, as it did before."
    • Why: stripViewConsoleDecorations only strips row ids (view.zod.ts lines 500-534); the other two classes are stripped in protocol.ts (stripReadDecorations at line 20026 and DERIVED_PROVENANCE_KEYS). The changeset's "Unchanged" bullet already states it correctly. Should-fix, body only; no code change.
    • Stale once the must-fix lands (under "What changes", "One platform writer fixed" bullet): "It now adds data only to a flat list view (patch changeset under ui/.changeset)."
    • Replacement: "It now adds data only to a flat list view (patch changeset for @object-ui/data-objectstack under the root .changeset, which is the only changeset directory the release reads)."
    • The same correction applies to the 维护者速读 line "本 PR 已修" context and to os-dev-report 6450470105's "patch changeset under ui/.changeset"; the report is a record and stays as written, the body is the dev's to update.
  • Claim amendment 6450527026 names "one ui/.changeset patch entry" as the added file surface. The entry belongs in the root .changeset/ (see ②); moving it stays inside the original claim's own wording ("one changeset with its ADR-0087 marker" is the root directory, and the adapter's note needs no marker because it is not breaking). No further amendment is needed for the move; the seat may want to correct the amendment's path so the next reader does not copy it.
  • Check-runs on 66637aea7e4813ce7072768442ba2cb6d7e2f295, observed 2026-10-27T18:47:44Z: completed and green are Lint & Repo Gates, Governed Surface Queue Guard, Build & E2E UI, Build UI Deps, Check Changeset, Check PR Size, Plan Test Core shards, Spec property liveness, Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, every Lint · lane, Dogfood Regression Gate, PR-body guards, Flag docs affected by code changes, Check Documentation Links, Auto Label, filter. Still in progress at that time: Build Core, Lint UI, Type Check UI, Type Check · workspace, Test Core (1/8 to 8/8), Test UI (1/16 to 16/16). Skipped by design: the opt-in harnesses, Build Docs, Dogfood Verify CLI, Temporal Conformance. No red among the completed runs. The landing still waits on the required contexts finishing green; that is the queue's reading, not this record's.

④ Design judgment

view.zod.ts against the code it cites and the scenario it is accepted against: the change holds. The #5599 ruling kept .strip() only while the save stored the original body; #20051 (parsed body stored) and #20456 (round-trip keys declared) removed both reasons, and the file's own docblocks are rewritten to say so rather than deleted, keeping the derive-by-reference rule (viewItemArmShape shared by both doors) intact. closeWireMember is a small, local helper that reuses strictObjectError and closedObject rather than a second strictness vocabulary; it registers each member in declarationStore() like any strictObject, so the alias / known-key audit sees the three new surfaces. The refusal order (which key, the fix, the upgrade-guide pointer, then history) follows the #5955 rule. The ui/ fix ships with the removal that breaks it, as Post-Task Checklist step 4 requires. Nothing here advertises more than it enforces.

Must-fix: move ui/.changeset/26711-create-view-envelope-data.md to .changeset/26711-create-view-envelope-data.md unchanged and delete the ui/.changeset copy; no release reads ui/.changeset, so as committed the @object-ui/data-objectstack fix ships with no CHANGELOG line (② above). One file move; the schema change, the tests and the spec changeset need nothing.
Should-fix: the two PR-body sentences above (the strip attribution, and "under ui/.changeset" once the move lands).

Everything else in this PR holds; a re-review of the moved file is a diff read of one path, and this record's ① and ④ judgments carry over to the head that moves it.

Implemented-by: claude/project-thread-694qho
Reviewed-by: session_01WGufzfhobWEgUUrjz4rrbh

VERDICT: FAIL

…oot changeset directory

Every @object-ui package releases through the root .changeset; the
imported ui/.changeset is read by nothing.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufziYCUHZQE3MXucvWe9
…r moves

The strip-site count for view.zod.ts reads 2 now that the item wire arms
closed; the filterableFields absence walk excludes the refusal pin that
authors the key on purpose; two objectql save fixtures stop carrying an
undeclared key and pin its refusal instead.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufziYCUHZQE3MXucvWe9
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5b87033b5ad58bab6669a375530d0aff85d199db
Local-runs: none
Scope: .changeset/26711-create-view-envelope-data.md, packages/spec/scripts/strictness-ledger.test.ts, packages/spec/src/ui/view-list-filterable-fields-retirement.test.ts, packages/objectql/src/protocol-meta.test.ts, packages/objectql/src/protocol-publish-rollback.test.ts + packages/spec/scripts/lib/strictness-ledger.ts, packages/spec/src/ui/view.zod.ts, packages/spec/src/ui/view-wire-unknown-key-refusal.test.ts, packages/spec/src/shared/suggestions.zod.ts, packages/metadata-protocol/src/protocol.ts, packages/metadata-protocol/src/protocol.save-union-issues.test.ts, docs/audits/2026-07-unknown-key-strictness-ledger.md, .changeset/config.json, scripts/check-changeset-fixed.mjs; and, carried over from the full record on 66637aea (PR comment 6450818608), its whole Scope line.

Delta record over the full review at 66637aea7e4813ce7072768442ba2cb6d7e2f295 (comment 6450818608, VERDICT FAIL, one must-fix: the @object-ui/data-objectstack changeset sat in ui/.changeset, which no release reads). Judged here: the PR's three commits since that head, 1c246bd2, a3aeacbf and the merge 5b87033b, against card #26711 (body, triage 6444389072, claim 6447314891, os-dev-report 6450470105, claim amendment 6450527026), the PR body as it now reads, the file list (23 paths), git diff origin/main...pr26851, the failing job logs on the prior head (Test Core 1/8 job 122392250786, Test Core 7/8 job 122392250971) and the check-runs on this head. Nothing was built, run or re-run.

① Derived judgments

The must-fix is closed by 1c246bd2: right. The commit is a pure rename (similarity index 100%), ui/.changeset/26711-create-view-envelope-data.md to .changeset/26711-create-view-envelope-data.md; at the head git ls-tree shows the root file and no ui/.changeset/26711-* copy. The entry's content is unchanged from what the full record already judged right: '@object-ui/data-objectstack': patch, a body that names the before (the door dropped data, the view saved), the now (refused naming data), the unchanged flat-list case and the unaffected console create flow, and both declaration lines (Clause-②: no, Metadata-protocol: none). The root .changeset/config.json at the head lists @object-ui/data-objectstack in its fixed group (line 91), so changeset version from the repository root reads the entry, bumps the package with the group and compiles the note into its CHANGELOG. check:changeset-fixed (scripts/check-changeset-fixed.mjs) measures only that every public workspace package is enumerated in the fixed group and that every listed name exists; it reads no changeset file and takes no position on the level, so a patch entry for a group member is accepted by construction, and Check Changeset, Lint & Repo Gates and Lint · CI, release & engine gates are all green on this head. Nothing under scripts/ or .github/ reads ui/.changeset (the single textual hit is a self-test fixture path in scripts/pm/check-governed-merges.mjs, which asserts that path is not governed). Post-Task Checklist step 3 is met for the adapter now.

a3aeacbf fixes exactly the two Test Core reds on the prior head, and each pin measures a consequence of the narrowing rather than bending to go green. The prior head's logs show shard 1/8 red in @objectstack/spec test:repo with two files (scripts/strictness-ledger.test.ts: "expected 2 to be 4"; src/ui/view-list-filterable-fields-retirement.test.ts: one offender in the absence walk) and shard 7/8 red in @objectstack/objectql with two files (protocol-meta.test.ts "[#20051] stores the console's declared round-trip keys, and drops the objectName alias"; protocol-publish-rollback.test.ts "diffMetaItem returns structured top-level key diff"). The commit touches those four files and nothing else.

  • strictness-ledger.test.ts, 4 to 2: the instrument (analyzeSites in scripts/lib/strictness-ledger.ts) counts z.object( / strictObject( / z.looseObject( call sites and walks the chain for posture. At the head view.zod.ts has three z.object( sites: the form field base (line 3681, no chain, strip), buttons (line 4759, .strict(), strict) and ViewColumnStateSchema (line 5415, .describe() only, strip). Two strip sites is the measured count, the ViewItemWireSchema arms having become strictObject( in the PR's own schema change; the overlays were .extend(...).strip() and were never counted, as the ledger row says. The pin's new history line ("4 to 2 at spec: the view save door silently strips unknown top-level keys from the view envelope (200, not stored) while object, page and dashboard bodies refuse them 422 unrecognized_keys #26711", naming the two sites left) follows the file's own convention of recording each count move with its arithmetic, and agrees with the ledger row (line 949) and the regenerated counts artifact (counts/ui.md: view.zod.ts | 2 | 59). check:strictness-ledger runs in Spec property liveness, green on this head. A real consequence, recorded honestly.
  • view-list-filterable-fields-retirement.test.ts, one EXCLUDED entry: the walk is structural (an object literal whose own key is filterableFields), and view-wire-unknown-key-refusal.test.ts line 113 authors exactly that ({ ...CLEAN.listOverlay.body, filterableFields: ['name'] }) to assert the live tombstone still raises its own invalid_type / never prescription on the closed inline list view rather than a bare unknown-key refusal, which is the one pin that proves closing the member did not swallow the kit's prescription. The kit's own rule for EXCLUDED is that each entry's job is to spell the retired key as a key, with its reason; the three sibling pins (view-union-retirement-prescription, lint-liveness-properties, the lint twin) are excluded on the same ground. The alternative the walk itself names, a computed key, is the thing the walk declares invisible, so that would have been the bend; the exclusion with a reason is the honest route. RESIDUE stays empty.
  • protocol-meta.test.ts: the stage (iv) pin asserted that isPinned / isDefault / sortOrder are stored and the undeclared objectName alias is dropped. It now asserts the first half unchanged (persisted equals the declared body, object is lead) and, as a second step on a cleared mock, that the same body plus objectName: 'lead' is refused with INVALID_METADATA / 422, the message carrying "Did you mean objectName → object?", and no insert. That is the narrowing's consequence stated at the layer the test lives on: the body (validView: object, viewKind: 'list', columns, no config) lands on the inline list member, whose refusal is built with VIEW_WIRE_ALIASES (view.zod.ts line 5614) through closeWireMember, and strictUnknownKeyError renders the rename exactly as `objectName` → `object` (suggestions.zod.ts line 419, 429); the same string is pinned per member in view-wire-unknown-key-refusal.test.ts lines 123-127. The save's message is view/all_leads failed spec validation: plus the first findings as path: message (specValidationFindings, default face), and the metadata-protocol pin at protocol.save-union-issues.test.ts line 441 already relies on a branch-level message reaching err.message through the same union flattening. Is a real writer still sending objectName? No: the full record read every non-test view write in ui/ (buildViewConfigSaveBody, createRuntimeMetadata with viewEnvelope, the toolbar patches through mergeViewPatch, PublicFormsPage), and at this head an unfiltered grep of objectName across ui/packages/app-shell/src/views, data-objectstack/src/index.ts, plugin-view and plugin-list finds only local variables, action props and route params, never a key on a view body; viewIdentity.ts records that every write stamps name. The spec(ui)+objectui: declare the console's round-trip keys on the stored view overlay (#20051 stage ii, ruling 甲) #20456 census note in view.zod.ts (line 6645, "a saved view's toolbar save writes it back") describes the console before viewIdentity.ts; the alias entry exists for an author who still spells it, and the pin now measures that author's answer. Intent kept, consequence pinned.
  • protocol-publish-rollback.test.ts: the test's job is the structured diff between two stored versions (added is ['rowHeight'], changed is ['columns', 'label']); extra: 1 was a stage (iv) rider showing an undeclared key never reached a version because the save stored the parsed body. With the door closed the rider would refuse the whole second save and the diff could never run, so removing it is the only way to keep the test's own assertion, and the comment records why. The claim the rider carried ("an undeclared key never reaches a version") is now pinned at its stronger form by protocol.save-union-issues.test.ts (spec: the view save door silently strips unknown top-level keys from the view envelope (200, not stored) while object, page and dashboard bodies refuse them 422 unrecognized_keys #26711 block: 422, rows.size 0) and by the read-path _diagnostics pin. No seed or writer sends extra; it was a fixture name. Intent kept.

The merge 5b87033b brings nothing of its own. git log --cc -p -1 5b87033b prints no hunk (no conflict resolution, no evil merge). Its parents are a3aeacbf and 20457f6f, which is origin/main as fetched, so git merge-base pr26851 origin/main is origin/main itself and git diff origin/main...pr26851 is the PR's own content: 23 files, equal to the GitHub file list. Measured, not inferred: git diff f79ec289 a3aeacbf (the PR's commits over the main it had merged before) and git diff origin/main pr26851 are byte-identical, and main touched none of the 23 paths between f79ec289 and 20457f6f. Of the full record's Scope files, main's merge changed only packages/metadata-protocol/src/protocol.ts (the #26853 package draft doors, hunks at lines 4619-7914 and 12961-13584), far from the view save door at 20074-20900 where stripReadDecorations (20078), the DERIVED_PROVENANCE_KEYS strip (20089) and the schema gate sit; view.zod.ts, strict-object.ts, suggestions.zod.ts, metadata-protection.zod.ts, ADR-0087, data-objectstack/src/index.ts and the four console writers are unchanged between 66637aea and this head.

Every PASS judgment of the full record still holds at this head. The narrowing (strictObject on the two ViewItemWireSchema arms, closeWireMember on the two overlays), the writers' accept set, the empty STILL_STRIP, the live tombstones, the aged-out trio's upgrade-guide sentence, the moved spec and metadata-protocol pins, the consumer-side strips and the createView fix are all in files the three new commits do not touch and main's merge does not alter; the diff-of-diffs above is the proof that the hunks are the same ones that were judged.

② Semver level

  • @objectstack/spec: minor, Clause-②: no (narrowing), Metadata-protocol: narrowed, ADR-0087 marker not-required (no-migration-prescription). Unchanged since the full record and still right; .changeset/26711-view-write-door-unknown-key-refused.md is untouched by the three commits.
  • @object-ui/data-objectstack: patch, Clause-②: no, Metadata-protocol: none, now at .changeset/26711-create-view-envelope-data.md. Right on level, right on both lines, and now in the one directory the release reads (① above). The fixed group will carry the package to the spec's minor with the rest of the monorepo while the note itself is a patch, which is how every other @object-ui/* root entry (25769, 25883, 25918, 25922, 25923, 25992, 26690) ships.
  • Generated spec artifacts: none changed by the three commits; Spec property liveness and the Lint lanes are green on this head, and Type Check · source gates (which runs check:generated) is still in progress at the time below.

③ Boundary flags

  • Claim and amendment: claim amendment 6450527026 names "one ui/.changeset patch entry"; the entry now lives at the root .changeset, which is where the original claim's "one changeset" wording already pointed. The seat may correct the amendment's path in prose; no further amendment is owed, as the full record said.
  • a3aeacbf touches packages/objectql test files, which neither claim 6447314891 nor amendment 6450527026 names by path. Both name "the spec tests" and the rule for anything the narrowing refuses ("fixed, not tolerated"); these are reverse pins that froze the strip in a consumer package's tests, re-judged with no runtime change, the same kind the claim's spec and metadata-protocol pins are. Inside the claim's intent; noted so the next reader is not surprised by the path.
  • PR body truth. The two sentences the seat rewrote are verified against the tree: (1) "The preprocess still removes the console's row ids on filter[] / sort[] before the check; the save door removes the read decorations (_diagnostics, _draft) and the server-stamped provenance (_packageId, _packageVersion, _provenance) before it validates, as it did before." True: stripViewConsoleDecorations in view.zod.ts strips row ids only, and protocol.ts at this head strips _diagnostics / _draft at line 20078 and the three provenance keys at line 20089 before the schema gate. (2) "patch changeset for @object-ui/data-objectstack under the root .changeset, which is the only changeset directory the release reads." True at this head (① above). Two further body sentences are now incomplete, both should-fix and body only:
    • Under "Tests", the "Re-judged fixtures that froze the strip" list names the spec and metadata-protocol files but not the four a3aeacbf re-judged: packages/objectql/src/protocol-meta.test.ts, packages/objectql/src/protocol-publish-rollback.test.ts, packages/spec/scripts/strictness-ledger.test.ts and packages/spec/src/ui/view-list-filterable-fields-retirement.test.ts. Add them, with one line each on what the pin now measures (the alias refused by name; the diff fixture without the undeclared rider; the measured strip count 2; the refusal pin excluded from the absence walk by reason).
    • Under "Verification", the locked runs name spec, metadata-protocol, lint, metadata, the 19 ui files and the adapter, and the logs are from 66637aea. No run of @objectstack/objectql or of spec's repo vitest project (test:repo, the project the two shard 1/8 pins live in) is recorded for a3aeacbf, so the four pins' first measurement is this head's CI (Test Core 1/8 and 7/8). Say so, or add the two runs. The os-dev-report 6450470105 is a record of the earlier head and stays as written.
  • The 维护者速读 and the rest of the body (the three members, the decorations, the tombstones, the aged-out trio, the aliases, the createView fix, STILL_STRIP, the ledger, the corpus numbers) read true against this head; the live showcase and corpus measurements were made on earlier heads of the same diff, which the body says.
  • Governed surfaces: none in the 23-file list; Governed Surface Queue Guard green on this head.
  • Check-runs on 5b87033b5ad58bab6669a375530d0aff85d199db, observed 2026-10-27T19:59:21Z: completed and green are Lint & Repo Gates, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, the three Lint · scoped lanes, Governed Surface Queue Guard, Check Changeset, Check PR Size, Check Documentation Links, PR-body guards, Plan Test Core shards, Spec property liveness, Build UI Deps, Dogfood Regression Gate, Flag docs affected by code changes, Auto Label, filter. Still in progress at that time: Build Core, Build & E2E UI, Lint UI, Type Check UI, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, Test Core (1/8 to 8/8), Test UI (1/16 to 16/16). Skipped by design: the opt-in harnesses, Build Docs, Dogfood Verify CLI, Temporal Conformance, the Dogfood shard matrix. No red among the completed runs. On the prior head the reds were Test Core 1/8 and 7/8 (the four pins above) and Test UI 12/16 (actionKeys.pin.test.ts, main's feat(spec): a greyed action says why — disabledReason beside disabled #26786 red, fixed by fix(ui): the action key inventory lists disabledReason #26859 which this merge brings in, comment 6450811781). The landing waits on the required contexts finishing green; that is the queue's reading, not this record's.

④ Design judgment

The delta changes no schema, rule or runtime: one file move and four re-judged pins. Each pin is judged against the code it cites. The ledger pin records what the instrument measures and why the number moved, in the file's own history idiom, so the next count move has to come through the same line. The absence-walk exclusion names the one pin whose job is to author the retired key and says what it proves, which is the kit's own §4 discipline, and leaves the walk's radius and residue untouched. The two objectql pins keep their original assertions (stored round-trip keys; a structured diff of declared keys) and replace the strip finding with the refusal, measured through the real saveMetaItem with a control that the clean body still stores. The createView note now reaches the adapter's CHANGELOG. The full record's ④ on view.zod.ts carries over unchanged, since the file is byte-identical between the two heads.

Must-fix: none.
Should-fix: the two PR-body gaps under ③ (the "Tests" list missing the four a3aeacbf files; the "Verification" section recording no @objectstack/objectql or spec test:repo run for the re-judged pins, whose first measurement is this head's Test Core 1/8 and 7/8). Body only; no code change.

Implemented-by: claude/project-thread-694qho
Reviewed-by: session_01WGufzfhobWEgUUrjz4rrbh

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · 2026-10-27T20:24Z

Head 5b87033b5ad58bab6669a375530d0aff85d199db. Card #26711, claim 6447314891 with amendment 6450527026 (adds the createView fix in @object-ui/data-objectstack), dev report 6450470105.

  • Contract review: owed at CONTRACT_REVIEW_TIER because this is a narrowing of view.zod.ts.
    • The full review (6450818608) at 66637aea was a FAIL with one must-fix: the adapter's changeset sat in ui/.changeset, which no release reads. Everything else was judged right:
      • the view item record and the inline list and form overlays now refuse an undeclared top-level key with unrecognized_keys, with the same keys declared as before;
      • every console and adapter writer sends only declared keys;
      • the live tombstones keep their prescriptions;
      • striped / bordered / virtualScroll correctly get the upgrade-guide sentence and no re-added tombstone.
    • The delta review (6452260177) at this head was a PASS:
      • 1c246bd2 moves the changeset to the root .changeset as a pure rename.
      • a3aeacbf fixes the two Test Core shards this PR broke, and each pin measures a real consequence: the strictness-ledger strip count 4 → 2, the filterableFields walk's EXCLUDED entry for this PR's own refusal pin, and the objectql fixtures that held an undeclared objectName / extra.
      • 5b87033b merges main (no rebase) with no hunk of its own.
  • Body: the seat rewrote two sentences after the full review, then added the CI round's fixtures and runs after the delta review.
  • Shape: Fixes #26711, 23 files, +626/−149. @objectstack/spec minor with ADR-0087 not-required (no-migration-prescription), and @object-ui/data-objectstack patch. Clause-②: no (narrowing) and Metadata-protocol: narrowed. check-governed-merges --pr 26851: NOT governed.
  • Gates: check-runs on this head are 58 success, 8 skipped and 0 red. mergeable_state is clean.
  • Landing: Tier S. pr_ready + automerge_enable in this act.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 20:25
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 20:25
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 8a218fc Oct 27, 2026
67 checks passed
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 protocol:ui size/l tests tooling

Projects

None yet

2 participants