Skip to content

docs(kernel): document services.audit as the slot it actually is - #9587

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-9534-audit-service-doc-shape
Aug 18, 2026
Merged

docs(kernel): document services.audit as the slot it actually is#9587
os-steve merged 1 commit into
mainfrom
claude/issue-9534-audit-service-doc-shape

Conversation

@claude

@claudeclaudeBot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Fixes#9534

What was wrong

content/docs/kernel/runtime-services/audit-service.mdx was filed against the wrong seam. It is written as services.audit and lives under runtime-services/, so it reads as the kernel service slot — but every claim on it described SettingsAuditSink, a constructor-injected sink that service-settings receives directly and that is never registered as, or resolved from, that slot.

A reader following the page and calling getService('audit').record({ ... }) got a TypeError.

What the code actually says

Re-derived from source rather than from the card's phrasing:

Claim on the pageTruthWhere
Canonical source service-settings/src/settings-service.types.tspackages/plugins/plugin-audit/src/auth-event-audit.tsthe slot's only registrant
record(entry)recordAuthEvent(event)AuthEventAuditSink, auth-event-audit.ts:112
action: 'set' | 'reset'action: 'login' | 'logout'AuthSessionAuditAction, auth-event-audit.ts:68
"a throwing sink propagates its error and fails the caller's write"False for both shapessee below
write-only; query history via services.datacorrect — kept verbatim

Exactly one registrant, AuditPlugin (audit-plugin.ts:117), and exactly one in-repo consumer, plugin-auth (auth-plugin.ts:363, typed AuthEventAuditSurface, whose inline shape matches field-for-field). No other shape occupies the slot in this repo or in objectui, so "the registered service" is unambiguous.

⚠️ Worth flagging for anyone re-deriving this later: the card's own reproduction command, git grep "registerService(\s*'audit'", returns zero hits — the real registration spans two lines. The conclusion was right; the command printed in the card does not reproduce it. A line-based grep with a working positive control still reported nothing.

The claim the card did not name

The card listed the method and the event union. The sweep found a third, independent error in Typical Errors, which said the caller awaits record() without a surrounding try/catch. That is backwards for both shapes:

  • recordAuthEvent catches every failure internally and never throws (auth-event-audit.ts:255-263);
  • the settings service wraps its this.audit.record(...) call in try/catch (settings-service.ts:1459-1482), a guard added deliberately so a ledger cannot fail a write that already landed.

Also stale even as a description of the settings sink: the documented record() parameter list predates tenantId.

The rewritten Typical Errors section states the real posture — never throws, reports once per process then drops to debug, silently no-ops without an engine or a userId, and the datasource-split cause behind most failing inserts.

Deliberately unchanged

  • Title services.audit and the page's path. The 2026-06 hand-written docs audit ruled services.* a chapter-wide descriptive convention rather than a literal registry key, and sibling pages follow it. Here it happens to also be the literal key. Keeping the path also keeps content/docs/kernel/index.mdx and meta.json valid, so this PR touches one file.
  • experimental stability. Same audit ruled it a hand-asserted label with no code enum — not this card's call.
  • The closing sys_audit_log / sys_activity query note, kept verbatim per the card.

A short "Not this: the settings audit sink" section now names the record() shape, its real canonical source, and why it is unreachable from this slot — so the shape the page used to carry stays discoverable, and the two are reconciled on the page instead of silently colliding.

Verification

Gate union re-derived from the actual changed path with node scripts/pm/dispatch-gates.mjs (identical to the dispatched list — no new families) and run at 0b4d38c12, the final commit:

EXIT[pnpm check:nul-bytes]=0
EXIT[pnpm check:docs-audit-scope]=0
EXIT[pnpm check:docs-redirects]=0
EXIT[pnpm check:role-word]=0
EXIT[pnpm --filter @objectstack/spec run check:empty-state]=0
EXIT[pnpm --filter @objectstack/spec run check:liveness]=0
EXIT[pnpm --filter @objectstack/spec run check:strictness-ledger]=0
EXIT[pnpm --filter @objectstack/spec run check:variant-docs]=0

check:docs-audit-scope confirms the page stays in the docs-accuracy-audit scope (179 hand-written docs in sync); check:role-word passes with the file still absent from the baseline (zero occurrences).

No changeset: content/docs/** only, no published package changes — skip-changeset applied.

Reported, not edited

Three findings are outside this card's one-file surface and were filed unassigned rather than ridden along:


Generated by Claude Code

The page described `record()` / `'set' | 'reset'`, which is
`SettingsAuditSink` — a constructor-injected sink, never registered as or
resolved from the kernel `audit` slot. The slot's only registrant is
plugin-audit's `createAuthEventAuditSink`, whose surface is
`recordAuthEvent(event)` with `action: 'login' | 'logout'`.
Rewritten around the real slot, plus a disambiguation section naming the
settings sink so the shape stays discoverable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Aug 18, 2026
@os-steveos-steve added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 18, 2026 — with Claude
@os-steve
os-steve marked this pull request as ready for review August 18, 2026 12:44
@os-steve
os-steve added this pull request to the merge queueAug 18, 2026
Merged via the queue into main with commit 7604fccAug 18, 2026
26 checks passed
@os-steve
os-steve deleted the claude/issue-9534-audit-service-doc-shape branch August 18, 2026 13:20
os-steve added a commit that referenced this pull request Aug 18, 2026
…nter, in a form published readers can follow (#9589)
PR #9531 dropped this README's "See Also" pointer to the runtime-services audit
page because the page documented the settings sink (`record()` / `'set' | 'reset'`)
as if it were the `audit` slot. PR #9587 rewrote the page around the real slot, so
the omission's reason has stopped holding.
Restored because the page carries three things this README deliberately does not:
the slot's own failure posture (`recordAuthEvent` never throws; error once per
process then debug; row lost, nothing retries; silent no-op with no engine or no
`userId`), the event's field-by-field shape, and the settings-sink disambiguation
with its `TypeError` consequence. The README's own slot section states the
interface and the closed action union and stops there.
The restored line is not the removed line. That one was labelled "Audit Logging
Best Practices" — a guide the page has never been — and spelled
`/content/docs/.../audit-service.mdx`, which resolves for neither audience a
published README has: on npm and GitHub a root-relative href resolves against
npmjs.com / github.com, not the docs site. The replacement uses the absolute
`https://docs.objectstack.ai/docs/...` form that create-objectstack's published
READMEs already use.
The one pre-existing site-root-relative docs link in the same file is converted to
the same absolute form; its page and heading anchor both exist, only the spelling
was unfollowable off the docs site.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-steve@claude