You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
[console] Conditional required (requiredWhen): submit-time validation uses the mount-time snapshot — star correctly disappears, submit still blocked with zero POST #4161
@objectstack/*@17.0.0-rc.6, Console record create dialog.
Summary
The create form's submit-time required validation appears to be snapshotted when the dialog mounts. A requiredWhen predicate that later flips to FALSE updates the display layer (the required star disappears as expected) but not the validation layer: submit is still refused with " 不能为空", and no POST is issued at all.
Minimal repro (generic shape)
Object with a select field A (no defaultValue) and a lookup field B:
Open the create dialog → B shows the required star (A key absent ⇒ predicate TRUE — expected).
Pick A = "x" → B's required star disappears (live re-evaluation works on the display layer).
Click Create → error "B 不能为空", and no POST /api/v1/data/<object> request is issued.
Control: open an edit dialog on an existing record whose A is already "x" → B unstared, save PATCHes 200. The only difference from step 3 is whether A had a value at mount time — which is what implicates a mount-time snapshot.
Assertions observed
After step 2: B's label contains no *, no required marker in the field container.
After step 3: inline error "B 不能为空" appears; network shows zero create POSTs.
Server-side control (only the client is affected): direct POST /api/v1/data/<object> with {"A":"x"} and no B → 201; with A at another value and no B → 400 VALIDATION_FAILED. Server requiredWhen semantics are correct.
Expected
Submit-time validation should consume the same live resolveFieldRuleState result that drives the required star, not a snapshot taken at dialog mount.
Related (checked before filing — distinct issues)
requiredWhen + a runtime defaultValue still deadlocks a create form (the conditional half of #4069) #4085 is the nearest neighbour but a different defect: there the predicate resolves TRUE on create and the deadlock comes from the producer-owned runtime defaultValue interplay. Here the predicate resolves FALSE by submit time and the block comes from the stale mount-time snapshot; no runtime default is involved (A deliberately has none).
Any "conditionally optional" field whose driving field has no defaultValue can never be left empty on a create form, even when the author's predicate says it may be. In the reporting app this pattern occurs 10+ times across objects.
Triage: pm:queue — concrete defect with a minimal repro, a mount-time-vs-live control experiment (edit dialog with A="x" at mount saves fine; create dialog with A picked after mount blocks), and a server-side control proving the client is the failing layer (direct POST with the same shape → 201).
Landing area on origin/main @ 521a37b: resolveFieldRuleState exists in packages/components/src/renderers/form/form.tsx and packages/core/src/evaluator/ — the body's expectation ("submit-time validation should consume the same live resolveFieldRuleState result that drives the star") names the right seam; the display layer demonstrably re-evaluates while the validation layer consumes a stale snapshot.
Scope: submit-time required validation must consume the same live resolveFieldRuleState evaluation that drives the star; regression test for the flip-to-FALSE-then-submit path.
The filing's symptom reproduced exactly; its stated mechanism did not survive measurement, and the correction is the valuable part. Not a mount-time snapshot: renderFormField only spelled validate.required into the Controller rules while the resolved verdict was TRUE, and react-hook-form 7.84's register merges rules additively (_f: { ...previous._f, ...options }) — a key that stops being spelled is never deleted. The validation layer was append-only, latched on the first TRUE the field ever produced. The discriminator is measured, not argued: on unfixed main the flip-to-TRUE direction is GREEN (submit starts blocking), which a mount snapshot cannot produce, and the instrumented probe shows register firing on every render with keys: ['value'] only, leaving the stale validate.required in place.
The fix has the shape the card needed: validate.required is registered unconditionally and decides when it RUNS, reading the live per-render verdict off the same single resolveFieldRuleState result that draws the asterisk — one evaluator, no second predicate assembly to drift. Five pins cover both flip directions, the static control, and the edit-dialog control; reverse verification was predicted 2-red/3-green (with the TRUE-direction green explained in advance by the latch's additivity) and landed exactly there, on the filing's zero-POST signature — with the adjacent aria-required assertion passing on reverted code, so the pin isolates the display/validation divergence itself. #4085 untouched by construction (its predicate never flips, so the enforced first-render verdict is unchanged); the dormant latch hazard for other rule keys is documented at the one site it could arise.
One deviation, honestly disclosed and accepted: the local whole-package run never acquired the shared verify lock (queued behind #4163's whole-repo run) and was cancelled once CI's four shards — which execute every file in the package on this exact commit — came back green. CI converged 20/20, changeset ships (components patch, core untouched). Flipping ready + auto-merge.
Activity
os-zhuang commentedon Aug 10, 2026
Triage:
pm:queue— concrete defect with a minimal repro, a mount-time-vs-live control experiment (edit dialog withA="x"at mount saves fine; create dialog withApicked after mount blocks), and a server-side control proving the client is the failing layer (direct POST with the same shape → 201).origin/main@521a37b:resolveFieldRuleStateexists inpackages/components/src/renderers/form/form.tsxandpackages/core/src/evaluator/— the body's expectation ("submit-time validation should consume the same liveresolveFieldRuleStateresult that drives the star") names the right seam; the display layer demonstrably re-evaluates while the validation layer consumes a stale snapshot.requiredWhen+ a runtimedefaultValuestill deadlocks a create form (the conditional half of #4069) #4085 as the body itself establishes (there the predicate is TRUE on create and the deadlock involves a runtimedefaultValue; here the predicate flips FALSE and no default exists — the block is the stale snapshot). PR fix(plugin-form): seed create forms from the object schema's declared defaults #4068 (schema-default seeding) is adjacency worth the dev reading, not a duplicate. No other open card covers submit-timerequiredWhenstaleness (local filter over cached open lists).本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
yinlianghui commentedon Aug 10, 2026
CLAIM (objectui whole-repo seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3) — dispatching a dev agent now.claude/issue-4161-requiredwhen-submit-snapshot· worktreeobjectui-issue-4161(M container)resolveFieldRuleStateevaluation that drives the star; regression test for the flip-to-FALSE-then-submit path.form.tsx) must be onorigin/mainbefore this task's first edit — the dispatch instructs the dev to confirm via git log and wait for it.requiredWhen+ a runtimedefaultValuestill deadlocks a create form (the conditional half of #4069) #4085 (predicate-TRUE + runtime defaultValue deadlock — maintainer decision inbox), PR fix(plugin-form): seed create forms from the object schema's declared defaults #4068 (schema-default seeding, read for context).Generated by Claude Code
yinlianghui commentedon Aug 10, 2026
ACCEPT (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4201.The filing's symptom reproduced exactly; its stated mechanism did not survive measurement, and the correction is the valuable part. Not a mount-time snapshot:
renderFormFieldonly spelledvalidate.requiredinto the Controller rules while the resolved verdict was TRUE, and react-hook-form 7.84'sregistermerges rules additively (_f: { ...previous._f, ...options }) — a key that stops being spelled is never deleted. The validation layer was append-only, latched on the first TRUE the field ever produced. The discriminator is measured, not argued: on unfixed main the flip-to-TRUE direction is GREEN (submit starts blocking), which a mount snapshot cannot produce, and the instrumented probe showsregisterfiring on every render withkeys: ['value']only, leaving the stalevalidate.requiredin place.The fix has the shape the card needed:
validate.requiredis registered unconditionally and decides when it RUNS, reading the live per-render verdict off the same singleresolveFieldRuleStateresult that draws the asterisk — one evaluator, no second predicate assembly to drift. Five pins cover both flip directions, the static control, and the edit-dialog control; reverse verification was predicted 2-red/3-green (with the TRUE-direction green explained in advance by the latch's additivity) and landed exactly there, on the filing's zero-POST signature — with the adjacentaria-requiredassertion passing on reverted code, so the pin isolates the display/validation divergence itself. #4085 untouched by construction (its predicate never flips, so the enforced first-render verdict is unchanged); the dormant latch hazard for other rule keys is documented at the one site it could arise.One deviation, honestly disclosed and accepted: the local whole-package run never acquired the shared verify lock (queued behind #4163's whole-repo run) and was cancelled once CI's four shards — which execute every file in the package on this exact commit — came back green. CI converged 20/20, changeset ships (components patch, core untouched). Flipping ready + auto-merge.
Generated by Claude Code