Repository navigation
Commit abd2545
fix(cli): print the deployment-policy unbound-flow class under a dim ℹ glyph (#22409)
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 new
`isDeploymentPolicyClass(reason)` and the class loop in
`printAutomationSummary`. 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.
- ⛔ Not changed: the automation plugin's binding, the scheduled-work
switch, `@objectstack/service-automation`'s own log level, `serve.ts`
and `packages/spec`.
## How the class is told apart: identity, not prose
`isDeploymentPolicyClass` is `reason === SCHEDULED_WORK_DISABLED_REASON`
(`@objectstack/types`). Measured on `origin/main` at `11d119ab1`:
- The engine records `scheduledWorkDisabledReason(policy)` from the
policy reading that refused the bind (`activateFlowTrigger`).
`describeUnboundReason` reads that record back for
`getTriggerBindingAudit`, and `collectAutomationSummary` passes it
through unchanged.
- For every policy the environment resolves, that function returns the
constant byte for byte: `resolveScheduledWorkPolicy` never sets
`hostDisabledReason` (pinned in `env.test.ts`).
- The test file's real-producer leg boots the real
`AutomationServicePlugin` on a `LiteKernel` and reads the summary
through `collectAutomationSummary`. 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.
- The check does not call
`scheduledWorkDisabledReason(resolveScheduledWorkPolicy())`. On that
reading the call returns the same constant by construction, and the
resolver throws on an unrecognised `OS_TENANCY_POSTURE`, which a printer
must not do.
- No structured kind was needed. So neither `serve.ts` (held by PR
#22381) nor the spec contract is touched.
## Pins (`format.boot-warning-classes.test.ts`)
- **Real boot** (eight scheduled flows, switch off): the one schedule
line now starts with ` ℹ 8 flows declare `.
- **Policy class:** prints dim (opening SGR `ESC[2m`, no yellow) under
`ℹ`, with its flows and first sentence. Controls: a missing trigger and
a binding failure keep a yellow (`ESC[33m`) `⚠`.
- **⛔ Identity, not words:** three reasons that only quote the policy
all keep `⚠`. 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.
- **`restatedAbove` is 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.
- The existing first-sentence pin moves from `⚠` to `ℹ`.
## Evidence (head `62c86cba9`, branched from `11d119ab1`)
**Public door.** `objectstack dev --seed-admin --fresh -p 38421` on
`examples/app-showcase`, with the CLI built from this head. The Flows
section, SGR stripped and the shared sentence cut to an ellipsis:
```text
Flows: 30 flow(s) 20 bound to triggers (record_change, schedule, time_relative, api) · 7 draft
ℹ 1 flow declares a 'time_relative' trigger but is NOT bound — disabled by deployment policy — … : showcase_task_due_reminder
ℹ 1 flow declares a 'schedule' trigger but is NOT bound — disabled by deployment policy — … : showcase_scheduled_digest
reasons cut to their first sentence — --log-level debug prints each flow's full reason
Seeds: com.example.showcase 132 rows
ℹ Boot diagnostics — 1 informational (2 more already listed above):
```
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/dist` and the SDUI manifest). They come
from the environment, not from this change.
**Tests and gates**, all at `62c86cba9`:
| Check | Result |
|:---|:---|
| `pnpm --filter @objectstack/cli exec vitest run --project unit
--maxWorkers=2` | 270 files, 3971 tests passed, lock `VERDICT
command-exit 0` |
| `format.boot-warning-classes.test.ts` alone | 21 of 21 passed |
| `pnpm --filter @objectstack/cli typecheck` | exit 0 (`tsc --noEmit`,
then `check:test-typecheck` over `tsconfig.test.json`) |
| `pnpm lint` (full: `eslint . --no-inline-config`) | exit 0, no
findings |
| derived gate families | `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 to `5804f510beed`, equal to
HEAD, with `git diff HEAD` empty.
- **Leg 1**, identity forced to `false`: 3 failed, 18 passed. The
failures are the real-boot `ℹ` pin, the first-sentence pin and the
dim/`ℹ` pin.
- **Leg 2**, identity replaced by `reason.includes('disabled by
deployment policy')`: 1 failed, 20 passed. The failure is the identity
pin.
The test imports `./format.js`, which vitest resolves to
`src/utils/format.ts`, so neither leg depended on a `dist` rebuild.
## Acceptance notes
- **Host-injected policy.** A per-kernel `ScheduledWorkPolicy` that
carries its own `hostDisabledReason` keeps `⚠`. That sentence is the
host's, not the documented default, and the printer cannot know it. For
this banner the case is dormant: `git grep` finds 0 non-test producers
of `hostDisabledReason:` 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 plus `serve.ts`), which this PR
does not add.
- **Contract wording.** `FlowRuntimeState.reason` in 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.
- **Stale prose this change makes inaccurate, left unedited** (outside
the claim's file surface):
- `docs/qa/platform-checklist/areas/platform-core.json`
acceptance[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.
- The still-pending
`.changeset/22073-boot-warning-one-line-per-class.md` quotes the class
with `⚠`. This PR's changeset states the move to `ℹ`, and the two would
ship in the same release.
- **Line order is unchanged** (first-seen). A dim policy line can still
print above a `⚠` class: the one-line-per-class order belongs to the
earlier ruling and was not re-ruled.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01BmsuLyUeuG5CNpZFMH1jzS)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent e75dced commit abd2545
3 files changed
Lines changed: 173 additions & 7 deletions
File tree
- .changeset
- packages/cli/src/utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 116 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
93 | 94 | | |
94 | 95 | | |
95 | 96 | | |
| 97 | + | |
| 98 | + | |
96 | 99 | | |
97 | 100 | | |
98 | 101 | | |
99 | 102 | | |
100 | 103 | | |
101 | 104 | | |
| 105 | + | |
102 | 106 | | |
103 | 107 | | |
104 | 108 | | |
| 109 | + | |
105 | 110 | | |
106 | 111 | | |
| 112 | + | |
107 | 113 | | |
108 | 114 | | |
109 | 115 | | |
| |||
206 | 212 | | |
207 | 213 | | |
208 | 214 | | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
209 | 219 | | |
210 | 220 | | |
211 | 221 | | |
| |||
280 | 290 | | |
281 | 291 | | |
282 | 292 | | |
283 | | - | |
| 293 | + | |
284 | 294 | | |
285 | 295 | | |
286 | 296 | | |
| |||
298 | 308 | | |
299 | 309 | | |
300 | 310 | | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
301 | 416 | | |
302 | 417 | | |
303 | 418 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
14 | 18 | | |
15 | 19 | | |
16 | 20 | | |
| |||
1587 | 1591 | | |
1588 | 1592 | | |
1589 | 1593 | | |
| 1594 | + | |
| 1595 | + | |
| 1596 | + | |
| 1597 | + | |
| 1598 | + | |
| 1599 | + | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
| 1603 | + | |
| 1604 | + | |
| 1605 | + | |
| 1606 | + | |
| 1607 | + | |
| 1608 | + | |
| 1609 | + | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
| 1619 | + | |
| 1620 | + | |
| 1621 | + | |
| 1622 | + | |
| 1623 | + | |
| 1624 | + | |
| 1625 | + | |
1590 | 1626 | | |
1591 | 1627 | | |
1592 | 1628 | | |
| |||
1640 | 1676 | | |
1641 | 1677 | | |
1642 | 1678 | | |
| 1679 | + | |
| 1680 | + | |
| 1681 | + | |
| 1682 | + | |
| 1683 | + | |
1643 | 1684 | | |
1644 | 1685 | | |
1645 | 1686 | | |
1646 | 1687 | | |
1647 | 1688 | | |
1648 | | - | |
1649 | | - | |
1650 | | - | |
1651 | | - | |
1652 | | - | |
1653 | | - | |
| 1689 | + | |
| 1690 | + | |
| 1691 | + | |
| 1692 | + | |
| 1693 | + | |
1654 | 1694 | | |
1655 | 1695 | | |
1656 | 1696 | | |
| |||
0 commit comments