Repository navigation
Commit 5330afd
feat(plugin-list,app-shell): read an absent userActions.editInline as on, the v18 spec default (objectui#12086) (#12092)
Fixes #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>1 parent 029bdaf commit 5330afd
14 files changed
Lines changed: 163 additions & 117 deletions
File tree
- .changeset
- content/docs/plugins
- packages
- app-shell/src/views
- core/src/utils
- __tests__
- plugin-list/src
- __tests__
- plugin-view/src/__tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
518 | 518 | | |
519 | 519 | | |
520 | 520 | | |
521 | | - | |
522 | | - | |
523 | | - | |
524 | | - | |
525 | | - | |
526 | | - | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
527 | 529 | | |
528 | 530 | | |
529 | 531 | | |
530 | | - | |
| 532 | + | |
531 | 533 | | |
532 | 534 | | |
533 | 535 | | |
| |||
537 | 539 | | |
538 | 540 | | |
539 | 541 | | |
540 | | - | |
541 | | - | |
542 | | - | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
543 | 547 | | |
544 | 548 | | |
545 | 549 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
12 | 13 | | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
104 | 105 | | |
105 | 106 | | |
106 | 107 | | |
107 | | - | |
| 108 | + | |
108 | 109 | | |
109 | | - | |
110 | | - | |
| 110 | + | |
| 111 | + | |
111 | 112 | | |
112 | 113 | | |
113 | | - | |
114 | | - | |
| 114 | + | |
| 115 | + | |
115 | 116 | | |
116 | 117 | | |
117 | | - | |
| 118 | + | |
118 | 119 | | |
119 | | - | |
120 | | - | |
| 120 | + | |
| 121 | + | |
121 | 122 | | |
122 | 123 | | |
123 | | - | |
| 124 | + | |
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
127 | 128 | | |
128 | 129 | | |
129 | | - | |
| 130 | + | |
130 | 131 | | |
131 | 132 | | |
132 | 133 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
499 | 499 | | |
500 | 500 | | |
501 | 501 | | |
502 | | - | |
| 502 | + | |
503 | 503 | | |
504 | 504 | | |
505 | 505 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
565 | 565 | | |
566 | 566 | | |
567 | 567 | | |
568 | | - | |
569 | | - | |
570 | | - | |
571 | | - | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
572 | 577 | | |
573 | 578 | | |
574 | 579 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
367 | 367 | | |
368 | 368 | | |
369 | 369 | | |
370 | | - | |
| 370 | + | |
371 | 371 | | |
372 | 372 | | |
373 | 373 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1523 | 1523 | | |
1524 | 1524 | | |
1525 | 1525 | | |
1526 | | - | |
1527 | | - | |
1528 | | - | |
1529 | | - | |
1530 | | - | |
1531 | | - | |
| 1526 | + | |
| 1527 | + | |
| 1528 | + | |
| 1529 | + | |
| 1530 | + | |
| 1531 | + | |
| 1532 | + | |
| 1533 | + | |
1532 | 1534 | | |
1533 | 1535 | | |
1534 | 1536 | | |
1535 | 1537 | | |
1536 | 1538 | | |
1537 | 1539 | | |
1538 | 1540 | | |
1539 | | - | |
1540 | | - | |
| 1541 | + | |
| 1542 | + | |
1541 | 1543 | | |
1542 | 1544 | | |
1543 | 1545 | | |
| |||
0 commit comments