Repository navigation
feat(spec)!: ListView.userActions.editInline defaults to true on the v18 line; editInline: false is the opt-out (#22605, spec half) - #22629
Conversation
…18 line; editInline: false is the opt-out Maintainer ruling 2026-10-10, verbatim: 「乙 v18 把 spec 默认翻成 true,editInline: false 变成关法。」 A list view is editable in place by default under the permission gate that already exists. The declared default of UserActionsConfigSchema.editInline moves false -> true with the describe rewritten; the declared default change is ledgered at major 17 and the D3 semantic entry list-view-edit-inline-default-on is added at protocol 18. Pins: an absent key parses to true, an explicit false stays false, the view-level inlineEdit still parses as before. Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
…ages for the editInline default; add the changeset gen:migration-registry concatenates the D3 entry into registry.ts; the spec build moved ui/UserActionsConfig:editInline to true in authorable-defaults/ui.json under the declared chain; gen:docs rewrote the four editInline rows in references/ui/view.mdx and page.mdx. spec-changes.json and docs/protocol-upgrade-guide.md are not regenerated here: the PR-stage check projects the registry in memory and compares no committed copy, and the committed copies on origin/main already carry sibling drift this diff does not own. Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
…it-inline-default-on
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 7ea2f37356cdc3787e2053c4786707be157a22a5 && git checkout 7ea2f37356cdc3787e2053c4786707be157a22a5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f368b7e980d3df201620098be965523b50d6df19 fc3733b9714493610f60cc80e7849f36471a6548 && git checkout -B drift-repro f368b7e980d3df201620098be965523b50d6df19 && git merge --no-ff fc3733b9714493610f60cc80e7849f36471a6548
node scripts/docs-audit/affected-docs.mjs --json f368b7e980d3df201620098be965523b50d6df19 |
Contract reviewServed-tier: Read-only: the card body and its four comments (triage ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL Generated by Claude Code |
…set declares Clause-②: no Patch round on the contract review (a moved default neither widens nor narrows the accept set; the banner and the ADR-0087 marker carry the breaking signal) and the Test Core red it found: packages/cli/test/migrate-meta-engine-guidance.test.ts refuses a tracker id in printed guidance. The entry's reason now says "the one-vocabulary rule objectui ruled for the fold"; the citation moves to the file's leading comment, as the other entries do; the DEFAULT_CHANGES_BY_MAJOR[17] reason is rewritten the same way; registry.ts regenerated. Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Round 2, scoped: what moved since the round-1 head What moved. ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Landing pre-checks at
|
… on, the v18 spec default (objectui#12086) (objectstack-ai#12092) Fixes objectstack-ai#12086 Clause-②: no The consumer half of objectstack-ai/objectstack#22605 (the v18 default flip, landed as objectstack-ai/objectstack#22629). List views read an absent `userActions.editInline` as on (`!== false`): a list view is editable in place by default, under the permission gate that already exists, and `userActions: { editInline: false }` makes it read-only in place. The `inlineEdit` fold is unchanged, and so is the gate. ## What changed - **`ListView`** (`@object-ui/plugin-list`): `inlineEditOffered` returns `false` only for an explicit `editInline: false`; anything else falls through to the unchanged `listInlineEdit` affordance verdict (the object editable in place, and the principal's `update` grant). The Gap 2 docblock now states the v18 default and the opt-out. - **ADR-0047 interface page** (`@object-ui/app-shell`, `InterfaceListPage`): `inlineEdit: userActions.editInline !== false`, and the "default off" comment is corrected. - **Pins** (each kept load-bearing by a twin that differs in one input): - `ListView.permissions.test.tsx`: a view that declares nothing is offered the toggle for a principal with `update` and not for one without it (same schema, permission differs); with no `PermissionProvider` it is offered (fail-open kept); an absent `editInline` reads on and the grid opens out of edit mode; the compact popover offers the entry for an absent key, and its control withholds it for `editInline: false`; `editInline: false` withholds the toggle from a fully permitted principal. The stored `inlineEdit: true` case (offered, opens in edit mode) is unchanged and still green. - `InterfaceListPage.editInlineFold-5144.test.tsx`: an absent page `editInline`, and a page with no `userActions` block, now read on on both sides of the fold; an explicit `editInline: false` is the opt-out. - `normalize-list-view.inlineEditFold-5144.test.ts`: the fold's table is unchanged (the fold applies no default); its docblock now says which reader the flip changes. - **Docs**: `content/docs/plugins/plugin-view.mdx`, the "neither key" row of the inline-editing table now reads "offered | no" (opens out of edit mode), and the prose says on by default, with `editInline: false` as the opt-out. - **Changeset**: `.changeset/12086-edit-inline-default-on.md`, `minor` for `@object-ui/plugin-list` and `@object-ui/app-shell`, with the behaviour change in the prose. ## Measured before writing (the PM's three mechanism assumptions) 1. **The spec half is on objectstack `origin/main`.** `packages/spec/src/ui/view.zod.ts` declares `editInline: z.boolean().default(true)` (read with `git show origin/main:`; `git merge-base --is-ancestor f53f14bb36 origin/main` answered exit 0, which self-certifies). The installed `@objectstack/spec` here is 17.7.0, whose dist still declares `editInline: z.boolean().default(false)`. **Does any objectui code read the installed default?** No runtime reader does. The quoted-name grep for `UserActionsConfigSchema` and for `ListViewSchema` / `ViewSchema` `.parse(` / `.safeParse(` across `packages/*/src` and `apps/` finds the parse calls only in tests and in two `FormViewSchema` reads (positive control: the same grep finds those two). The one test that parses the installed defaults, `normalize-list-view.foldOutputAuthorable-5435.test.ts` ("the ON/OFF default asymmetry"), asserts `group` / `hideFields` / `rowColor` only, not `editInline`, so it does not fight the new read. One display-only reader exists: the metadata-admin View inspector derives its JSON Schema from the installed `ViewSchema` with `z.toJSONSchema`. It writes nothing, and its boolean switch renders `!!value` and ignores a schema default. See Acceptance notes. 2. **The two sites are the only absent-as-off readers.** Every `editInline` reader in `packages/*/src` outside tests, from the quoted exact-name grep (positive control: the same grep hits the known `ListView.tsx` and `InterfaceListPage.tsx` sites): `ListView` `inlineEditOffered` (was `!== true`); `InterfaceListPage` (was `=== true`); the `normalizeListViewSchema` fold (`typeof === 'boolean'`, no default); and the `@object-ui/plugin-view` `ObjectView` `userActions` merge (a spread of three folded layers, no default). The other hits are comments, the `detail.editInline*` i18n keys (a different namespace), and the grid's own `userActions` comments. The assumption holds. The `inlineEdit` readers carry the edit MODE (`!!schema.inlineEdit`, the named-view relay, `ObjectGrid`'s `editable`); none reads an absent `editInline` as off. 3. **The cloud window (see below).** No example app or fixture in this repo declares nothing and relies on the old default for a read-only list through a `ListView`-rendered path. The quoted grep for `editInline`, `inlineEdit` and `userActions` over `apps/` and `examples/` hits 0 (positive control: the same grep finds `listViews` / `object-grid` / `object-view` there). The list views that do exist there (`examples/byo-backend-console`'s `object-view` with a named `grid` view, the schema-catalog `object-view` / `object-grid` entries, `apps/console`'s dev `object-grid`) render through `ObjectGrid` directly, with no host `renderListView`. On that path `ObjectGrid`'s `editable` comes from the view's `inlineEdit` and never from `editInline`, so this change does not move them. ## The cloud window (named, as triage directs) Cloud consumes objectui `main` on its frozen v17 framework. There, a list view that declares neither `userActions.editInline` nor `inlineEdit` now offers the inline-edit toggle on the console object page. An ADR-0047 interface page that declares nothing now opens its grid editable in place. Both happen before cloud's spec says the default is on. Both stay under the permission gate: a principal without `update` on the object, or an object that is not editable in place, gets neither. This is the maintainer's chosen posture, so no hold is asked for. The cloud apps are not in this container, so whether any cloud view relied on the v17 default for a read-only list was **not measured here**. The spec card's own measurement names one cloud interface page that already declares `editInline: false` explicitly. That is a reading carried from objectstack-ai/objectstack#22605, not re-taken here. ## One mechanism note for the seat (the interface page) The interface page wires no inline-edit toggle: no `onInlineEditChange`, no `compactToolbar`. Its `userActions.editInline` therefore reaches `ListView` as the grid's edit MODE (`inlineEdit`), which the fold also turns into the "offered" key. So with this flip, an interface page that declares nothing does not "gain the toggle". It opens its grid editable in place, for a principal the gate admits. That is what the card's `!== false` orders for this site, and it is what the spec's new `describe` says ("a user who may update the object edits a cell in place"). A literal toggle on this page would be a new host wiring and is not done here. It is raised in the report. ## File surface Within the claim: `ListView.tsx` (`inlineEditOffered` and its docblock), `InterfaceListPage.tsx` (the read and its comment), `ListView.permissions.test.tsx`, `normalize-list-view.inlineEditFold-5144.test.ts`, the interface-page tests (`InterfaceListPage.editInlineFold-5144.test.tsx`, and the `inlineEdit` reason string in `InterfaceListPage.relayCensus-11572.test.ts`), `plugin-view.mdx`, and one changeset. **In-surface consequences: comments and test titles this flip made false.** These are text-only, with no code or assertion change, and they sit outside the claim's file list: - `packages/core/src/utils/normalize-list-view.ts`: two docblocks said the spec default is `.default(false)` and that `ListView` reads an absent pair as off. - `packages/app-shell/src/views/ObjectView.tsx`: the `keepInlineEditModeForTheSession` docblock quoted the v17 `describe` ("the list is read-only unless the author opts in"). - `packages/app-shell/src/views/ObjectView.inlineEditSessionOnly-5144.test.tsx`: one test title said "reads off by the spec default". Its assertion (the fold leaves both keys absent) is unchanged. - `packages/plugin-list/src/__tests__/ListView.test.tsx`: two comments cited the spec's `.default(false)`. - `packages/plugin-view/src/__tests__/ObjectView.namedViewEditInlineFold-5144.test.tsx`: the docblock said `ListView` offers inline editing "only where that reads `true`". Nothing is added to a package entry: no prop, export, type member or locale key. ## Verification All runs below are on this branch. Heavy runs went through the shared verify lock, and each line quotes that run's own `VERDICT command-exit` line. Test runs use the repo-root form `pnpm exec vitest run --maxWorkers=2 PATHS`. - **Targeted pins, on the final head `9cd778d1d`** (after merging `origin/main` `de302c73`): the four touched pin files, the five other `InterfaceListPage*` tests, `ObjectView.inlineEditSessionOnly-5144`, the two `normalize-list-view` fold tests and the plugin-view named-view fold test. Result: `Test Files 13 passed (13)`, `Tests 264 passed (264)`, `VERDICT command-exit 0`. The same 13 files gave the same counts on `efa9e08f1`, before the merge. - **`@object-ui/plugin-list`, whole package, on `efa9e08f1`**, plus the four app-shell `ObjectView` tests that render the real `ListView` with role-based button queries or import the interface page (`kanbanLane-8193`, `storedOptionsBag-10380`, `listUrlState-11860`, `viewWriteRefusal-11583`). Result: `Test Files 129 passed (129)`, `Tests 1444 passed (1444)`, `VERDICT command-exit 0`. - **Ablation (a one-time proof; no test file kept).** Each leg was run from the committed head through `ablation-replace.mjs`, whose anchor hit x1 and went to x0, and whose blob changed. Each was restored to the HEAD blob with `git diff HEAD` empty, so the restore was proven on disk. - Leg A put the v17 read back into `inlineEditOffered` (`?.editInline !== true`). `ListView.permissions.test.tsx` went `4 failed | 19 passed (23)`. The four red cases are exactly the predicted ones: offered with `update` on a view that declares nothing; offered with no `PermissionProvider`; an absent `editInline` reads on; the compact entry is offered for an absent key. "WITHOUT update loses it" stays green under both reads, as expected. - Leg B put `inlineEdit: userActions.editInline === true` back on the interface page. `InterfaceListPage.editInlineFold-5144.test.tsx` went `2 failed | 2 passed (4)`: the two absent cases. - **Type-check** (`pnpm --filter PKG type-check`, which is `tsc --noEmit && tsc -p tsconfig.test.json`), after building the dependency closure with `turbo run build --filter=PKG^... --concurrency=2` (`28 successful, 28 total`). `@object-ui/core`, `@object-ui/plugin-list`, `@object-ui/plugin-view` and `@object-ui/app-shell` all exit 0 with 0 `error TS` on `efa9e08f1`. `@object-ui/app-shell` exits 0 again on `9cd778d1d`, since main's two merged PRs touched app-shell source. - **eslint on the 11 touched `.ts` / `.tsx` files** (`--format json`, counted from its output): 0 errors. Every file's error/warning count is identical to its base-blob count (lint run over `git show 023f00d:FILE` via `--stdin`). The repo's `eslint.config.js` sets no `parserOptions` / `projectService`, so linting is not type-aware, and this diff cannot move a verdict on an untouched file. - **Gates, re-run on `9cd778d1d`:** `check:new-line-citations` (`0 new citation(s)`), `check:control-bytes`, `check-changeset-presence`, `changeset:check` (no `major`), `check-changeset-overwrite`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:doc-fences`, `check:doc-types` and `check-doc-links`. All exit 0. The four `vi.mock` / test-path gates exit 0 on `efa9e08f1`. - **NOT MEASURED: `check:doc-snippets` and `check:doc-examples`.** Reason: exit 2, PREREQUISITE NOT MET. Both need a scoped build of more packages than this run built (`app-shell`, `cli`, `plugin-ai` … unbuilt). Their input is unchanged by this diff, measured instead. The fenced blocks of `plugin-view.mdx` extracted at base and at head are byte-identical (md5 `3fd26d2a…` both, 18 blocks). The diff touches 0 lines carrying `@example` or a fence in `packages/*/src`. Control: `ListView.tsx` carries one `@example`, which is untouched. - **Declared narrowing: `@object-ui/app-shell`'s full suite was not run locally**; CI runs it. The local app-shell set is every test that imports `InterfaceListPage`, the session-only toggle pin, and the `ObjectView` tests above. This is a declared narrowing, not a proof that nothing else in app-shell observes the toggle. ## Acceptance notes Observations only. Not filed, and not changed here. - **Metadata-admin View inspector shows default-true booleans as off.** `SchemaForm`'s boolean branch renders `checked={!!value}` and ignores the JSON Schema `default`. So in the View inspector, an absent `userActions.search` / `sort` / `filter` (default `true`) already shows OFF while the toolbar reads it ON. `userActions.editInline` joins that set once objectui installs a spec 18 build. The installed 17.x schema still says `default: false`, so the switch shows off either way, and after this PR the list reads on. This was read from source; no public door was exercised, so it is not filed. Carrier: none. - **The direct `ObjectGrid` path does not read `editInline`.** On an authored `object-view` with a named `grid` view and no host `renderListView`, `ObjectGrid`'s `editable` is the named view's `inlineEdit` (or `table.editable`). `userActions.editInline` is not read there. So `editInline: false` does not withhold edit mode from a named view that also says `inlineEdit: true`, and a view with neither key has no toggle to enter edit mode. This predates and is unchanged by this PR. No named producer declares both keys on such a view, so it is not filed. Carrier: none. - **The pending objectui#5144 changeset — corrected in this PR** (patch round, the seat's answer B; written into this body by the seat, `domain:ui` seat 3). `.changeset/5144-editinline-fold.md` is unreleased, and in net it stated the opposite default to this release. This PR's change made it false, so it is corrected here. Its frontmatter is byte-identical (same bump declarations). Before → after: 1. "List views read `userActions.editInline` with the spec's default, off." → "… with the spec's default, on since objectstack-ai/objectstack#22605 (`editInline: z.boolean().default(true)`), and `editInline: false` is the opt-out." 2. "**Breaking for a view that relied on the old default.**" → "**The spec's default, read as declared.**" 3. "The spec declares `userActions.editInline` with `.default(false)`: the list is read-only unless the author opts in." → "… with `.default(true)` (objectstack-ai/objectstack#22605): a list view is editable in place by default, under the permission gate, and `editInline: false` is the opt-out." 4. "The interface page already read it that way." → deleted. The paragraph now closes with "The flip of the default, and the interface page that reads it, are the objectui#12086 entry of this release." 5. "The object-list toolbar did not: it read …" → "The object-list toolbar used to read …". Only the lead-in changed, because it leaned on the deleted sentence 4. 6. "A grid view that declares neither … no longer offers the inline-edit toggle … and never opens in edit mode." → "… offers the inline-edit toggle, on the wide toolbar and in the compact settings popover, and opens out of edit mode." 7. "- a view with neither key reads off." → "- a view with neither key reads on, the spec's default." 8. "**What to do.** A view that should offer inline editing declares `userActions.editInline: true`. The toggle then stays offered whatever the user does with it." → "**What to do.** A list whose records are read-only by nature declares `userActions.editInline: false`. A view that declares `userActions.editInline: true` keeps the toggle offered whatever the user does with it." Unchanged: the other fold bullets, the session-only toggle, the named-view precedence, the two costs and the no-export line. This PR's own `.changeset/12086-edit-inline-default-on.md` was corrected in the same round, because its quote of the deleted 5144 sentence became false. It now says the previous release (17.7.0) also offered the toggle for an undeclared view, measured from the parent of objectui#12036, and the interface-page heading carries the behaviour change. --- _Generated by [Claude Code](https://claude.ai/code/session_01B1gHb9baeX7oioD5sHVm7z)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>
Part of #22605
Clause-②: no
The spec half of #22605:
ListView.userActions.editInlinedefaults totrueon the v18 line, anduserActions: { editInline: false }is the opt-out. Theskills/**paragraph the card also asks for is a separate pull request (Tier H), so this one isPart of, notFixes.The ruling
Maintainer, 2026-10-10, verbatim (filed by the director seat on the card): 「乙 v18 把 spec 默认翻成 true,editInline: false 变成关法。」 A list view is editable in place by default, under the permission gate that already exists. The rule of objectui#5144 stands (one vocabulary, the
inlineEditfold, declared = enforced); only the default's value changes. Not taken: declaringeditInline: trueon every existing view (objectstack-ai/hotcrm#2048 and #22604 are superseded by the ruling, and stay open here — this PR does not act on either), and an app-level default. The permission gate is untouched. The consumer half is objectui's, objectstack-ai/objectui#12086; in the window the console at the pinned objectui still reads an absent key as off, so nothing regresses against today.What changed
packages/spec/src/ui/view.zod.ts—UserActionsConfigSchema.editInlineisz.boolean().default(true),describerewritten (on by default; a user who may update the object edits a cell in place with the field's type-aware widget, under the permission gate; declareeditInline: falseto make the list read-only in place). The schema docblock records the ruling beside the group/hideFields/rowColor asymmetry it already explains.packages/spec/scripts/lib/default-changes.ts—DEFAULT_CHANGES_BY_MAJOR[17]gainsui/UserActionsConfig:editInlinefalse→truewith its reason. This is what letscheck:authorable-surfaceaccept the moved fingerprint;CURRENT_MAJORis the package major (17) until the version pass takes the group to 18. The build printed📌 1 declared default change(s) accepted (#4666): ui/UserActionsConfig:editInline: false → true.packages/spec/src/migrations/entries/semantic/18.list-view-edit-inline-default-on.ts— the D3 semantic entry (protocol 18), concatenated intoregistry.tsbygen:migration-registry.packages/spec/authorable-defaults/ui.json— regenerated by the build:ui/UserActionsConfig:editInline = true.content/docs/references/ui/view.mdx(3 rows),content/docs/references/ui/page.mdx(1 row) — regenerated bygen:docs:optional (default: true)with the new describe..changeset/22605-spec-list-view-edit-inline-default-on.md—@objectstack/spec: minor,Clause-②: no(a moved default changes no accept set; the breaking signal is the banner and the ADR-0087 marker), the launch-window BREAKING banner naming the author who relied on the documented default for a read-only list and the remedyeditInline: false, the FROM → TO table, the measured reach, and the ADR-0087 markerregistered list-view-edit-inline-default-on.minorbecause the launch-window convention ships a breaking change asminor(check-changeset-no-major), and the fixed group already carries the pendingmajorthat opens the v18 line, so the level moves no version on its own.packages/spec/src/ui/view.test.ts— the defaults pin flips totrue, plus four pins: an absent key parses totrue(onUserActionsConfigSchemaand throughListViewSchema), an explicitfalsestaysfalse, an explicittruestaystrue, and the view-levelinlineEdit: truestill parses as before with nouserActionsblock materialised (the fold is the renderer's; the spec keeps both spellings as declared).Measured first (the PM's mechanism assumptions)
view.zod.ts:1535read.default(false)with the "Off by default" describe — corrected. The four reference rows regenerate. Nothing else in the tree states the old default:content/docs/ui/views.mdxlists the view-levelinlineEditwith no default;content/docs/protocol/objectui/layout-dsl.mdx:667is aboutrecord:details' owninlineEdit(a different key, renderer default on);packages/spec/src/data/object.zod.tsretired-key guidance says only thateditInlineis a view key;object-strictness-batch20.test.tslists key names. Left as they are, with reasons:.changeset/console-20c6d351ad74.mddescribes the console at pin20c6d351ad74, which still reads an absent key as off until objectui#12086 lands and the pin moves (true of that build);CHANGELOG.md:1677andpackages/spec/CHANGELOG.mdare release-owned history.check:adr-0087-registrationdemands a marker on a declared-breaking changeset;not-required (no-migration-prescription)is refused when the body carries a prescription, and this one prescribeseditInline: false. The honest disposition isregistered: a D3 semantic entry at step 18, the same class asview-pagination-page-size-default-50and protocol 12'srest-requireauth-default-flip. No key is removed (no tombstone, noRETIRED_KEYS_BY_MAJORrow) and no D2 conversion exists — a mechanical pass writingeditInline: falseinto every silent view would preserve the old posture and defeat the ruling. The registry regenerated byte-exact after mergingorigin/main(which brought the sibling entrypage-slots-details-beside-tabs-refused, not adjacent in id order;check:migration-registrygreen). A second ledger also applies: the authorable-defaults ratchet refuses an undeclared default move, soDEFAULT_CHANGES_BY_MAJORcarries the hop.e11ef3a7bd(git grep, tests and changelogs excluded): 0editInline: falseand 0editInline: trueacrossexamples/**andpackages/**metadata. Views that declare nothing and now gain the toggle: 38 list-kind views with an explicittypeacross 10 example*.view.tsfiles (entries with notypeare grid by default and count the same way), 12interfaceConfigpage lists across 5 showcase page files (11 declare auserActionsblock, none namingeditInline), 105 list-kind views across 40 platform*.object.tsfiles inpackages/**. One view declares the view-levelinlineEdit: true(the showcasetaskgrid saved view) and keeps opening in edit mode. The only test that asserted the old default isview.test.ts(expect(config.editInline).toBe(false)), flipped here; no other package's tests read the view-leveluserActionsdefault (consumer tests onuserActionsare the object-level CRUD block). The card's readings outside this repository (cloudsys-package.page.ts:105declareseditInline: false; HotCRM's 41 undeclared views) were not re-measured here.skills/objectstack-ui/rules/list-views.mdis hand-written: the only generated lifts insideskills/**arereferences/_index.mdandreferences/react-blocks.md(GENERATED_SURFACE_EXCEPTIONS). Its paragraph is PR 2.Tests (post-merge head
24d9bdcbf0, shared-box readings under the verify lock)pnpm --filter @objectstack/spec build— green;check:generated—✓ All 15 generated artifacts are up to date.pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2(the package'stestscript) —Test Files 642 passed (642) · Tests 19150 passed | 1 todo (19151).src/ui/view.test.ts src/ui/page.test.ts src/migrations/migrations.test.ts src/migrations/semantic-relevance.test.ts—4 passed · 740 passed; therepo-project filesscripts/step18-rationale-merge.test.ts scripts/authorable-defaults.test.ts scripts/build-schemas-check-mode.test.ts—4 passed · 146 passed(withsemantic-relevance).pnpm --filter @objectstack/spec typecheck—tsc --noEmit,check:scripts-typecheckandcheck:test-typecheckgreen (52 file(s) / 246 error(s) / 135 pinned signature(s) held, the shrink-only debt ledger unchanged).test:repobeyond those three files is declared to CI (Test Core).Ablation (reverse verification)
node scripts/ablation-replace.mjs --file packages/spec/src/ui/view.zod.ts --anchor 'editInline: z.boolean().default(true)' --replacement 'editInline: z.boolean().default(false)' -- pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/ui/view.test.ts, at head24d9bdcbf0, after the fix was committed. Mutation proved on disk: anchor 1 → 0, replacement 0 → 1, blob6979ade6158a→e0e02cd7f858. Direction observed: red, exactly the two default pins —should apply default valuesandan absent key parses to true(expected false to be true),2 failed | 462 passed (464). Restore proved: blob after restore6979ade6158a== blob at HEAD,git diff HEADempty, tree clean. Resolution path: the pin imports the schema from the package's ownsrc(nodistin the path), so no rebuild sits between the mutation and the reading.Gates (local, at
24d9bdcbf0)Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; the tool reads the change set off the merge base): 115 families. Reconciled with--ran: 115 derived, 114 run, 0 NOT-MEASURED, 1 UNRUN — the UNRUN ispnpm check:dual-build-cjs-loads, declared NOT MEASURED by the dispatch (it needs a whole-workspace build). Every other derived family exited 0, after building the prerequisites some of them load (@objectstack/formula,lint,client,client-react,objectql), exceptpnpm check:dts-closure, which exited 1 for a missingdist/underplugin-approvalsandplugin-dev: it reads every package's built declarations, so without the whole-workspace build it is NOT MEASURED, not red (CI's Build Core covers it). The artifact-roster block (49 families): 46 run, every one exit 0 exceptpnpm check:published-readme-exports, exit 3 (44 unbuilt packages, NOT MEASURED for the same reason); the three PR-context guards (check-closing-target-claim,check-partof-closing-keyword,check-single-claim-paths) run once this PR has a number, and their readings go in the report comment on the card. Staleness note the tool prints: the derivation tree is two commits behindorigin/main83b8b80728, and the one derivation input that moved isscripts/platform-object-tenancy-census.json(the tenancy-census roster); that family ran green on the older copy, and no path of this diff overlaps those two commits.Lint, as a proven narrowing rather than the CI-owned
pnpm lint:pnpm exec eslint --no-inline-config --format jsonover the.ts/.mjsfiles in the two-point diff — 24 files linted, 0 errors, 0 warnings (the count is from the JSON output; the set is wider than this PR's own 9 files (233 insertions, 7 deletions off the merge basee22315238f) because the two-point diff includes the merged sibling commits). Population, read fromeslint.config.mjs:**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}minus itsignores, so the.md,.mdxand.jsonfiles of this diff are outside it (eslint reports them "ignored because no matching configuration"). Invariance: the config declares noparserOptions.projectand no typed@typescript-eslintrules, so this diff cannot move the verdict of any file it does not touch.Acceptance notes
packages/spec/spec-changes.jsonanddocs/protocol-upgrade-guide.mdare not regenerated here. Since [Decision] generated protocol-change records (spec-changes.jsonper-major section,docs/protocol-upgrade-guide.md): keep committing them, or generate them at publish like thereleasesection #22449 B′ the PR-stagecheck:spec-changesandcheck:upgrade-guideproject the registry in memory and compare no committed copy (the publish lane generates the shipped one), andorigin/mainturned the upgrade guide into a pointer stub while this branch was open; a regeneration on the pre-merge tree carried 30-odd lines of sibling drift this diff does not own, so it was reverted. The entries README still says to regenerate both; the generator header is the newer authority. Noted, not filed.editInline !== falseinListView.tsxandInterfaceListPage.tsx, theplugin-view.mdxrow, the flipped pins) is list views: read an absentuserActions.editInlineas on (editInline !== false), the consumer half of objectstack-ai/objectstack#22605 (the v18 default flip) objectui#12086, filed by the claiming seat; nothing in objectui moves here.Generated by Claude Code