Repository navigation
fix(core,plugin-form,types): a blank gate on the three remaining paths is diagnosed, never a silent true; mirror keys the spec narrowed refuse a blank (objectui#11262) - #11283
Conversation
…s is diagnosed, never a silent true; mirror keys the spec narrowed refuse a blank (objectui#11262) ADR-0137 D4: evaluateCondition's legacy path, evalRowPredicate's bare-blank fallback and sectionFields' attachVisibility now report a blank gate through the [blank] guard objectui#8069 put on the dialect: 'cel' route. No drawn verdict moves. ADR-0137 D1 on objectui's two gate mirrors whose spec key refuses a blank: SelectOptionSchema.visibleWhen and FormFieldSchema.visibleOn carry the triad's refined wire. BaseSchema's objectui-only gates stay diagnosed, not refused. Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec Co-authored-by: Claude <noreply@anthropic.com>
…ui#11262 pin The package's test type-check (tsconfig.test.json) reads mock.calls as any, so the filter callback's parameter needs its own annotation. Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs: card #11262 (body; triage ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #11262
Clause-②: no (narrowing)
ADR-0137 D4 says a blank gate predicate is "diagnosed, never a silent
true". objectui#8069 (PR objectui#11233) diagnosed thedialect: 'cel'route. Three paths still drew a blank gate in silence, and each now reports through that same guard:isBlankPredicateTextdecides, andevalFieldPredicate's[blank]report speaks. No drawn verdict moves. A blank gate is still "no gate" (D3/D4). ADR-0137 D1 now also covers objectui's two gate mirrors that override a spec key the spec narrowed: they refuse a blank at authoring, as the spec does. Direction: triage comment 5918741052. Claim: 5919879003.Narrowing
SelectOptionSchema.visibleWhenandFormFieldSchema.visibleOnparsed'', whitespace, and an envelope whosesourceis blank.@object-ui/types, minor): the same values, at the key, with the spec's own sentence (EVALUATED_EXPRESSION_SOURCE_REQUIRED).@objectstack/spec17.5.0 declares both keys asEvaluatedExpressionInputSchema, which refuses a blank. That is the dataSelectOptionSchema.visibleWhenand the form view field'svisibleWhen/ deprecatedvisibleOn. objectui's mirror overrides each key to keep its own wire, and it was wider than the spec on the same key.ExpressionWireSchema.optionsby reference and the parsed value is the authored one. The runtime verdict of a stored blank gate is also unchanged ("no gate" plus a diagnostic).BaseSchema'svisible/hidden/disabledhave no spec twin, so they stay D4-diagnosed and are not refused. The triage recorded this, and theFieldRulePredicateWireSchemadocblock records it too.What changed
ExpressionEvaluator.evaluateCondition's legacy path (packages/core). The CEL route's blank block moved into one private method,answerBlankGate, and both routes call it. The legacy path used anif (!condition)and a whitespace-onlytrim()to answertruein silence. Now, after the envelope unwrap, a blank text (a bare''or whitespace, or a dialect-less envelope's blanksource) goes to the same method. Every mode still answerstrue,throwOnErrorincluded.onFaultreceives the[blank]reason, and every other caller gets the built-in warning. Both routes share one locator, so''and{ dialect: 'cel', source: '' }land on one dedupe key and print one line. This path goes first becauseSchemaRenderer'svisibleWhen/visible/visibleOn/visibilitylegs pass the raw authored value into it.evalRowPredicate(listConditional.ts). The bare-blank early return is removed. A blank string now takes the route its blank-envelope twin already took (no legacy marker can match blank text). It gets the same verdict, which is the caller's fallback, and the same report: labelled throughwarnEvalErroron thewarnOnErrorroute, and through the canonical helper on the single-eval route. This removes a blank test instead of adding one.sectionFields'attachVisibility(packages/plugin-form). A blank predicate is still dropped, so the field draws with no view-level gate. The drop is now reported, naming the field.isBlankPredicateTextreplaces the two localtrim()tests. The function reports only what it drops (see the premise note below).form.zod.ts.SelectOptionSchema.visibleWhenandFormFieldSchema.visibleOnnow carryFieldRulePredicateWireSchema, the refinement PR objectui#11233 put on the triad. That refinement passes the predicate TEXT tostripImportedDefaults(EvaluatedExpressionInputSchema)from@objectstack/spec/sharedand restates no rule of its own. The const moved aboveSelectOptionSchema, its first reader, to avoid the temporal dead zone. It keeps its name, so thefieldRules.tsdocblock that cites it stays true. The spec import is the same one PR objectui#11233 added, soimported-defaults-8317andterminal-unknown-key-refusal-11073need no new crossing row. Both suites are green.content/docs/guide/metadata-diagnostics.mdnow covers blank gates. The twodeclaredPredicate.tsdocblock passages that quoted the removedif (!trimmed) return true/if (!source.trim())lines are corrected. These are comment-only edits, made because this diff made the quotes false.Premise check (card body read against
origin/mainf61dab169){ dialect: 'cel', source: '' }was already diagnosed. Silent were a bare'', whitespace, and a dialect-less blank envelope.{ source: '' }was already diagnosed. Only the bare-string spelling was silent.attachVisibilitydrops a blank on two chains,meta.visible_on ?? meta.visibleOnand a form-view entry'sfd.visibleWhen ?? fd.visibleOn. It does not drop one on the runtime-field chain (fd.visibleOn). There the field is the output, the blank stays on it, andform.tsxreports it at render (pinned by the existingform.tsxcontrol "a blank view-level visibleOn is a layout GATE: no gate plus a diagnostic").attachVisibilitytherefore reports only when the output does not carry the blank. The new pin records the runtime-field chain as "carried, not reported a second time".FormPagedoes not usesectionFields. It carries the view predicate onto its row and judges it inisFieldVisiblethroughevalFieldPredicate, so a blank view-level predicate was already reported there onorigin/main. Measured: the diagnostic assertion passes on its own (-ton that row only, withFormPageuntouched by this diff). In the full file the line was swallowed, because the earlier stored-blank row put''on the same field (notes) under the same locator ("visibleWhen of field 'notes'"), and the one-time dedupe is module state per file. The restated control moves the view-level blank tostatusand asserts the[blank]line naming it. Its comment, which said the page normalizes throughsectionFields, is corrected.Census before narrowing (the triage's "no silent narrowing" clause)
visibleOn/visibleWhenfound no stored or example fixture carrying a blank value, in either the string form or thesourceform. The matches are test inputs to renderers or to the triad, plus the two control rows below.origin/maincopy ofbase-schema-predicate-envelope-7530.test.tswas run against the narrowedform.zod.ts. Result: 2 failed / 60 passed. The two are exactly the rows that recorded PR objectui#11233's wider reading: "the form GATE legs carry the same object" and "FormFieldSchema.visibleOn (the view-level gate) and an option's visibleWhen still parse a blank". Those two rows are restated below. No stored or first-party fixture turned red, so the narrowing went ahead.packages/types/src/suite is green with the narrowing (see Verification). That includes the parity ledgers,select-option-spec-parity,imported-defaults-8317,terminal-unknown-key-refusal-11073anddetail-view-field-options-10296.Pins
packages/core/src/evaluator/__tests__/blankGateDiagnosed-11262.test.tscovers paths 1 and 2 for'', whitespace and{ source: '' }. Each row checks the verdict and the report. Path 1 also coversonFaultandthrowOnError. Path 2 covers both routes and both fallback directions, plus rowless. There is a one-diagnosis-point row per path: a legacy/bare blank and its CEL/envelope twin print ONE line between them. Controls:undefined/nulland written predicates are silent. Each case gets a fresh module graph.packages/plugin-form/src/sectionFields.blankGate-11262.test.tscovers the four dropping chains × the three spellings (no gate, plus one[blank]line naming the field). It also pins that the runtime-field chain is carried and not reported again. Controls: written predicates are carried silently, and junk is dropped silently.wizardVisibleWhenFault-8069) and page control (FormPage.visibleWhenFault-8069) are restated with the diagnostic added.base-schema-predicate-envelope-7530.test.ts:BaseSchema's three gates still parse a blank.Ablation (each leg run with
ablation-replace.mjsfromobjectstack, atadaa4072b)The mutation was proven on disk each time: anchor count went from 1 to 0 and the blob changed. The restore was proven each time: blob equals HEAD and
git diff HEADis empty. Every mutated file is imported relatively by its pins, so nodist/is on the path. Baseline for all five pin files: 153 passed.truewithoutanswerBlankGatereturn fallbackrestored''/ whitespace rows, both one-diagnosis-point rows, and rowless. The{ source: '' }rows stay green, since they were already diagnosedattachVisibility's report removedSelectOptionSchema.visibleWhenback toExpressionWireSchemaFormFieldSchema.visibleOnback toExpressionWireSchemaThe first path 2 attempt was a null op, and the tool refused it (the replacement contained the anchor). It was re-anchored and re-run, and only the re-run is reported above.
Verification
All heavy runs went through
os-verify-lock.sh(slotissue-11262). Verdicts are read from pass counts and the lock'sVERDICTline. Base isf61dab169.fac88b93eis the implementation commit.adaa4072b(HEAD) adds only a type annotation to the new plugin-form pin, which the plugin-form type-check asked for.adaa4072b:scripts/__tests__/: 177 files passed (2 skipped), 5371 tests passed;@object-ui/plugin-formtype-check EXIT 0;@object-ui/consoletype-check EXIT 0, afterturbo run build --filter='@object-ui/console^...': 34 tasks successful;ExpressionEvaluator.ts, one fewer onsectionFields.ts);check-changeset-presence,check-changeset-no-major,check:control-bytes,check:new-line-citations(0 new),markdown-test-inputs --audit: all EXIT 0.fac88b93e:packages/core/src/plus 46 consumer test files: 240 files, 4712 passed. The consumers are every test outside core that namesevaluateCondition/evalRowPredicate/useCondition/useRowPredicateor authors a blank gate literal, across react, app-shell, components, plugin-grid, plugin-detail, permissions, types, plugin-form and console;packages/plugin-form/src/: 156 files in three chunks (27 / 65 / 64), 1819 passed, 1 skipped;apps/console/src/components/: 36 files, 437 passed;@object-ui/typesand@object-ui/coretype-check EXIT 0. The four packages' type-check programs list every changed test file (checked with--listFiles).packages/types/src/together withpackages/core/src/evaluator/and the sectionFields tests: 317 files, 7956 passed. This ran on the tree before the comment-onlydeclaredPredicate.ts/ docs / changeset edits that went intofac88b93e.plugin-formsingle run hit its 300 s timeout under contention (exit 124, 0 failures before the kill);vitest-invocation-guard(overlapping filters).pnpm test/pnpm lintfarm is CI's.Acceptance notes (observations, not filed)
Same-family blank-gate silencers outside this card's three paths (source reads only, no public-door measurement):
pred === ''beforeevalRowPredicate:useRowPredicate(packages/react),evalRowActionVisibilityin bothplugin-grid'sRowActionMenuandcomponents'data-table, andevalCreatePredicateinapp-shell'sRelatedRecordActionsBridge;partitionRowsByPredicate'spred === ''returns every row in silence, while' 'reachesevalRowPredicatewith fallbackfalseand excludes every row (onorigin/maintoo; diagnosed since this diff). That is a verdict split between two blank spellings, which needs a ruling rather than a mechanical change;expressionfalls throughruleToPredicatesilently;ObjectForm's flat path dropsvisible_on: ''by truthiness.Named in the report for the seat to route to this family.
On
FormPage, a field's object-levelvisibleWhenand its view-levelvisibleWhenshare one locator, so a blank in both slots prints one line.The flat Modal/Drawer builder (
flatFields.ts) does not carry an object field'svisible_onat all. This is a drift observation that was not exercised.Generated by Claude Code