Repository navigation
Commit ecb4e69
feat(lint)!: refuse a bulk action's confirmText hole over an unbound root, a single-brace token, a hole holding logic and a blank sentence at validate, compile and the publish gate (#25451)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6Jao2bTBgLstWH97J6JDCaAAFh)_
Fixes #25205
Clause-②: no (narrowing)
Metadata-protocol: narrowed
The refusal half of ruling `6285948976` item 1 on #24798, verbatim: "an
unbound root and a single-brace token are refused by name at
`objectstack validate`, `compile` and the publish gate through the one
judge the flow text slots use
(`automation/flow-text-slot-template.ts`)". #24947 (merged as
`925fd0772c`) typed the slot and shipped every finding as a warning.
Under the amended 「甲」 (#6015 `6303865025`) this narrowing ships now, in
18.x, as its own BREAKING minor, shaped like #25002's page-predicate
bounds.
## What changes
- **The bulk `confirmText` pass refuses**
(`packages/lint/src/validate-expressions.ts`, `bulkConfirmTextIssues`).
Every finding is now `severity: 'error'`, pushed the way the flow text
slots push theirs, and the `BULK_CONFIRM_TEXT_WARNING_ONLY` suffix is
gone. The judge, the walk and the sentences are unchanged. The four
refused texts are a hole over a root the def does not bind, a
single-brace `{x}` token, a hole holding logic or an unknown formatter,
and a blank sentence.
- **How it reaches each door.** `objectstack validate` and `compile`
fail on an `error` from `validateStackExpressions`. The publish gate
splits `errors` from `advisories` by severity (`runtime-gate.ts`), so an
active `view` or `object` write, and a draft promotion, now answer `422
INVALID_METADATA` with an `expression-invalid` issue at
`bulkActionDefs[N] confirmText`. No door code moves.
- **The blank `''` arm is kept**
(`packages/spec/src/ui/bulk-action.zod.ts`, comment and describe only).
The doors refuse a blank sentence in this same change, as they refuse
the other three. The parse is what the boot load
(`computeMetadataDiagnostics` in `loadMetaFromDb`) and the read path
judge a stored view with. Dropping the arm would make a stored blank
sentence an invalid row on load, a narrowing wider than the three doors
the ruling names, and one #25002's precedent avoids ("Stored pages are
not migrated and are not refused on read"). With the arm kept, every
bulk `confirmText` finding is one kind of verdict: a door refusal over
text that parses. A stored view keeps loading until its next save, and
that save is judged. The new door test (d) pins this for a blank and for
an unbound hole.
- **Prose made false by the flip, corrected:** the slot's `.describe()`
(it said "as a warning"), the regenerated references
(`content/docs/references/ui/{bulk-action,component,view}.mdx`), the
`authoring-rules.ts` registration comment, a `runtime-gate.test.ts`
comment, and the dogfood `expression-conformance.ledger.ts` row's
`enforcement` text.
- **Changeset** `.changeset/25205-bulk-confirm-text-refusal.md`:
`@objectstack/lint` and `@objectstack/spec` `minor`, both declaration
lines, a FROM/TO block, and the ADR-0087 disposition `not-required
(no-migration-prescription)`, worded like #25002's.
## Producers, measured first (through the built `@objectstack/lint`)
- Shipped corpus at `925fd0772c`: the only non-test bulk defs with a
`confirmText` are the 6 in
`examples/app-showcase/src/ui/views/project.view.ts`, all holeless: 0
findings, against 1 for a planted `{{ nope }}` control. No
`platform-objects`, `skills/` or other example module declares one.
- HotCRM `origin/main` `1e71d5e` (read-only):
`src/sales/views/account.view.ts` declares the 3 bulk sentences
(`{{count}}`, and `{{tier}}` on the def whose param is `tier`): 0
findings, against an `error` for a planted `{count}` control. The other
9 of the card's 12 are their es-ES / ja-JP / zh-CN translations in
locale bundles, which this pass does not read; they use the same two
roots. No producer is refused.
## Tests (head `6a4c0663`)
- `validate-expressions.bulk-confirm-text.test.ts`: the pins flip from
`warning` to `error`, the "no error at all" case inverts to "no warning
at all", and the gate block now asserts `errors` with empty `advisories`
on all three view shapes, plus a single-brace and a blank case per
shape.
- New door block in
`packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts`,
through the real `saveMetaItem`: (a) each of the four texts is refused
with `INVALID_METADATA` / 422 and nothing is stored, and the draft
promotion is refused too; (b) the door's finding equals
`runAuthoringRules('build', …)`'s on `rule`, `where` and `message`; (c)
control: the HotCRM sentence saves; (d) a stored view with a blank or an
unbound sentence loads (`invalid: 0`) and its next save is refused, the
row untouched.
- **Ablation** (`scripts/ablation-replace.mjs`, `severity: 'error'` back
to `'warning'` in the push helper; blob `b86ef1862f5b` → `7ab1d8f7b6ad`;
`ablation-dist-preflight.mjs` found the marker in 4 built files of
`packages/lint/dist`): lint `Tests 11 failed | 6 passed`, door `Tests 8
failed | 1 passed` (the passing one is control (c)). Restore leg: blob
back to HEAD, `git diff HEAD` empty, lint rebuilt, preflight `--absent`
clean, door 9/9 green. A first attempt's preflight spelled the dist
marker with single quotes and read ABSENT although the build carried it;
that run is not counted, and the run above is the re-take with the
emitted spelling.
- `pnpm --filter @objectstack/lint test`: 156/156 files, 7196 tests;
typecheck exit 0.
- `pnpm --filter @objectstack/metadata-protocol test`: 261 passed / 3
skipped files, 21456 passed / 19 skipped; typecheck exit 0.
- `pnpm --filter @objectstack/spec test`: 712/712 files, 21657 passed, 1
todo; typecheck exit 0. `check:generated` green after `--fix`
regenerated the three references.
- Full dogfood (`pnpm --filter @objectstack/dogfood test`, owed by the
`(narrowing)` arm) at `e644a80a`: 290 passed / 3 skipped files, 2494
passed / 18 skipped. `6a4c0663` changes one string in the ledger row (it
dropped a tracker id that `check:doc-authoring` named);
`expression-conformance.test.ts` re-ran there: 8/8.
- Derived gates: `dispatch-gates --commands` gives 112 families, the
same list at both heads. Results are in the os-dev-report comment.
## Acceptance notes
- Translated bulk `confirmText` strings in translation bundles are still
not judged for holes, as before this change: the ruling binds the def's
slot. #24947's report carries this to #24814.
- Contract review at `CONTRACT_REVIEW_TIER` is the seat's. No governed
surface is touched (`.claude/**`, `docs/adr/**`, `AGENTS.md`,
`skills/**`, `.github/workflows/**`).
- #24814, the parallel text-slot sweep, also edits
`validate-expressions.ts`; this diff stays inside the bulk block and its
two comments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01MrzJbTRTRRTVangBSZz6JR
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0b8dbea commit ecb4e69
11 files changed
Lines changed: 242 additions & 66 deletions
File tree
- .changeset
- content/docs/references/ui
- packages
- lint/src
- metadata-protocol/src
- qa/dogfood/test
- 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 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | | - | |
| 51 | + | |
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
928 | 928 | | |
929 | 929 | | |
930 | 930 | | |
931 | | - | |
| 931 | + | |
932 | 932 | | |
933 | 933 | | |
934 | 934 | | |
| |||
0 commit comments