fix(app-shell): declare the spec's escalation.enabled default in the flow-node inspector - #8615
Merged
Merged
Conversation
…e flow-node inspector `FLOW_NODE_CONFIG`'s approval group declared `defaultValue: 'false'` for `escalation.enabled` while the installed `@objectstack/spec` (17.3.0) defaults the key to `true`. An approval node omitting the key escalates at runtime, but the inspector drew the toggle OFF and hid `timeoutHours` / `action` / `escalateTo` / `notifySubmitter` behind it. `defaultValue` has two read sites, so the flip repairs both: `controllerAdmits` resolves an unset `showWhen` controller through it, and the boolean control seeds its checked state from it. Arms the tripwire that could not have caught this: the reconciliation ledger in `flow-node-config.spec-reconciliation.test.ts` now walks the whole escalation block and derives every expected value from the installed `ApprovalEscalationSchema`, in both directions, with a vacuity guard. The prior tripwire for this key read only the hand-written table, so no spec bump could redden it. Pins that a stored `enabled: false` still hides every sibling — the non-regression axis for a fix that revealed them unconditionally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
This was referenced Sep 8, 2026
os-justin
marked this pull request as ready for review
September 8, 2026 16:40
os-justin
enabled auto-merge
September 8, 2026 16:40
Contributor
✅ 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
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6620
The probe, run rather than read
The card's restart probe cannot produce the reading it asks for, exactly as the dispatch predicted. Both spellings, run on this worktree against installed
@objectstack/spec17.3.0:The restart criterion is met and the divergence is live: the table declared
'false', the runtime appliestrue.What changed
The flip.
FLOW_NODE_CONFIG's approval group declareddefaultValue: 'false'forescalation.enabled.defaultValuehas exactly two consumers — confirmed by reading, as the dispatch asked:controllerAdmitsflow-node-config.tsshowWhencontroller — the gating oftimeoutHours/action/escalateTo/notifySubmitterbooleancontrolFlowNodeConfigField.tsxSo the one-line flip repairs the rendered toggle as well as the gating. (
FlowNodeInspector.tsx'sv.defaultValueis a different property — a flow variable declaration, not aFlowConfigField.)The tripwire, armed. This is the substance of the card.
flow-node-config.inactiveRetained.test.tswas described as this card's tripwire, but its assertion reads only the table and never the spec, so no spec bump could ever redden it. The reconciliation ledger inflow-node-config.spec-reconciliation.test.tsnow covers the whole escalation block instead ofnotifySubmitteralone, in both directions, deriving every expected value bysafeParse-ing a minimal block:⛔ No literal
'true'anywhere in the assertion. A literal restates the exact claim that drifts, and would pass just as happily on the next upstream flip — which is how this one stayed invisible.Pins updated to the new truth, not worked around. Three rows reddened from the flip, all three predicted, all three correct:
flow-node-config.inactiveRetained.test.ts— the absent-controller row flipped totoBeNull(), which is what its own comment instructed the next seat to do. Given a lit control (the same node with a storedenabled: falsestill flagscontroller-off), so the newtoBeNull()is a measurement rather than an absence.FlowNodeInspector.declaredDefault.test.tsx— PR test(app-shell): measure and pin what a declared flow-node defaultValue renders (objectui#6830) #8431's row "the offline table hides the same siblings, because it declares the gate false" now reads "REVEALS". Offline and online agree about this node for the first time.#6620boolean row, which pinned that the seed shipped no worded claim because the declaration was wrong. It now derives its expectation from the installed spec and asserts the rendered toggle carries it.Stale text repaired. The two comments named in the dispatch, plus a third the same grep surfaced:
FlowNodeConfigField.tsx's boolean branch cited "one declares the OPPOSITE of the spec" as a live secondary argument for shipping no caption. The arm-A no-caption ruling stands on its own; the discharged argument is marked as discharged so it is not cited again.Non-regression axis
Derived from a plausible wrong fix, not from the bug's shape: revealing the sub-fields unconditionally satisfies "omitted-key nodes now reveal" completely while destroying the author's ability to switch escalation off. Pinned as a new case naming all four siblings individually — an assertion over one of them passes on a fix that leaks the other three.
Red-leg evidence — every new pin observed failing
Leg A — revert the fix (
'true'back to'false'; on-disk proof: injected 1, removed 0). The derived ledger named the divergence itself:Leg B — the wrong fix (
controllerAdmitsmade to admit unconditionally). The non-regression pin reddened while the bug-shaped rows stayed green, which is what makes the axis discriminating:Both legs restored from
HEADand proven byte-identical (git diff HEADempty; blob hashfaa4b533on disk ==faa4b533at HEAD).Caricature — reported honestly
The mutation run was the table declares every
booleanfield's default as'true'. My new pins stay green under it, and structurally must: post-fix, both boolean fields the offline table declares a default for genuinely are'true', so no spec-derived assertion can tell "declared true because the spec says true" from "declared true because everything is true". Detecting it needs an assertion not derived from the spec — precisely the decorative literal the card rules out.It is caught anyway, by a complementary pre-existing pin — the declaration-surface census in the same file, which pins which fields declare a default rather than their values:
⇒ the two pins are complementary by design: the derived ledger checks values against the spec, the census checks membership. The broader caricature ("declare every field's default
'true'") is caught by my ledger alone, becauseescalation.actionis aselectwhose spec default is'notify'and it sits inside the same block-wide walk.Verification
Run from the repo root, root-relative paths, nothing after a bare
--.vitest run packages/app-shell/vitest run apps/console/vitest run examples/console-starter/pnpm --filter @object-ui/app-shell run type-checktsc --noEmitandtsc -p tsconfig.test.json)eslinton the 5 changed filescheck:control-bytescheck:vi-mock-specifiers,check:esm-specifierscheck-changeset-presence.mjs/check-changeset-no-major.mjsmajorcheck-governed-queue-guard.mjs --testAffected packages read from
turbo ls --affectedagainst the pinned base, not guessed:app-shell,console, and the two example consoles.type-checkreally measured the tests —tsc -p tsconfig.test.json --listFileslists all four edited test files, so this is not the "excluded from the project, reads green" trap.LEG D (harness death), from the JSON reporter, not the text one: 259 suites, 0 failed,
assertionResultscounted 803 ==numTotalTests803, 0 anomalies — noFailed Suites, no zero-test files.Blast radius was measured before touching any pin: across
packages/app-shell/src/views/metadata-admin/the flip alone moved 3 tests out of 2535 — the three named above, and nothing else.Notes for the reviewer
.changeset/6620-escalation-enabled-default.md,patchon@object-ui/app-shell(nevermajor— objectui's major is the cross-repo pin)..changeset/6794-notify-submitter-default.mdstill closes with "installed spec and table agree on it today" aboutescalation.enabled, which this PR makes false. It is an unreleased changeset owned by the release pipeline, so I left it alone rather than edit a file a release run may be consuming; my own changeset states the corrected fact and lands in the same CHANGELOG section.🤖 Generated with Claude Code
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Generated by Claude Code