Repository navigation
Commit 7db1332
Fixes #20349
Flight G1a of #19332 (ruling 5861442317).
Clause-②: no
## What
Five live keys that the object and permission-set schemas declare had no
form row, so an author could reach them only through the Source tab.
Each now has exactly one row. Each row copies a row that a registered
form already has for the same node shape. The four-locale catalogue rows
are in the same PR.
| key | form, section | control | the row it copies |
|:--|:--|:--|:--|
| `object.highlightFields` | `object.form.ts`, Basics (beside
`nameField`) | `widget: 'string-tags'` | `view.form.ts`
`searchableFields` |
| `object.searchableFields` | `object.form.ts`, Basics | `widget:
'string-tags'` | `view.form.ts` `searchableFields` |
| `object.access` | `object.form.ts`, Advanced (beside `sharingModel`) |
`type: 'composite'` over one declared `default` select (`public` /
`private`) | `object.form.ts` `lifecycle` |
| `object.requiredPermissions` | `object.form.ts`, Advanced | `widget:
'json'` | `object.form.ts` `validations` |
| `permission.adminScope` | `permission.form.ts`, System Permissions |
`widget: 'json'` | `permission.form.ts` `objects` / `tabPermissions` /
`rowLevelSecurity` |
The ruling's widget rules, as applied here:
- `requiredPermissions` is the one union in this flight: `string[]` or a
strict `{read, create, update, delete}` map. It takes `json` and never
`string-tags`.
- The two field-name lists take `string-tags`, as free text.
The help text says what the runtime does with each value, including what
absence resolves to. Each claim was checked against its reader:
- `access` absent means public: `security-plugin.ts` reads `isPrivate`
as `access?.default === 'private'`.
- An empty `requiredPermissions` skips the capability gate:
`security-plugin.ts` normalizes it to empty buckets.
- A dangling `highlightFields` entry is refused at `error` on the object
publish door: `validateObjectFieldRefs`, `runtimeTypes: ['flow',
'object']`.
- A dangling or virtual `searchableFields` entry is refused at `error`:
`validateSearchableFields`, including `object`.
No schema's accepted input changes, and no export changes. What changes
is the form payload that `getMetaTypes()` serves and the translation
keys that `os i18n extract` walks.
## The ruling's objectui premise, re-checked at the `.objectui-sha` pin
The ruling carries this premise for the first form flight to re-check:
"`string-tags` reads a non-array as `[]`; `field-multi` binds nothing on
an object draft". I read objectui
`f8a9d0fb0596f4521076628e2bbfe27e6ce67d52` (the pin on `main`) from
source. No browser was run. **Both halves hold.**
- `string-tags` —
`packages/app-shell/src/views/metadata-admin/widgets.tsx:1290`: the
widget sets `tags` to the value when the value is an array, and to an
empty array otherwise. `add` and `remove` then write `next`, which is
built from `tags`, back through `onChange`. So a stored map would be
replaced by a list on the first edit. **HOLDS.**
- `field-multi` — `ResourceEditPage.tsx:889-893`: the field catalogue's
source object comes from `interfaceConfig.source`, `data.object`,
`object` or `objectName` on the draft. The object type's served JSON
schema declares none of those four (probe on this tree: 43 top-level
keys, all four absent, lit control `name` present). So the picker would
offer an empty list. **HOLDS.**
Also read at the pin, because the new rows depend on it:
`resolveFieldFace` in `SchemaForm.tsx:667-750`.
- `json` is not a registered widget. It is in
`KNOWN_PASSTHROUGH_WIDGETS`, so the face comes from the node after
`resolveUnionBranch`.
- For `requiredPermissions`, the served node is an `anyOf` of
array-of-string and an object with four properties.
- A stored map resolves to the object branch, which renders as a nested
form.
- A stored list, or a create with no value, resolves to the array
branch, which renders as the scalar list control.
- `adminScope` is served inline as an object with properties, so it
renders as a nested form.
- Nested-form edits merge. `setField` spreads the stored value and
writes only the one key, so no key the author did not touch is
rewritten.
- Schema defaults are placeholders and are never written on mount. So an
untouched `adminScope` stays absent.
## Residue of the reconciliation gate
The gate's own helper block (`metadata-form-zod-reconciliation.test.ts`,
the whole file copied into a scratch probe that was never committed) was
run at the root coordinate over every object-rooted type. It counts
offerable keys, minus offered keys, minus root `omit` rows. The probe
asserted two controls in the same run:
- lit control: `field.accept`, a G1b key, is in the residue on both
trees;
- dark control: the fabricated key `object.zzFabricated20349` is in no
residue.
| tree | residue | per type |
|:--|:--|:--|
| merge base `15bf186f5` | **28** | object 9 · field 12 · action 6 ·
permission 1 |
| this branch (same `src/` as head `353e708a`) | **23** | object 5 ·
field 12 · action 6 · permission 0 |
Removed: `object.access`, `object.highlightFields`,
`object.requiredPermissions`, `object.searchableFields`,
`permission.adminScope`. Added: none. Both probe runs: 1 file, 58 tests
passed.
## Verification
All test runs went through `scripts/pm/os-verify-lock.sh`, and each
reported `VERDICT command-exit 0`.
| run | result |
|:--|:--|
| `pnpm --filter @objectstack/spec test` | Test Files 557 passed (557) ·
Tests 16497 passed, 1 todo (16498) |
| `pnpm --filter @objectstack/spec test:repo` | Test Files 35 passed
(35) · Tests 634 passed (634) |
| `pnpm --filter @objectstack/platform-objects test` | Test Files 55
passed (55) · Tests 911 passed (911) |
| `pnpm --filter @objectstack/spec typecheck` | exit 0 (`tsc --noEmit`,
`check:scripts-typecheck`, `check:test-typecheck` OK: 53 files, 255
errors, 142 signatures held by the ledger) |
| `pnpm --filter @objectstack/platform-objects typecheck` | exit 0
(`check:test-typecheck` OK: 1 file, 3 errors, 2 signatures held by the
ledger) |
| lint `src/validate-predicate-path-refs.test.ts` (reads the object
form) | 1 file, 54 tests passed |
| cli unit `test/i18n-coverage.test.ts` (reads the form registry) | 1
file, 20 tests passed |
| `pnpm check:i18n` (after the closure build it names) | all 9 packages
in sync; the catalogues regenerate byte-for-byte from the forms |
| `pnpm --filter @objectstack/spec check:generated` | all 15 generated
artifacts up to date |
Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 85 commands. All 85 were run, and
each exit code was written to disk before it was read. `--ran` reports:
85 derived, 85 run, 0 NOT-MEASURED, 0 UNRUN.
- `check:dual-build-cjs-loads` first exited 3 (PREREQUISITE NOT MET:
nine packages had no `dist/`). The missing `dist/` directories then
existed, and the re-run exited 0. The re-run's code is the one recorded.
- `check:entry-nameability` prints its standing structural NOT MEASURED
line for two entries with no callable export. That line is independent
of this diff.
No ablation was run. This PR adds rows and moves population pins; it
adds no guard. The residue before/after above is the measurement of the
delta.
## Hand-over account
This branch was resumed from `bc1d395d3`, the previous dev run's pushed
head. It was re-read hunk by hunk and re-verified in full.
- `7ce01726` (the five rows): kept. Every help-text claim was checked
against its runtime reader, and every comment claim about the renderer
was checked at the pin (above).
- `3820f4a7` and `3ae44a78` (catalogue rows and re-extract): kept. After
the merge, `pnpm check:i18n` regenerates them byte-for-byte. The
source-hash rows the first commit added were removed by the second, net
zero.
- `abbf9c7f` (echo-decision pins and changeset): kept. The pins were
already complete: the platform-objects suite passes on the merged tree
with no further pin edit.
- `353e708a` (this run): the changeset lacked the `Clause-②: no` line.
It now carries that line, and it names the pin that the renderer
description is read at.
- `e505f724`: merge of `origin/main` via `scripts/pm/os-regen-merge.sh`.
No os-regen path conflicted, and nothing needed regenerating
(`check:generated` is green).
## Acceptance notes
- The ruling says "a misspelling is refused loudly at parse". The
refusal is at the publish door and in `os validate` (the two lint rules
above, at `error`), not in the Zod parse: `ObjectSchema` does not judge
field names. The changeset says so.
- `metadata-form-zod-reconciliation.test.ts` has a dark-control comment
reading "`object.access` is authorable and no form offers it". After
this PR a form offers it. The assertions under that comment still hold,
because they test ruled-class admission and not whether a key is
offered. That file is the ledger, which is outside this flight's
surface. Carrier: the #19333 item 2 wiring, which edits that file.
- Nothing pins the ruling's rule that union values never take
`string-tags` across the registry. A future row could flip
`requiredPermissions` back without any test turning red. This is not a
defect today. It is noted for the #19333 wiring, which is where a
registry-wide union check would live.
- A nested-form face cannot unset an object key. Once an author types
into `adminScope`, clearing every sub-field leaves an empty object, and
the parse then refuses it loudly because `businessUnit` is required. The
failure is loud, it comes from the generic objectui renderer, and it is
the same for every nested-form row. Carrier: none.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2c31070 commit 7db1332
9 files changed
Lines changed: 182 additions & 6 deletions
File tree
- .changeset
- packages
- platform-objects/src/apps/translations
- spec/src
- data
- security
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
67 | 75 | | |
68 | 76 | | |
69 | 77 | | |
| |||
269 | 277 | | |
270 | 278 | | |
271 | 279 | | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
272 | 292 | | |
273 | 293 | | |
274 | 294 | | |
| |||
2079 | 2099 | | |
2080 | 2100 | | |
2081 | 2101 | | |
| 2102 | + | |
| 2103 | + | |
| 2104 | + | |
| 2105 | + | |
2082 | 2106 | | |
2083 | 2107 | | |
2084 | 2108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
67 | 75 | | |
68 | 76 | | |
69 | 77 | | |
| |||
269 | 277 | | |
270 | 278 | | |
271 | 279 | | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
272 | 292 | | |
273 | 293 | | |
274 | 294 | | |
| |||
2079 | 2099 | | |
2080 | 2100 | | |
2081 | 2101 | | |
| 2102 | + | |
| 2103 | + | |
| 2104 | + | |
| 2105 | + | |
2082 | 2106 | | |
2083 | 2107 | | |
2084 | 2108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
67 | 75 | | |
68 | 76 | | |
69 | 77 | | |
| |||
269 | 277 | | |
270 | 278 | | |
271 | 279 | | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
272 | 292 | | |
273 | 293 | | |
274 | 294 | | |
| |||
2079 | 2099 | | |
2080 | 2100 | | |
2081 | 2101 | | |
| 2102 | + | |
| 2103 | + | |
| 2104 | + | |
| 2105 | + | |
2082 | 2106 | | |
2083 | 2107 | | |
2084 | 2108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
895 | 895 | | |
896 | 896 | | |
897 | 897 | | |
898 | | - | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
| 901 | + | |
| 902 | + | |
| 903 | + | |
899 | 904 | | |
900 | 905 | | |
901 | | - | |
| 906 | + | |
902 | 907 | | |
903 | 908 | | |
904 | 909 | | |
| |||
908 | 913 | | |
909 | 914 | | |
910 | 915 | | |
911 | | - | |
912 | | - | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
913 | 921 | | |
914 | 922 | | |
915 | 923 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1115 | 1115 | | |
1116 | 1116 | | |
1117 | 1117 | | |
1118 | | - | |
1119 | | - | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
1120 | 1123 | | |
1121 | 1124 | | |
1122 | 1125 | | |
| |||
0 commit comments