Skip to content

fix(service-messaging): close DeliveryPayload.severity to its declared vocabulary - #7382

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7174-severity-union-collapse
Aug 10, 2026
Merged

fix(service-messaging): close DeliveryPayload.severity to its declared vocabulary#7382
os-help merged 1 commit into
mainfrom
claude/issue-7174-severity-union-collapse

Conversation

@os-help

@os-help os-help commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7174

DeliveryPayload.severity was declared 'info' | 'warning' | 'critical' | string.
TypeScript absorbs a literal union member into the wider primitive it is unioned
with, so this was exactly string — the three names read as a closed
vocabulary but enforced nothing. severity: 'urgent' (and severity: '')
type-checked with no error, while three sibling declarations of the same
concept in this package were already closed:

  • messaging-service.ts:64EmitInput.severity: 'info' | 'warning' | 'critical'
  • channel.ts:40Notification['severity']: 'info' | 'warning' | 'critical'
  • inbox-message.object.ts:88 — a Field.select offering exactly those three

Premise re-verification (before implementing)

Confirmed on fresh origin/main: outbox.ts:25 still reads
severity?: 'info' | 'warning' | 'critical' | string;. Probed the index
signature ([k: string]: unknown) does not swallow the named property's
narrower type — a standalone tsc check confirms severity: 'urgent'
against the narrowed interface is rejected (TS2322), so the fix is a
real compiler gate, not a no-op. Premise valid — proceeded.

Same-day churn check (#7086): its PR (#7192, the Zod-side twin —
NotifyConfigSchema.severity) is already merged, closing to the same
info | warning | critical vocabulary. No conflict; vocabularies match.

Sweep for other 'a' | 'b' | string collapses in the services layer
(the card's suggested pass): grepped packages/services/**/*.ts for the
pattern — outbox.ts:25 was the only hit. No further filings needed.

Change

packages/services/service-messaging/src/outbox.ts — drop the trailing
| string, so DeliveryPayload.severity?: 'info' | 'warning' | 'critical'.

No runtime behaviour change. Traced every construction site of a
DeliveryPayload in-package:

  • messaging-service.ts (enqueueDeliveries, emit's inline fan-out path,
    the L2 event write) all write input.severity ?? 'info', where
    input: EmitInput and EmitInput.severity is already the closed union —
    so these sites type-check identically before and after.
  • dispatcher.ts:265 reads p.severity as Notification['severity']) ?? 'info'
    — an explicit cast, unaffected by narrowing the source type it casts from.
  • No downstream package (cli, runtime, plugin-webhooks, dogfood,
    the driver packages) references DeliveryPayload, NotificationDeliveryRecord,
    EnqueueDeliveryInput, or INotificationOutbox by name — the narrowing is
    fully contained to service-messaging. Consumer sweep direction used:
    pnpm --filter '...@objectstack/service-messaging' (downstream prefix).

Reverse verification (predicted before running)

Added outbox-delivery-payload-severity.test.ts — this package's tsconfig
does not exclude *.test.ts, so tsc --noEmit (the typecheck script)
reads it directly; no separate .pin.ts needed (PR #7140/#7206 convention).

Pin Predicted Measured
@ts-expect-error on severity: 'urgent', pre-fix (outbox.ts restored from origin/main via git checkout origin/main -- <path>, never git stash) unused-directive error (TS2578) — the collapsed union accepts 'urgent' error TS2578: Unused '@ts-expect-error' directive. — as predicted
Same pin, post-fix clean tsc --noEmit exits 0 — as predicted

Gates (local, scoped — full farm left to CI)

pnpm --filter '@objectstack/service-messaging^...' build     pass (dependency closure)
pnpm --filter @objectstack/service-messaging typecheck        pass
pnpm --filter @objectstack/service-messaging test              Test Files 17 passed | Tests 201 passed
npx eslint <changed files>                                       clean
node scripts/check-nul-bytes.mjs                                 OK (6741 files scanned)

Changeset

patch on @objectstack/service-messaging, following the #7140 precedent:
a type-side-only tightening with no runtime behaviour change still gets a
changeset because it narrows a publicly exported type — a consumer directly
assigning an out-of-vocabulary literal to DeliveryPayload.severity would
newly fail to compile.


Generated by Claude Code

…d vocabulary (#7174)

`'info' | 'warning' | 'critical' | string` collapses to exactly `string`
in TypeScript's type system — the trailing `| string` absorbs the three
literal members, so the closed vocabulary enforced nothing. Drop it,
aligning DeliveryPayload with its already-closed siblings
(messaging-service.ts EmitInput.severity, channel.ts Notification.severity,
the inbox-message select field).

Adds a compile-time pin (outbox-delivery-payload-severity.test.ts) proving
severity: 'urgent' is now a type error, reverse-verified against the
pre-fix declaration (@ts-expect-error goes unused / TS2578 pre-fix,
clean post-fix). No runtime behaviour change: every real construction
site already writes input.severity ?? 'info' where input.severity is
itself the already-closed EmitInput.severity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fkdTyGmMD5s8ZtEifvuGy
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 10, 2026 8:21am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-messaging.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/webhooks.mdx (via @objectstack/service-messaging)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/service-messaging)
  • content/docs/plugins/packages.mdx (via @objectstack/service-messaging)

1 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/service-messaging)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 10, 2026
@os-help
os-help marked this pull request as ready for review August 10, 2026 08:37
@os-help
os-help added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit f9a5c59 Aug 10, 2026
26 checks passed
@os-help
os-help deleted the claude/issue-7174-severity-union-collapse branch August 10, 2026 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

2 participants