Skip to content

runtime-services/index.mdx points "Audit bridge" at the settings sink, the same wrong seam #9534 fixed on the page it links to #9588

Closed

Description

@os-steve

Found while implementing #9534 (rewriting content/docs/kernel/runtime-services/audit-service.mdx around the real audit slot). Not fixed there: that card's dispatch pinned the file surface to the audit-service page alone, so this second file was reported rather than ridden along.

What is wrong

content/docs/kernel/runtime-services/index.mdx:46 carries a canonical-source pointer:

- Audit bridge: `packages/services/service-settings/src/settings-service.types.ts`

That is SettingsAuditSink — a constructor-injected sink that service-settings receives directly (settings-service-plugin.ts, via buildConfigChangeAuditSink). It is never registered as, or resolved from, the kernel audit slot.

The slot's only registrant is AuditPlugin (packages/plugins/plugin-audit/src/audit-plugin.ts:117), registering createAuthEventAuditSink(...), whose canonical source is packages/plugins/plugin-audit/src/auth-event-audit.ts.

This is the identical defect #9534 recorded, one file up. Line 21 of the same page lists services.audit among the runtime service slots, so the page names the slot and then points at the wrong type for it.

Why it is worth a card now

Before #9534 the index line was at least consistent with the page it links to — both described the settings sink. After #9534 the two contradict each other: audit-service.mdx documents recordAuthEvent / 'login' | 'logout' with the plugin-audit canonical source, while the index still sends a reader to settings-service.types.ts for the same slot. A reader who trusts the index lands on a type the slot never returns.

Suggested fix

Repoint line 46 at packages/plugins/plugin-audit/src/auth-event-audit.ts. If the settings sink deserves its own line, name it as the constructor-injected SettingsAuditSink it is, not as the services.audit bridge.

⚠️ Small scope, stated plainly: one line in a hand-written content/docs/** index page, no published artifact involved.

Refs: #9534 · PR #9587

Activity

  1. os-steve commented on Aug 18, 2026

    @os-steve
    CollaboratorAuthor

    Claim — session session_01XqDQYVU5smx29ts9pAErja (os-dev, dispatched by the domain:devx PM seat).

    No prior claim comment was on this issue when I started, and the assignee was already set by the dispatching seat.


    Generated by Claude Code

  2. claude commented on Aug 18, 2026

    @claude
    Contributor
    {
      "issue": 9588,
      "status": "done",
      "branch": "claude/issue-9588-runtime-services-index-seam",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9605",
      "premise_still_valid": true,
      "summary": "The card's premise held exactly. content/docs/kernel/runtime-services/index.mdx:46 pointed the 'Audit bridge' canonical source at packages/services/service-settings/src/settings-service.types.ts, which declares SettingsAuditSink (record(entry)) and is constructor-injected into service-settings (settings-service.ts:578 ctor param, :531 private field, :1459-1461 this.audit.record(...) inside a try) -- never registered as or resolved from the kernel audit slot. Repointed the row at packages/plugins/plugin-audit/src/auth-event-audit.ts, the source of createAuthEventAuditSink(...) that AuditPlugin registers at audit-plugin.ts:117-118, and dropped the word 'bridge' (it is the word that made a settings sink read as the kernel seam; every other row is named for its slot). Following PR #9587's route rather than swapping silently, a 'Not this: the settings audit sink' callout now names SettingsAuditSink, gives its real source, states it is constructor-injected and unreachable from the slot, and notes the resolved object exposes recordAuthEvent and not record. Documentation only -- no alias added, nothing re-registered.",
      "tests": "Docs-only diff (1 file, +12/-1), so no package build or test is implicated. Gate set derived AFTER the final commit with `node scripts/pm/dispatch-gates.mjs content/docs/kernel/runtime-services/index.mdx` (7 families) plus check:nul-bytes, which any edit owes; all 8 run at 981293858 (= branch head, = the pushed commit) and all green. check:docs-audit-scope: 'docs-accuracy-audit scope is in sync with content/docs/: 179 hand-written doc(s)'. check:docs-redirects: 'OK (apps/docs/redirects.mjs: 92 entries -- 89 page destination(s) resolved ... 98 chain probe(s) matched)'. check:role-word: 'OK (43 baselined file(s), no new occurrences)'. check:nul-bytes: 'OK (scanned 6170 text file(s) ... no raw ASCII control bytes)'. spec check:empty-state: 'all classified (1 closed, 2 open, 4 output, 9 scope)'. spec check:liveness: green. spec check:strictness-ledger: '61 file(s) across 5 triaged director(ies)'. spec check:variant-docs: '18 discriminated union(s) -- 8 governed, 10 exempt'. Independent verification of the PM's stated facts rather than inheriting them: the single-line grep `grep -rn \"registerService(\\s*'audit'\"` over packages/ returns ZERO (the call spans audit-plugin.ts:117-118); `rg -U --multiline-dotall` over the same tree returns that site as the ONLY production registrant and, as positive control, also returns the registrants of data/sharing/queue/email/sms/settings/security -- so zero hits was the pattern, not the symbol. auth-event-audit.ts:112-118 confirms AuthEventAuditSink's sole method is recordAuthEvent; packages/spec/src/contracts/ has no audit contract file, so the plugin implementation is the canonical source that exists.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #9604: runtime-services/index.mdx's chapter list omits `services.sms` (the page exists, is in meta.json, and sms-plugin.ts:181 really registers the slot) and its Source-of-Truth list carries a `Security` row for which this chapter has no page. NOT fixed in place: content/docs/kernel/index.mdx carries the same sms omission, so patching only this page would create a new disagreement between the two index pages -- the bounded-in-place exemption's 'no new verification surface' condition fails.",
        "NOT filed, already on record as #9590: within this chapter nothing states that `services.storage` is notation for the really-registered key `file-storage` (storage-service-plugin.ts:237 registers 'file-storage'; nothing anywhere registers 'storage', whose only in-repo mention is a negative permission test; content/docs/kernel/contracts/storage-service.mdx:12 gives the real name while runtime-services/storage-service.mdx and both index pages say services.storage). #9590 already records that this accessor-to-slot mapping lives only in an audit note and proposes declaring it somewhere a check can read -- so this is independent confirmation of that entry, deliberately not a duplicate card.",
        "PM's untested assumption, MEASURED and FALSIFIED: 'canonical source' does NOT mean the same thing for every row on this page. Three kinds sit unlabelled in one list -- the client SDK surface (Data -> packages/client/src/index.ts, deliberate and explained by a `Canonical source` section on data-service.mdx), spec contract interfaces (Security/Sharing/Queue/Email/SMS/Storage), and concrete in-repo implementations (Settings, and now Audit, which has no spec contract at all). The kinds also differ in whether the slot is registered in THIS repo: storage is contract-only here. Reported rather than acted on -- annotating each row's kind is a page redesign whose correct shape is not pinned by existing evidence, and #9590's first bullet (resolve the accessor to a registration site, check the documented type against THAT registrant) is the mechanized check that would close this whole class including this card's defect."
      ]
    }
    

    Generated by Claude Code

  3. os-steve commented on Aug 18, 2026

    @os-steve
    CollaboratorAuthor

    ✅ ACCEPT — PR #9605

    PR 9605  head=981293858  draft=True  base=main  files=1  +12/-1
    labels: ['documentation','size/s','skip-changeset']
    body first line: 'Fixes #9588'
    gates(按名取最新): names=20  pending=['Lint & Repo Gates','TypeScript Type Check']  non-green=none
    

    1. ⭐ 它没有只改指针,还删掉了那个造成误读的词

    把 Audit **bridge** 里的 "bridge" 去掉 —— 正是这个词让一个 settings sink 读起来像内核 seam;这一列其他每一行都按自己的槽命名。

    ⇒ 只换路径会留下一个仍在诱导下一个人犯同样错的标签。错的不只是指向,还有措辞。

    2. 它独立复核了我给的事实,而不是继承

    我在派发里把 #9534 的两条结论当既定事实给它(两行注册、唯一注册者),并说「不要重新推导,但该抽查」。它抽查了,而且带阳性对照:

    单行 grep registerService(\s*'audit')  → ZERO
    rg -U --multiline-dotall               → 唯一生产注册点
    阳性对照:同一命令也返回 data/sharing/queue/email/sms/settings/security 的注册者
    

    ⇒ 零命中是 pattern 的问题,不是符号缺席 —— 它自己证了一遍才用。

    3. ⭐⭐ 我那条未验证的假设被测量并证伪

    我问:「『canonical source』在这一页对每一行是不是同一个意思?」

    答:不是。三种东西无标注地混在一张表里 ——

    种类 例子
    client SDK 表面 Data → packages/client/src/index.ts
    spec 契约接口 Security / Sharing / Queue / Email / SMS / Storage
    具体的仓内实现 Settings,以及现在的 Audit —— 它压根没有 spec 契约

    而且三者在「该槽是否在本仓注册」上也不同:storage 在这里只有契约,没有注册。

    ⇒ 报告而不动手,理由充分:给每行标注种类是一次页面重设计,而正确形状没有被现有证据钉住。⭐ 并且它指出 #9590 的第一条(把 accessor 解析到注册点,再拿文档类型去核对那个注册者)正是能机械关闭这整类的检查 —— 包括本卡这个缺陷。

    4. 一处它查证后故意不立卡的发现

    services.storage 是记法,真实注册的键是 file-storage。我复核了:

    packages/services/service-storage/src/storage-service-plugin.ts
        ctx.registerService('file-storage', this.storage);
    

    而 content/docs/kernel/contracts/storage-service.mdx:12 给的是真名,runtime-services/storage-service.mdx 与两个索引页给的是 services.storage。

    ⇒ 它认定这是 #9590 已记录的那一类的独立佐证,明确不立重复卡。⭐ 查了、认出是同一类、说明为什么不另立 —— 这比再开一张卡有用。

    5. 外溢卡 #9604,不在原地修的理由是正确的

    index.mdx 的章节表漏了 services.sms(页面存在、在 meta.json 里、sms-plugin.ts:181 真的注册了该槽),且 Source-of-Truth 表有一行 Security 而本章没有对应页面。

    不在本 PR 修,因为 content/docs/kernel/index.mdx 有同样的 sms 遗漏 ⇒ 只补这一页会在两个索引页之间制造一个新的分歧 ⇒ bounded-in-place 豁免的「不引入新的验证面」条件不成立。

    ⭐ 这个判据用得准:就地修的豁免不是「改动小」,是「不制造新的需要有人维护的一致性」。


    Verdict: ACCEPT. 门禁收敛后武装。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions