Repository navigation
[objectui#6830 arm A, boolean half] seed the flow-node inspector's boolean control from the declared defaultValue #8451
Description
Filed by the domain:ui PM seat (session_01YBWFb5YgMU5dw8p2VKj16S), splitting the already-ruled direction A of objectui#6830 into its two halves. ⛔ Not claimed.
This half is blocked on nothing. The select half is objectui#8450 and must land first.
The ruling this implements
Triage ruled A — "show, do not write" on 2026-09-04 (objectui#6830, comment 5544689302). ⛔ Not reopened here. objectui#6830's own measurement (PR #8431, landed) removed the only argument against it: defaultValue has a live reader (controllerAdmits resolves an unset showWhen controller through it, so a declared default decides which fields are on screen) and a second writer (json-schema-to-fields converts engine-published JSON-Schema default into it), so retirement was never available and ADR-0049 resolves to enforce by elimination.
Scope — the boolean control only
FlowNodeConfigField's case 'boolean' plus one i18n key pair. That is the whole change.
⛔ Do not touch the select control. "Render the declared default as placeholder text" is impossible until objectui#8450 is fixed, and reaching for a different affordance there instead would be re-deciding a ruled direction.
The precondition is already answered — do not re-litigate it
Triage asked whether the inspector can distinguish an absent key from a stored false before an affordance is designed. Measured in PR #8431: it can — the distinction survives to the component boundary (getFieldValue returns undefined vs false) and is destroyed by value === true at the control. The two currently render byte-identical DOM, asserted by outerHTML equality.
⇒ The implementable shape is: a declared-true gate whose key is absent shows checked, while an explicit stored false shows unchecked — and those two must stop rendering identically.
⚠️ One of the ten declared defaults is WRONG
On installed @objectstack/spec 17.3.0, ApprovalEscalationSchema parses { timeoutHours: 24 } to enabled: true, while the offline table declares 'false'. So today the escalation sub-fields are revealed online and hidden offline for a node omitting enabled — a live divergence, pinned by PR #8431.
⇒ Showing declared defaults will show a wrong one until objectui#6620 lands. Whoever takes this must say what they did about that: shipping it visibly wrong is not acceptable, and silently special-casing it is worse. Options include gating on objectui#6620, or deriving that one row from the spec rather than the table.
The pin
PR #8431's FlowNodeInspector.declaredDefault.test.tsx already pins the current behaviour with 13 cases, and ablation leg B in that PR is exactly this implementation — so this repair cannot land silently: those rows go red the moment it works, and turning them green is the acceptance criterion.
Related
objectui#6830 (the parent, and the ruling) · PR #8431 (the measurement and the pin) · objectui#8450 (the select half's blocker) · objectui#6620 (the wrong declared default)
Dedup
Bounded: this is a split-out of objectui#6830 filed by the PM that reviewed it, so the parent is the only prior art and it is cited. No independent search was run.
Activity
os-justin commentedon Sep 8, 2026
Claim: session
session_01YBWFb5YgMU5dw8p2VKj16S· branchclaude/issue-8451-flownode-boolean-declared-defaultPM dispatch (
domain:uiseat). The assignee and this comment are set by the PM on the dev's behalf — the dev inherits both, posts no second claim, and never writes the assignee field.Decision frame, supplied up front (a standing correction: two dispatches today reached a dev without one):
ApprovalEscalationSchemaparses{ timeoutHours: 24 }toenabled: truewhile the offline table declares'false'). Showing declared defaults therefore shows a wrong one. Say what you did about it — shipping it visibly wrong is not acceptable, and silently special-casing it is worse. Gating on objectui#6620 or deriving that row from the spec are both defensible; picking one and not saying so is not.Generated by Claude Code
os-justin commentedon Sep 8, 2026
os-dev-report
{ "issue": 8451, "status": "done", "branch": "claude/issue-8451-flownode-boolean-declared-default", "pr": "https://github.com/objectstack-ai/objectui/pull/8473", "premise_still_valid": true, "summary": "FlowNodeConfigField's case 'boolean' now seeds from the declared defaultValue: checked = isUnsetFieldValue(value) ? field.defaultValue === 'true' : value === true. isUnsetFieldValue is exported from flow-node-config.ts so the re-show rule, controllerAdmits and the control share ONE definition of 'the author stored nothing' instead of three copies. Nothing is written (pinned: a seeded render patches the draft zero times). PR #8431's pin went 13 cases to 17: its three leg-B rows were rewritten against the repaired behaviour, every select case carried over untouched, and four cases added. Scope held to the boolean control; no i18n key was needed or added, and that omission IS the answer to the wrong declared default.", "affordance_shape": "The distinction is carried by the checkbox state itself. Declared-'true' + absent key renders CHECKED; stored false renders UNCHECKED; the two were outerHTML-identical before and are not now. Absent vs stored-TRUE still render alike, deliberately: both mean true at runtime, so drawing them apart would invent a distinction the metadata does not have. NO caption naming the default ships -- the card budgeted 'one i18n key pair' for one and it was refused on measurement, see wrong_declared_default.", "wrong_declared_default": "Measured: EXACTLY TWO boolean fields in the offline table declare a default -- approval.escalation.notifySubmitter ('true', correct) and approval.escalation.enabled ('false', wrong; spec 17.3.0 applies true). So a caption asserting 'this is the declared default' would have shipped a false statement on HALF the surface it annotates. Neither option the brief offered survived measurement (see contradicts_brief). What I did instead, three parts: (1) shipped the SEED and no words -- the seed renders escalation.enabled byte-identically to before (unchecked), so no new claim about that declaration reaches an author; (2) pinned that absence: 'boolean: the #6620-wrong declaration ships NO worded claim' asserts the gate's label element carries its label and nothing else, so a later caption cannot be added without this reddening; (3) ran objectui#6620's own restart probe -- IT FIRES -- and reported it on that card (comment 5577663478), without touching its labels or fix surface.", "tests": "BASELINE: vitest run FlowNodeInspector.declaredDefault.test.tsx -- 13 passed, exit 0. AFTER IMPL, UNMODIFIED PIN: 3 failed / 10 passed, reproducing PR #8431 ablation leg B EXACTLY by name ('boolean: an unset key draws an UNCHECKED box, though the table declares true' / 'renders identically whether the key is missing or explicitly false' / 'the boolean control exists, is a checkbox, and commits the author edit'). AFTER PIN REWRITE: 17 passed, exit 0. AREA: vitest run packages/app-shell/src/views/metadata-admin/ -- 238 files, 2481 passed + 1 skipped, exit 0. DECLARED NARROWING: the whole-package run (650 test files) was attempted and the container killed it at its foreground ceiling (exit 144); the narrowing is measured, not assumed -- a repo-wide grep for importers of the three changed modules returns 21 files, ALL under views/metadata-admin/; the one hit outside it (providers/ExpressionProvider.tsx:324) is a prose comment naming the path, not an import, and the same grep shape with a lit control (MetadataClient) does return files outside the directory. CI runs the full farm. ABLATION, both legs mutating the READ SITE from a committed tree, both proved on disk by git hash-object vs the HEAD blob PLUS removed-text/injected-text grep counts in both directions (1->0 and 0->1) with the matched line printed, both restored BY STATE (git diff HEAD empty AND hashes equal), trap on EXIT INT TERM with absolute paths. HEAD blob 085103868528ff4bd971a7d6bd00776797163f4b. LEG A (seed removed, 'const checked = value === true') mutated blob d45bb482d38da528ca61a87241fd27fbef7e7af8 -> 5 failed / 12 passed. LEG B (ALWAYS CHECKED, 'const checked = true') mutated blob 2937d11f607172fffcaf7af26e73c91f848f7d02 -> 5 failed / 12 passed. GATES: closure build exit 0; type-check exit 0 (tsc --noEmit AND tsc -p tsconfig.test.json, pin file listed once by --listFiles with a lit and a zero-hit negative control); lint exit 0 / 0 errors, the three changed files errors=0 warnings=0 against a lit control that does report; check:control-bytes, check:vi-mock-specifiers, check:vi-mock-inherit, check:designer-field-key-parity, check:i18n-keys all OK; check-governed-queue-guard --test on all four paths 'NOT GOVERNED'; check-changeset-presence, -no-major, -fixed, -overwrite all green.", "always_checked_would_pass": "NO -- measured, not argued. Ablation leg B replaced the read site with 'const checked = true' and reddened five rows including 'boolean: a stored FALSE beats the declared true and draws an UNCHECKED box' and 'boolean: a field declaring NO default still draws unchecked when unset'. The stored-false row is marked load bearing in the file so it is not later softened into 'differs from absent', which an always-checked control fails only by accident.", "changeset": "@object-ui/app-shell: patch. check-changeset-presence verdict: '3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/8451-flownode-boolean-declared-default.md'. Argued in the file and the PR: no prop, option or metadata key is added and no authored document changes meaning -- one control stops contradicting the runtime for metadata that already parses. minor weighed and not taken (no new capability an author can reach for); major is forbidden here. skip-changeset was NOT used -- the brief is right that it is a phantom label in this repo.", "mcp_calls": "10 -- 2 reads on #8451, 1 on PR #8431, 1 search_issues, 2 reads on #6620, 1 create_pull_request, 1 read-back of PR #8473, 1 comment on #6620, 1 comment here.", "open_questions": [], "out_of_scope_findings": [ "NOT filed as a new issue, deliberately -- it belongs to an existing card and was posted there as objectui#6620 comment 5577663478, the same pattern PR #8431's seat used (comment 5465060700). objectui#6620's restart condition is MET and the card still carries pm:on-hold. Its triage comment 5442325479 sets the criterion 'installed @objectstack/spec parses an escalation block with enabled omitted as enabled === true'; on installed 17.3.0 it does. Lifting the hold is PM triage, not mine.", "Same comment, second finding: objectui#6620's restart PROBE cannot produce that reading as written. It spells the import as require('@objectstack/spec'), where ApprovalEscalationSchema is undefined on 17.3.0, so it throws TypeError instead of printing a boolean -- and it fails toward leaving the card asleep. Working specifier is @objectstack/spec/automation.", "Same comment, third finding: two in-repo comments are now factually stale (flow-node-config.spec-reconciliation.test.ts 'installed spec and table agree on it today'; flow-node-config.inactiveRetained.test.ts 'installed spec is 17.2.0, still .default(false)'), and the second, described as objectui#6620's tripwire, reads only the TABLE and never the spec -- so a spec bump alone can never redden it. Both stayed green through the flip. Not armed from this PR: arming it is objectui#6620's own fix." ], "contradicts_brief": [ "The brief said 'deriving that row from the spec' is defensible. Measured: it is not available to this card. Display-only derivation makes the panel contradict itself (a ticked 'SLA escalation' above four hidden dependants). Feeding both display and showWhen means flipping the table's defaultValue to 'true', which changes VISIBILITY for every approval node omitting the key, reds the pin row 'the offline table hides the same siblings, because it declares the gate false' -- a row leg B did NOT predict, so out of this card's sanctioned blast radius -- and flips objectui#6620's tripwire. That is objectui#6620's fix performed from an unrelated PR.", "The brief framed 'gating on objectui#6620' as the other defensible option. Gating reads as WAIT, and the measurement says there is nothing to wait for: #6620's own restart probe fires on the installed spec today. The honest act is to report that the hold should lift, not to park behind it.", "The issue body specified the change as \"FlowNodeConfigField's case 'boolean' plus one i18n key pair. That is the whole change.\" No i18n key was added. The key pair would have paid for a caption naming the declared default, and that caption is exactly the artefact that would have shipped the #6620-wrong declaration to authors in words -- on half the fields it annotates. The seed alone needs no string.", "PR #8431's report says FlowConfigField.defaultValue has 'exactly one consumer in the repo, isFieldVisible' (also stated in objectui#6620 comment 5465060700). After this PR there are two: the boolean control is the second reader. This is in objectui#6620's favour -- its one-line flip now repairs the rendered toggle as well as the gating, which is the symptom in its own title.", "Operational, not about the ruling: objectui has no scripts/pm/os-verify-lock.sh (only check-half-states.mjs lives there), so the shared-verify-lock discipline in the dispatch contract has no entry point in this repo. Runs here were serialised by hand. Separately, a sibling agent's vitest run in /home/user/objectui-issue-8459 matched a pgrep for my own command shape -- killing by process name would have taken it out." ] }Generated by Claude Code