Repository navigation
service-analytics: three fail-closed resolver faults answer 403 PERMISSION_DENIED, the same body as a real denial, where the platform answers a permission-store outage 503 SERVICE_UNAVAILABLE #25561
Description
Filing gate: ① a product defect with named locations (finding class (a): one closing card for the family; per-site rows below). Filed by domain:services seat 1 (seat post #6021) · os-sales · session session_016g7nYP9wdNx1GDa5XDr7Uy. ⛔ Not a claim.
Reader: the domain:server lane, packages/services/service-analytics, on the next PR that touches analytics admission refusals.
Dedupe (open AND closed): "analytics fail-closed resolver fault 403 PERMISSION_DENIED 503 SERVICE_UNAVAILABLE read admission field-level exposure unresolved". No hit (nearest: #25544 / PR #25560, where the consumer side of this was met).
reach: POST /api/v1/analytics/dataset/query and the cube query door. Measured by reading main b531016f6d; no live fault injected.
Source: the domain:server cli seat, while working #25544 (PR #25560).
type: Bug
What is wrong
Three of the analytics door's refusals are operator-side faults: a resolver threw, or could not answer. All three answer with the same wire body as a real denial: { code: 'PERMISSION_DENIED', status: 403, object }. A caller cannot tell "you may not read this" from "the server could not decide". A consumer that needs to tell them apart has to parse the English message.
site (main b531016f6d) |
what happened | answer today |
|---|---|---|
read-admission.ts assertObjectsReadable, catch branch (throw readAdmissionDeniedError(objectName) after "read-admission resolution failed … (fail-closed)") |
the object-level admission provider threw | 403 PERMISSION_DENIED "reading X is not permitted for this user", byte-identical to the genuine denial two lines below |
field-read-admission.ts fieldReadUnresolvedError |
the field-level read permissions could not be resolved | 403 PERMISSION_DENIED |
api-exposure-door.ts exposureUnresolvedError |
the API exposure declaration could not be read | 403 PERMISSION_DENIED |
Fail-closed is right: none of these may admit. The defect is only in what the refusal claims. Each one asserts a denial the server never reached.
The platform already ruled this class
packages/core/src/security/authz-store-unavailable.ts ends the same defect on the authz context. Its maintainer ruling of 2026-08-30 is quoted there verbatim: 「读失败 fail-loud —— 权限库不可达时不再解析为「已认证零能力」,而是响亮拒绝(与真实能力拒绝的 403 可区分),让宕机不再伪装成一次逐字节相同的能力否决。」
The spelling it chose is SERVICE_UNAVAILABLE / 503. That is an existing member of the closed ADR-0112 vocabulary (StandardErrorCode), mapped to 503 by HttpStatusErrorCodeMap. So the fix adds no new code and no new envelope key. The reason field or three new codes suggested at the source are not needed, and a field would be quiet by default, as that file's "Why a THROW, and not a field" section measures.
Expected
- The three fault sites answer
503 SERVICE_UNAVAILABLE, naming the object and nothing else. The cause stays in the server log at the site, as today. - A real denial (
assertObjectsReadable's!admittedbranch, the field-level and exposure verdicts) keeps403 PERMISSION_DENIEDunchanged. /analytics/dataset/queryclassifies a thrown error bycodeplus a 4xxstatus(analytics 的 filter 拒收到不了调用方:service 侧多数拒收没有 ADR-0112 信封,REST 面又用 message 正则嗅探,一律答 500 #5352). A 5xx must still render as that code, not fall through to500 ANALYTICS_QUERY_FAILED. Pin that path.- The no-oracle rule in
readAdmissionDeniedError's docblock still holds. A 503 names no layer, grant or existence fact. It says only that the server could not decide.
Notes for the dispatcher
- This changes the wire for a fault path, so the title takes
!and needs a changeset. Read theClause-②arm against AGENTS.md at claim time. - Grep tests for each of the three sentences and for
PERMISSION_DENIEDbeside a thrown provider: pins that assert today's 403 on a fault leg move with the fix. - After this lands, PR fix(metadata-protocol): a widget probe refused by read admission names the caller's read access, not a dataset edit #25560's consumer can read
codeinstead of the message.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 24, 2026
Claim: PM loop ·
domain:cliseat 1 · p2 · 2026-10-24T01:36ZSession:
session_01CxBKycaiUpA1yGx1XyoGqXAccount:
os-sales(the seat's linked user; always the card's assignee)Branch:
claude/issue-25561-analytics-fault-503Worktree:
objectstack-issue-25561Domain:
domain:server(assigned to this seat by the coordinator, 2026-10-24T01:34Z, after the services seat filed it from this seat's #25544 finding)Seat:
domain:cli#1(seat post #6024; 由Sales账户的项目负责)File surface (read on
origin/maince18e73483; stop on a breach):packages/services/service-analytics/src/read-admission.ts(theassertObjectsReadablecatch branch),field-read-admission.ts(fieldReadUnresolvedError),api-exposure-door.ts(exposureUnresolvedError), their tests underservice-analytics/src/__tests__/, the/analytics/dataset/queryrelay pin (read inpackages/rest, a test only if the 5xx path is unpinned),packages/metadata-protocol/src/build-probes.ts+ its widget-refusal test (the PR #25560 consumer: aSERVICE_UNAVAILABLErefusal must not fall to the dataset fix), one changeset.Container & model:
S,mode:subagent,model: default tierClause-②: no
Basis for the Clause-② line: three fault-path refusals keep refusing and change only their code and status (403
PERMISSION_DENIED→ 503SERVICE_UNAVAILABLE, an existing ADR-0112 member); no request shape that was served is refused. A wire change, so the title takes!and the changeset a BREAKING banner; the arm is re-read against AGENTS.md on the measured diff.Responsibility: n/a — filed by
domain:servicesseat 1 from #25544's dev report.Thread-read: none
Serial constraints cleared: none of the 12 open PRs touches
service-analyticsorbuild-probes.ts(file lists read in this act).Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report
{ "issue": 25561, "status": "done", "branch": "claude/issue-25561-analytics-fault-503", "pr": "https://github.com/objectstack-ai/objectstack/pull/25595", "session": "session_01CxBKycaiUpA1yGx1XyoGqX", "premise_still_valid": true, "summary": "The three operator-side FAULT refusals in service-analytics now answer 503 SERVICE_UNAVAILABLE instead of the denial's 403 PERMISSION_DENIED: the read-admission catch branch (provider threw), the field-level reader throw, and the exposure-declaration lookup throw. All three go through one shared non-exported constructor, readAdmissionUnresolvedError in read-admission.ts. It follows the core authz-store-unavailable precedent ({code, status, object}), names only the object, and leaves the cause in the site log. Fail-closed is unchanged, and real denials (the !admitted branch, the field verdicts, exposure 404/405) are byte-unchanged. The dataset door already relays a declared 5xx through declaredServerFaultAnswer, so packages/rest has no source change; a new rest seam test pins producer plus route. build-probes.ts gives a SERVICE_UNAVAILABLE throw its own server-side-fault fix. Changeset: patch on @objectstack/service-analytics and @objectstack/metadata-protocol, BREAKING banner, Clause-② no, Metadata-protocol none, ADR-0087 not-required.", "tests": "Tree sha 18b8b6fa5d (service-analytics and metadata-protocol files byte-identical to their state at each full run). service-analytics vitest (whole pkg): Test Files 185 passed (185), Tests 4517 passed | 262 skipped. The first run with the fix had 10 failed / 4503 passed: exactly the 10 fault-leg pins asserting 403, now moved to 503 in read-admission-gate, field-read-admission-gate, field-query-admission-gate, api-exposure-door and unexposed-definition-registration. metadata-protocol (whole pkg): Test Files 263 passed | 3 skipped, Tests 21472 passed | 19 skipped. build-probes-widget-refusal-fix.test.ts is 8/8 on 18b8b6fa5d (2 new SERVICE_UNAVAILABLE cases). rest src/analytics*: Test Files 23 passed, Tests 298 passed | 22 skipped. New rest analytics-read-gate-fault-envelope.test.ts is 7/7 on 18b8b6fa5d: 3 faults are 503, their twins are 403/403/404, plus a 200 control. runtime http-dispatcher + packages-publish-drafts-probe-caller-context: 251 passed. typecheck exit 0 for service-analytics, metadata-protocol and rest. Ablation (fix committed first): ablation-replace.mjs reverted the read-admission catch to throw readAdmissionDeniedError plus a marker (anchor 1 to 0, blob 323b9767 to 83a362db); service-analytics rebuilt; ablation-dist-preflight showed the marker present in dist/index.js and dist/index.cjs (exit 0). read-admission-gate.test.ts: 6 failed | 19 passed (the fault case on 3 doors x 2 strategies). rest seam: 1 failed | 6 passed (the object-level fault row). Restore: blob == HEAD 323b9767, git diff HEAD empty; after the rebuild, preflight --absent reported the marker absent from 6 built files, the tree clean, and the rest seam 7/7.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack derived 72 commands. All 72 were run locally; `--ran` reconciliation: 'Run reconciliation - 72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero: all 72 exit-coded, none 3). Final exits are all 0. The first pass had red: check-rest-log-spy-declared exit 1 (the new rest test spied console.error without OS_REST_LOG; fixed in this PR, re-run exit 0). check:dts-closure exit 1, check:dual-build-cjs-loads exit 3 PREREQUISITE and check:type-check-debt exit 3 were tree state from a concurrent closure build rewriting dist/; re-run on the settled tree, all exit 0. pnpm lint via os-verify-lock: 'VERDICT command-exit 0'. CI on PR #25595 head 18b8b6fa5d at report time: 38 check-runs, 6 success, 7 skipped, 25 queued/in_progress (Build Core queued, Governed Surface Queue Guard in_progress) - in_progress.", "line_budget": "Size S dispatched; measured 472 changed lines (+404 / -68) in 12 files per dispatch-gates. Source: read-admission.ts, field-read-admission.ts, api-exposure-door.ts and build-probes.ts. Tests: 5 analytics test files, 1 new rest test file, 1 metadata-protocol test. Plus 1 changeset. Most of the growth is docblocks and the new rest seam test.", "files_changed": [ ".changeset/25561-analytics-read-gate-fault-503.md", "packages/metadata-protocol/src/build-probes-widget-refusal-fix.test.ts", "packages/metadata-protocol/src/build-probes.ts", "packages/rest/src/analytics-read-gate-fault-envelope.test.ts", "packages/services/service-analytics/src/__tests__/api-exposure-door.test.ts", "packages/services/service-analytics/src/__tests__/field-query-admission-gate.test.ts", "packages/services/service-analytics/src/__tests__/field-read-admission-gate.test.ts", "packages/services/service-analytics/src/__tests__/read-admission-gate.test.ts", "packages/services/service-analytics/src/__tests__/unexposed-definition-registration.test.ts", "packages/services/service-analytics/src/api-exposure-door.ts", "packages/services/service-analytics/src/field-read-admission.ts", "packages/services/service-analytics/src/read-admission.ts" ], "deviations": [ "PR body footer: ends with the AGENTS.md / role-file session-URL footer form (rule line, then a single _Generated by [Claude Code](https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX)_ line), not the dispatch's robot-emoji 'Generated with' line. The role file wins on conflict, and two footers are what AGENTS.md warns against.", "PR body line order: the dispatch's two attribution lines come first, so 'Fixes #25561' is on line 4, not line 1 as the role file says. The attribution line is an HTML-comment shape, which the role file bans in GitHub text. It was kept because the repo's own recognisers (record-recognisers.mjs, check-governed-queue-guard.mjs) read exactly that line; the relay read-back reports the body stored byte-identical, 8848 of 8848 bytes.", "Added one packages/rest TEST (no rest source change). The route relay was already pinned for a hand-built declared 503, but the producer-plus-route seam with a real AnalyticsService was not; the dispatch asks to pin the 503 through the real route.", "Removed the two non-exported per-file constructors (fieldReadUnresolvedError, exposureUnresolvedError) in favour of one shared readAdmissionUnresolvedError, so the 503 names no layer (the dispatch's no-oracle rule). No package export changed.", "Cube query door (/api/v1/analytics/query, runtime dispatcher-plugin errorResponseBase): read, not pinned; it relays declared status and code generically. A runtime test is outside the file surface." ], "dogfood_battery": "18b8b6fa5d · os-verify-lock 'VERDICT command-exit 0' (pnpm --filter @objectstack/dogfood test after the @objectstack/dogfood^... closure build, no OS_TEST_SHARD, no path) · Test Files 291 passed | 3 skipped (294) · Tests 2505 passed | 18 skipped (2523) · no failures", "mcp_calls": "0 - no MCP GitHub calls", "api_writes": "2 relay strokes via scripts/pm/ write tools: (1) fleet-write pr_create: POST /repos/objectstack-ai/objectstack/dispatches, executed by the relay as POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/25595/assignees [os-sales], run 40057862663 success; (2) post-stamped os-dev-report comment: POST /repos/objectstack-ai/objectstack/issues/25561/comments. git push (several pushes to the branch) is not a REST write.", "open_questions": [ { "question": "Should the object-level plugin bridge's UNUSABLE branch (plugin.ts admitObjectRead auto-bridge: the security service lookup threw, or the service exposes neither canReadObject nor explain, which also covers a deployment with no security service on the in-repo kernels) also answer 503 instead of returning false and getting 403?", "options": [ "A: throw from the UNUSABLE branch so assertObjectsReadable answers 503. This moves the pinned admission-bridge-resolution.test.ts 403 cases and touches the #22235 ruling's refusal (code unspelled there).", "B: keep 403. The #22235 ruling frames a deployment with no security service as a declared refusal, not an outage." ], "recommendation": "A for a registered-but-broken service (same class as this card); ask the maintainer whether the never-registered case under the #22235 ruling also moves, since that ruling is the authority there." } ], "out_of_scope_findings": [ "class: a · reach: named producer: service-analytics plugin.ts admitObjectRead auto-bridge UNUSABLE branch returns false, so POST /api/v1/analytics/dataset/query answers 403 PERMISSION_DENIED for a security service that could not be resolved, a fault that reads as a denial. Pinned at 403 on main by admission-bridge-resolution.test.ts ('DENIES when resolving the security service THROWS', 'DENIES when the security service exposes neither canReadObject nor explain', and the dataset-query case). The same family as #25561, outside its file surface; not fixed here. dedupe words: analytics admitObjectRead bridge unusable security service 403 PERMISSION_DENIED 503 SERVICE_UNAVAILABLE fault" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
ACCEPT → PR #25595 comment 6382488582 ·
domain:cliseat 1 · sessionsession_01CxBKycaiUpA1yGx1XyoGqX· 2026-10-24T09:22ZGenerated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Landed ·
domain:cliseat 1 · sessionsession_01CxBKycaiUpA1yGx1XyoGqX· 2026-10-24T10:14Zmerged: true, merge commitf1d6be029e, merged 2026-10-24T10:14:21Z through the merge queueorigin/mainf1d6be029eis an ancestor oforigin/main, single parent (squash)gh-readonly-queue/main/*head for PR 25595 remainsState: card closed
completedby the PR'sFixes #25561.pm:dispatchedstripped in this act. The plugin bridge's UNUSABLE branch (still 403, the #22235 ruled answer) is outside this card and stays with the maintainer's ruling; nothing about it is decided here.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Ruling: needs_decision fork (PR #25595 dev report 6379262084,
open_questions[0]) · letter A · maintainer 「25561 同意」 · recorded bydomain:cliseat 1 (seat post #6024) · sessionsession_01CxBKycaiUpA1yGx1XyoGqX· 2026-10-24T13:18ZThe maintainer (Sales) ruled in the project's ruling thread, on the decision card that recommended A. This is a maintainer ruling, not an auto-adjudication.
The question
packages/services/service-analytics/src/plugin.ts, the auto-bridgedadmitObjectRead: its UNUSABLE branch (resolving thesecurityservice threw, or the service exposes neithercanReadObjectnorexplain) answersreturn false, so the caller gets403 PERMISSION_DENIED, the same bytes as a real denial. On the in-repo kernels (ObjectKernel,LiteKernel) asecurityservice that was never registered lands in the same branch, because their synchronousgetServicethrows on a miss. PR #25595 moved the three other analytics fault paths to503 SERVICE_UNAVAILABLE.503 SERVICE_UNAVAILABLEfor both a registered-but-broken service and a never-registered one.packages/coreto brand the synchronousgetServicemiss, or a message match.Why A
SERVICE_UNAVAILABLEas a service that "exists but is temporarily down or not loaded" (packages/spec/src/api/dispatcher.zod.ts). A service that was never registered is "not loaded", so C's split is not needed.Execution (decided once)
plugin.ts: the UNUSABLE branch of the object-level bridge throws a declared503 SERVICE_UNAVAILABLE(readAdmissionUnresolvedError) instead ofreturn false. The query is still refused before any read.admission-bridge-resolution.test.ts: the three UNUSABLE 403 pins become 503. The real denials (canReadObject/explainanswering false) stay 403.!title,@objectstack/service-analyticspatch changeset with a BREAKING banner (a status on the wire moves), as PR fix(service-analytics)!: an analytics read gate that could not decide answers 503 SERVICE_UNAVAILABLE, not the denial's 403 #25595.Executor:
domain:cliseat 1, one PR, S.os-decision-facets
① 长远:#25595 已立「故障不冒充拒绝」,A 让分析门四处一致;spec 对 SERVICE_UNAVAILABLE 的定义含「not loaded」,无需 C 的新区分。
② 拉动:坏掉场景即 #25561 的运维故障类;从未注册的部署无实测;消费端已按 code 分流。
③ 防错:B 让按 code 分流的消费端把宕机当无权;C 若按消息判别是反模式;A 与 core 先例同形。
④ 不扩散:A 复用现有码,不加键、不加门;B 零;C 动 core。
Prior rulings read: admitObjectRead,SERVICE_UNAVAILABLE,security-service,PERMISSION_DENIED,fault-as-refusal,admission-bridge,degrade,fail-closed → 23 hits; ADR-0056 D2; none on the code; thread: #22235 6056384408/6056824332
Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Release: the ruling-A follow-up (
6384696061) · sessionsession_01CxBKycaiUpA1yGx1XyoGqX(domain:cliseat 1, seat post #6024) · because the maintainer voided that seat · todomain:serverseat 1 (seat post #6367), same accountdomain:serverseat 1 (verbatim: 「开」) to carry the in-hand server work.f1d6be029e); this card stays closed.Claim: ruling-A follow-up (one PR,
Refs #25561, notFixes) ·domain:serverseat 1 (seat post #6367, project run by the Sales account, 由Sales账户的项目负责) · p2 · 2026-10-24T13:34ZSession:
session_01JrXUZYyTV4i588KLZRxSvJAccount:
os-sales(the seat's linked user; the card's assignee)Branch:
claude/issue-25561-bridge-503Worktree:
objectstack-issue-25561Domain:
domain:serverSeat:
domain:server#1File surface:
packages/services/service-analytics/src/plugin.ts(the auto-bridgedadmitObjectReadUNUSABLE branch throwsSERVICE_UNAVAILABLE/503 for a registered-but-broken and a never-registeredsecurityservice; the untrue "A security service is wired…" log line corrected),src/read-admission.tsif the shared constructor needs it, the service-analytics tests that pin that branch's 403, anypackages/restseam pin of the same, and a patch changeset on@objectstack/service-analytics. After it lands, a dated note on #22235.Container & model:
M,mode:subagent(general-purpose subagent loading.claude/agents/os-dev.mdas its role file),model: default tier(dispatch-gates.mjs --tier: "no path-derived mandate"),effort: highClause-②: no
Metadata-protocol: none
Responsibility: service-analytics' own bridge produces the 403-for-a-fault; the platform answers a permission-store outage 503 (
authz-store-unavailable), which PR #25595 already followed for the three other analytics fault paths; reached by any deployment whosesecurityservice is missing or broken.Thread-read: 6384696061
Serial constraints cleared: no open PR touches
packages/services/service-analytics/src/plugin.ts(read this act).Resume-from: none (no pushed branch)
Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim amendment (
Premise:line, read before dispatch) ·domain:serverseat 1 (seat post #6367) · sessionsession_01JrXUZYyTV4i588KLZRxSvJ· 2026-10-24T13:43ZPremise: holds, read by a read-only subagent on
origin/mainbaae6ed1.packages/services/service-analytics/src/plugin.ts's auto-bridgedadmitObjectReadstill answers its UNUSABLE branch withreturn false(so the caller'sreadAdmissionDeniedError, 403), after anerrorlog whose text claims a security service is wired, which is untrue for the never-registered case.readAdmissionUnresolvedError(503SERVICE_UNAVAILABLE, from PR #25595) exists insrc/read-admission.tsto reuse. The 403 pins of that branch are insrc/__tests__/admission-bridge-resolution.test.ts(three cases) andsrc/__tests__/admission-absence-report.test.ts(one case); none underpackages/rest. No open PR touchesservice-analytics.Dispatch: one in-seat subagent dev (general-purpose loading
.claude/agents/os-dev.md); PR saysRefs #25561.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report
{
"issue": 25561,
"status": "done",
"branch": "claude/issue-25561-bridge-503",
"pr": "#25673",
"session": "session_01JrXUZYyTV4i588KLZRxSvJ (mode:subagent, the PM session's id; run as a general-purpose agent loading .claude/agents/os-dev.md as its role file)",
"premise_still_valid": true,
"summary": "Ruling A (6384696061) is implemented as one draft PR, #25673, which says Refs #25561. In service-analytics plugin.ts, the auto-bridged admitObjectRead's UNUSABLE branch now throws readAdmissionUnresolvedError(object) instead of return false, so the gate answers 503 SERVICE_UNAVAILABLE naming the object. That covers a broken security service, one that exposes neither canReadObject nor explain, and one never registered on ObjectKernel or LiteKernel. Fail-closed is unchanged, and real false verdicts stay 403, byte-unchanged. The untrue error log sentence (A security service is wired on this deployment) is corrected in the object-level bridge, and also in the row-scope bridge in the same file, which printed the same false sentence for the same two cases; that bridge's answer does not change. Doc comments that called the never-registered answer a denial are respelled (plugin.ts, read-admission.ts, analytics-service.ts, scripts/analytics-reconcile/boot.ts). No docs/adr edit was needed. The changeset is a service-analytics patch with a BREAKING banner. The voided session's WIP patch was applied as-is and then finished. Its comment lines were rewrapped and its row-scope log fix kept. A fifth pin the premise read did not list was found by the whole-package run and moved: read-scope-bridge-resolution.test.ts, both auto-bridges live, throwing resolver. Deviation from the dispatch text: commits carry the model-free trailer pair (Claude-Session plus Co-authored-by: Claude), not the model-named Co-Authored-By line the dispatch asked for, because AGENTS.md and os-dev.md forbid a model identifier in that pair and os-dev.md wins over a dispatch on conflict.",
"tests": "Head 032ae18 (= 2489588 plus a merge of origin/main #25658, which touches only .claude/skills/pm-dispatch/references and scripts/pm/check-half-states.mjs; the service-analytics tree is byte-identical). pnpm --filter @objectstack/service-analytics test at 2489588: VERDICT command-exit 0, Test Files 185 passed (185), Tests 4517 passed, 262 skipped (4779). The first run, before the row-scope pin moved, was red on exactly that pin (1 failed, 184 passed). typecheck: VERDICT command-exit 0. eslint --no-inline-config on the 7 changed source files: exit 0. Dogfood battery at 032ae18 (owed by the bang title): closure build command-exit 0; pnpm --filter @objectstack/dogfood test VERDICT command-exit 0, Test Files 293 passed, 3 skipped (296), Tests 2509 passed, 18 skipped (2527).",
"gates": "dispatch-gates --commands at 032ae18 derived 82 commands; all 82 run, each recorded as cmd :: exit N, all exit 0. --ran verdict: 82 derived famil(ies) accounted for, 82 run, 0 NOT-MEASURED (a DERIVED zero; all 82 recorded an exit code and none of them is 3). Earlier run at 2489588: check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, no dist) and check-plugin-teardown-shape --self-test exit 3 (pinned fixture commit unreachable on the shallow clone). Both exit 0 at the final head, after the dogfood closure build and a single-commit fetch of the fixture. CI on PR 25673 at report time: Auto Label, filter, Check PR Size, Check Documentation Links, the claim and closing-keyword guards success; Test Core 1-6, Build Core, Lint lanes, Type Check lanes, Check Changeset, Governed Surface Queue Guard, Dogfood Regression Gate in_progress.",
"ablation": "On the committed fix, via scripts/ablation-replace.mjs: anchor throw readAdmissionUnresolvedError(object); replaced by return false; (anchor x1 to x0, blob 83715b24c049 to 0e7c50c8c5b1). The subject resolves from source (tests import ../plugin.js), so there is no dist leg. Red: Test Files 3 failed (3), Tests 6 failed, 12 passed (18). Every changed negative pin failed with Expected code SERVICE_UNAVAILABLE / status 503, Received code PERMISSION_DENIED / status 403. The failing pins were: the throw case, the neither-method case, the ObjectKernel and LiteKernel never-registered cases, the absence-report real-kernel case, and the row-scope both-bridges-live case. The 12 that stayed green include every real-denial 403 pin. Restore: blob after restore 83715b24c049 == HEAD, git diff HEAD empty. Green rerun: Test Files 3 passed (3), Tests 18 passed (18).",
"dogfood_battery": "032ae18225 · os-verify-lock VERDICT command-exit 0 · Test Files 293 passed, 3 skipped (296) · Tests 2509 passed, 18 skipped (2527) · no failures",
"relay_writes": "3, all through scripts/pm: fleet-write pr_create (draft, assignees os-sales; read back as 7790 bytes sent, 7790 stored, identical, assignees os-sales), fleet-write pr_request_reviewers os-sales on 25673 (run success; read back as requested_reviewers os-sales), and post-stamped os-dev-report comment on 25561",
"api_writes": "0 outside the relay and post-stamped; git push to claude/issue-25561-bridge-503 (empty-branch probe plus 3 pushes) is not a REST write",
"mcp_calls": "0. No MCP GitHub tool was called, read or write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted in PR Acceptance notes, not filed · the bridge's refusal now logs two error lines per query (the bridge's own line carrying the cause, plus assertObjectsReadable's catch line), the same catch line every throwing provider already gets"
]
}
objectstack-fleet commentedon Oct 24, 2026
ACCEPT → PR #25673 comment 6385890367 ·
domain:serverseat 1 (seat post #6367) · sessionsession_01JrXUZYyTV4i588KLZRxSvJGenerated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Landed (ruling-A follow-up
6384696061) ·domain:serverseat 1 (seat post #6367) · sessionsession_01JrXUZYyTV4i588KLZRxSvJ· 2026-10-24T15:57Zmerged: true, merge commit2e6d446bad, merged 2026-10-24T15:56:48Z through the merge queueorigin/main2e6d446badis an ancestor oforigin/main, single parent (squash)gh-readonly-queue/main/*head for PR 25673 remainsState: the card stays closed (PR #25595 closed it; this PR was
Refs #25561). The follow-up is done and the seat's hold on it ends with this note. A dated note goes on #22235, whose ruled answer this changes.Generated by Claude Code