docs(service-messaging): mark the sys_notification_subscription expansion not-yet-wired and align principal with the resolver - #10027
Conversation
…t-yet-wired (#9807) The object header described a live `audience: 'subscribers'` expansion that does not exist: `AudienceSpec` has no such member, `EmitInput.audience` is required, and no `RecipientResolver` branch expands a topic's subscriptions. Mark it NOT WIRED so the Setup grid is not read as a live routing control, and align the `principal` description with the forms the resolver really accepts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PnJHU45vPJj5UQrxe946Bx
`check:i18n` verdict on the annotation commit: `services/service-messaging DRIFTED (1)`. The default (en) bundle is a copy of the source, rewritten from it on every run (#8543), so the `principal` description edit has to land here too. Generator output — `node scripts/check-i18n-bundles.mjs --write`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PnJHU45vPJj5UQrxe946Bx
The `principal` description is a shipped user-visible string (it reaches dist/index.js and the en translation bundle as Setup help text), so this is a real patch changeset rather than a `skip-changeset` comment-only diff. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PnJHU45vPJj5UQrxe946Bx
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅ What this run could not see
Coarse fallback — 4 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 b4220897dfbe34aed74a8579d85aaaffdcb02f67 && git checkout b4220897dfbe34aed74a8579d85aaaffdcb02f67
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f2920e1fea0be91d86700705bc89728ebd27042f a0954815f88096faf5b3182cb69d57b100f3fbb2 && git checkout -B drift-repro f2920e1fea0be91d86700705bc89728ebd27042f && git merge --no-ff a0954815f88096faf5b3182cb69d57b100f3fbb2
node scripts/docs-audit/affected-docs.mjs --json f2920e1fea0be91d86700705bc89728ebd27042f |
…scription-expansion-not-wired
Fixes #9807
Implements the maintainer ruling recorded in comment 5339441800 — Option B, adopted verbatim. Option A (build the Layer-3
'subscribers'expansion) is not implemented here and nothing in this diff moves toward it.What changed
audience: 'subscribers'causes the resolver to expand a topic's subscriptions is replaced with a marker saying the expansion is NOT WIRED in this repo, naming the three facts a reader can re-derive it from, and stating that delivery today comes from the explicitaudiencepassed toemit(). The genuinely-true content (what a subscription is; the contrast withsys_notification_preference) is kept.principalfield description — widened from four forms to the sixRecipientResolver.resolveOne()really accepts for a string spec.entranslation bundle — regenerated (see Declared surface widening below).patch, notskip-changeset(see Changeset decision).Plus a merge of today's
main(see CI investigation). The three content commits are unchanged since first review.Premise re-verification (measured, not recalled)
notification-subscription.object.ts:9-13, wrapping across five lines.git grep "'subscribers'" -- '*.ts'→ exactly one hit repo-wide, line 11 of that same header. Counter-checked against a term known present:audiencehits 6 files in the package, so the search is live rather than silently matching nothing.AudienceSpec(messaging-service.ts:50-52) isstring | { ownerOf: { object, id } }— no'subscribers'member;EmitInput.audienceis required.grep sys_notification_subscriptionacross the repo: outsideservice-messaging/srconly ADR/design prose, a driver test, changelogs,platform-object-names.ts, and a comment inobject.zod.ts. Inside the package: the declaration, its own tests, generated translations, and the Setup nav entry. No runtime read.The
principalforms were verified by readingrecipient-resolver.ts:129-146directly rather than trusting the card: prefix branchesuser:/role:/team:/owner_of:<object>:<id>, thenlooksLikeEmail()→resolveEmail()(matched againstsys_user, kept verbatim when no user matches), then bare user id as the fallback for anything unmatched.Confidence gap — what I measured and what I could not
objectstack— zero runtime consumers (above).objectui— measured, confirmed zero.git grepforsys_notification_subscription,notification_subscription,notificationSubscription,notification-subscriptionall return nothing. Counter-checked live:sys_inbox_messagereturns 20+ files including the real inbox path, and readinguseHomeInbox.ts,sharedUserFeeds.tsandInboxPopover.tsxshows the inbox UI reads exactlysys_inbox_message,sys_notification_receiptandsys_activity. So the inbox renders the materialized delivery, never subscription rows.cloud—Declared narrowing: the wording is deliberately scoped — "NOT WIRED in this repo", "nothing here reads these rows at runtime" — so it stays true even if
cloudturns out to read them.Declared surface widening — ONE file beyond the dispatched surface
The dispatched surface was
notification-subscription.object.tsalone.packages/services/service-messaging/src/translations/en.objects.generated.tswas added becausecheck:i18nfailed on the annotation commit:The
enbundle is a copy of the source, rewritten on every run (#8543); the field description reaches it as theprincipal.helpleaf. The file is generator output —node scripts/check-i18n-bundles.mjs --write, one line changed, no hand edits — same gate family, so it adds no verification surface.The patch round added no second widening. The
Test Coreinvestigation below produced no code change: no test was edited, skipped, quarantined or loosened, and the only new commit is the merge ofmain.Changeset decision — on evidence
Not a pure comment diff, so
skip-changesetwould be wrong. Measured after building the package:grep -rl "owner_of:object:id" packages/services/service-messaging/dist/→dist/index.js,dist/index.cjs(+ both.maps). The description is a runtime string in the shipped artifact.entranslation bundle, i.e. what an admin reads in the Setup "Notification Subscriptions" grid.So: a real
patchchangeset for@objectstack/service-messaging.CI investigation — the red
Test Core (2/3)on980dec44eThat shard reported
Failed: @objectstack/example-showcase#test. Investigated as ours. It did not reproduce, and no defect attributable to this diff was found.CI captured no assertion to act on. I pulled the complete failing job log (5083 lines) and searched it:
example-showcaseappears exactly twice, both in the error summary. NoTest Filesline, noTestsline, noFAIL, noAssertionError, and none of the showcase's test names appear anywhere. The task that failed produced zero output, while other packages on the same shard streamed theirs into the same log.turbo.jsonsets no output suppression ontest.The guard that was supposed to disambiguate could not.
ci.ymlstates that a red suite plus a green completeness check means real test failures; the run printedcheck-test-completeness: OK (11 package(s), 3617 test(s) declared and all 3617 accounted for). Butcheck-test-completeness.mjsregexes summary lines present in the log, so a package that printed nothing contributes no row and is invisible to it. Its green never covered the showcase.What I measured instead:
980dec44e, dependency closure builtTest Files 21 passed (21)/Tests 337 passed (337)mainTest Files 21 passed (21)/Tests 337 passed (337)examples/app-showcase@objectstack/service-messaging; reproducedturbo ls --affected+partition-test-shards.mjslocallya0954815fTest Coreshards green, aggregateTest Coregreenservice-messaging). A green shard 2 alone would prove nothing about it; shard 3 is the one that carries it, and it is green.Cannot find package '@objectstack/spec/data'-class, i.e. an unbuilt dependency closure in a freshly recreated worktree, not an assertion. Recorded because it reads exactly like "this change broke imports". All numbers above are from runs with the closure built.The underlying event was neither reproduced nor explained, and I did not manufacture a fix for it. The diagnostic hole it exposed is filed as #10032.
Verification — re-run at the merge head
a0954815fGate union re-derived with
node scripts/pm/dispatch-gates.mjs(no path arguments) against the new merge base899052acc— still the same 3 paths and the same gate set.check:slot-lookup✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new.·baseline key set verified against 899052a: no files added.← re-quoted: the ratchet baseline moved with the merge basecheck:i18ncheck-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys).check:test-source-aliascheck-test-source-alias OK — 72 packages with tests scanned; …check:type-source-resolutioncheck-type-source-resolution OK — 76 packages with a tsconfig.json scanned; …check:changeset-gate-self-tests✓check:objectui-changeset✓ objectui-range --self-test: all checks passedcheck-adr-0087-registration.mjs✓ … this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).check-changeset-no-major.mjs✓ This diff introduces no 'major' bump.check-empty-changeset.mjs✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).check-affected-docs.mjs✓ affected-docs self-test: 262 cases pass.(exit 0)Tests / typecheck at the merge head —
pnpm --filter @objectstack/service-messaging test→Test Files 22 passed (22)/Tests 242 passed (242);typecheck→ exit 0 withtsc --noEmitechoed. Counts unchanged frommain(measured both sides in the same worktree at first review: base also 22/242).Ablation: NOT APPLICABLE. Nothing executable changed — a header comment, one description string, one generated bundle leaf, one changeset. There is no guard to remove and no behaviour to mutate, so an ablation here would be fabricated rather than informative.
All heavy commands ran through
scripts/pm/os-verify-lock.sh.Out-of-scope findings filed
check:i18nnorcheck:i18n-coveragecan see it. Measured on this diff.turbo run testtask can land in Test Core with ZERO captured output, and the completeness guard's green does not cover a package that printed nothing #10032 — a failingturbo run testtask can land inTest Corewith zero captured output, and the completeness guard's green does not cover a package that printed nothing. Filed out of this patch round.Not addressed here
#9807covers only the annotation. The Layer-3 expansion itself remains unbuilt and unclaimed — see the report'sopen_questionsfor the two judgement calls I did not want to make silently.Generated by Claude Code