Repository navigation
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
Conversation
…ingle-brace token, a hole holding logic and a blank sentence at validate, compile and the publish gate The bulk confirmText pass in validateStackExpressions now emits every finding as an error, as the flow text slots do, so `objectstack validate` and `compile` fail and the publish gate answers 422 INVALID_METADATA. The parse is unchanged: the blank-string arm stays, so a stored view keeps loading until its next save. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrzJbTRTRRTVangBSZz6JR
…REAKING minor changeset with its ADR-0087 disposition Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrzJbTRTRRTVangBSZz6JR
… row's prose Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrzJbTRTRRTVangBSZz6JR
📓 Docs Drift CheckThis PR changes 2 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 87ac96bc6050bc8c1e7389b29d0d998c386b0e9c && git checkout 87ac96bc6050bc8c1e7389b29d0d998c386b0e9c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2b7c331537ae54c2060b8a64bd025f9ca6fbcdb9 6a4c06637b95c94871cec630475fc6c7ebaa5473 && git checkout -B drift-repro 2b7c331537ae54c2060b8a64bd025f9ca6fbcdb9 && git merge --no-ff 6a4c06637b95c94871cec630475fc6c7ebaa5473
node scripts/docs-audit/affected-docs.mjs --json 2b7c331537ae54c2060b8a64bd025f9ca6fbcdb9
|
Contract reviewServed-tier: Inputs read at 2026-10-23T14:54Z: card #25205 (body + 3 comments: ① Derived judgments
② Semver level
③ Boundary flagsDev flags (os-dev-report
Reviewer flags:
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 · ACCEPT ·
|
Requested by Tesla · project thread
Fixes #25205
Clause-②: no (narrowing)
Metadata-protocol: narrowed
The refusal half of ruling
6285948976item 1 on #24798, verbatim: "an unbound root and a single-brace token are refused by name atobjectstack validate,compileand the publish gate through the one judge the flow text slots use (automation/flow-text-slot-template.ts)". #24947 (merged as925fd0772c) typed the slot and shipped every finding as a warning. Under the amended 「甲」 (#60156303865025) this narrowing ships now, in 18.x, as its own BREAKING minor, shaped like #25002's page-predicate bounds.What changes
confirmTextpass refuses (packages/lint/src/validate-expressions.ts,bulkConfirmTextIssues). Every finding is nowseverity: 'error', pushed the way the flow text slots push theirs, and theBULK_CONFIRM_TEXT_WARNING_ONLYsuffix 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.objectstack validateandcompilefail on anerrorfromvalidateStackExpressions. The publish gate splitserrorsfromadvisoriesby severity (runtime-gate.ts), so an activevieworobjectwrite, and a draft promotion, now answer422 INVALID_METADATAwith anexpression-invalidissue atbulkActionDefs[N] confirmText. No door code moves.''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 (computeMetadataDiagnosticsinloadMetaFromDb) 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 lint 18.x:page-predicate-over-budgetbecomes an error, refusing a page component props CEL slot over the platform parse bounds (the refusal half of #24953) #25002's precedent avoids ("Stored pages are not migrated and are not refused on read"). With the arm kept, every bulkconfirmTextfinding 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..describe()(it said "as a warning"), the regenerated references (content/docs/references/ui/{bulk-action,component,view}.mdx), theauthoring-rules.tsregistration comment, aruntime-gate.test.tscomment, and the dogfoodexpression-conformance.ledger.tsrow'senforcementtext..changeset/25205-bulk-confirm-text-refusal.md:@objectstack/lintand@objectstack/specminor, both declaration lines, a FROM/TO block, and the ADR-0087 dispositionnot-required (no-migration-prescription), worded like lint 18.x:page-predicate-over-budgetbecomes an error, refusing a page component props CEL slot over the platform parse bounds (the refusal half of #24953) #25002's.Producers, measured first (through the built
@objectstack/lint)925fd0772c: the only non-test bulk defs with aconfirmTextare the 6 inexamples/app-showcase/src/ui/views/project.view.ts, all holeless: 0 findings, against 1 for a planted{{ nope }}control. Noplatform-objects,skills/or other example module declares one.origin/main1e71d5e(read-only):src/sales/views/account.view.tsdeclares the 3 bulk sentences ({{count}}, and{{tier}}on the def whose param istier): 0 findings, against anerrorfor 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 fromwarningtoerror, the "no error at all" case inverts to "no warning at all", and the gate block now assertserrorswith emptyadvisorieson all three view shapes, plus a single-brace and a blank case per shape.packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts, through the realsaveMetaItem: (a) each of the four texts is refused withINVALID_METADATA/ 422 and nothing is stored, and the draft promotion is refused too; (b) the door's finding equalsrunAuthoringRules('build', …)'s onrule,whereandmessage; (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.scripts/ablation-replace.mjs,severity: 'error'back to'warning'in the push helper; blobb86ef1862f5b→7ab1d8f7b6ad;ablation-dist-preflight.mjsfound the marker in 4 built files ofpackages/lint/dist): lintTests 11 failed | 6 passed, doorTests 8 failed | 1 passed(the passing one is control (c)). Restore leg: blob back to HEAD,git diff HEADempty, lint rebuilt, preflight--absentclean, 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:generatedgreen after--fixregenerated the three references.pnpm --filter @objectstack/dogfood test, owed by the(narrowing)arm) ate644a80a: 290 passed / 3 skipped files, 2494 passed / 18 skipped.6a4c0663changes one string in the ledger row (it dropped a tracker id thatcheck:doc-authoringnamed);expression-conformance.test.tsre-ran there: 8/8.dispatch-gates --commandsgives 112 families, the same list at both heads. Results are in the os-dev-report comment.Acceptance notes
confirmTextstrings in translation bundles are still not judged for holes, as before this change: the ruling binds the def's slot. feat(spec,lint): a bulk action's confirmText is a template slot over count and its params, judged at validate, compile and the publish gate #24947's report carries this to spec: type the remaining person-facing text slots astemplateslots —confirmText×3,successMessage/errorMessage×6,emptyText×2 (ADR-0032 Decision 2 and 3; the same mechanism as #24798) #24814.CONTRACT_REVIEW_TIERis the seat's. No governed surface is touched (.claude/**,docs/adr/**,AGENTS.md,skills/**,.github/workflows/**).templateslots —confirmText×3,successMessage/errorMessage×6,emptyText×2 (ADR-0032 Decision 2 and 3; the same mechanism as #24798) #24814, the parallel text-slot sweep, also editsvalidate-expressions.ts; this diff stays inside the bulk block and its two comments.🤖 Generated with Claude Code
https://claude.ai/code/session_01MrzJbTRTRRTVangBSZz6JR