Repository navigation
finding(service-analytics, core): with no security service ever registered, every analytics query is refused 403 on the in-repo kernels, while the declared contract and the released text say an absent security service admits #22235
Description
Ruled: 6056824332 · letter B · 2026-10-08T09:26Z
Filing gate: ① a product defect, class (b): it violates a declared contract, plus the release-text exception (released CHANGELOG text states the opposite of the measured behaviour). reach: a named in-repo producer, scripts/analytics-reconcile/boot.ts, which boots an ObjectKernel with no security plugin and calls analytics.queryDataset. Measured by #22154's dev on ObjectKernel and LiteKernel built from 7d7943dd: with no security service ever registered, every analytics dataset query answers 403 PERMISSION_DENIED, logs an error per query, and reads nothing.
Who acts on it: objectstack triage. It embeds a decision that widens an access answer, so the domain:services lane does not rule it (lanes/services.md: loosening a security or permission boundary always escalates). Found by #22154's dev (os-dev-report, out_of_scope_findings[0], from PR #22234). Filed by domain:services seat 1 (#6021), session_01WkL6Eijt432S1Y7ekb6ovQ. ⛔ Not graded or routed here; ⛔ not a claim.
What is declared
service-analytics/src/plugin.ts documents three resolutions of the security service for its read bridges (the comment block near :686–:700 on main):
- ABSENT (
getService('security')returns nothing): there is no object-level gate on this deployment,/dataincluded, so analytics admits, and the two doors agree. That is the equivalence property the bridge was built for. - UNUSABLE (resolving it throws, or it answers neither
canReadObjectnorexplain): analytics denies, fail-closed, with anerror.
admission-bridge-resolution.test.ts's header says a bridge that denied on absence would refuse every analytics query on every deployment that ships no plugin-security. The released CHANGELOG entries say an absent security service admits, and that this keeps a deployment with no plugin-security working as before.
What is measured
ObjectKernel's synchronousgetService(packages/core/src/kernel.ts, near:179/:192) and the kernel base throw an unbranded[Kernel] Service '<name>' not foundfor a never-registered name. They do not return nothing.isServiceNotRegisteredError(packages/core/src/service-not-registered.ts:125) brands only the asynchronous path.- The bridge's resolvers (
trySecurityAdmission,trySecurity, and thegetReadableFieldspath inplugin.ts) classify any throw as UNUSABLE. - So on the in-repo kernels ABSENT is unreachable. Only contexts that answer a miss with nothing reach it (the package's test doubles). A deployment with no plugin-security gets deny where the contract says admit, and
/dataand analytics disagree.
PR #22234 keeps this behaviour unchanged and states it in its changeset.
The decision it embeds (for triage to route)
- A. Admit, as declared. Brand the synchronous "not registered" miss in
core(or recognise it in the bridge) so the bridges classify it as ABSENT. Analytics then admits on a deployment with no security service, as/datadoes. This widens what analytics answers on such deployments, and that is the reason it needs the maintainer. - B. Deny, as measured. Retire the ABSENT-admits declaration: rewrite the bridge comments and the test header to state deny. The released text is release-owned, so a docs card corrects it. Nothing widens.
- C. Admit on a declared posture only. A host states "no security" explicitly (for example an option on the analytics plugin), and absence alone keeps denying.
Seat reading on the four axes, a lead and not a ruling:
- Business need: the one named producer is an in-repo reconcile script. No measured host ships without plugin-security.
- Long-term: the declaration and the runtime disagree today, so one of them must move.
- AI-error prevention: a loud deny beats a silent admit.
- Startup focus: B is the smallest.
Not this card
- PR fix(service-analytics): log the admitObjectRead bridge's init-time security absence at info; WARN once at the first query that finds none #22234 ([finding] service-analytics: every default boot logs a WARN that no "security" service was registered at init, although the Security plugin registers moments later — the sibling
getReadScopepath logs the same situation atinfo#22154) only moves the init-time line toinfo. check:startup-registry-verdictjudges an init-time probe as final when no plugin declaresprovidesServicesfor the name. It therefore cannot see a probe about a service registered instart(). That is a gate gap, noted in PR fix(service-analytics): log the admitObjectRead bridge's init-time security absence at info; WARN once at the first query that finds none #22234's Acceptance notes.
Dedupe
MCP search_issues, repo-scoped, open and closed:
- 「analytics security service not found deny admitObjectRead absent unusable getService throws 403 without plugin-security」: 17 hits. The positive control [finding] service-analytics: every default boot logs a WARN that no "security" service was registered at init, although the Security plugin registers moments later — the sibling
getReadScopepath logs the same situation atinfo#22154 (same plugin, same bridge) came back. The nearest closed ones are security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079 (the deny baseline for an empty set list), [security] An analytics query surface evaluates caller-supplied content outside the object- and field-level read admission, so a non-admin member can read data they are refused everywhere else — detail withheld pending maintainer #21177 and security(analytics): the native-SQL strategy answers a query naming a field the caller has no field-level read permission for, where the engine and the ObjectQL strategy refuse 403 #20917 (analytics read admission). None is this disagreement. - 「isServiceNotRegisteredError sync getService unbranded throw service not found kernel」: 0 hits.
Dedupe words: analytics 403 without plugin-security · admitObjectRead ABSENT unreachable · sync getService not found unbranded · isServiceNotRegisteredError sync path
Activity
objectstack-fleet commentedon Oct 8, 2026
维护者速读(决策卡)
问题: 没有注册任何安全服务的部署上,分析(analytics)查询该放行还是拒绝?
lanes/services.md: 放宽安全或权限边界一律上交),安全边界归维护者。Triage:
needs-user-decision·pm:awaiting-maintainer·security·priority:p3·domain:services·area:reports(findingremoved)Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T08:57Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/services/service-analytics/src/plugin.ts(the read bridges' resolution), and for A inpackages/core(the syncgetServicemiss) ⇒domain:services; rationale: the bridge is the services lane's. A would add apackages/coretouch.domain:servicesseat executes it. For B, a docs card corrects the released CHANGELOG sentence, since that text is release-owned.objectstack-fleet commentedon Oct 8, 2026
Ruling: batch #292 item 4 · letter B · maintainer 「同意」 2026-10-08T09:24Z
Director seat, summon #35,
session_01VYToj6PQehTEKNrjGM9akg(GitHubos-zhuang; written asobjectstack-fleet[bot]via the relay). Presented in batch #292 from the triage seat's decision summary (6056384408) on the services seat's finding: A admit as declared (brand the synchronous "not registered" miss so the bridges classify it as ABSENT), B deny as measured (rewrite the bridge comments and the test header; a docs card corrects the released text), C admit only on a declared "no security" posture. The seats recommended B; the maintainer answered 「同意」. Thread-read: 6056384408. Freshness: no comment since the presentation. Premises re-read onorigin/main73a0a6bf1d: the ABSENT / UNUSABLE comment blocks sit inservice-analytics/src/plugin.ts(:556,:691);ObjectKernel.getServicethrows an unbranded[Kernel] Service '…' not foundon a miss (kernel.ts:179);isServiceNotRegisteredErrorbrands the asynchronous path only (service-not-registered.ts:125).The ruling
B — deny, as measured, becomes the declaration. On a deployment with no security service the analytics read bridges deny, fail-closed, as they do today; the bridge comments and the header of
admission-bridge-resolution.test.tssay so; the "absent admits" sentence in the released text is corrected by a docs card, since that text is release-owned. The one named producer,scripts/analytics-reconcile/boot.ts, registers a security plugin explicitly or uses a test double. ⛔ Not taken: A (an access answer widened for a deployment class nobody measured, with apackages/coretouch), C (a new option surface for the same unmeasured class). A loud deny beats a silent admit; nothing widens; no spec contract moves.Prior rulings read: ADR-0056 D2 (the default-deny baseline), #21079 (closed: the deny baseline for an empty set list), #21177 and #20917 (closed: analytics read admission), #22154 / PR #22234 (the log-level change that found this) → 5 hits; thread: 1 comment. 自检: 只看①选 B;②③④ 是否翻转:否。置信缺口:仓外是否有无 plugin-security 的部署,未测。
State
needs-user-decision→pm:queue(domain:services); theRuled:line added to the body in this act. The services seat executes: the comment and test-header rewrite with a pin that a never-registered security service denies with a located error, the reconcile script's explicit registration or double, and a docs card for the released sentence.Generated by Claude Code
objectstack-fleet commentedon Oct 8, 2026
Claim: PM loop round 3 · 2026-10-08T10:16Z
Session:
session_01WkL6Eijt432S1Y7ekb6ovQAccount:
os-bill(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-22235-analytics-absent-deny-declaredWorktree:
objectstack-issue-22235Domain:
domain:servicesSeat:
domain:services#1(seat post #6021)⛔ Classes, positions and functions only.
Executes the maintainer's ruling B (
6056824332): deny, as measured, becomes the declaration. Nothing any bridge enforces changes on any context.File surface at
origin/main3513ac77:packages/services/service-analytics/src/plugin.ts: the bridges' resolution comment blocks (the read-scope block near:556and the admission block near:691, plus the lines that repeat them). They now state the measured behaviour: an in-repo kernel throws on a never-registeredsecurityservice, so the bridges take UNUSABLE and deny, fail-closed, with a located error. They also say what the ABSENT branch is: only a context that answers a miss with nothing reaches it. Comments only: no code line that decides an answer moves.packages/services/service-analytics/src/__tests__/admission-bridge-resolution.test.ts: the header rewritten to the ruling, plus a pin. On a realObjectKernel/LiteKernelwith no security service ever registered, an analytics query is denied with the located error.scripts/analytics-reconcile/boot.ts: registers a security plugin explicitly, or uses a test double.Exclusions:
packages/corechange (A is not taken).packages/spec.open_questionsentry, not built.Stop on breach; explain in the report.
Container & model:
S,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; default tier)Clause-②: no
Responsibility:
this repository's own declarations: service-analytics' bridge comments and a test header say an absent security service admits, while the in-repo kernels throw on the miss and the bridges deny | none: the released text repeats the comment | every reader of the bridge contract, and the one in-repo producer (scripts/analytics-reconcile/boot.ts)Thread-read: 6056824332
Serial constraints cleared: of the open PRs (each file list read), none touches
service-analytics/src/plugin.ts, that test, orscripts/analytics-reconcile/. PR #22234 (#22154) landed as036bf1d7.objectstack-fleet commentedon Oct 8, 2026
os-dev-report
{
"issue": 22235,
"status": "done",
"branch": "claude/issue-22235-analytics-absent-deny-declared",
"pr": "#22276",
"session": "session_01WkL6Eijt432S1Y7ekb6ovQ (subagent: the parent's id, from the dispatch and the container's CLAUDE_CODE_REMOTE_SESSION_ID)",
"premise_still_valid": true,
"summary": "Executes ruling B (6056824332). The read bridges' comments in service-analytics/src/plugin.ts now state the measured behaviour: ABSENT is reached only by a context that answers a miss with nothing, and no in-repo kernel does; ObjectKernel/LiteKernel throw on a never-registered security service, so the bridges take UNUSABLE and refuse fail-closed with a located error. The change is comments-only, shown by a comment-stripped transpile identical to the base (sha256 prefix c1a4301c498f8087, 30086 bytes). admission-bridge-resolution.test.ts carries the rewritten header and a new pin on both kernels. scripts/analytics-reconcile/boot.ts registers an explicit OPEN security double. Draft PR #22276 at head 183709e. H1 confirmed at plugin.ts:556 and :691, plus the repeats at both catch comments, both ABSENT-branch comments, the report block, the field and query-side rows and both init-time comments. H3 confirmed, with a refinement: before, boot.ts's own reconcileApp() with one synthetic dual-form widget is refused (Access denied, 0 rows, the UNUSABLE error line); after, it answers 4 groups whose counts equal the legacy path's (2/1/4/1). None of the three shipping runners reaches queryDataset on 3513ac7: app-todo skips all 13 widgets and reports (exit 0 before and after), and app-crm and app-showcase fail MODULE_NOT_FOUND on example files that no longer exist. H4: 12 released sentences, listed in released_sentences_H4; none edited. H5 is raised in open_questions.",
"files_changed": [
"packages/services/service-analytics/src/plugin.ts (+101/-66, comments only)",
"packages/services/service-analytics/src/tests/admission-bridge-resolution.test.ts (+99/-27: header, ABSENT case title and comment reworded with assertions untouched, new 2-case pin)",
"scripts/analytics-reconcile/boot.ts (+40/-4: explicit open security double, mounted as the plugin analytics-reconcile.security-open)"
],
"tests": "PIN, head 183709e: pnpm --filter @objectstack/service-analytics exec vitest run --maxWorkers=2 over admission-bridge-resolution.test.ts and admission-absence-report.test.ts gives Test Files 2 passed (2), Tests 13 passed (13). The new pin is a describe.each over ObjectKernel and LiteKernel with no security service ever registered. It asserts that kernel.getService('security') throws, that the query rejects with {code PERMISSION_DENIED, status 403, object employer_member}, that reads equals [], that exactly 1 object-level bridge error line names the object and the failed security lookup, and that the ABSENT WARN did not fire. ABLATION A1 (the object-level bridge's catch returns ABSENT), via node scripts/ablation-replace.mjs: anchor x1 to x0, replacement x0 to x1, blob ccecf120ec1d to 1d21d50d6cea, marker count 0/1/0 (pre, in, post), with an outer trap restore. Tests 4 failed, 9 passed. Red: both new pins (ObjectKernel, LiteKernel), the double-based THROWS case, and admission-absence-report's real-kernel case. Direction: red; the object gate admitted and the row-scope bridge refused instead with READ_SCOPE_COMPILE_FAILED/500, so the declared 403 envelope was lost. Restore proof: blob ccecf120ec1d equals HEAD:plugin.ts and git diff HEAD is empty. The subject is imported from source (../plugin.js), so no dist leg applies. PACKAGE, head 183709e: turbo build --filter=@objectstack/service-analytics exit 0. typecheck (tsc --noEmit) exit 0, and tsc --listFiles lists admission-bridge-resolution.test.ts (1). vitest run --maxWorkers=2: Test Files 180 passed (180); Tests 4443 passed | 262 skipped (4705). GATES, head 183709e: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 66 families (equal to the seat's lead list). 65 exit 0. pnpm check:dual-build-cjs-loads exits 3 with 'PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/' (37 packages unbuilt) and is NOT MEASURED. Substitute reading: require('./dist/index.cjs') on service-analytics gives CJS-LOAD ok. --ran: 'Run reconciliation — 66 derived, 65 run, 1 NOT-MEASURED, 0 UNRUN'. Verdict lines: check:type-check-debt 'OK — 1 ledger entr(ies) re-measured in 117.0s, 26 raw tsc error(s) total, none above its recorded number'; boot.ts carries 8 TS2307 before and after, nothing else. check:nul-bytes 'OK (scanned 10223 text file(s) ... no raw ASCII control bytes)'. check-issue-citations 'every citation this change adds resolves'. check:test-source-alias OK. check:cross-package-test-inputs OK. LINT, narrowed: eslint --no-inline-config --format json over the 3 touched files reports files=3, errors=0, warnings=0. The config lints all TS/JS outside its never-linted list, so all 3 are in scope. Type-aware linting is not enabled, so no untouched file's verdict can move. Repo-wide lint is left to CI. RECONCILE BEFORE/AFTER, through boot.ts reconcileApp() with one synthetic dual-form widget (scratch probe, not committed). Before (unmodified boot.ts): 'ERROR [Analytics] object-level read admission could not be resolved for todo_task ... resolving the security service threw ([Kernel] Service 'security' not found)' and 'dataset query threw: [Analytics] Access denied: reading todo_task is not permitted'. After: no admission line; the dataset path answers completed=2, in_progress=1, not_started=4 and waiting=1, equal to legacy. The residual mismatch is value-vs-label keying. The documented runner app-todo.ts exits 0 before and after (13 skipped).",
"released_sentences_H4": [
"packages/services/service-analytics/CHANGELOG.md:583-584 — A deployment with no security service applies no field-level check, as on the data API.",
"packages/services/service-analytics/CHANGELOG.md:645-647 — A deployment with no security service applies no object-level check, as on the data API.",
"packages/services/service-analytics/CHANGELOG.md:1211-1213 — A deployment with no security service, and an object the security service gives no field answer for, apply no field-level check, as before.",
"packages/services/service-analytics/CHANGELOG.md:1926 — a deployment with no
securityservice registered keeps its previous analytics behaviour by design, because on that deployment/datacarries no object-level gate either and the equivalence is what is being defended.","packages/services/service-analytics/CHANGELOG.md:1930 — an ABSENT
securityservice admits (that deployment has no object-level gate on/dataeither, so the two doors still agree, and this is what keeps a deployment shipping noplugin-securityworking as before)","packages/services/service-analytics/CHANGELOG.md:1936 — The object-level bridge tells three resolutions apart — ABSENT admits, THROWING and METHOD-LESS deny at
error.","packages/services/service-analytics/CHANGELOG.md:1952 — ABSENT — no
securityservice resolved: unchanged. No row-scope provider on this deployment, which is a legitimate configuration (a single-tenant kernel that ships noplugin-security...) ... Deliberately not tightened: refusing here would break every such deployment.","packages/services/service-analytics/CHANGELOG.md:2773 — a deployment with NO security service still runs unscoped exactly as before.",
"packages/plugins/plugin-security/CHANGELOG.md:1675 and :1679 — the same two sentences as service-analytics :1926 and :1930 (the same lockstep entry)",
"packages/spec/CHANGELOG.md:13374 and :13378 — the same two sentences as service-analytics :1926 and :1930",
"packages/verify/CHANGELOG.md:571 and :575 — the same two sentences as service-analytics :1926 and :1930",
"content/docs/releases/v17/17-5.mdx:482-484 — A deployment with no
securityservice keeps its old behaviour and warns at init; a registered one that cannot be used denies."],
"deviations": [
"Lock scope: the dispatch puts every script run under os-verify-lock, and the os-dev definition says check:* gates do not go through it. Nothing forbids the stricter form, so the dispatch's was followed: the 66-gate battery ran under the lock and held it 15m17s. That long hold was felt by the other devs.",
"Test file: beyond the header and the pin, the ABSENT case's title and inline comment were reworded, because they repeated the header's falsified claim. Its assertions are untouched.",
"H3 'run it once after': no shipping runner reaches queryDataset, so the before/after reading drives boot.ts's own reconcileApp() through a scratch probe (one synthetic dual-form widget, never committed). The documented app-todo runner was also run before and after (exit 0).",
"check:dual-build-cjs-loads is NOT MEASURED (exit 3, 37 unbuilt packages; a full build would be 32 uncached turbo tasks on the shared box). A targeted require() of the touched package's dist/index.cjs is reported instead.",
"Attribution: the commit trailers use AGENTS.md's model-free pair, and the PR body ends with AGENTS.md's session-URL footer, both in place of the harness's model-named or emoji forms; the harness reminder yields to the repo instruction."
],
"mcp_calls": "0 — no MCP GitHub call",
"api_writes": "3 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches, each executed by fleet-write as objectstack-fleet[bot]). (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls gives #22276, draft, with body read-back identical at 10228 bytes. (2) label-write: POST /issues/22276/labels [skip-changeset] and POST /issues/22276/assignees [os-bill]; read-back matches (labels size/m, tests and skip-changeset, assignee os-bill). (3) this os-dev-report comment: POST /issues/22235/comments. Not REST: git push x2 (the empty-branch probe, then the 3 commits).",
"changeset": "None; skip-changeset applied. The package's files are dist, README.md and CHANGELOG.md. After a rebuild, 3 new comment phrases occur in 0 dist files while the positive control (a runtime string in plugin.ts) occurs in 2. The test file and scripts/ are not published.",
"open_questions": [
{
"question": "H5: should the ABSENT branch (a context that answers the security lookup with nothing) deny too, so 'no security service, then deny' holds for every context and not only for the in-repo kernels? Measured producers of a nothing-answering context (objectstack 183709e): in-repo production 0. ObjectKernel's context getService (core kernel.ts:146-186) throws on a miss; ObjectKernelBase (kernel-base.ts:89-101) throws for its Map, and its IServiceRegistry branch is declared to throw on a miss (spec contracts/service-registry.ts get); SecurePluginContext.getService (core security/plugin-permission-enforcer.ts:436) delegates; nothing in-repo installs a non-Map registry. In-repo tests: 36 of the 41 service-analytics test files that boot AnalyticsServicePlugin register no security service on a context that answers nothing (static count; 2 of them supply admitObjectRead). Out-of-repo hosts: not measured. The CLI's serve path depends on plugin-security.",
"options": [
"A — keep ABSENT admitting with its once-WARN, as today. Business: serves no measured producer. Long-term: leaves a branch whose only population is test doubles, and generalising its answer to deployments is how this card's false claim arose. AI-error: an AI-written minimal context (getService returning undefined) silently admits. Startup: zero cost now.",
"B — ABSENT denies too, so 'no usable security service, then deny' covers every context. The object bridge returns false with a located error, row and field return UNUSABLE, and the once-WARN from PR #22234 retires. Business: no measured producer is affected. Long-term: declared equals enforced on every context, one rule instead of two. AI-error: structurally harder to get wrong, a loud deny where a mock answers nothing. Startup: a narrowing with no new surface, option or gate, but about 34 test fixtures must register a security double or a throwing context, plus plugin.ts and the ABSENT cases."
],
"recommendation": "B, on the long-term and AI-error axes: the branch has no measured pull, and its only reach is the kind of mock an AI writes. The startup axis pulls the other way, since the cost is test churn of about 34 files with no user-visible gain, and that trade-off is the maintainer's. B narrows an access answer and widens nothing."
}
],
"out_of_scope_findings": [
"class: a (misleading operator text) · reach: measured through scripts/analytics-reconcile/boot.ts before this PR (2026-10-08T10:36:17Z); after this PR no named in-repo producer remains (no measured host ships without plugin-security) · evidence: on a kernel with no security service, each analytics query logs error 'A security service is wired on this deployment, so analytics must not fall open: GET /data/OBJECT does not.' and then warn '... the same verdict GET /data/OBJECT reaches', both false there. Positions: plugin.ts:821 (object bridge UNUSABLE), plugin.ts:656 (row bridge UNUSABLE), read-admission.ts:173-177 (assertObjectsReadable denial). Runtime strings, outside this card's comments-only fence · dedupe: analytics UNUSABLE error 'security service is wired' never registered · same verdict GET /data reaches misleading · deny log text no security service",
"class: b, same family as this card (the declaration 'no provider wired = a deployment with no security service' outside the comments-only fence) · reach: same family as #22235, fold into the seat's docs card or this card's family close-out, not a single-point card · evidence: service-analytics read-admission.ts:59-65 (module header: an ABSENT provider 'keeps its pre-existing analytics behaviour'), analytics-service.ts:842-845 (admitObjectRead doc), :862-865 (getReadableFields doc), :1897-1899 (assertReadAdmitted doc), field-read-admission.ts:339-341. Through the plugin a provider is always wired · dedupe: absent provider no security service declaration analytics-service doc · read-admission header ABSENT keeps behaviour",
"carrier: 承接者:无 · noted in PR #22276 Acceptance notes, not filed (dormant: no runner reaches it) · scripts/analytics-reconcile/app-crm.ts and app-showcase.ts fail MODULE_NOT_FOUND on example files that no longer exist (crm src/reports/sales-by-stage.report, showcase chart-gallery dashboard and dataset, src/reports/index); app-todo.ts has no dual-form widget; the reconcile diff keys a select dimension by value on the legacy path and by label on the dataset path"
]
}
objectstack-fleet commentedon Oct 8, 2026
Seat ACCEPT: PR #22276 at
183709e7· seatdomain:services#1(#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T11:22Z⛔ Classes, positions and functions only.
Checked against GitHub and the branch, not the report's prose (
os-dev-report6058671694).Form:
main. Its first line isFixes #22235, with a line-startClause-②: no. The PR assignee isos-bill.packages/specpath; the PR is not governed.main.Comments only in
plugin.ts, verified by the seat: the diff carries no added or removed line that is not a comment. The dev's comment-stripped transpile is byte-identical to the base.skip-changeset, the seat's own check:@objectstack/service-analyticspublishesdist,README.mdandCHANGELOG.md. The new comment phrases occur in nodistfile, while a runtime-string control does. The test file andscripts/are not published. No user-visible change ships, so the label stands.The ruling B rewrite:
:556and:691, and every repeat) state the measured behaviour: an in-repo kernel throws on a never-registeredsecurityservice, so the bridges deny via UNUSABLE with a located error. They also say what ABSENT is.The pin: on
ObjectKernelandLiteKernelwith no security service ever registered:getService('security')throws;PERMISSION_DENIED/403naming the object, and nothing is read;Ablation A1 (the catch returns ABSENT) turned 4 red, restored to the HEAD blob.
scripts/analytics-reconcile/boot.ts: it now mounts an explicit open security double. Measured before: refused, 0 rows. After: 4 groups equal to the legacy path. None of the three shipping runners reachesqueryDatasettoday; the dormant runners are noted in the PR.Evidence:
check:dual-build-cjs-loadsis NOT MEASURED (37 packages unbuilt); the dev's targetedrequire()of the package'sdist/index.cjsloads.Filed from this round: docs: the released text and four source comments still say an absent security service admits analytics reads; the maintainer ruled deny as measured (objectstack-ai/objectstack#22235, ruling B) #22279, the ruling's docs half. It covers the 12 released sentences the dev listed (CHANGELOGs and
17-5.mdx, release-owned) and the four source comments outside this PR's fence.Carried, not filed:
Landing to-do:
Lint & Repo GatesandTypeScript Type Checkmust besuccesson the current head, with every other check green or an expected skip.objectstack-fleet commentedon Oct 8, 2026
Landed ·
domain:servicesseat 1 (#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T12:28Z.PR #22276 merged through the merge queue as
58707166. It executes the maintainer's ruling B. Onorigin/main:service-analytics/src/plugin.ts, comments only: the bridges' comment blocks state the measured behaviour. On an in-repo kernel, a never-registeredsecurityservice makes the lookup throw, so the bridges deny via UNUSABLE with a located error. The blocks also say what ABSENT is.ObjectKernelandLiteKernelwith no security service ever registered, the query rejects withPERMISSION_DENIED/403naming the object, and nothing is read.scripts/analytics-reconcile/boot.tsmounts an explicit open security double, so the reconcile run reads again.The PR's
Fixesline closed the cardcompleted. This note also removespm:dispatchedand the assignee.domain:devx).fix(service-analytics): declare the measured deny on a kernel with no…
docs(releases): amend the released "absent security admits analytics …
docs(service-analytics): the source comments state the ruled deny for…
objectstack-fleet commentedon Oct 24, 2026
Dated note ·
domain:serverseat 1 (seat post #6367) ·os-sales·session_01JrXUZYyTV4i588KLZRxSvJ· 2026-10-24T15:57Z · ⛔ not a reopenSince PR #25673 (merge
2e6d446bad, 2026-10-24T15:56:48Z), under the maintainer's ruling A on #25561 (6384696061, 「25561 同意」), the analytics object-level admission bridge no longer answers a missing or unusablesecurityservice as a denial. Where this card's ruling B recorded403 PERMISSION_DENIEDfor a never-registered service on the in-repo kernels, the answer is now503 SERVICE_UNAVAILABLEnaming only the object; the query is still refused before any read, so fail-closed is unchanged. A real denial still answers 403. The context that answers the lookup with nothing (ABSENT) is untouched. The bridge comment blocks this card's PR #22276 wrote are respelled to match in the same PR.Generated by Claude Code