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
把 mapDataError 的直通改为与 resolveErrorResponse 同款:4xx 截断措辞、5xx 保状态丢措辞留 code。注意 rest.test.ts 的 does NOT pass through an explicit 5xx status 与 rest-4xx-message-truncation.test.ts 的 5xx never enters this branch at all 两条 pin 是按现状写的,改动需一并重写(#5489 已把这两条从纯否定断言升级为钉住实际落点,便于下一手对照)。
正文点名的两条 pin 原样健在:rest.test.ts:2328(“does NOT pass through an explicit 5xx status”)、rest-4xx-message-truncation.test.ts:145(“5xx never enters this branch at all”)⇒ 改动仍需一并重写这两条;
Findings triage round (#4949 discipline): HOLD maintained (finding kept), domain stays domain:cli. Stale-premise check @ origin/main80f7dc6. Two real updates below — the cost model of the fix changed, and clause 2 was re-measured against a file that moved.
1. The divergence itself: unchanged
packages/rest/src/rest-server.ts moved since the last grading — 64cd0108 (#5811 / PR #6122, 2026-08-07T02:54Z, "/analytics/query no longer echoes RLS policy field names") — so this was re-read rather than assumed:
mapDataError passthrough is still 4xx-only: error.status >= 400 && error.status < 500 (:728);
resolveErrorResponse is still 400–599 (:1025);
mapDataError still has 11 direct call sites in the file (the CRUD data routes that bypass resolveErrorResponse).
⇒ the two doors still give two answers to "the producer declared a status". Body premise intact.
2. NEW — there is now a third answer, and it is a named, tested predicate
PR #6122 promoted the withhold criterion into @objectstack/types as declaresServerFault(err) (status >= 500+ non-empty code), with two consumers reading it: rest-server.ts:7001 (the analytics dataset route) and runtime/src/dispatcher-plugin.ts:500.
That matters to this issue in one specific way: the fix this body proposes ("make mapDataError's passthrough match resolveErrorResponse: 4xx truncate the wording, 5xx keep the status, drop the wording, keep the code") now has an existing spelling — the same predicate, already pinned by packages/types/src/error-leak.test.ts. The change is no longer "invent a criterion and defend it"; it is "read the criterion the repo already agreed on at two other boundaries". The two pins named in the body (rest.test.ts:2328, rest-4xx-message-truncation.test.ts:145) still have to be rewritten, so the cost is not zero — but it dropped.
3. Clause 2 re-measured (a live 5xx producer) — still NOT satisfied, and now for a sharper reason
The scan found something the 08-06 round reported as zero: a non-test producer that declares a 5xx with a code now exists —
packages/services/service-analytics/src/read-scope-sql.ts:166-172 — readScopeCompileError() sets code = READ_SCOPE_COMPILE_FAILED, status = 500.
But it does not reach this issue's failure mode. It exits through the analytics faces — rest-server.ts:7001 (a hand-built res.status(500).json(...)) and dispatcher-plugin.ts:500 — not through any of mapDataError's 11 direct call sites on the CRUD data routes. Also re-checked, unchanged from the 08-06 reading: metadata-protocol/src/protocol.ts:1162 (503), :6224 (501), :10546 (500) are meta-route producers ⇒ resolveErrorResponse's door. Positive control (per Operational notes 6, a zero reading needs one): the same scan surface does return 4xx producers, e.g. service-analytics/src/analytics-service.ts:1622 (status: 404 / CUBE_NOT_FOUND) — the scanner is not broken.
⇒ still no producer that declares 5xx and can reach a CRUD data route. Today's users still cannot hit this.
4. Verdict: HOLD — restart conditions, one refined
(unchanged) A live producer declaring 5xx that reaches one of mapDataError's direct call sites on the CRUD data routes ⇒ that is live status-code degradation, promote to pm:queue immediately.
(refined) Or a maintainer ruling that mapDataError and resolveErrorResponse converge — a public-contract trade-off, so a decision card. What changed: the implementation of that ruling is now declaresServerFault, not a fresh predicate; whoever writes the card should say so, because it is most of the argument for the cheap side.
修法不变,仍是本单给出的那条:把 mapDataError 的直通改成与 resolveErrorResponse 同款(4xx 截断措辞、5xx 保状态丢措辞留 code)。本单已经点名的两条 pin(rest.test.ts 的 does NOT pass through an explicit 5xx status、rest-4xx-message-truncation.test.ts 的 5xx never enters this branch at all)需一并重写。
Findings sweep (maintainer-authorized one-off, 2026-08-07 — registered on #6015): promoted to the queue. Restore-invariant: the repo already ruled (#5437 / PR #5464) that declared 5xx statuses pass through resolveErrorResponse; mapDataError's 4xx-only branch breaks that parity on every CRUD data route. No live producer yet is why it sat as a finding — but the two doors disagreeing on the same declaration is the defect, not the traffic. finding → pm:queue.
Claim: PM loop round 5 (cli lane) — queued by the maintainer-authorized 2026-08-07 findings sweep (comment 5223600933); the same-file serial constraint cleared today (#6349→PR #7372 merged 2443bb4c4, #7035→PR #7293 merged earlier — no open PR touches rest-server.ts at dispatch time).
Session: session_0158ZQo7LiHSxGWpYKuPq1wu
Branch: claude/issue-5582-mapdataerror-5xx-passthrough
Worktree: objectstack-issue-5582
Domain: domain:cli (as labeled)
File surface: packages/rest/src/rest-server.ts (mapDataError passthrough branch), the two pins the body names (packages/rest/src/rest.test.ts ~:2328 "does NOT pass through an explicit 5xx status", packages/rest/src/rest-4xx-message-truncation.test.ts ~:145 "5xx never enters this branch at all"), new coverage, changeset. Criterion spelling: the existing declaresServerFault predicate from @objectstack/types (per the 08-07 triage reading — read, not re-invented). ⛔ No driver-side edits: the drivers already declare truthfully (ADR-0112, #5907); the flattening is consumer-side.
Container & model: M card, mode:subagent, model: opus
Serial constraints cleared: in-flight #6878/PR #7396 is plugin-hono-server + qa/http-conformance — file-disjoint; queue entries pr-7304/7306/7379/7187/7201 none touch packages/rest.
Live-producer note for the implementer: since #5907, driver-sql/driver-turso throw status=501 code=NOT_IMPLEMENTED for count_distinct/array_agg/string_agg and that reaches mapDataError's direct call sites today — the fix's acceptance case should show 501/NOT_IMPLEMENTED surviving instead of degrading to 500/INTERNAL_ERROR.
ACCEPT — PR #7402 reviewed and being driven to landing. All 26 check runs green (ESLint and TypeScript Type Check conclusions verified directly, not from the report).
What shipped: mapDataError's explicit-status passthrough widened to 400–599 — the same door resolveErrorResponse opens — with #5437's ruled 5xx disposition: keep the declared status, keep the machine-readable code, drop the prose unconditionally. The 4xx arm (truncation, object retained, #5423) is untouched. The live #5907 producer now surfaces 501 NOT_IMPLEMENTED on the CRUD data routes instead of degrading to 500 INTERNAL_ERROR; both pins the body named were rewritten to the new contract, and a new 28-case suite covers the band, half-declarations, sanitization, and three in-process route walks.
The dev corrected the dispatch, and the correction stands — acknowledged in the open. The dispatch said to gate the 5xx passthrough on declaresServerFault; the dev gated the status on the band and used the predicate for the code half only, with measured grounds: declaresServerFault requires a non-empty code, so predicate-gating the status would have left a no-code declared 502 still re-derived from message text — the exact #5437 shape — and both original pins are built on a no-code 502. resolveErrorResponse answers that shape with status preserved and no code invented ("the PRODUCER names the condition"), and mapDataError now matches it exactly. That is full parity, which was the ruled intent.
Ordering decision (PM's call under the escalation bar, veto window open): the dev's open question — should a declared 5xx outrank the text classifiers below — is resolved as A, as shipped. Grounds: resolveErrorResponse's door likewise runs first, #5437 ruled against a declared status being overwritten by a keyword heuristic, and option B would re-create precisely that defect for the inputs the heuristics recognise. No live producer declares a 5xx with classifier-recognisable text (scanned), and the new §4 case pins the live half: undeclared driver conflicts still answer 409 UNIQUE_VIOLATION across all three dialects. This is recorded here rather than escalated because existing rulings determine the answer; the maintainer can veto by commenting.
Follow-up filed as #7407 (driver docblock paragraph falsified by this PR — comment-only, finding, deliberately unqueued).
Activity
os-zhuang commentedon Aug 5, 2026
发现分诊轮判级:持有(留
finding,补domain:cli域标)。mapDataError直调点的组合今日不存在(ERR_DATASOURCE_UNAVAILABLE有自己的 503 分支,meta 路由走resolveErrorResponse那道门),今天没有用户撞上;且同领地/meta/:type上一个未分类的服务端故障被报成 HTTP 400 —— handleRouteError 的兜底把 outage 说成客户端错误 #5489 在飞(assignee 活跃),正文点名的两条 pin 测试就在其文件面内,现在入队必然同文件撞车。/meta/:type上一个未分类的服务端故障被报成 HTTP 400 —— handleRouteError 的兜底把 outage 说成客户端错误 #5489 落地后复核直通分支是否已顺带对齐;未对齐且出现活体 5xx 数据路由生产者(或维护者裁mapDataError与resolveErrorResponse合流)时晋级。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
claude commentedon Aug 6, 2026
发现分诊轮(#4949 纪律):维持持有(留
finding),域维持domain:cli。上一轮重启条件的第一个子句本轮已结算,记录如下,后续轮次不必再测这一半。重启条件复核(条件为合取,只满足了一半)
上一轮(08-05 20:00Z)写的是:「#5489 落地后复核直通分支是否已顺带对齐;未对齐且出现活体 5xx 数据路由生产者(或维护者裁
mapDataError与resolveErrorResponse合流)时晋级」。/meta/:type上一个未分类的服务端故障被报成 HTTP 400 —— handleRouteError 的兜底把 outage 说成客户端错误 #5489 已 closed/completed ⇒ 复核触发;origin/main@9e3709a实测)。mapDataError的直通分支仍是error.status >= 400 && error.status < 500(packages/rest/src/rest-server.ts:723),而resolveErrorResponse一侧的 400–599 判据仍在(:1020,status >= 400 && status < 600)—— 两道门对「生产者声明了 status」依旧给两个答案;rest.test.ts:2328(“does NOT pass through an explicit 5xx status”)、rest-4xx-message-truncation.test.ts:145(“5xx never enters this branch at all”)⇒ 改动仍需一并重写这两条;packages/objectql/src、packages/drivers/*/src、packages/metadata*/src、packages/services/*/src下status = 50x/status: 50x(非测试)零命中,阳性对照同一扫描面的status: 4xx有命中(如objectql/src/overlay-precedence.test.ts:209的 403 一族),证伪「扫描器坏了」。因此:持有,重启条件收窄为单条
#5489 是否已对齐这一问已答完(没有),⛔ 后续轮次不要重复测。唯一剩下的晋级触发点:出现一个声明 5xx 且能走到 CRUD 数据路由mapDataError直调点的活体生产者(那时它是活体状态码降级,直接入队),或维护者裁定mapDataError与resolveErrorResponse合流(那是两道门收敛的契约取舍,走决策卡)。在此之前它仍是「今天没有用户撞得到的静默降级」。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
claude commentedon Aug 7, 2026
Findings triage round (#4949 discipline): HOLD maintained (
findingkept), domain staysdomain:cli. Stale-premise check @origin/main80f7dc6. Two real updates below — the cost model of the fix changed, and clause 2 was re-measured against a file that moved.1. The divergence itself: unchanged
packages/rest/src/rest-server.tsmoved since the last grading —64cd0108(#5811 / PR #6122, 2026-08-07T02:54Z, "/analytics/query no longer echoes RLS policy field names") — so this was re-read rather than assumed:mapDataErrorpassthrough is still 4xx-only:error.status >= 400 && error.status < 500(:728);resolveErrorResponseis still 400–599 (:1025);mapDataErrorstill has 11 direct call sites in the file (the CRUD data routes that bypassresolveErrorResponse).⇒ the two doors still give two answers to "the producer declared a status". Body premise intact.
2. NEW — there is now a third answer, and it is a named, tested predicate
PR #6122 promoted the withhold criterion into
@objectstack/typesasdeclaresServerFault(err)(status >= 500+ non-emptycode), with two consumers reading it:rest-server.ts:7001(the analytics dataset route) andruntime/src/dispatcher-plugin.ts:500.That matters to this issue in one specific way: the fix this body proposes ("make
mapDataError's passthrough matchresolveErrorResponse: 4xx truncate the wording, 5xx keep the status, drop the wording, keep thecode") now has an existing spelling — the same predicate, already pinned bypackages/types/src/error-leak.test.ts. The change is no longer "invent a criterion and defend it"; it is "read the criterion the repo already agreed on at two other boundaries". The two pins named in the body (rest.test.ts:2328,rest-4xx-message-truncation.test.ts:145) still have to be rewritten, so the cost is not zero — but it dropped.3. Clause 2 re-measured (a live 5xx producer) — still NOT satisfied, and now for a sharper reason
The scan found something the 08-06 round reported as zero: a non-test producer that declares a 5xx with a code now exists —
packages/services/service-analytics/src/read-scope-sql.ts:166-172—readScopeCompileError()setscode = READ_SCOPE_COMPILE_FAILED,status = 500.But it does not reach this issue's failure mode. It exits through the analytics faces —
rest-server.ts:7001(a hand-builtres.status(500).json(...)) anddispatcher-plugin.ts:500— not through any ofmapDataError's 11 direct call sites on the CRUD data routes. Also re-checked, unchanged from the 08-06 reading:metadata-protocol/src/protocol.ts:1162(503),:6224(501),:10546(500) are meta-route producers ⇒resolveErrorResponse's door. Positive control (per Operational notes 6, a zero reading needs one): the same scan surface does return 4xx producers, e.g.service-analytics/src/analytics-service.ts:1622(status: 404/CUBE_NOT_FOUND) — the scanner is not broken.⇒ still no producer that declares 5xx and can reach a CRUD data route. Today's users still cannot hit this.
4. Verdict: HOLD — restart conditions, one refined
mapDataError's direct call sites on the CRUD data routes ⇒ that is live status-code degradation, promote topm:queueimmediately.mapDataErrorandresolveErrorResponseconverge — a public-contract trade-off, so a decision card. What changed: the implementation of that ruling is nowdeclaresServerFault, not a fresh predicate; whoever writes the card should say so, because it is most of the argument for the cheap side./meta/:type上一个未分类的服务端故障被报成 HTTP 400 —— handleRouteError 的兜底把 outage 说成客户端错误 #5489 align the passthrough?" — answered on 08-06: no, and fix(runtime,types)!:/analytics/query不再回显 RLS 策略字段名 —— 声明式 server-fault withhold 提升为两个边界共享 (#5811) #6122 did not align it either. Both readings are now on the record; re-running them buys nothing.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 7, 2026
这条不再是观察类了:#5907 的 PR 造出了第一个活体生产者。
本单正文说「仓内声明 5xx 且能走到数据路由直调点的生产者,我没有测到活体」。#5907(聚合函数拒收信封,两驱动同改)按车道裁决给能力缺口类条件取了 ADR-0112 标准目录的
NOT_IMPLEMENTED/ 501:packages/drivers/driver-sql/src/sql-driver.ts—mapAggregateFunc对count_distinct/array_agg/string_agg(spec 已声明、SQL 后端编不出)packages/drivers/driver-turso/src/remote-transport.ts— 同一条件的远程半边这三个函数过得了协议门(它们在
AggregationFunction枚举里,metadata-protocol的#4254形状校验放行),所以它们是从驱动一路抛到数据路由的 —— 正是本单描述的直调点。走mapDataError的结果:即状态码与
code一起丢失:声明的NOT_IMPLEMENTED被换成INTERNAL_ERROR,调用方读到的是「服务器炸了」,而不是「这个后端没实现这个已声明的能力」。比本单正文测到的 502→500 更重一档 —— 那次只丢状态码保真度,这次连 ADR-0112 的语义码本身都被覆写。对分诊的两点补充:
mapDataError的直通改成与resolveErrorResponse同款(4xx 截断措辞、5xx 保状态丢措辞留code)。本单已经点名的两条 pin(rest.test.ts的does NOT pass through an explicit 5xx status、rest-4xx-message-truncation.test.ts的5xx never enters this branch at all)需一并重写。Unsupported aggregate function两面都是裸Error(code/status 皆 undefined),且三个 spec 已声明的聚合函数无任何 SQL 后端编译 #5907 不夹带这一修:它落在packages/rest,与本单的驱动面零重叠,而且驱动侧该做的事(在抛出点如实声明code/status,ADR-0112)已经做完 —— 消费侧把它压平是这一单的领地。drivers(sql,turso):Unsupported aggregate function两面都是裸Error(code/status 皆 undefined),且三个 spec 已声明的聚合函数无任何 SQL 后端编译 #5907 的uncompilableAggregateFunctionErrordocblock 已把这个实测后果记在原地,并指回本单,免得下一个读者当成驱动的 bug 重新排查一遍。Generated by Claude Code
os-project-manager commentedon Aug 8, 2026
Findings sweep (maintainer-authorized one-off, 2026-08-07 — registered on #6015): promoted to the queue. Restore-invariant: the repo already ruled (#5437 / PR #5464) that declared 5xx statuses pass through
resolveErrorResponse;mapDataError's 4xx-only branch breaks that parity on every CRUD data route. No live producer yet is why it sat as a finding — but the two doors disagreeing on the same declaration is the defect, not the traffic.finding→pm:queue.Generated by Claude Code
os-help commentedon Aug 10, 2026
Claim: PM loop round 5 (cli lane) — queued by the maintainer-authorized 2026-08-07 findings sweep (comment 5223600933); the same-file serial constraint cleared today (#6349→PR #7372 merged
2443bb4c4, #7035→PR #7293 merged earlier — no open PR touchesrest-server.tsat dispatch time).Session:
session_0158ZQo7LiHSxGWpYKuPq1wuBranch:
claude/issue-5582-mapdataerror-5xx-passthroughWorktree:
objectstack-issue-5582Domain:
domain:cli(as labeled)File surface:
packages/rest/src/rest-server.ts(mapDataErrorpassthrough branch), the two pins the body names (packages/rest/src/rest.test.ts~:2328 "does NOT pass through an explicit 5xx status",packages/rest/src/rest-4xx-message-truncation.test.ts~:145 "5xx never enters this branch at all"), new coverage, changeset. Criterion spelling: the existingdeclaresServerFaultpredicate from@objectstack/types(per the 08-07 triage reading — read, not re-invented). ⛔ No driver-side edits: the drivers already declare truthfully (ADR-0112, #5907); the flattening is consumer-side.Container & model: M card,
mode:subagent,model: opusSerial constraints cleared: in-flight #6878/PR #7396 is
plugin-hono-server+qa/http-conformance— file-disjoint; queue entries pr-7304/7306/7379/7187/7201 none touchpackages/rest.Live-producer note for the implementer: since #5907, driver-sql/driver-turso throw
status=501 code=NOT_IMPLEMENTEDforcount_distinct/array_agg/string_aggand that reachesmapDataError's direct call sites today — the fix's acceptance case should show 501/NOT_IMPLEMENTEDsurviving instead of degrading to 500/INTERNAL_ERROR.Generated by Claude Code
os-help commentedon Aug 10, 2026
ACCEPT — PR #7402 reviewed and being driven to landing. All 26 check runs green (ESLint and TypeScript Type Check conclusions verified directly, not from the report).
What shipped:
mapDataError's explicit-status passthrough widened to 400–599 — the same doorresolveErrorResponseopens — with #5437's ruled 5xx disposition: keep the declared status, keep the machine-readablecode, drop the prose unconditionally. The 4xx arm (truncation,objectretained, #5423) is untouched. The live #5907 producer now surfaces501 NOT_IMPLEMENTEDon the CRUD data routes instead of degrading to500 INTERNAL_ERROR; both pins the body named were rewritten to the new contract, and a new 28-case suite covers the band, half-declarations, sanitization, and three in-process route walks.The dev corrected the dispatch, and the correction stands — acknowledged in the open. The dispatch said to gate the 5xx passthrough on
declaresServerFault; the dev gated the status on the band and used the predicate for the code half only, with measured grounds:declaresServerFaultrequires a non-empty code, so predicate-gating the status would have left a no-code declared 502 still re-derived from message text — the exact #5437 shape — and both original pins are built on a no-code 502.resolveErrorResponseanswers that shape with status preserved and no code invented ("the PRODUCER names the condition"), andmapDataErrornow matches it exactly. That is full parity, which was the ruled intent.Ordering decision (PM's call under the escalation bar, veto window open): the dev's open question — should a declared 5xx outrank the text classifiers below — is resolved as A, as shipped. Grounds:
resolveErrorResponse's door likewise runs first, #5437 ruled against a declared status being overwritten by a keyword heuristic, and option B would re-create precisely that defect for the inputs the heuristics recognise. No live producer declares a 5xx with classifier-recognisable text (scanned), and the new §4 case pins the live half: undeclared driver conflicts still answer409 UNIQUE_VIOLATIONacross all three dialects. This is recorded here rather than escalated because existing rulings determine the answer; the maintainer can veto by commenting.Follow-up filed as #7407 (driver docblock paragraph falsified by this PR — comment-only,
finding, deliberately unqueued).Generated by Claude Code
fix(rest): a declared 5xx status survives mapDataError on the CRUD da…
docs(driver-sql): uncompilableAggregateFunctionError's 501 now surviv…
fix(rest): honour a hook refusal's declared `statusCode` at the `/api…
rest: serve a sandboxed body's declared HTTP status on /api/v1/data (o…
fix(rest): serve a sandboxed hook's own sentence on the bulk write ro…
fix(rest): relay a producer-declared 5xx on `/analytics/dataset/query` (
fix(rest): the /references door answers both 501 refusals in one ADR-…
rest/meta: the /references door answers both 501 refusals in one ADR-…