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
Require a guaranteed warn channel on any sink type that declares an optional error (#9754) (#10555)
* feat(devx): a sink declaring an optional `error` must declare a guaranteed `warn` (#9754)
An optional `error` with no declared alternative is a contract that permits
silence. #9657 and #9748 repaired the call-site spellings; this is the type
contract behind them.
- `SweepLogger` (plugin-email) and `ProjectionLogger` (plugin-security) now
declare `warn` non-optionally, so `{ info }` is no longer a legal sink and
the durability reports cannot evaporate against one. `error` stays optional
— hosts do inject reduced sinks (#9754 option C, falsified).
- New producer-side checker `scripts/check-optional-error-sink-contract.mjs`
(`pnpm check:optional-error-sink`): structural population, both narrowings
reported as positive counts on every run, 13-case `--self-test`, and a
shrink-only ledger holding the 15 sinks in `packages/**` that still permit
silence. Not wired into CI — #9754 forbids more merge-blocking.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
* test(plugin-security): drop the phantom compile-time pin its tsconfig cannot evaluate
`pnpm check:type-check-coverage` caught it in this PR's own diff: plugin-security's
tsconfig excludes `**/*.test.ts` (the package carries a TEST_DEBT ledger entry),
so a `@ts-expect-error` in that file evaluates NEVER — a pin that reads like proof
and holds nothing (AGENTS.md → "Build & Test").
The runtime half stays: a `{ info }` sink is shown hearing NOTHING — not the
failure, not the count, not the reassuring "reconciled" line. The compile-time
half lives in plugin-email's `outbox-sweep.test.ts`, whose package does compile
its tests (observed red as `error TS2578: Unused '@ts-expect-error' directive`
when the contract is reverted), and the type contract of both sinks is held by
`pnpm check:optional-error-sink`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
* docs(plugins): unglue the two sink doc comments (`rejected). *`)
A heredoc concatenation joined the option-C paragraph and the call-site
backstop note onto one line in both interfaces. Comment text only; no code,
type or test changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
---------
Co-authored-by: Claude <noreply@anthropic.com>
`SweepLogger` and `ProjectionLogger` now declare `warn` as a REQUIRED channel, so a sink handed to the boot outbox sweep or to permission-set reconciliation can no longer be one that prints nothing (#9754)
7
+
8
+
Both interfaces declared every member optional — `info?`, `warn?`, `error?` — which made `{ info }` a legal sink. Against such a sink both durability reports evaporated: each reaches for `error`, finds none, falls back to `warn`, and finds none of that either. For the sweep that is mail the platform accepted and never delivered, summarised to nobody; for reconciliation it is a permission set that will not survive a re-provision, with the `info` "reconciled" line skipped as well, so the sink heard neither the failure nor the reassurance.
9
+
10
+
#9657 and #9748 repaired the call-site spellings. This is the other half, and the half that cannot regress: an optional `error` with no guaranteed alternative is a contract that permits silence, so an author reading the interface can write a report that never prints and be right about the type. Requiring `warn` makes that unrepresentable at the point of authoring rather than catchable one gate-run later.
11
+
12
+
`error` deliberately stays optional on both types — hosts do inject reduced sinks, and requiring `error` would foreclose the `{ warn }`-only host the drivers were written for.
13
+
14
+
If you pass a logger of your own and it declares no `warn`, add one; the kernel `Logger`, `ctx.logger` and `console` all satisfy the tightened shape unchanged. Consumers reach these types through `@objectstack/plugin-security`'s exported `ProjectionDeps`; `SweepLogger` is internal to `@objectstack/plugin-email`.
15
+
16
+
The rule now has a checker of its own: `pnpm check:optional-error-sink` scans every sink type in `packages/**`, reports the population as a census on every run, and carries a shrink-only ledger of the 15 sinks that still permit silence.
0 commit comments