Skip to content

Commit 88a0b0f

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-19785-merge-actions-shape-refusal
2 parents 5c5d1db + fae8703 commit 88a0b0f

4 files changed

Lines changed: 172 additions & 84 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
`view/layout-without-binding` — the `gantt`, `timeline` and `map` warnings no longer tell an author the renderer falls back to literal default field names, because objectui deleted those floors; each now names the refusal screen the renderer shows instead, and the keys that clear it (#19630).
6+
7+
Re-derived at the objectui pin this repo builds against (`87af769e9`), not at objectui's head, both halves of each path a list view takes:
8+
9+
- **`gantt`** — `ListView.tsx`'s `case 'gantt'` restates only declared bindings (objectui#7070 deleted the `start_date` / `end_date` floors, objectui#7499 the `progress` / `dependencies` ones); `ObjectGantt`'s `getGanttConfig` returns `null` without both dates and the component renders "Gantt configuration required". The body now names `gantt.startDateField`, `gantt.endDateField` and `gantt.titleField`, the three keys `GanttConfigSchema` requires.
10+
- **`timeline`** — the `startDateField || 'created_at'` floor is gone (objectui#7070 step three) and `ObjectTimeline` renders "Timeline date axis required"; the `titleField || 'name'` default still stands, and the body says so. It names `timeline.startDateField` and `timeline.titleField`, the two keys `TimelineConfigSchema` requires.
11+
- **`map`** — `locationField || 'location'` is gone on both faces (objectui#8169): `ObjectMap` no longer guesses coordinate field names and its `hasCoordinateBinding` gate renders "Map configuration required", for an absent `map` block and for a declared block that names neither coordinate form alike. Both `map` messages — the absent-block body and the block-present one, which had quoted `locationField || 'location'` as the renderer's read — now describe that refusal and name `map.locationField` or the `map.latitudeField` + `map.longitudeField` pair.
12+
13+
`kanban` and `tree` were re-read at the same pin and still floor (or infer) a binding, so they keep the generic body. The `VIEW_BINDING_BLOCKS` docblock rows carry asserting pin citations, so the next `.objectui-sha` bump reds on them instead of leaving them to go stale.
14+
15+
No severity moves and no finding appears or disappears: every route stays `warning`, consistent with #16577's ruling B for the `calendar` route (both doors loud — `os validate` and a named refusal at render), which these rows now measure too without extending or reopening it. No schema, export or accept set moved: this corrects prose and three warning strings. `Clause-②: no`

‎packages/spec/src/kernel/functional-completeness.test.ts‎

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ describe('checkViewCompleteness — layout bindings', () => {
125125
// ⛔ What it is NOT is one shared mechanism. This comment used to say the
126126
// renderer's fallback for every one is a literal field name; objectui has
127127
// been deleting those floors one view type at a time, and for `calendar`
128-
// the renderer now REFUSES by name instead (#17445). The rule fires on all
129-
// six either way — that is what this pin holds — and the per-type
130-
// mechanism belongs to the table's docblock, which states each row's own
131-
// measurement and which of them have gone stale.
128+
// (#17445), `gantt`, `timeline` and `map` (#19630) the renderer now REFUSES
129+
// by name instead. The rule fires on all six either way — that is what this
130+
// pin holds — and the per-type mechanism belongs to the table's docblock,
131+
// which states each row's own measurement.
132132
it.each(['kanban', 'calendar', 'gantt', 'timeline', 'map', 'tree'])('flags a %s view missing its block as a WARNING', (type) => {
133133
const f = only(checkViewCompleteness({ type }) as never);
134134
expect(f.rule).toBe(VIEW_LAYOUT_WITHOUT_BINDING);
@@ -186,15 +186,43 @@ describe('checkViewCompleteness — layout bindings', () => {
186186
expect(f.severity).toBe('warning');
187187
});
188188

189-
it('the five types with no per-type body keep the generic one — an override, not a rewrite', () => {
190-
// ⚠️ An honest pin: it records WHICH body each type receives, NOT that
191-
// the body is true of each. The table's re-measurement note says `gantt`,
192-
// `timeline` and `map` inherit this sentence pending corrections of their
193-
// own; `kanban` and `tree` were re-read at the same ref and still floor a
194-
// literal. Correcting one of the three means adding an entry beside
195-
// `calendar`'s and moving that type out of this list — a deliberate edit,
196-
// which is the point.
197-
for (const type of ['kanban', 'gantt', 'timeline', 'map', 'tree']) {
189+
it.each([
190+
// [#19630] Re-derived at the pin this repo builds against, both halves of
191+
// each path: `ListView.tsx` restates only DECLARED bindings (objectui#7070
192+
// for the gantt / timeline date axes, objectui#7499 for gantt progress /
193+
// dependencies, objectui#8169 for map), and the component then renders a
194+
// named refusal screen. The generic body asserted literal floors as the
195+
// reason for the warning; this pin stops that from coming back.
196+
{ type: 'gantt', component: 'ObjectGantt.tsx', screen: 'Gantt configuration required',
197+
keys: ['gantt.startDateField', 'gantt.endDateField', 'gantt.titleField'] },
198+
{ type: 'timeline', component: 'ObjectTimeline.tsx', screen: 'Timeline date axis required',
199+
keys: ['timeline.startDateField', 'timeline.titleField'] },
200+
{ type: 'map', component: 'ObjectMap.tsx', screen: 'Map configuration required',
201+
keys: ['map.locationField', 'map.latitudeField', 'map.longitudeField'] },
202+
])('the $type body describes the REFUSAL, not a deleted literal fallback (#19630)', ({ type, component, screen, keys }) => {
203+
const f = only(checkViewCompleteness({ type }) as never);
204+
expect(f.rule).toBe(VIEW_LAYOUT_WITHOUT_BINDING);
205+
expect(f.message).not.toContain('falls back to literal default field names');
206+
expect(f.message).toContain(screen);
207+
expect(f.message).toContain(component);
208+
// ⛔ The half the correction had to PRESERVE: the prescription. Each key
209+
// the renderer's refusal screen names is the key the body tells the
210+
// author to declare.
211+
for (const key of keys) expect(f.message).toContain(key);
212+
// Severity is untouched and CONSISTENT with #16577's ruling B (comment
213+
// `5634033966`): a route that refuses BY NAME stays warning-class under
214+
// ADR-0078 §1 because both doors are loud. These rows now measure that
215+
// shape; they do not extend or reopen the ruling.
216+
expect(f.severity).toBe('warning');
217+
});
218+
219+
it('the two types with no per-type body keep the generic one — an override, not a rewrite', () => {
220+
// ⚠️ An honest pin: it records WHICH body each type receives. `kanban`
221+
// and `tree` are the two rows re-read at the pin that still floor (or
222+
// infer) a binding, so the generic sentence is still true of them. A type
223+
// that stops flooring gets an entry beside `calendar`'s and moves out of
224+
// this list — a deliberate edit, which is the point.
225+
for (const type of ['kanban', 'tree']) {
198226
const f = only(checkViewCompleteness({ type }) as never);
199227
expect(f.rule).toBe(VIEW_LAYOUT_WITHOUT_BINDING);
200228
expect(f.message).toContain('falls back to literal default field names');
@@ -212,13 +240,21 @@ describe('checkViewCompleteness — layout bindings', () => {
212240

213241
it('flags a `map` block that declares neither coordinate form — `map: {}` is the unbound view with braces', () => {
214242
// `ListMapConfigSchema` requires no key, so block presence alone would
215-
// bless `map: { titleField }` on its way to `locationField || 'location'`.
243+
// bless `map: { titleField }` — which objectui's `ObjectMap` refuses by
244+
// its `hasCoordinateBinding` gate exactly as it refuses an absent block.
216245
for (const map of [{}, { titleField: 'title' }, { latitudeField: 'lat' }, { longitudeField: 'lng' }]) {
217246
const f = only(checkViewCompleteness({ type: 'map', map }) as never);
218247
expect(f.rule).toBe(VIEW_LAYOUT_WITHOUT_BINDING);
219248
expect(f.severity).toBe('warning');
220249
expect(f.path).toBe('map.locationField');
221-
expect(f.message).toContain("locationField || 'location'");
250+
// [#19630] The literal `locationField || 'location'` floor this message
251+
// used to quote is gone on both faces (objectui#8169); the body names
252+
// the refusal the renderer shows instead, and the keys that clear it.
253+
expect(f.message).not.toContain("|| 'location'");
254+
expect(f.message).toContain('Map configuration required');
255+
expect(f.message).toContain('hasCoordinateBinding');
256+
expect(f.message).toContain('map.locationField');
257+
expect(f.message).toContain('map.latitudeField');
222258
expect(f.fix).toContain('locationField');
223259
expect(f.fix).toContain('latitudeField');
224260
}

0 commit comments

Comments
 (0)