Repository navigation
fix(cli): print the deployment-policy unbound-flow class under a dim ℹ glyph - #22409
Conversation
…info glyph Flows left unbound only because the deployment switched package-authored scheduled work off (the documented default) print as information in the startup banner's Flows section; every other unbound reason keeps its yellow warning. The class is recognised by identity with the engine's recorded sentence (SCHEDULED_WORK_DISABLED_REASON), never by its words. Text, order and the records handed back to Boot diagnostics are unchanged. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BmsuLyUeuG5CNpZFMH1jzS
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 28 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 9ee9fdb3ffa89915d770c3ba495c7720c5b6de8b && git checkout 9ee9fdb3ffa89915d770c3ba495c7720c5b6de8b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 11d119ab1868a658c5f43538f3026a56438b2ed6 62c86cba95aed93c690c7b6490de0e97a9bda344 && git checkout -B drift-repro 11d119ab1868a658c5f43538f3026a56438b2ed6 && git merge --no-ff 62c86cba95aed93c690c7b6490de0e97a9bda344
node scripts/docs-audit/affected-docs.mjs --json 11d119ab1868a658c5f43538f3026a56438b2ed6 |
Fixes #22255
Clause-②: no
What changes
The startup banner's
Flows:section prints one line per unbound-flow class (trigger type, reason), and every class printed as a yellow⚠. A class whose reason is the deployment's scheduled-work sentence (flows left unbound only because package-authored scheduled work is off, the documented default) now prints dim, underℹ. It keeps its count, trigger type, first sentence and flow list. Every other reason keeps its yellow⚠: a missing trigger, a binding failure, anything else. The unknown-target-object and shadowed-flow lines are untouched.This applies the routing of record (triage
6057431712), which executes the maintainer's direction on #22160, quoted verbatim: 「预期中的降级记 info」.packages/cli/src/utils/format.ts: a newisDeploymentPolicyClass(reason)and the class loop inprintAutomationSummary. Only the glyph and the color depend on the class. The line's text, the class order and the records handed back to Boot diagnostics (restatedAbove) are unchanged.@objectstack/service-automation's own log level,serve.tsandpackages/spec.How the class is told apart: identity, not prose
isDeploymentPolicyClassisreason === SCHEDULED_WORK_DISABLED_REASON(@objectstack/types). Measured onorigin/mainat11d119ab1:scheduledWorkDisabledReason(policy)from the policy reading that refused the bind (activateFlowTrigger).describeUnboundReasonreads that record back forgetTriggerBindingAudit, andcollectAutomationSummarypasses it through unchanged.resolveScheduledWorkPolicynever setshostDisabledReason(pinned inenv.test.ts).AutomationServicePluginon aLiteKerneland reads the summary throughcollectAutomationSummary. Its schedule line is now asserted to start withℹ, so the identity is pinned against what the producer actually records. If the producer is reworded, the line goes back to⚠, which is the loud direction.scheduledWorkDisabledReason(resolveScheduledWorkPolicy()). On that reading the call returns the same constant by construction, and the resolver throws on an unrecognisedOS_TENANCY_POSTURE, which a printer must not do.serve.ts(held by PR feat(core,cli,verify): bootStack composes what serve composes — item 1 stage 2 of #22301 (HELD at stop conditions) #22381) nor the spec contract is touched.Pins (
format.boot-warning-classes.test.ts)ℹ 8 flows declare.ESC[2m, no yellow) underℹ, with its flows and first sentence. Controls: a missing trigger and a binding failure keep a yellow (ESC[33m)⚠.⚠. They are a binding failure carrying the policy's first sentence, the whole sentence inside a longer record (the schedule trigger's own refusal shape), and the first sentence alone.restatedAboveis the same under both glyphs: a policy flow, a missing-trigger flow and a binding-failure flow, each with its producer audit record. Every flow is named once, and no Boot diagnostics block prints.⚠toℹ.Evidence (head
62c86cba9, branched from11d119ab1)Public door.
objectstack dev --seed-admin --fresh -p 38421onexamples/app-showcase, with the CLI built from this head. The Flows section, SGR stripped and the shared sentence cut to an ellipsis:Both class lines open with the dim SGR (
ESC[2m) in the raw capture, and Boot diagnostics still withholds the two restated records. That boot printed two other⚠lines, both about this worktree having no console build (packages/console/distand the SDUI manifest). They come from the environment, not from this change.Tests and gates, all at
62c86cba9:pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2VERDICT command-exit 0format.boot-warning-classes.test.tsalonepnpm --filter @objectstack/cli typechecktsc --noEmit, thencheck:test-typecheckovertsconfig.test.json)pnpm lint(full:eslint . --no-inline-config)dispatch-gates --ran: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero, every exit code recorded)The integration layer is declared to CI: the diff reaches no integration file and no spawn entry.
Ablation. Run on the committed fix with
scripts/ablation-replace.mjs. In each leg the anchor went from 1 hit to 0, and the restore brought the blob back to5804f510beed, equal to HEAD, withgit diff HEADempty.false: 3 failed, 18 passed. The failures are the real-bootℹpin, the first-sentence pin and the dim/ℹpin.reason.includes('disabled by deployment policy'): 1 failed, 20 passed. The failure is the identity pin.The test imports
./format.js, which vitest resolves tosrc/utils/format.ts, so neither leg depended on adistrebuild.Acceptance notes
ScheduledWorkPolicythat carries its ownhostDisabledReasonkeeps⚠. That sentence is the host's, not the documented default, and the printer cannot know it. For this banner the case is dormant:git grepfinds 0 non-test producers ofhostDisabledReason:in the tree, against a positive control of 4 hits in test files. Telling that class apart would need a structured kind on the audit row (the spec contract plusserve.ts), which this PR does not add.FlowRuntimeState.reasonin the spec contract says consumers render the reason and do not parse it. An equality check against the exported producer constant is not parsing, and no prose match was added.docs/qa/platform-checklist/areas/platform-core.jsonacceptance[2].verify describes the policy lines as⚠ … disabled by deployment policy. Its clause (fail on the misauthored⚠classes) still holds, and is now easier to apply..changeset/22073-boot-warning-one-line-per-class.mdquotes the class with⚠. This PR's changeset states the move toℹ, and the two would ship in the same release.⚠class: the one-line-per-class order belongs to the earlier ruling and was not re-ruled.Generated by Claude Code