Skip to content

Commit b35c025

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-15124-facade-query-envelope
2 parents ee8c411 + cb005e0 commit b35c025

31 files changed

Lines changed: 1065 additions & 96 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.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
`record:related_list.columns` now declares the SAME union the saved-view key declares — `z.union([z.array(z.string()), z.array(ListColumnSchema)])` — so a saved view's per-column decoration reaches the related list instead of being refused at the block door (#18639, the upstream half of objectui#9593).
6+
7+
**Clause-②: yes (widening)** — one published accept set grows: the key admitted `string[]` and now also admits `ListColumn[]`. Nothing previously admitted is refused, no key is renamed or retired, and no producer is required to write the new arm. Contract-review tier.
8+
9+
Two published declarations disagreed about one key. `RecordRelatedListProps.columns` (`ui/component.zod.ts`) was `z.array(z.string())`, while `listViews[].columns` (`ui/view.zod.ts`) was already the union — and objectui composes a saved view's `columns` onto this block **verbatim** (`dataSource.view` → `composeElementDataSource` → `savedViewColumns`). A view whose columns carried `label` / `width` / `hidden` / `summary` therefore arrived at a block that declared it could not carry them.
10+
11+
- **The same union, by reference — not a lookalike.** `ListColumnSchema` is imported from the view face rather than re-spelled, so the object arm is one def with two carriers. The pin asserts reference identity on both sides and then asserts block and saved view return the same verdict for every fixture: two spellings of one key is the defect this closes, so a second spelling would not have fixed it.
12+
- **The arms are exclusive, and the description says so because the schema enforces it.** `['name', { field: 'amount' }]` matches neither arm and is refused. The decoration also survives the parse — a description promising keys a parse strips would be the same defect one layer up, so the pin asserts the parsed value, not merely `success`.
13+
- **Unchanged, by ruling and by measurement.** `field.relatedListColumns` stays child field-name STRINGS only and still refuses a column object with its derivation prescription, and the `field-column-lists-canonicalized` conversion still folds an object entry on that key to its identity string. Both are pinned next to the widening so the fences cannot erode quietly.
14+
15+
No migration: authors writing `string[]` are unaffected, and the new arm is opt-in.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"@objectstack/knowledge-ragflow": patch
3+
---
4+
5+
The RAGFlow adapter now reads the declared key: a source's RAGFlow binding comes from `adapterConfig.datasetId`, not `options.datasetId`.
6+
7+
`KnowledgeSourceSchema` declares `adapterConfig` for adapter-specific configuration and is a plain `z.object` — it carries no `.passthrough()`, so any path that parses a source drops `options` before an adapter ever sees it. The adapter read `options` through a cast, which worked only because no path parses a source today. The cast is gone; there is no fallback that also reads `options` (Prime Directive #12 — one strict contract, no lenient consumer).
8+
9+
Migration, `FROM` → `TO`, one line per source:
10+
11+
```ts
12+
// FROM
13+
{ id: 'product_docs', adapter: 'ragflow', options: { datasetId: 'rgf_…' } }
14+
// TO
15+
{ id: 'product_docs', adapter: 'ragflow', adapterConfig: { datasetId: 'rgf_…' } }
16+
```
17+
18+
The same move applies to `rerankModel`, `similarityThreshold` and `vectorSimilarityWeight`, which the adapter reads from the same bag. A source left on the old spelling is refused by name — `RAGFlow adapter requires source.adapterConfig.datasetId on source '<id>'` — rather than silently retrieving nothing, so the upgrade is self-describing at the first call. Nothing an author could declare is removed: `options` was never a key `KnowledgeSourceSchema` accepted, which is why this carries no ADR-0087 conversion.
19+
20+
The package's published `README.md` moves with the adapter and now compiles against it — it was the one block of the 44 that #18915 could not repair, because correcting the spelling alone would have compiled and stopped working.
21+
22+
Clause-②: no

‎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)