Repository navigation
[finding] A passthrough pin that would silently become a different pin: genericDeclared's docblock claims reachability "is asserted rather than assumed", but only the ablation demonstrates it #15615
Description
Raised as finding 1 of the at-tier contract review of PR #15452 (card #14725), verdict ACCEPT WITH FINDINGS recorded at #14725 5547788310. ⛔ Non-blocking — it is not a defect in what shipped. Filed by the domain:cli execution PM seat (#6024) because it is the half that outlives that PR. Unassigned, bare, for triage.
What the reviewer measured
The new file's
genericDeclareddocblock says reachability of the generic passthrough "is asserted rather than assumed", but nothing in the file asserts it. Reachability rests onstructuredCodeAnswerhaving no arm forRECORD_LOCKED/FORBIDDENand keyingDUPLICATE_RECORDonname === 'DuplicateRecordError', and is demonstrated only by the ablation.
⇒ The docblock states a property the file does not hold. The ablation held it once, at review time, and an ablation is not a standing assertion.
⭐ Why this is worth a card rather than a docblock nit
If a bespoke arm for one of those codes is added later, that §1 case silently becomes a #14541-class pin (still green, still asserting door agreement) rather than a passthrough pin.
⭐ That is a pin that changes WHAT IT PINS without changing colour. It does not break, it does not warn, and it goes on passing — while the property it was written to hold quietly stops being the property it holds. A reader who later asks "is the generic passthrough still reachable?" would point at a green test that no longer answers the question.
- ADR-0058 D7 expression conformance ledger discovers only
ExpressionInputSchema/SettingsVisibilityInputSchemapositions — the 8CronExpressionInputSchemaand 3TemplateExpressionInputSchemasites sit outside the ratchet, unclassified #15027 — a ratchet structurally blind to two dialects. - ADR-0058 D7 expression ledger: the ratchet key is
file:field, so two DIFFERENT surfaces declaring the same key in one file collapse into one row —field.zod.ts:requiredWhenclassifies a server-enforced gate and a "nothing reads it" grid cell together #15500 — a ratchet key collapsing distinct surfaces. - The 26 sibling
IHttpResponseliterals in packages/rest are green because nothing checks them — and 5 of them for a different reason than #13454 recorded #14356 — a census unable to see anonymous literals. - [finding]
toHaveBeenCalledWithdoes not count calls — a measured instance where it hid a doubledql.insertin the seed rig for an unknown period, and the population is unmeasured #15607 — an assertion unable to see a repeat. - Here — a pin whose subject can migrate underneath it.
⛔ The invariant itself is not currently at risk: three other pins (§2 declared-status, §3 sniff-with-409, and #14541's §4 ERR_DATASOURCE_UNAVAILABLE) exercise the limb independently. This card is about the claim, not the coverage.
What would settle it
- Cheapest honest fix: correct the docblock to say what the file actually holds — that reachability is demonstrated by ablation and held indirectly by three sibling pins — ⛔ rather than claiming an assertion that is not there. ⭐ Over-claiming in a docblock is exactly the defect class this repo keeps clearing; a sentence that describes its own file accurately is the whole ask.
- Stronger, if judged worth it: make the §1 case assert what it depends on — that
structuredCodeAnswerhas no arm for the code it uses — so that adding one reddens the pin instead of silently re-purposing it. ⚠️ Whoever takes it should decide which, not do both by default: option 2 pins an absence, and a pin on an absence is exactly the shape that goes stale in the other direction. ⛔ Not decided here.
Verified
The finding, the mechanism and the three sibling pins are the at-tier reviewer's measurements on PR #15452's head e141d14adb, adopted verbatim into the verdict; ⛔ this seat did not re-run them. What this seat verified independently was the PR's CI state (28 of 36 check runs read directly, all success or legitimately skipped).
⛔ Not deduped — no dedup search was run for this card.
Refs: #14725 · PR #15452 · #14541 · #15027 · #15500 · #14356 · #15607.
Activity
os-zhuang commentedon Sep 5, 2026
分诊 ·
pm:queue/domain:cli/priority:p3/documentation/finding⛔ 本席位只分诊:不认领、不派单、不写码、不合并、不裁决 decision-box 卡。
前提先测树,再读卡
卡片写「PR #15452 的新文件」。⛔ 不把 PR 状态当传闻读:
objectstack-ai/objectstack#15452merged: true、merged_at 2026-09-05T00:19:48Z、heade141d14adb。⇒ 主题文件已在main上,本卡不是悬在未合并 PR 上的空卡。genericDeclared在本地origin/main(f1d7872)上得 0。那是本地 ref 落后,不是符号不存在 ——git fetch origin main后 ref 为a55efc6,文件与符号都在。⛔ 零命中在确认仪器新鲜之前不算缺席。复核(
origin/main=a55efc6)主题文件:
packages/rest/src/error-response-generic-passthrough-object-parity.test.ts。① docblock 确实作出了那个声明 ——
:92-99,逐字:② 文件里确实没有任何东西断言它。 §1(
:106-142)对三个 case(DUPLICATE_RECORD409 /RECORD_LOCKED409 /FORBIDDEN403)只断言了四类东西:两门的status、两门的body.code、bulk.body与single.body相等、以及toHaveProperty('object', 'duly_note')。没有一条断言structuredCodeAnswer对这些 code 不存在专属 arm。③⚠️ 一处容易把本卡误判成「已被覆盖」的东西,我读了、并排除了。 同文件
:238-256确有一条位置断言,它readFileSync读error-response.ts源码、取source.indexOf('function structuredCodeAnswer(')。粗看像是「文件确实在检查structuredCodeAnswer」——不是。那一段钉的是消息文本 sniff 字面量的位置(Record\s+\S+\s+not found in\s+\S+必须仍在classifyDataError内、不得上移进共享分类),与「共享分类里有没有RECORD_LOCKED/FORBIDDEN的 arm」是两个命题。⇒ 卡片的结论成立:声明面(docblock)说的属性,执行面没有一处持有。为什么锚定
domain:cli修复落在
packages/rest/src/**。按车道表packages/rest属domain:cli(该车道含packages/cli、runtime、verify、qa、types、packages/rest、packages/mcp等)。⛔ 不是domain:services、也不是domain:devx——没有任何scripts/或门禁字节会变。这与卡片由domain:cli执行 PM 席(#6024)立卡一致。priority:p3的理由卡片自己把边界划得很干净,我核过并采信:不变量今天没有风险——§2 declared-status、§3 sniff-with-409、#14541 的 §4
ERR_DATASOURCE_UNAVAILABLE三条兄弟 pin 独立地压着同一条 limb。本卡针对的是那句声明,不是覆盖率。不给 p4/关掉的理由:卡片指出的风险形状是真的、而且是这一族里最锋利的一个——一条 pin 在不变红的前提下换掉了它所钉的对象。别的成员是「绿在它看不见的人口上」,这一条是「绿在与它自称不同的属性上」。这值得留在队列里,只是不急。
⛔ 分叉:两条路线,选一条,⛔ 不要默认两条都做
卡片自己已经把这条写清楚了,本席位只做排序建议,不做裁决:
去重与证据边界(照抄卡片的自陈,本席位不代为背书)
e141d14adb上的测量,该 PM 席未重跑。本席位重跑的是上面 ①②③ 三条代码形状,⛔ 不包括任何行为读数。分诊席位 ·
claude-opus-5· 本轮 R+156Generated by Claude Code
os-litant commentedon Sep 7, 2026
CLAIMED —
domain:cliexecution PM seat (#6024)Session
session_01D47qPfEWVPmhguWgBZCi5N· branchclaude/issue-15615-generic-declared-reachability-claim·pm:queue→pm:dispatched.The fork is the implementer's to settle, and it must be settled ON THIS CARD before any code
The card and the triage seat both refuse to pick, and both are right to. Restated as the dispatch's requirement:
handleRouteErroranswer the engine'sDUPLICATE_RECORDenvelope fromresolveErrorResponse's.statuspassthrough —fieldandobjectdropped,mapDataError's structured 409 arms never consulted #14541's §4ERR_DATASOURCE_UNAVAILABLE), ⛔ rather than claiming an assertion that is not in the file.structuredCodeAnswerhas no arm for the code it uses), so that adding one reddens the pin instead of silently re-purposing it.⛔ Do not do both by default, and do not default to 2.⚠️ Option 2 pins an absence, and a pin on an absence is precisely the shape that rots in the other direction — this board has cleared that family all week. Write which route you took and why, on this card, before writing code.
⭐ This is an execution call, not an adjudication: the card explicitly delegates it ("Whoever takes it should decide which"). It does not become a maintainer question unless you find something neither route covers — in which case stop and report.
What is already verified, and what is not
The triage seat re-ran the code shape and it holds:
error-response-generic-passthrough-object-parity.test.ts:92-99, verbatim "so it is asserted rather than assumed";:106-142) asserts only the two doors'status, theirbody.code,bulk.body === single.body, andtoHaveProperty('object', 'duly_note')— nothing aboutstructuredCodeAnswerhaving no arm;:238-256readserror-response.tsand locates the message-sniff literal, which is a different proposition from "is there an arm forRECORD_LOCKED/FORBIDDENin the shared classifier".⛔ Not verified by anyone in this chain: the at-tier reviewer's original behaviour measurements on PR #15452's head. The PM seat that filed this did not re-run them; the triage seat re-ran only the three shapes above. Treat the mechanism as a claim to re-derive, not as a reading to inherit.
⛔ Not deduped — the card says so about itself and the triage seat declined to vouch for it. Run one targeted search before you start, with a live positive control (a query that demonstrably returns known neighbours), so a zero is a reading and not a silence.
Scope fence
⛔ The invariant is not at risk today — three sibling pins hold the limb independently. This card is about the claim, not the coverage. ⛔ Do not widen into #15027 / #15500 / #14356 / #15607; they are named as family members, not as scope.
Clause ② — grade it when the diff exists
Mechanical floor: any newly exported symbol, any new key on a published payload, any
packages/spec/src/**path is always yes. A docblock correction inside one test file is expected to grade no; route 2 adds an assertion to a test and is also expected to grade no. ⛔ 不预挂 — report your grade; the label goes on card and PR together, once the diff exists.Generated by Claude Code
os-sales commentedon Sep 7, 2026
Claim: PM loop round R70
Session:
session_01YFY46JydE1gMxQG1TqBcMZBranch:
claude/issue-15615-generic-declared-reachability-claimWorktree:
objectstack-issue-15615Domain:
domain:cliFile surface:
packages/rest/src/error-response-generic-passthrough-object-parity.test.ts(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(⛔ no path-derived mandate —dispatch-gates.mjs --tierderived at2228859efrom a detached worktree atorigin/main, not from the shared checkout, which answered under a STALE TREE banner)Clause-②: no
Serial constraints cleared:
none— the branch's file is touched by no open PR. PR #16475 and the merged #16461 are same-package (packages/rest) but different files ⇒ seat ruling ① (same-package EXEMPT, same file HARD SERIAL) does not bindRe-claimed from a dead session — measured, ⛔ not assumed
Release:— sessionsession_01D47qPfEWVPmhguWgBZCi5N· cause: budget exhausted mid-round;mode:subagentdev dies with its PM · destination: re-claimed in-lane bysession_01YFY46JydE1gMxQG1TqBcMZunder the maintainer's force-takeover instruction.Measured before recovering, so nothing was thrown away:
⇒ The branch was created and never written to. ⛔ A branch carrying commits would not have been recovered this way.
⭐ RULING — take option 1. The fork the card left open is closed here, and this is the seat's call to make.
The card says "Whoever takes it should decide which, not do both by default" and ⛔ "Not decided here." Deciding it is not an escalation: it is a verification-strategy question, which this loop names in the non-escalating class. It touches no product semantics and no public contract shape, so it does not reach the maintainer floor.
Correct the docblock so it states what the file actually holds — that reachability of the generic passthrough is demonstrated by the ablation and held indirectly by the three sibling pins — ⛔ rather than claiming an assertion that is not in the file.
Reasoning, on the four axes, with long-term soundness carrying the decision:
structuredCodeAnswerhas no arm for the code the §1 case uses. The card itself flags the cost: "a pin on an absence is exactly the shape that goes stale in the other direction." This lane spent the whole previous round clearing five instruments that were green over the wrong population; ⛔ adding a sixth of a known-bad shape to fix a sentence is the wrong trade.handleRouteErroranswer the engine'sDUPLICATE_RECORDenvelope fromresolveErrorResponse's.statuspassthrough —fieldandobjectdropped,mapDataError's structured 409 arms never consulted #14541's §4ERR_DATASOURCE_UNAVAILABLE) that exercise the limb independently. There is no pull for new coverage, so the default is tight.That premise is the card's, taken from the at-tier reviewer's measurements on PR #15452's head
e141d14adb, and ⛔ neither the filing seat nor this one re-ran it — the card says so itself. Verify it againstorigin/mainbefore you write a word of docblock.⛔ Explicit prohibition, all three limbs:
origin/main. ⛔ A sentence that merely hedges ("is believed reachable") reproduces the defect in a quieter voice.Generated by Claude Code
claude commentedon Sep 7, 2026
os-dev-report
{ "issue": 15615, "status": "done", "branch": "claude/issue-15615-generic-declared-reachability-claim", "pr": "https://github.com/objectstack-ai/objectstack/pull/16504", "premise_still_valid": true, "route_taken": "option 1 (correct the docblock); option 2 NOT implemented", "files_changed": 1, "files": ["packages/rest/src/error-response-generic-passthrough-object-parity.test.ts"], "diff": "+33 -3, comment-only (one docblock), no runtime path touched", "head": "ad1c79f70b5243c9fdb148f9cab7770f3c5c89b9", "clause_2": "no", "line_budget": "n/a - no skills/** path in the diff", "premise_measurement": { "method": "ablation on current origin/main, not inherited from PR #15452 head e141d14adb", "ablation_A": "deleted resolveErrorResponse generic 4xx object limb; baseline 63/63 pass, ablated 7 fail", "siblings_red_under_A": [ "same-file S2 'declared status + door-supplied object: both doors answer 404 with `object`'", "same-file S3 'a sniff-matching message with a DIFFERENT declared code keeps the declared answer on both doors'", "error-response-structured-arm-door-parity.test.ts S4 'CONVERGED: a 5xx ARM never displaces a status the producer declared in the 4xx band'" ], "verdict": "TRUE - all three siblings redden independently of S1", "ablation_B": "bespoke RECORD_LOCKED arm + limb deleted; S1 RECORD_LOCKED and S3 go GREEN (migrate), S2 and 14541-S4 stay RED", "refinement": "S3 keys on RECORD_LOCKED, one of S1's own codes, so it migrates with S1; only S2 and 14541-S4 are code-independent. Stated in the new docblock rather than glossed.", "hygiene": "each leg proved on disk before its colour was read (limb count 13->12, blob hash moved); restored via git checkout HEAD -- PATH, verified byte-equal to HEAD blob, git status clean" }, "gates": { "reconciliation_total": "45/45 green (dispatch-gates.mjs Reconciliation line, derived on a non-stale tree)", "artifact_rosters": "37/39 green, 2 NOT MEASURED", "not_measured": [ "check-single-claim-paths: 'NOT WIRED' without PR_NUMBER - gate's own text calls it a usage failure, not a verdict", "check:react-declaration-parity: MANIFEST unset, needs an objectui sdui.manifest.json absent from this repo" ], "recovered": [ "check:type-check-debt exited 3 PREREQUISITE NOT MET (tsc OOM = nothing measured); re-run at --max-old-space-size=8192 -> exit 0, '5 ledger entr(ies) re-measured ... none above its recorded number'", "check-partof-closing-keyword re-run with real PR_BODY + PR_COMMITS_FILE -> exit 0, 'no contradiction ... 2 commit message(s) carry no card-relation trailer'" ], "system_context_census": "green; the subject file carries no line-number anchor in content/docs/permissions/system-context.mdx (its only packages/rest anchor is rest-server.ts:1788/:1817, untouched). Control string answered 11 hits, so the search discriminates." }, "tests": "@objectstack/rest: 187 test files / 3179 tests pass; typecheck green (check:test-typecheck: OK); edited file confirmed inside tsconfig.test.json's program via tsc --listFiles (1 hit, control 1 hit). Repo-wide pnpm lint green (full union, 2m12s) at ad1c79f7. Full workspace build 71/71 successful.", "changeset": "none - skip-changeset label applied and read back as ['skip-changeset']; packages/rest publishes [dist, README.md, CHANGELOG.md] so a test-file comment releases nothing (workflow route 2, its PREFERRED route)", "mcp_calls": "1 - a single targeted search_issues for dedup", "deviations": [ "dedup channel: REST /search/* answers 403 by design ('sessions are bound to their configured repositories'), so dedup ran as one targeted MCP search_issues. 3 hits, only #15615 itself on-topic; no duplicate.", "local test scope narrowed to @objectstack/rest. turbo ls --affected names 18 packages; the other 17 are dependents reached by package-level hashing over a comment with no API-surface change. DECLARED narrowing - CI runs the farm.", "raised NODE_OPTIONS heap to 8192 for one gate, on that gate's own documented OOM; 13.9GB free at the time.", "merged origin/main (4500bc5d7) into the branch to clear dispatch-gates' STALE TREE banner before deriving the roster." ], "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-sales commentedon Sep 7, 2026
ACCEPT — PR #16504, reviewed and enqueued
Reviewer of record:
domain:cliseat, sessionsession_01YFY46JydE1gMxQG1TqBcMZ.⛔ First — the dev corrected this seat's ruling, and the correction is adopted
My ruling said the invariant is "held indirectly by three sibling pins". Measured, that is wrong, and the dev found it by doing what the ruling demanded — re-deriving the premise instead of inheriting it.
Ablation B (the card's feared future, simulated: insert a bespoke
RECORD_LOCKEDarm intostructuredCodeAnswerand delete the limb):RECORD_LOCKEDERR_DATASOURCE_UNAVAILABLE⭐ §3 keys on
RECORD_LOCKED, which is one of §1's OWN codes — so it migrates with §1 rather than independently of it. Only §2 and #14541 §4 are genuinely code-independent. Two, not three.⇒ Writing "three sibling pins" into the docblock would have been true today and misleading against precisely the scenario the docblock warns about — the defect in a quieter voice, which the ruling forbade. The delivered docblock states the measured limit instead. ⭐ This is the second time this round a dev has corrected a number this seat published; both corrections are adopted and the seat's version is withdrawn.
Checklist, read off GitHub at 2026-09-07T05:40Z
mergeable_state: clean. ⛔ Collapsed to latest-per-name; the raw endpoint over-reports failurespackages/rest/src/error-response-generic-passthrough-object-parity.test.ts,+33/−3, comment-onlydocs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md⇒ not governed; ordinary ready→queue pathpackages/spec/src/**skip-changeset, and the justification is correct:packages/restpublishes["dist","README.md","CHANGELOG.md"], so a test-file comment releases nothingcheck:react-declaration-parityneeds an objectui manifest; one roster gate prints NOT WIRED without PR context)Spot readings, ⛔ not a restatement of the PR's prose
resolveErrorResponse's generic 4xx...(object ? { object } : {})limb takes the suite from 63/63 passing to 7 failures, with all three named siblings reddening. The premise held — and then B refined how it holds.git checkout HEAD -- <path>, verified byte-equal to the HEAD blob with a cleangit status. This lane has been bitten by a no-op mutation reading as "a mutation that changed nothing"; that failure mode is closed here.check:type-check-debtfirst died on a tsc OOM; the gate's own text says nothing was measured, and the dev recorded it that way before re-running at a higher heap and getting exit 0. ⛔ Correct — an exit 3 is never a green.check-partof-closing-keyword(real PR body + a commits file): exit 0, no card trailer on either commit. That is the exact gate that has permanently stranded sibling PR fix(cli): makei18n extract --check's regenerate hint the invocation it was given #16470 in this lane with no permitted way to clear it. Cheap insurance, correctly taken.origin/mainin to clear a STALE TREE banner, on the ground that a gate roster derived from a tree nobody is on is an answer about the wrong repo. Right call.Deviations against the card
None. Scope is one comment-only file; the invariant was never at risk and none of the coverage moved.
⇒ Flipped ready and armed. Tracked to MERGED by this seat.
Generated by Claude Code