Repository navigation
Commit 4f5c4d7
feat(spec): declare
Fixes #22472
Clause-②: yes
## What this declares
`record:approval_decision`, the approval decision panel of a
`sys_approval_request` record page, is now a declared page component
type in `@objectstack/spec`. One node shows the request's decision
progress and its declared decision actions. This is the spec-lane half
of the maintainer's ruling on objectstack-ai/objectui#12045
(`6079807016`, letter 乙, answered 「同意」): a type with no
`ComponentPropsMap` row passes any props bag, so the ruling asks for a
row that `os validate` checks. It is an ordinary v18 widening (「17.x
不会发新版」).
| File | Change |
| :--- | :--- |
| `packages/spec/src/ui/page.zod.ts` | `record:approval_decision` joins
`PageComponentType` (Record Context group), and
`PRINT_REFUSED_PAGE_COMPONENT_TYPES` gets its print reason |
| `packages/spec/src/ui/component.zod.ts` | the `ComponentPropsMap` row
`emptyProps('record:approval_decision')`; the `emptyProps` docblock
lists it |
| `content/docs/references/ui/page.mdx` | regenerated (`gen:docs`): the
`PageComponentType` list and the `type` column |
|
`packages/spec/src/ui/component-record-approval-decision-22472.test.ts`
| pins at the row, the node, the page, the vocabulary and the print
classification |
| `packages/lint/src/validate-record-approval-decision-22472.test.ts` |
pins at the two authoring rules and the shared pipeline `os validate`
runs |
| `.changeset/22472-spec-record-approval-decision.md` |
`@objectstack/spec` `minor`, `Clause-②: yes (widening)` |
## Contract decisions (the spec seat's; relayed to objectui#12045)
1. **Name: `record:approval_decision`, as proposed.** `record` is a
reserved namespace (derived from the enum), so a misspelling there is a
`component-type-unknown` error that offers the declared spelling.
objectui already uses this name in its module-internal renderer and its
tests, so no rename is needed on their side.
2. **Props: an empty strict object, as proposed.** I measured this from
the renderer's read points on objectui `main` at `d03b022`
(`packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.tsx`,
landed in objectui `a368ccb`). `ApprovalDecisionRenderer` discards the
schema node (`schema: _schema`). It draws from the record context (the
bound request row, with its `viewer` and `decision_progress`) and from
`sys_approval_request`'s own declared actions. `className` and the
designer attributes it reads are node-level keys, never `properties`.
The file is not at this repo's `.objectui-sha` pin `f0268ad78`: `git
cat-file -e` at the pin answers 128, with
`record-approvals-renderer.tsx` at the same pin as the control answering
0. That is why the citation names objectui `main`, not the pin. The row
reuses the existing `emptyProps(type)` factory, the spelling of the nine
other zero-prop rows (`nav:menu`, `element:divider`,
`mcp:connect-agent`, …), so the refusal names the component.
3. **An enum member, not a row-only type.** `record:line_items`,
`element:definition-list` and the other row-only types entered the
vocabulary through the open string arm. The vocabulary admits a type
that way only on measured registration, publication and authorship
(`component-type-vocabulary.ts`; the `#20371` rows write all three
down). This type has none of the three yet. objectui keeps its renderer
unregistered until this declaration exists (its docblock says so), and
plugin-approvals authors the page after it (#22473). A spec-first
declaration is therefore an enum member. One consequence for objectui:
its palette-coverage test (`block-config.test.ts`, derived from
`PageComponentType.options`) asks for an offer-or-exclude decision for
the new member once objectui moves to `next` (objectui#12030).
## Measured hypotheses
- **H1 holds.** `record:approval_decison` → one `component-type-unknown`
finding, severity `error`, message offering
`'record:approval_decision'`. The declared spelling in the same position
→ no finding. Both are pinned in the lint test, through
`validateComponentTypes` and through `runAuthoringRules('validate', …)`.
- **H2: every table the member must appear in.** Grep for
`record:history` / `record:line_items` / `record:reference_rail` across
`packages/spec`, `packages/lint`, `content`, `scripts`:
- `PageComponentType` (page.zod.ts): added.
- `ComponentPropsMap` (component.zod.ts): added.
- `PRINTABLE_PAGE_COMPONENT_TYPES` /
`PRINT_REFUSED_PAGE_COMPONENT_TYPES` (page.zod.ts): added to the refused
map. `page-print.test.ts` requires every live known type to be
classified exactly once, and it passes.
- `content/docs/references/ui/page.mdx`: regenerated.
- Derived, not edited: `KNOWN_COMPONENT_TYPES`,
`KNOWN_COMPONENT_TYPE_CANDIDATES` and
`RESERVED_COMPONENT_TYPE_NAMESPACES` (unchanged: `record` was already
reserved). The lint print gate and its `it.each` iterate the map. The
`record:*`-on-react-page refusal in `validate-react-page-props.ts` is
prefix-based.
- Not a table of members: `scripts/format-type.test.ts` (a static
fixture that still lists the retired types),
`validate-page-field-bindings.ts` (rows for field-binding props only;
this type has none), `sdui.manifest.json` (objectui's build output).
- **H3 holds.** A no-props row is spelled `emptyProps(type)`, a
`strictObject` with no keys. Any key → `unrecognized_keys` naming ``
`record:approval_decision` `` and the key, and through lint one
`component-props-unknown-key` warning at `properties.KEY`. A node-level
key written inside `properties` (`className`) gets the "belongs on the
component NODE" prescription.
- **H4, measured and not fixed.** I ran `validateComponentTypes` /
`validateComponentProps` from this branch on a record page:
- `record:approvals` → `component-type-unknown/error`, no props finding.
- `record:attachments` → `component-type-unknown/error`, no props
finding.
- Controls: `record:approval_decision` and `record:details` → no type
finding, one props finding each.
Both are refused outright. objectui synthesizes both nodes at render
time (`buildDefaultPageSchema`); no authored producer exists in this
repo.
## Verification
Tests at `b101db36c7` (the final commit), all under
`scripts/pm/os-verify-lock.sh`:
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/ui/component-record-approval-decision-22472.test.ts
src/ui/page-print.test.ts src/ui/component-type-vocabulary.test.ts`: 3
files, 54 tests passed.
- `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2
src/validate-record-approval-decision-22472.test.ts
src/validate-print-page-blocks.test.ts
src/validate-component-types.test.ts`: 3 files, 94 tests passed, against
a spec `dist` rebuilt from this branch. On a stale `dist` the declared
type would read as `component-type-unknown`, so the green lint pins also
show that the rebuilt `dist` was read.
- Full suites, one locked batch joined with `&&`, `VERDICT command-exit
0`: `pnpm --filter @objectstack/spec typecheck` (incl.
`check:test-typecheck` OK) · `pnpm --filter @objectstack/lint typecheck`
(OK) · spec `vitest run --project local`: 632 files, 18837 passed, 1
todo · spec `vitest run --project repo`: 54 files, 915 passed · lint
`vitest run`: 132 files, 6003 passed.
- Ablation, one time and not kept: `node scripts/ablation-replace.mjs`
deleted the row line (anchor x1 → x0, blob `583b6e1e` → `908d18cc`). The
spec pin file went to 5 failed / 3 passed. The 3 that stayed green are
the vocabulary and print pins, which the enum member alone still
satisfies. Restored blob = HEAD `583b6e1e`, and `git diff HEAD` is
empty.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands` on this diff
derived 109 commands (a superset of the dispatch list, +29 from the docs
and lint-test paths). Every one ran with its exit code recorded. `--ran`
reconciliation: 109 derived, 107 run, 2 NOT-MEASURED, 0 UNRUN. Five
gates first exited 3 (unbuilt `@objectstack/lint` / client / objectql).
They passed (exit 0) after their packages were built. NOT MEASURED:
`check:dual-build-cjs-loads` and `check:type-check-debt`. Reason: both
refuse unless every workspace package is built (a whole-repo build),
which is left to CI. `pnpm --filter @objectstack/spec check:generated`
is green after `gen:docs`. `authorable-surface/` and `api-surface/` did
not move (the enum adds a value, not a key or an export).
## Acceptance notes
- **Placement is not checked.** The panel draws nothing outside a
`sys_approval_request` record page (objectui returns `null`), and no
rule refuses the type on another object's page. The row comment and the
changeset say so. Not filed: there is no producer yet, and the services
child is the only planned author.
- `content/docs/ui/pages.mdx` (hand-written) introduces its list as
"include", and that list already omits `record:line_items`. It is
outside this card's file surface, so it is left unchanged.
- File-surface note: the dispatch placed "the component type enum" in
`component.zod.ts`, but `PageComponentType` lives in `page.zod.ts`. That
file was already on the surface for the print tables. The lint test file
is the H3 pin for the `component-props-unknown-key` rule id, which lives
in `@objectstack/lint`. No lint source changed: the row needed no lint
hook.
- Consumers: objectstack-ai/objectui#12045 (renderer registration and
zod arm at its page-mount step) and #22473 (plugin-approvals' request
page, which carries `Blocked-by:` on this card).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN)_
---------
Co-authored-by: Claude <noreply@anthropic.com>record:approval_decision, the approval decision panel, with an empty strict ComponentPropsMap row (#22532)1 parent faf6348 commit 4f5c4d7
6 files changed
Lines changed: 251 additions & 4 deletions
File tree
- .changeset
- content/docs/references/ui
- packages
- lint/src
- spec/src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
260 | 260 | | |
261 | 261 | | |
262 | 262 | | |
263 | | - | |
| 263 | + | |
264 | 264 | | |
265 | 265 | | |
266 | 266 | | |
| |||
326 | 326 | | |
327 | 327 | | |
328 | 328 | | |
| 329 | + | |
329 | 330 | | |
330 | 331 | | |
331 | 332 | | |
| |||
Lines changed: 88 additions & 0 deletions
| 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 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
Lines changed: 117 additions & 0 deletions
| 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 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
406 | 406 | | |
407 | 407 | | |
408 | 408 | | |
409 | | - | |
| 409 | + | |
410 | 410 | | |
411 | | - | |
| 411 | + | |
| 412 | + | |
412 | 413 | | |
413 | 414 | | |
414 | 415 | | |
415 | 416 | | |
416 | 417 | | |
417 | 418 | | |
418 | | - | |
| 419 | + | |
419 | 420 | | |
420 | 421 | | |
421 | 422 | | |
| |||
9376 | 9377 | | |
9377 | 9378 | | |
9378 | 9379 | | |
| 9380 | + | |
| 9381 | + | |
| 9382 | + | |
| 9383 | + | |
| 9384 | + | |
| 9385 | + | |
| 9386 | + | |
| 9387 | + | |
| 9388 | + | |
| 9389 | + | |
| 9390 | + | |
| 9391 | + | |
| 9392 | + | |
| 9393 | + | |
| 9394 | + | |
| 9395 | + | |
9379 | 9396 | | |
9380 | 9397 | | |
9381 | 9398 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
187 | 197 | | |
188 | 198 | | |
189 | 199 | | |
| |||
898 | 908 | | |
899 | 909 | | |
900 | 910 | | |
| 911 | + | |
901 | 912 | | |
902 | 913 | | |
903 | 914 | | |
| |||
0 commit comments