Repository navigation
Commit 32fa194
feat(lint)!: component-props-invalid refuses a retired page-component prop as an error at os validate, os build and os lint (#23140)
Fixes #23026
Clause-②: no (narrowing: `os validate` stops accepting a retired
page-component prop; triage's grade `6123043175` reads the same)
## What this does
The component-props gate's error upgrade, the step its own header
recorded ("the warning-period inventory is the acceptance baseline for
the error upgrade, and the upgrade is its own step once the inventory is
empty"), scoped to the class triage named: **a retired key**.
- `component-props-invalid` now files a `retiredKey()` tombstone's
verdict as **`error`**. `os validate`, `os build` and `os lint` refuse
the stack, and the message carries the tombstone's own prescription.
- Every other finding of the rule stays a **`warning`**. That covers
other value refusals (including the two closed-set refusals that just
landed: a url action's `target` scheme from PR #23076, and a
`record:details` section `count` from PR #23080), retired enum values,
undeclared keys and the missing `dataSource.object` binding.
- `validateComponentProps` becomes `tier: 'gating'` in
`authoring-rules.ts`, which the wiring guard requires of any rule that
emits `error`. It already ran on all three commands.
- The two conversion-registry sentences that promised a refusal now name
the doors that actually refuse. Comment-only, see below.
## Measured before acting
**Inventory (H2), at `21c86444e`, before the change:**
| corpus | door | retired-key findings | every other `component-props-*`
finding |
|:--|:--|--:|--:|
| `examples/app-crm`, `app-todo`, `app-multi-package`, `app-showcase` |
`os validate --json` | 0 | 0 |
| the five shipped pages outside them: `platform-objects`
sys-organization, sys-position, sys-user; `plugin-approvals`
sys-approval-request; `qa/downstream-contract` welcome | the rule on the
normalized stack | 0 | 0 |
| `packages/apps/*` (account, setup, studio), the `create-objectstack`
blank template, CLI generators | none authors a page | n/a | n/a |
Re-measured on the merged head `f88009a41`: the same zeros. The page
instrument was checked against a positive control (one fixture page with
a retired key, an invalid value and an unknown key), which it counted 1
/ 1 / 1. Class (b) is therefore **0**, and that is the next step's
baseline. Whether (b) should also move is an open question in the
report, not part of this diff.
**How a retired key is told apart (H1).** It is read off the issue,
never off its text. A `retiredKey()` is `z.never(...).optional()`, which
raises `invalid_type` with `expected: 'never'`. The spec's private
`isRetiredKeyIssue` in `flow-node-config-refusals.ts` uses the same
discriminator. A schema-graph walk of `ComponentPropsMap` finds **50**
never-nodes:
- 48 key tombstones;
- 2 retired-element rows (`user:profile`, `ai:chat_window`;
`retiredComponentProps`, the same issue shape with the element's
prescription), refused the same way;
- 2 of the key tombstones sit under a union arm (`object-form` /
`object-master-detail-form` `sections[].fields[]`, the object arm's
`publicPicker`). There zod collapses the arm failures into one
`invalid_union`, so the classifier applies the existing one-arm rule of
`unrecognizedKeysFromUnionArm`: exactly one arm raised tombstone issues
and nothing else. A mixed arm stays a warning, and that is pinned.
Retired enum **values** (`enumWithRetiredValues`) raise an enum issue,
not `invalid_type`/`never`, so they are not swept in. The `element:text`
variant migration text that calls that finding advisory therefore stays
true.
**Both doors on one fixture (H4).** The fixture was a `defineStack` app
with `object-grid` `properties.defaultFilters`:
| door | before (`21c86444e`) | after |
|:--|:--|:--|
| `os validate` | exit 0, "Validation passed", one
`component-props-invalid` warning | exit 1, "Author-time rules failed (1
issue)", the prescription printed |
| `os build` | exit 0, artifact written (1.8 KB), one warning | exit 1,
no artifact |
| `os lint` | n/a | exit 1, "1 error(s)" |
No door still passes. The runtime publish door (Studio / REST `/meta` /
MCP) does not run this rule (`surfaces: CLI_ONLY`), so a page saved
there is not refused. That is written into the rule header, the registry
sentences and the changeset, and nothing claims otherwise.
**Registry text (H3).** `packages/spec/src/conversions/registry.ts`:
- the record-picker reconciliation docblock said "the loader rejects it
loudly with the prescription";
- the `object-grid-default-filters-removed` docblock said "refused at
the parse with the prescription".
The load path keeps the open `properties` bag as written
(`PageComponent.properties` is `z.record`). Both sentences now name `os
validate`, `os build` and `os lint` (the component-props gate). This is
a comment-only edit. On this head the first round stopped at these two
sentences; the patch round below corrects six more. It publishes
nothing: `@objectstack/spec`'s `files` ships `dist` plus
`src/**/*.zod.ts`, and on a built dist the old sentences have 0 hits
while a string literal from the same entry hits (positive control).
## Tests
- `packages/lint/src/validate-component-props.test.ts` changes:
- 4 pins flip from warning to error: `breadcrumb`, detail `sortField`,
`displayField`, and the wiring pin from `advisory` to `gating`.
- 10 new pins:
- the card's specimen as an error, with the prescription read back from
the spec's own tombstone, not copied prose;
- every top-level tombstone in the map, found from the schema graph (not
from the issue shape), refused as an error at its own key;
- the retired-element bag;
- the union-arm tombstone as an error, and a mixed union arm as a
warning;
- one error plus one warning in a single bag;
- the #23076 url `target` refusal and the #23080 section `count` refusal
both stay warnings;
- an unknown key and a missing binding stay warnings;
- the shared `runAuthoringRules('validate')` pipeline carries the error.
- `packages/spec/src/ui/form-field-public-picker-retirement.test.ts`
gains one exclusion: the props-lint pin authors `publicPicker` to prove
the union-arm refusal, exactly as the `breadcrumb` and
detail-`sortField` absence pins already exclude that same file.
- Readings, all at `f88009a41`, all under the shared verify lock:
- `@objectstack/lint`: 137 files / 6624 tests passed; `typecheck` (tsc
plus the test layer under `tsconfig.test.json`) passed, debt ledger
unchanged;
- `@objectstack/cli` unit layer for validate / build / author-time: 5
files / 61 tests passed (`build-warning-truncation-notice`,
`generate-scaffold-validates`, `validate-build-gate-parity`,
`validate-per-package-authoring-seam`, `src/utils/author-time-rules`).
The integration layer is declared to CI, since no spawn entry was
touched;
- `@objectstack/spec`: the 20 test files that read
`conversions/registry.ts` passed (1060 tests); `typecheck` passed;
`check:generated` reports all 14 artifacts current against a freshly
built dist;
- example apps: their `os validate` reruns are in the inventory table.
No example metadata changed, so no example package owes its own suite.
- **Ablation.** One-time mutations, each run under an absolute-path trap
that restored from `HEAD` and proved the blob equal to `HEAD`, with `git
diff HEAD` empty:
- classification disabled at the dispatch point: exactly the 9
retired-key pins go red, and 66 stay green (all four stays-a-warning
pins among them);
- the union-arm branch removed: exactly the union-arm pin goes red (1
failed / 74 passed).
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` on the final head `f88009a41` derived 88
commands. 87 ran, each exited 0, with exit codes captured before any
pipe. `--ran` reconciles as "88 derived, 87 run, 1 NOT-MEASURED, 0
UNRUN".
- NOT MEASURED: `check:type-check-debt`. Reason: a whole-workspace build
plus `--re-measure`; CI owns it (dispatch instruction).
- `check:dual-build-cjs-loads` first refused with PREREQUISITE NOT MET
(8 unrelated packages had no dist). After building them it passed.
- The derivation notes `origin/main` moved 4 commits past `f88009a41`
while the union ran. One family input changed in that range
(`scripts/engine-double-contract.pinned.json`); CI's merge ref reads it.
## Changeset
`.changeset/23026-retired-component-prop-refused.md`:
`@objectstack/lint: major` on the v18 pre line, `Clause-②: no
(narrowing)`, FROM → TO table, and the ADR-0087 disposition
`already-registered`. It names the 20 ledger ids that retired the
refused keys. `check:adr-0087-registration` and
`check-changeset-no-major` judged it green. The clause-② level axis is
PR-scoped and CI reads it.
## Patch round: the registry sentences the first head left false
The contract-tier review FAILed `f88009a41` (record 6137697373 on this
PR): two `registry.ts` docblocks still called the props gate "advisory"
for keys this PR refuses as an `error`. The seat amended the claim's
file surface (6137721990 on #23026) to every comment sentence in
`conversions/registry.ts` and `migrations/entries/**` that states what
the component-props gate does with a tombstoned key or a retired value
and is false on the head. Commit `f1905ac90` is comment-only: 2 files, 6
sentences. No code, test, changeset or other file changed.
**Made false by this PR, corrected:**
1. `registry.ts:10621-10625` (`page-header-breadcrumb-removed`)
- BEFORE: "The tombstone on `PageHeaderProps` refuses it for a live
author (advisory, via the props lint: `PageComponentSchema.properties`
is an open bag)."
- AFTER: "The tombstone on `PageHeaderProps` refuses it for a live
author: `os validate`, `os build` and `os lint` refuse it as an `error`
with the prescription (the component-props gate, `@objectstack/lint`;
the load path keeps the open `properties` bag as written)."
2. `registry.ts:13565-13569`
(`object-master-detail-form-detail-sort-field-removed`)
- BEFORE: "The tombstone refuses the key for a live author (advisory,
via the props lint: `PageComponentSchema.properties` is an open bag)."
- AFTER: "The tombstone refuses the key for a live author: `os
validate`, `os build` and `os lint` refuse it as an `error` with the
prescription (the component-props gate, `@objectstack/lint`; the load
path keeps the open `properties` bag as written)."
**False before this PR, folded in under the seat's amendment
6137721990.** Each is the same defect class as the card, a mechanical
sentence edit, unclaimed elsewhere, and in the same gate family.
3. `registry.ts:8333-8340` (`element-text-variant-heading-levels`; the
review cited it as `:8327-8328`). This is pre-existing.
- BEFORE: "`retiredFromLoadPath`: an author is refused at parse with the
prescription rather than silently rewritten; stored rows replay it ..."
- AFTER: "`retiredFromLoadPath`: an author is not silently rewritten —
`os validate`, `os build` and `os lint` report an old spelling as an
advisory `component-props-invalid` finding carrying the prescription
(the component-props gate, `@objectstack/lint`: a retired enum value is
a value verdict, a warning; the load path keeps the open `properties`
bag as written); stored rows replay it ..."
4. `registry.ts:5836-5839` (`page-tabs-type-to-tab-style`). This is
pre-existing and was found by the sweep.
- BEFORE: "The tombstone still carries the refusal at the authoring site
(`tsc`) and at load (the parse), whatever the walk reached."
- AFTER: "The tombstone still carries the refusal at the authoring site
(`tsc`) and at `os validate` / `os build` / `os lint`, where the
component-props gate refuses it as an `error` (the load path keeps the
open `properties` bag as written), whatever the walk reached."
5. `registry.ts:8032-8037` (`time-default-utc-suffix-dropped`). This is
pre-existing and was found by the sweep; only the `element:button`
position was false.
- BEFORE: "Retired from the load path: an author is refused at parse,
and data at rest and `os migrate meta` replay it."
- AFTER: "Retired from the load path: an author is refused at parse —
except on an `element:button`'s inline action, which sits in the open
`properties` bag the load path keeps as written, where the refusal is an
advisory `component-props-invalid` finding at `os validate` / `os build`
/ `os lint` (the component-props gate, `@objectstack/lint`) — and data
at rest and `os migrate meta` replay it."
6.
`migrations/entries/retired-keys/18.ui__PageHeaderProps__breadcrumb.ts:16-19`.
The residue argument is kept whole; only the present-tense "advisory",
false on this head, is dated.
- BEFORE: "this row is parsed only by the advisory props lint, which
writes nothing back — so no released toolchain emitted `breadcrumb:
true` ..."
- AFTER: "this row is parsed only by the props lint (advisory in every
release that shipped the default), which writes nothing back — so no
released toolchain emitted `breadcrumb: true` ..."
**Measured on the head, not argued.** The built rule at `f1905ac90`
gives:
| case | finding |
|:--|:--|
| `page:tabs` `type` | `error` |
| `page:header` `breadcrumb` | `error` |
| `details[].sortField` | `error` |
| `element:text` `variant: 'heading'` | `warning` (enum issue,
prescription carried) |
| `element:button` inline-action `time` param `defaultValue:
'09:00:00Z'` | `warning` (a `custom` issue at
`action.params.0.defaultValue`) |
| `record:chatter` `position: 'sidebar'` | `warning` |
| `object-form` `layout: 'grid'` | `warning` |
The sweep read 21 `registry.ts` sites and every `migrations/entries/**`
hit of the props-gate vocabulary. The rest are true as written; the
per-site list with evidence is in the `os-dev-report` on #23026.
**Gates on `f1905ac90`.** The derivation gives 89 commands: the 88 of
`f88009a41` plus `check:spec-changes`, which the `migrations/entries`
edit adds. 19 ran, each exit 0:
- `check-spec-docblock-symbol-anchors`, both halves;
- `check-issue-citations`, both spellings;
- `check:docs`, `check:spec-changes`, `check:upgrade-guide`,
`check:liveness`, `check:authorable-surface`;
- `check:doc-authoring`, `check:nul-bytes`;
- comment-mask adoption, both halves; `check-drift-comment`;
`check-affected-docs`;
- closing-keyword parity, both halves;
- `check:pm-prior-rulings`, `check:cross-package-test-inputs`.
`check:generated` also ran: all 14 artifacts are current against a
freshly built dist. The spec tests that read `registry.ts` (11 files in
`src/conversions`, the migrate-sentence class pin, and 20 more) passed:
32 files, 1520 tests. `--ran` reconciles as "89 derived, 19 run, 70
NOT-MEASURED, 0 UNRUN". The 69 other lines are carried from `f88009a41`,
where each exited 0, because the patch adds comment text only.
`check:type-check-debt` stays NOT MEASURED by dispatch.
### Patch round 2: the docs that state the gate's tier
The seat found a published page that states the tier this PR changes
(amendment 6138049056 on #23026), plus a sibling sentence in the same
class. Commit `175a546b8` is docs-only: two files under `content/docs/`,
with no code, test, changeset or `packages/**` change.
1. `content/docs/deployment/validating-metadata.mdx:473` and `:488`, the
"one gate, four doors" table. Made false by this PR.
- BEFORE: "SDUI component props" sat only in the row headed "Advisory:
record titles, semantic field pointers (ADR-0085), form-section layout,
action placement, SDUI component props, seed replay/state safety,
capability references, liveness", ticked on `os validate`, `os build`
and `os lint`, with no runtime publish tick.
- AFTER: a new gating row, "Retired SDUI component props — a key
`ComponentPropsMap` tombstones (`retiredKey()`), refused with its
prescription (#23026)", ticked on `os validate`, `os build` and `os
lint`, with no runtime publish tick. The advisory row now reads "SDUI
component props (every finding but a retired prop)".
2. `content/docs/protocol/objectui/layout-dsl.mdx:741-745`, the callout
under the `custom.*` example. Pre-existing, folded in under 6138049056.
- BEFORE: "For the platform's own types the authoring rules dispatch
`ComponentPropsMap` and reject a misspelled prop; a `custom.*` type has
no entry there, so its props are validated only by whatever registered
the component."
- AFTER: "For the platform's own types the authoring rules (`os
validate`, `os build`, `os lint`) dispatch `ComponentPropsMap`: a
misspelled prop is reported as a warning, and a retired prop is refused
as an error, with its prescription. A `custom.*` type has no entry
there, so its props are validated only by whatever registered the
component."
**Measured on the built rule at this head:**
| case | result |
|:--|:--|
| a misspelled prop (`page:header` `titel`) | `warning`
`component-props-unknown-key` |
| `object-grid` `defaultFilters`, `page:header` `breadcrumb` | `error`
`component-props-invalid` |
| a wrong-typed `limit`, or a missing `dataSource.object` | `warning`
`component-props-invalid` |
| a `custom.*` node | no finding |
The registry entry reads `tier: gating`, commands `build`, `lint` and
`validate`, and surfaces `cli` only, so the runtime publish column stays
empty.
**Sweep** of `content/docs/**`: `releases/` is excluded as
release-owned, and `references/` as generated from spec `.describe()`
text, which this PR does not change. The two pages above are the only
sentences that state the props gate's tier. The other hits concern other
rules, or schemas the load path parses:
- strict-schema refusals in `ai/tools`, `automation/approvals`,
`automation/flows`, `data-modeling/field-types`;
- validating-metadata §5, §10 and `:357`;
- layout-dsl `:219`, `:411`, `:449`, `:626`, `:754`;
- `ui/views.mdx:453`;
- the query `searchFields` mentions.
The per-site list is in the `os-dev-report` on #23026.
**Gates on `175a546b8`.** The derivation gives 109 commands: the 89 of
`f1905ac90` plus 20 that the `content/docs` paths add. 32 ran, each exit
0:
- the 20 added: doc frontmatter, doc route spelling, docs section name
and section landing index (each with its self-test);
`check:doc-security-posture`, `check:skill-examples`,
`check:corpus-claim-drift`, `check:doc-anchors`,
`check:docs-audit-scope`, `check:docs-redirects`,
`check:docs-single-h1`, `check:docs-spec-enumerations`,
`check:published-readme-links`, `check:react-page-adapter-contract`,
`check:skill-identifier-liveness`, `check:vendor-version-stamps`;
- `check:doc-authoring`, `check-affected-docs`, `check-drift-comment`,
`check:docs`, `check:docs-transcript-drift`;
- closing-keyword parity and issue citations, both halves of each;
- `check:nul-bytes`, `check:cross-package-test-inputs`,
`check:pm-prior-rulings`.
`check:skill-examples` first refused with PREREQUISITE NOT MET
(`@objectstack/client-react` was unbuilt); after building it, it exited
0. "Check Documentation Links" is lychee in CI and has no local form in
this container: NOT MEASURED. These edits add no link. `--ran`
reconciles as "109 derived, 32 run, 77 NOT-MEASURED, 0 UNRUN": 8 lines
carried from `f1905ac90`, 68 from `f88009a41`, and
`check:type-check-debt` by dispatch. Each carry holds because this round
changes two `content/docs` pages and no input of those families.
### Patch round 3: the shipped docblocks in `component.zod.ts`
The contract-tier review FAILed `175a546b8` (record 6138451028) on one
shipped sentence: the docblock above the `breadcrumb` tombstone still
called the props lint "advisory", and `component.zod.ts` ships in the
tarball (`src/**/*.zod.ts`). The seat amended the surface (6138461923)
to that file's comments plus four non-shipped sites. Commit `6280ca3b0`
is comment-only: 4 files, +24/-15. The diff guard counts 0 non-comment
lines. No schema, `retiredKey()` guidance string or `.describe()` text
changed; those belong to #23145's claim.
1. `component.zod.ts:654-658`, the docblock above `breadcrumb`. Made
false by this PR; dated the way its retired-keys twin was, with the
residue argument kept whole.
- BEFORE: "this row is parsed only by the advisory props lint, which
writes nothing back — so no retired-default residue stage is owed."
- AFTER: "this row is parsed only by the props lint (advisory in every
release that shipped the default), which writes nothing back — so no
retired-default residue stage is owed."
2. `component.zod.ts:284-303`, the #5068 header. Made false by this PR.
- BEFORE: "The gate is WARNING-level in this first step. ... the
inventory is the acceptance baseline for the error upgrade. Two of the
three entries are now cleared: ... What remains before the upgrade to
error is the page rewrites (`page:card.visible` → the component-level
`visibleWhen`, #5776's tab `key` → `value`), not a declaration in this
file."
- AFTER: "The gate was WARNING-level in this first step. ... the
inventory was the acceptance baseline for the error upgrade. Two of the
three entries were cleared here: ... The page rewrites (...) closed the
third, and #23026 took the upgrade for one class once the inventory
measured empty: a `retiredKey()` tombstone's verdict is an `error` at
`os validate` / `os build` / `os lint`. Every other finding of the gate
stays a warning (#23147 carries the rest)."
3. `component.zod.ts:4849-4854`, the #7751 object-* section. The review
read it as true for its subject. My probe refutes that: these rows carry
tombstones (`object-grid` `defaultSort` and `navigation.view`,
`object-kanban` `quickAdd`), and each is an `error` on this head. The
maintainer ruling quotation is kept verbatim.
- BEFORE: "findings on these entries are advisory, still gated on the
#5068 inventory (the ruling: ...)."
- AFTER: "findings on these entries were advisory, gated on the #5068
inventory (the ruling: ...). Once that inventory measured empty, #23026
made one class an `error` — a `retiredKey()` tombstone's verdict, such
as a retired key these rows carry; every other finding on them stays
advisory (#23147 carries the rest)."
4. `ui/page-header-breadcrumb-retirement.test.ts:19-21` and 5.
`ui/master-detail-detail-sort-field-retirement.test.ts:22-24`. Not
shipped.
- BEFORE: "reached by the advisory props lint and never by the page
parse: an existing page is never hard-refused."
- AFTER: "reached by the props lint — which refuses this tombstone as an
`error` at `os validate` / `os build` / `os lint` — and never by the
page parse: an existing page is never hard-refused at load."
6. `ui/element-flat-binding-retirement.test.ts:201-202`. Not shipped.
- BEFORE: "The open `properties` bag is the props lint's to judge
(advisory), not the page parse's."
- AFTER: "The open `properties` bag is the props lint's to judge (a
retired flat key is an `error` at `os validate` / `os build` / `os
lint`), not the page parse's."
**Kept as written, with evidence:**
- `conversions/walk.ts:371` is read in context. Its story is
`page:header` `description`, which is not a tombstone; the strict row's
`unrecognized_keys` makes it a `component-props-unknown-key` warning,
which the probe confirms at a nested site. So "advisory" is true there.
- `component.zod.ts:5686` (`exportOptions`, "advisory"): a bare array
and a `pdf` format are both warnings, per the probe.
- `component.zod.ts:3376` and `:3429` (the props gate "refusing every
authored key" of `element:filter` / `element:form`): those keys are
tombstones, now errors.
**Still false after this round. Not edited, because the round's single
push was spent before a wider grep found them:**
- `packages/lint/src/validate-page-field-bindings.ts:233-234`: "the
#5068 props gate reports the key ... That gate is advisory and
CLI-only". The keys are the tombstoned `displayField` / `searchFields`,
now errors.
- `ui/page-header-breadcrumb-retirement.test.ts:356-357` and
`ui/master-detail-detail-sort-field-retirement.test.ts:447-448`: "The
props-lint pin authors the key to assert the advisory warning". Those
pins now assert an error.
All three are non-shipped comments. One three-sentence comment commit
closes the class.
**Measured on the head.** The built rule at `6280ca3b0` gives:
| case | result |
|:--|:--|
| `page:header` `breadcrumb` | `error` |
| `object-grid` `defaultSort` and `navigation.view`, `object-kanban`
`quickAdd` | `error` |
| an `object-grid` `pagination.pageSize` of the wrong type, the unknown
key `filters` | `warning` |
| `exportOptions` refusals | `warning` |
| a nested `page:header` `description` | `warning`
`component-props-unknown-key` |
| `element:repeater` `properties.object` | `error` |
| an `element:filter` key | `error` |
**Gates on `6280ca3b0`.** The derivation gives the same 109 commands as
`175a546b8`. 17 ran, each exit 0:
- `check:docs`;
- `check-spec-docblock-symbol-anchors`, both halves;
- `check:spec-changes`, `check:liveness`, `check:authorable-surface`,
`check:api-surface`, `check:exported-any`;
- closing-keyword parity and issue citations, both halves of each;
- `check:nul-bytes`, comment-mask adoption (both halves),
`check:doc-authoring`, `check:cross-package-test-inputs`.
`check:generated` reports all 14 artifacts current after a real spec
build from this head. Tests: the three touched spec files passed (96
tests), and `validate-component-props.test.ts` passed (75 tests).
`--ran` reconciles as "109 derived, 17 run, 92 NOT-MEASURED, 0 UNRUN":
24 lines carried from `175a546b8`, 1 from `f1905ac90`, 66 from
`f88009a41`, and `check:type-check-debt` by dispatch. Each carry holds
because this round changes comment text only.
### Patch round 4: the last three comments of the class
Round 3's widened grep found three more false comments after that
round's push. The seat amended the surface to cover them (6138732286 on
#23026). Commit `20c00221c` is comment-only: 3 files, +9/-7. The diff
guard counts 0 non-comment lines. None of the three ships.
1. `packages/lint/src/validate-page-field-bindings.ts:232-239`, about
the tombstoned `displayField` / `searchFields`. The registries-differ
argument is kept: the gate neither precedes nor suppresses this rule.
- BEFORE: "the #5068 props gate reports the key with its rename/delete
prescription. That gate is advisory and CLI-only and lives in a
different registry (`authoring-rules`) from this suite, so it neither
precedes nor suppresses this rule — ..."
- AFTER: "the #5068 props gate refuses the key as an `error` at `os
validate` / `os build` / `os lint`, with its rename/delete prescription
(it stays advisory for every other finding). That gate is CLI-only and
lives in a different registry (`authoring-rules`) from this suite, so it
neither precedes nor suppresses this rule — ..."
2. `ui/page-header-breadcrumb-retirement.test.ts:356-357` and 3.
`ui/master-detail-detail-sort-field-retirement.test.ts:447-448`, the
absence pins' `EXCLUDED` comments.
- BEFORE: "The props-lint pin authors the key to assert the advisory
warning an author meets at `os validate` / `os build` / `os lint`."
- AFTER: "The props-lint pin authors the key to assert the refusal (an
`error`) an author meets at `os validate` / `os build` / `os lint`."
**The class is closed.** The class grep on `20c00221c` over
`packages/spec/src` and `packages/lint/src` combined round 3's patterns
with the widened ones ("advisory warning", "gate is advisory"), plus the
advisory-adjective and props-gate-with-tier-word sweeps. It returns no
false hit. Every remaining hit falls into one of these groups:
- dated or past-tense text: `component.zod.ts:284-303`, `:656`,
`:4849-4854`, the retired-keys comment, and this PR's own lint text;
- true for its subject: `walk.ts:371` (a `description` unknown-key
warning), `component.zod.ts:5686` (`exportOptions` value verdicts), and
the `registry.ts.template` typed-member strings;
- another rule's own "advisory": liveness, seed safety, readonly hook
writes, the flow package closure, security posture.
The per-hit list is in the `os-dev-report` on #23026.
**Gates on `20c00221c`.** The derivation gives the same 109 commands. 10
ran, each exit 0: comment-mask adoption and issue citations (both halves
of each), `check:doc-authoring`, closing-keyword parity (both halves),
`check:nul-bytes`, lint `check:doc-formula-expressions`,
`check:cross-package-test-inputs`. Tests under the verify lock: the two
touched spec files passed (40 tests), and
`validate-page-field-bindings.test.ts` passed (38 tests). `--ran`
reconciles as "109 derived, 10 run, 99 NOT-MEASURED, 0 UNRUN": 8 lines
carried from `6280ca3b0`, 24 from `175a546b8`, 1 from `f1905ac90`, 65
from `f88009a41`, and `check:type-check-debt` by dispatch. Each carry
holds because this round changes three comments only.
## Acceptance notes
- **File surface vs the claim.** Paths outside the claim's original
list, each since added by a seat amendment:
- `packages/lint/src/authoring-rules.ts`: the `tier` flip that the
wiring guard requires for any rule emitting `error`, plus its comment;
- `packages/spec/src/conversions/registry.ts`: the two H3 sentences,
which the dispatch pre-authorized, and the patch round's five docblock
sentences (amendment 6137721990); plus
`packages/spec/src/migrations/entries/retired-keys/18.ui__PageHeaderProps__breadcrumb.ts`,
one comment;
- `packages/spec/src/ui/form-field-public-picker-retirement.test.ts`:
the one exclusion above.
- `content/docs/deployment/validating-metadata.mdx` and
`content/docs/protocol/objectui/layout-dsl.mdx`: patch round 2
(amendment 6138049056).
- `packages/spec/src/ui/component.zod.ts` (comments only) and three spec
test files' comments: patch round 3 (amendment 6138461923).
- `packages/lint/src/validate-page-field-bindings.ts`, one comment:
patch round 4 (amendment 6138732286).
No example or template needed a fix (inventory 0). No CLI test pinned
the old warning.
- Two sibling absence pins (`page-header-breadcrumb-retirement.test.ts`,
`master-detail-detail-sort-field-retirement.test.ts`) described this
lint test file as asserting "the advisory warning"; patch round 4
corrected both `EXCLUDED` comments to say it asserts the refusal, an
`error`.
- `validateComponentProps` keeps `surfaceReason:
RUNTIME_NEEDS_FULL_SNAPSHOT` ("reads a stack-wide collection the
per-write snapshot does not carry"). The rule is page-local, so that
reason looks inaccurate, but I did not measure a page write's snapshot.
Whether the gating rule should cross to the runtime door is its own
rollout decision, with a false-refusal budget over stored tenant pages.
Carrier: none.
---
_Generated by [Claude
Code](https://claude.ai/code/session_012uFegdfR6K6uqvLzt7XsdZ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent de24373 commit 32fa194
14 files changed
Lines changed: 376 additions & 110 deletions
File tree
- .changeset
- content/docs
- deployment
- protocol/objectui
- packages
- lint/src
- spec/src
- conversions
- migrations/entries/retired-keys
- ui
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
470 | 470 | | |
471 | 471 | | |
472 | 472 | | |
| 473 | + | |
473 | 474 | | |
474 | 475 | | |
475 | 476 | | |
| |||
484 | 485 | | |
485 | 486 | | |
486 | 487 | | |
487 | | - | |
| 488 | + | |
488 | 489 | | |
489 | 490 | | |
490 | 491 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
739 | 739 | | |
740 | 740 | | |
741 | 741 | | |
742 | | - | |
743 | | - | |
744 | | - | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
745 | 746 | | |
746 | 747 | | |
747 | 748 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1155 | 1155 | | |
1156 | 1156 | | |
1157 | 1157 | | |
1158 | | - | |
1159 | | - | |
1160 | | - | |
1161 | | - | |
1162 | | - | |
1163 | | - | |
1164 | | - | |
1165 | | - | |
1166 | | - | |
1167 | | - | |
1168 | | - | |
1169 | | - | |
1170 | | - | |
1171 | | - | |
1172 | | - | |
1173 | | - | |
1174 | | - | |
1175 | | - | |
| 1158 | + | |
| 1159 | + | |
| 1160 | + | |
| 1161 | + | |
| 1162 | + | |
| 1163 | + | |
| 1164 | + | |
| 1165 | + | |
| 1166 | + | |
1176 | 1167 | | |
1177 | 1168 | | |
1178 | | - | |
| 1169 | + | |
1179 | 1170 | | |
1180 | 1171 | | |
1181 | 1172 | | |
| |||
0 commit comments