Skip to content

Commit 888bfff

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-18639-related-list-columns-union
2 parents 7469c1d + 1b82c51 commit 888bfff

27 files changed

Lines changed: 934 additions & 99 deletions
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
Gallery, kanban and timeline view configs declare an author-settable row ceiling.
6+
7+
`GalleryConfigSchema`, `KanbanConfigSchema` and `TimelineConfigSchema` each gain a
8+
`limit` member — a positive integer, default **100** — saying how many records the
9+
view fetches. The default is APPLIED by the schema rather than only described, and
10+
the key's own text states the other half of the contract: when the ceiling applies,
11+
the renderer must show a visible truncation signal, because a bounded view that
12+
looks complete is worse than an unbounded one. `DEFAULT_VIEW_ROW_LIMIT` is exported
13+
so a consumer reads that number instead of re-declaring it.
14+
15+
The knob belongs in the protocol because two renderers already cap by author choice
16+
off keys the protocol never declared: objectui's kanban board fetches
17+
`$top: schema.limit ?? DEFAULT_KANBAN_LIMIT` with `limit` declared in
18+
`@object-ui/types` alone, its timeline does the same off a component props
19+
interface, and its gallery caps not at all. `limit` is the name those consumers
20+
already read, so this declaration absorbs the consumer-local keys instead of
21+
introducing a second spelling of one concept.
22+
23+
Nothing is removed, renamed or narrowed, and no document that parsed before is
24+
refused now. Two things to know when upgrading:
25+
26+
- a parsed gallery / kanban / timeline config carries `limit: 100` where the author
27+
wrote no ceiling, so code that compares a parsed config against a literal object
28+
sees the new member;
29+
- `KanbanConfigParsed` is now declared (ADR-0122) because that schema has two shapes
30+
for the first time; `KanbanConfig` is unchanged and remains the author state.
31+
32+
The non-grid four — gantt, calendar, map and tree — are deliberately untouched:
33+
their rows stay bounded by a platform ceiling the renderer owns, because a gantt's
34+
range, a map's camera fit and a tree's parent chain are computed over the whole set.
35+
36+
Clause-②: yes (widening)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): the three shipped confirmation-gate prescriptions state the gate in the present tense — they were denying a door that exists (#17487)
6+
7+
Clause-②: no
8+
9+
No accept-set change and no export moves. `ToolSchema` still refuses
10+
`requiresConfirmation` with a located parse error, `ActionSchema` still accepts
11+
`ai.requiresConfirmation` in both directions, and `check:authorable-surface` /
12+
`check:api-surface` are byte-identical across this diff. What moves is text.
13+
14+
Three customer-facing prescriptions were written while the runtime confirmation
15+
door was a separate, unlanded change, and each said so in the present tense. The
16+
door has since landed on `main` — `actionConfirmationRefusal`, called pre-dispatch
17+
by `invokeBusinessAction` in `@objectstack/runtime`, with the `confirm` member
18+
grown on the MCP `run_action` tool in the same change. From that moment the
19+
published prose DENIED a door that exists, and it denied it in the dangerous direction: an author who
20+
reads it concludes the safety flag stops nothing and either arranges a human in
21+
the loop some other way or stops setting the flag — losing the gate exactly when
22+
it starts working. That is the ADR-0049 false-compliance defect with the sign
23+
flipped.
24+
25+
**The three carriers**, all of them shipped text rather than comments:
26+
27+
1. the `requiresConfirmation` entry of `TOOL_RETIRED_KEY_GUIDANCE`
28+
(`ai/tool.zod.ts`), which reaches consumers as the parse error on the
29+
`.strict()` `ToolSchema` — the one channel every consumer bumping
30+
`@objectstack/spec` is guaranteed to hit;
31+
2. the ADR-0087 D3 entry's `replacement`, and
32+
3. its `acceptanceCriteria` — what `spec-changes.json`,
33+
`docs/protocol-upgrade-guide.md` and `os migrate meta` project to consumers.
34+
35+
FROM → TO, on the sharpest of the three (the acceptance criterion):
36+
37+
```
38+
was: Do NOT try to "prove the gate" by invoking the operation without the
39+
confirmation member: ... before that ships the call is not refused, it
40+
RUNS the destructive operation.
41+
now: ... that gate is PERFORMED: invoking the operation over an AI-exposed
42+
door without the confirmation member is REFUSED with
43+
ACTION_CONFIRMATION_REQUIRED (428) and nothing runs, so that call is a
44+
real check you can make rather than a destructive experiment.
45+
```
46+
47+
**The corrections carry the door's BOUNDS, because over-promising here is the
48+
same defect in the other direction.** Each prescription now states, as the door
49+
itself declares them: the refusal is `ACTION_CONFIRMATION_REQUIRED` / 428 naming
50+
the action and the member `confirm: true`; it is a GATE, not a queue — nothing
51+
is parked and a refused call did not run, no record read and none written; the
52+
enforced set is the doors that enforce the author's `ai.exposed` opt-in, today
53+
the action door reached from the MCP `run_action` tool, while REST `/actions` is
54+
not `ai.exposed`-gated and sits outside the gate; only the author's declared
55+
`ai.requiresConfirmation: true` refuses, while the wider listing heuristic
56+
advises and never refuses; and `confirm: true` is an unverifiable caller claim,
57+
so the gate makes FORGETTING loud without proving a human.
58+
59+
`ai/tool-confirmation-prescription-tense.pin.test.ts` is the tie that was
60+
missing the first time: it reads the three shipped strings AND the runtime door,
61+
so a prescription that re-acquires a not-yet-shipped denial fails, and a door
62+
that is removed, narrowed off the DECLARED flag, unhooked from
63+
`invokeBusinessAction`, or widened onto REST `/actions` fails naming both files.
64+
The denial predicate is fed the three retired sentences verbatim, so it cannot
65+
pass by the prose merely falling silent.
66+
67+
**On release ordering.** The door ships in the same release this correction
68+
does: the runtime changeset that carries it (`action-confirmation-gate-enforced`)
69+
is still pending alongside this one, and one `changeset version` run consumes
70+
both. A release cut before this lands is the failure this card exists to end —
71+
the runtime refusing calls while the published spec text tells authors the flag
72+
stops nothing.

‎content/docs/references/api/protocol.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
16741674
| **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration |
16751675
| **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) |
16761676
| **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration |
1677-
| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout |
1677+
| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout |
16781678
| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout |
16791679
| **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout |
16801680
| **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration |
@@ -1759,7 +1759,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
17591759
| **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration |
17601760
| **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) |
17611761
| **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration |
1762-
| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout |
1762+
| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout |
17631763
| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout |
17641764
| **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout |
17651765
| **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration |

‎content/docs/references/data/object.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ const result = ApiMethod.parse(data);
374374
| **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration |
375375
| **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) |
376376
| **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration |
377-
| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout |
377+
| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout |
378378
| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout |
379379
| **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout |
380380
| **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration |

0 commit comments

Comments
 (0)