You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
runtime-services/index.mdx points "Audit bridge" at the settings sink, the same wrong seam #9534 fixed on the page it links to #9588
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:
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.
{
"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."
]
}
Activity
os-steve commentedon Aug 18, 2026
Claim — session
session_01XqDQYVU5smx29ts9pAErja(os-dev, dispatched by thedomain:devxPM seat).claude/issue-9588-runtime-services-index-seamobjectstack-issue-9588content/docs/kernel/runtime-services/index.mdx(this file only;audit-service.mdxis held by PR docs(kernel): document services.audit as the slot it actually is #9587 and was not touched)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
claude commentedon Aug 18, 2026
{ "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
os-steve commentedon Aug 18, 2026
✅ ACCEPT — PR #9605
1. ⭐ 它没有只改指针,还删掉了那个造成误读的词
⇒ 只换路径会留下一个仍在诱导下一个人犯同样错的标签。错的不只是指向,还有措辞。
2. 它独立复核了我给的事实,而不是继承
我在派发里把 #9534 的两条结论当既定事实给它(两行注册、唯一注册者),并说「不要重新推导,但该抽查」。它抽查了,而且带阳性对照:
⇒ 零命中是 pattern 的问题,不是符号缺席 —— 它自己证了一遍才用。
3. ⭐⭐ 我那条未验证的假设被测量并证伪
我问:「『canonical source』在这一页对每一行是不是同一个意思?」
答:不是。三种东西无标注地混在一张表里 ——
packages/client/src/index.ts而且三者在「该槽是否在本仓注册」上也不同:storage 在这里只有契约,没有注册。
⇒ 报告而不动手,理由充分:给每行标注种类是一次页面重设计,而正确形状没有被现有证据钉住。⭐ 并且它指出 #9590 的第一条(把 accessor 解析到注册点,再拿文档类型去核对那个注册者)正是能机械关闭这整类的检查 —— 包括本卡这个缺陷。
4. 一处它查证后故意不立卡的发现
services.storage是记法,真实注册的键是file-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