Found while implementing #9517 (rewriting plugin-audit's published README). The old README linked this page as its "See Also" reference for the audit service; checking whether the link was safe to keep turned up a shape mismatch on the page itself. ⛔ Not fixed in PR #9531 — different file, different package, and that PR's scope was a maintainer-ruled rewrite of one published README. The link was dropped there rather than propagating a claim I had measured to be wrong.
What is wrong
content/docs/kernel/runtime-services/audit-service.mdx presents services.audit as a runtime service slot, and documents its method as:
services.audit.record(entry: {namespace: string;
key: string;
scope: SpecifierScope;
...
action: 'set'|'reset';
...
}): Promise<void>|voidBut the kernel service slot actually named audit has a different shape entirely. Measured:
- Exactly one registrant.
git grep "registerService(\s*'audit'" over packages/**/*.ts returns one hit — packages/plugins/plugin-audit/src/audit-plugin.ts, which registers createAuthEventAuditSink(...). AuditPlugin.providesServices = ['audit'] declares the same. - Its surface is one method,
recordAuthEvent(event: AuthSessionAuditEvent), where AuthSessionAuditEvent.action is the closed union 'login' | 'logout' (auth-event-audit.ts:68). - Exactly one consumer.
git grep "getService[^)]*'audit'" returns one non-test hit — packages/plugins/plugin-auth/src/auth-plugin.ts:363, typed as AuthEventAuditSurface.
The record() shape the page documents is real, but it is SettingsAuditSink (packages/services/service-settings/src/settings-service.types.ts:163) — a constructor argument, passed directly into the settings service at settings-service-plugin.ts:213 via buildConfigChangeAuditSink(engine, ctx.logger). It is never registered as, or resolved from, the kernel audit slot. The page's own "Canonical source" line points at settings-service.types.ts, which is consistent with it describing the sink type — but it is filed under runtime-services/ and written as services.audit, which is the slot.
⇒ A reader following this page and calling getService('audit').record({...}) gets a TypeError: the resolved object has no record. The method they need is recordAuthEvent, and it accepts neither 'set' nor 'reset'.
Why it is worth a card
Two shapes share one name across a doc page and a service slot, and nothing reconciles them. The page is not stale in the ordinary sense — the API it describes exists — it is filed against the wrong seam, which is the harder kind to notice: every symbol in it is real, so a grep for phantom identifiers (the check that catches #9517-class drift) will not flag it.
⚠️ Small scope, stated plainly so it is not over-graded: this is a content/docs/** page, not a published npm artifact, so it does not carry #9517's "this is what the package page renders" weight. It is a correctness bug in the docs, not a published capability claim.
Suggested fix
Decide which surface the page is for, then make it say so:
- if it documents the settings sink — retitle away from
services.audit, move it out of runtime-services/, and name it as the constructor-injected SettingsAuditSink it is; or - if it documents the runtime slot — rewrite it around
recordAuthEvent / 'login' | 'logout', and point querying readers at sys_audit_log as it already does.
The page's closing note ("For querying audit history, use services.data against sys_audit_log/sys_activity") is correct either way and should survive.
Refs: #9517 · PR #9531 (which dropped the link rather than propagate the mismatch) · #9532 (the other published-docs drift found in the same pass).
Found while implementing #9517 (rewriting plugin-audit's published README). The old README linked this page as its "See Also" reference for the audit service; checking whether the link was safe to keep turned up a shape mismatch on the page itself. ⛔ Not fixed in PR #9531 — different file, different package, and that PR's scope was a maintainer-ruled rewrite of one published README. The link was dropped there rather than propagating a claim I had measured to be wrong.
What is wrong
content/docs/kernel/runtime-services/audit-service.mdxpresentsservices.auditas a runtime service slot, and documents its method as:But the kernel service slot actually named
audithas a different shape entirely. Measured:git grep "registerService(\s*'audit'"overpackages/**/*.tsreturns one hit —packages/plugins/plugin-audit/src/audit-plugin.ts, which registerscreateAuthEventAuditSink(...).AuditPlugin.providesServices = ['audit']declares the same.recordAuthEvent(event: AuthSessionAuditEvent), whereAuthSessionAuditEvent.actionis the closed union'login' | 'logout'(auth-event-audit.ts:68).git grep "getService[^)]*'audit'"returns one non-test hit —packages/plugins/plugin-auth/src/auth-plugin.ts:363, typed asAuthEventAuditSurface.The
record()shape the page documents is real, but it isSettingsAuditSink(packages/services/service-settings/src/settings-service.types.ts:163) — a constructor argument, passed directly into the settings service atsettings-service-plugin.ts:213viabuildConfigChangeAuditSink(engine, ctx.logger). It is never registered as, or resolved from, the kernelauditslot. The page's own "Canonical source" line points atsettings-service.types.ts, which is consistent with it describing the sink type — but it is filed underruntime-services/and written asservices.audit, which is the slot.⇒ A reader following this page and calling
getService('audit').record({...})gets aTypeError: the resolved object has norecord. The method they need isrecordAuthEvent, and it accepts neither'set'nor'reset'.Why it is worth a card
Two shapes share one name across a doc page and a service slot, and nothing reconciles them. The page is not stale in the ordinary sense — the API it describes exists — it is filed against the wrong seam, which is the harder kind to notice: every symbol in it is real, so a grep for phantom identifiers (the check that catches #9517-class drift) will not flag it.
content/docs/**page, not a published npm artifact, so it does not carry #9517's "this is what the package page renders" weight. It is a correctness bug in the docs, not a published capability claim.Suggested fix
Decide which surface the page is for, then make it say so:
services.audit, move it out ofruntime-services/, and name it as the constructor-injectedSettingsAuditSinkit is; orrecordAuthEvent/'login' | 'logout', and point querying readers atsys_audit_logas it already does.The page's closing note ("For querying audit history, use
services.dataagainstsys_audit_log/sys_activity") is correct either way and should survive.Refs: #9517 · PR #9531 (which dropped the link rather than propagate the mismatch) · #9532 (the other published-docs drift found in the same pass).