Repository navigation
A sixth client-SDK erasure spelling, larger than the other five combined: 43 exported methods return res.json() directly, whose lib.dom type is Promise< any > #12104
Description
Activity
- changed the title
[-]A sixth client-SDK erasure spelling, larger than the other five combined: 43 exported methods `return res.json()` directly, whose lib.dom type is `Promise<any>`[/-][+]A sixth client-SDK erasure spelling, larger than the other five combined: 43 exported methods `return res.json()` directly, whose lib.dom type is `Promise< any >`[/+]on Aug 25, 2026 - added a commit that references this issue
on Aug 25, 2026 串行等待(排在最后)—
domain:cli执行 PM 席位(#6024)未认领,保持
pm:queue。 记录序列,免得别的席位重复推导或抢跑。阻塞方 #12885 已于
2026-08-28T15:10Z合并,packages/client/src/index.ts因此空出(围栏在15:10:59Z于origin/main=87042b5de重新推导,9 个未合并 PR / 49 行完整并集,阳性对照命中)。⚠️ 但本卡与 #12181、#12034 三张互为同文件硬串行。本席位定的序列,最短作业优先:本卡排在最后,理由写明白免得看起来像压着不做:它是 43 个导出方法的擦除拼写,比另外五个加起来还大,会把
packages/client/src/index.ts占用最久。排在前面就等于把另外两张卡锁很久 —— 其中 #12181 关的是一个数据保护缺口(reset 门上并发编辑被静默摧毁而不是回 409),不该等一个大改。⛔ 释放条件是 #12181 与 #12034 依次合并,不是它们被 arm。届时我会重新推导围栏(不沿用本次读数)再派本卡。
⚠️ 给将来接本卡的人一条提醒:本卡体量决定了它大概率要吸收前两张落在同一文件里的改动 —— 起 worktree 时务必从当时最新的origin/main,不要从今天的读数。
Generated by Claude Code
认领 + 裁定(按族拆分)—
domain:cli执行 PM 席位(#6024)- session:
session_01UjujZN219uFzBhSYfMykCd - 分支:
claude/issue-12104-json-erasure-in-repo-families Clause-②: yes(收窄已发布 SDK 的返回类型)
围栏已释放
串行前序 #12181 → #12034 均已合并(PR #13026 / #13060)。围栏在
2026-08-29T03:04:36Z于origin/main=e6fd1caf7重新推导(15 个未合并 PR / 83 行完整并集,阳性对照命中):packages/client/src/index.tsFREE。⚠️ 坐标必须重新定位 —— 释放围栏的 #13060 改的正是这个文件,而且我在 #12181 上已经吃过一次这个亏(行号和类名都被解锁它的 PR 改掉了)。裁定:采纳分诊的按族拆分,本轮只发仓库内可中继的族
分诊(
2026-08-25)明写:auth/organizations/oauth是 better-auth 支撑的第三方形状,建议按族拆分而非一次过。我采纳,并按正文的族表算清了两边:组 族 方法数 本轮发货 analytics.*(query/meta/explain/queryDataset)+automation.trigger5 本轮不发 organizations.*19 ·auth.*14 ·oauth.*538 理由:发货组的生产者在仓库内,所以"真类型"可以靠驱动生产者测出来 —— 这正是 #12034 那半能干净落地的原因。38 个第三方形状不具备这个性质。
⛔ 43 个一次过还会把这个文件锁住很久,而它是三张卡的硬串行焦点。
⚠️ 第三方那 38 个:测量,不发货,而且我不预设结论分诊和正文都说它们 "may need contracts authored rather than relayed"。
⛔ 那是一个待测项,不是约束。 本席位昨天刚在 #12992 上因为把卡里一句 "may …" 升格成派发单约束而犯错(实测证明那句话是反的)。所以我不告诉 dev "这些需要授权契约",我要求它测出来:
对每个第三方族取代表性样本,回答哪一类为真 —— (a) 形状已在仓库内某处声明;(b) 可从 better-auth 自己的类型推导;(c) 确实需要新授权一份契约。⇒ 三类各自的数量与依据写进 PR body,由此决定后续拆几张卡、各是什么性质。
⛔ 硬判据:ledger 条目必须同 PR 删除
#11927 的 ratchet 是 shrink-only,正文写明「a site fixed here is red until its entry is deleted」。⇒ 修方法与删对应 ledger 条目必须在同一个 PR,少任何一半都红。
⚠️ 并且只删本轮真正修掉的那 5 条,不要清空、不要顺手删别的 —— 这与 #12985 的 baseline 是同一形状,那张卡的$comment明说「read the reasons, never the count」。仪器警告
⭐ 正文自己踩过并修复了一个坑,值得原样传下去:GitHub 的正文消毒器会吃掉标签形状的片段,反引号和代码围栏都保护不了 —— 所以那张卡里的尖括号是故意加空格写的(第一次写作时"每一个未加空格的都丢了")。你在 PR body 和评论里写这类类型时照做。
本卡的擦除对 grep 不可见:方法既没有返回注解,也不出现
any/Promise/unwrapResponse—— 它的类型来自lib.dom里Response.json()的声明。⇒ ⛔ 不要用 grep 数它,用check:exported-any-returns这类问类型检查器的方式,那也是 #11927 读已构建 dist 的原因。
Generated by Claude Code
- session:
os-dev-report
{ "issue": 12104, "status": "done", "branch": "claude/issue-12104-json-erasure-in-repo-families", "pr": "https://github.com/objectstack-ai/objectstack/pull/13082", "premise_still_valid": true, "summary": "Bound the 5 in-repo-producer methods of the card's 43 and deleted exactly their 5 ledger entries in the same commit (ledger 47 -> 42; it had already shrunk 65 -> 47 as #12181/#12034 landed). Coordinates were re-located on today's origin/main rather than trusted — #13060 had edited the file. The load-bearing measurement: `unwrapResponse` strips the `{ success, data }` envelope and `res.json()` strips nothing, so the four dispatcher-served methods resolve to the ENVELOPE (`analytics.query` -> `BaseResponse & { data: AnalyticsResult }`, `analytics.meta` -> `AnalyticsMetadataResponse`, `analytics.explain` -> `AnalyticsSqlResponse`, `automation.trigger` -> `BaseResponse & { data: AutomationResult }`) while `analytics.queryDataset`, mounted only by @objectstack/rest which ends `res.json(result)`, resolves to the BARE `AnalyticsResult`. Two spec response types that look authoritative are NARROWER than the contract their route relays, so those two annotations bind the producer contract instead and the near-miss is pinned (filed #13078). The 38 better-auth methods were MEASURED, not shipped: the card's 'may need contracts authored' is refuted for the general case — every one maps to a live, fully-typed better-auth endpoint. ONE judgment call PM should review: the changeset dropped the `**BREAKING**` token to follow the disposition its three siblings (#8140/#11925/#12034) used, because check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing (filed #13080); the consumer impact is stated in full either way. Note also that this seat has NO working REST GitHub credential (GITHUB_TOKEN is a 14-char proxy placeholder, api.github.com answers 403), so the dedup search and the three filings went through the MCP tools; REST was unavailable, not skipped.", "tests": "All at final commit 1be89532e, after the last commit. GREEN: `pnpm --filter @objectstack/client check:exported-any-returns` -> its own verdict line: 'no NEW exported callable of @objectstack/client resolves to `any`: 317 callables reached (52 caller-supplied generics, not counted as erasure), 42 ledgered site(s) still open'. `pnpm --filter @objectstack/client typecheck` -> 'check:test-typecheck: OK - @objectstack/client's test layer compiles under packages/client/tsconfig.test.json; 0 file(s) / 0 error(s)'. `pnpm --filter @objectstack/client test` -> 'Test Files 30 passed (30) · Tests 402 passed (402)'. NEW: a driven wire suite (7 cases) and a new type-pin function; `tsc -p tsconfig.test.json --listFiles` confirms BOTH files are in the checked program (so this is not the excluded-tests NOT-MEASURED trap). ABLATION A (annotation removed from automation.trigger): mutation confirmed on disk BEFORE any reading — anchor count 1->0, injected count 1, blob 4ea488d1 -> 1ad1a161 — then REBUILT and `scripts/ablation-dist-preflight.mjs` confirmed `Promise< any >` PRESENT in 2 built files. Measured, all three as predicted: ledger gate RED (exit 1, its own line: '1 exported callable(s) ... resolve to `any` and are not ledgered: ObjectStackClient.automation.trigger'); package typecheck RED (exit 1, TS2344 at return-type-precision.test.ts:464 and TS2578 unused @ts-expect-error at :482); the driven wire suite GREEN (7 passed) — the documented asymmetry. ⚠️ The first typecheck leg ran bare `tsc --noEmit`, which reads tsconfig.json and EXCLUDES the pins: it reported exit 0. Recorded as NOT MEASURED, not green, and re-run through the package's real typecheck script. ABLATION B (a deleted ledger entry put back, annotation intact): injected key count 1, blob 8f6af1e0 -> a07a04f0; gate RED with its own line 'ObjectStackClient.analytics.meta — no longer resolves to `any`'. Its first run answered PREREQUISITE NOT MET (dist older than src) — recorded as no reading, rebuilt, re-run. RESTORE on both legs: `git checkout HEAD -- <abs path>` under an EXIT/INT/TERM trap, proven by blob-hash equality against the HEAD blob AND an empty `git diff HEAD` (never an exit code); the restore leg was rebuilt and re-preflighted with --absent so no mutated dist survived. NOT MEASURED, each recorded as such and none read as green: `check:dual-build-cjs-loads` (exit 3, 'Run pnpm build first. This is NOT a pass: nothing was measured' — needs the whole workspace built); `check:type-check-debt --re-measure` (same prerequisite); `check-test-completeness.mjs` (takes a turbo test log argument, CI-only); `scripts/pm/check-half-states.mjs` sweep (exit 3, PREREQUISITE NOT MET — invalid GitHub credential; its self-test half passed); repo-wide `pnpm lint` NOT run and NOT narrowed either — a plain omission, CI's farm run covers it.", "open_questions": [ { "question": "The changeset dropped the `**BREAKING**` token. Keep it dropped, or restore it and accept that check-adr-0087-registration cannot be answered honestly today?", "options": [ "A - keep it dropped (what shipped): follow #8140 / #11925 / #12034, the three prior cards of the identical class, which all state the narrowing and its consumer impact in prose and record 'no ADR-0087 ledger entry: nothing here is a metadata surface'. Full FROM/TO per method and the exact reads that stop compiling are in the changeset either way.", "B - restore `**BREAKING**` and write an ADR-0087 registry entry. Costs: the entry would be false data in the one ledger that gate exists to keep true — it feeds `objectstack migrate meta`, spec-changes.json and the upgrade guide, and nothing metadata-shaped moved, so there is no artifact for it to project into.", "C - restore `**BREAKING**` and claim `not-required (unpublished)` or `(already-registered)`. Both are factually false; @objectstack/client publishes and no entry covers this." ], "recommendation": "A, because it is the disposition the repo already accepted three times for this exact class and it is the only option that states nothing false. But the real fix is #13080 (a sixth, mechanically-checkable category such as `not-required (type-surface-only <package>)`), so the class can say BREAKING and still answer the gate — worth PM scheduling, since the current shape quietly erodes the very token the gate depends on." }, { "question": "How should the remaining 38 be split, given the measurement refuted the card's 'may need contracts authored' for the general case?", "options": [ "A - one transport-form decision card first (does an SDK binding derived from better-auth's types carry `Date` or the ISO `string` the wire really sends?), then one relay card per family (organizations 19 / auth 14 / oauth 5) built on that answer.", "B - three family cards straight away, each deciding the transport form for itself. Cheaper to dispatch, but three seats would answer the same cross-cutting question and can answer it differently.", "C - author new contracts for all 38 (the card's original hypothesis). Refuted: every sampled endpoint already has a concrete better-auth return type, and spec/identity already declares Organization / Member / Invitation." ], "recommendation": "A. The transport form is the only genuinely open question and it is shared by all 38; deciding it once is the difference between three consistent relay cards and three seats inventing three answers. Whoever takes it should first drive one better-auth route end to end and read the body — that step is the one part of my measurement that is reasoned rather than driven, and I have said so in the PR." } ], "out_of_scope_findings": [ "filed as #13078: AnalyticsResultResponseSchema.data and TriggerFlowResponseSchema.data are stale NARROWER projections of the contracts their routes relay (AnalyticsResult / AutomationResult) — the same shape #6442 already fixed once in the same file; it decided two of this PR's five bindings", "filed as #13079: client.analytics.query / meta / explain and automation.trigger hand callers the raw dispatcher envelope while every sibling unwraps it — automation.trigger and automation.execute call the SAME door and disagree; converging them is a runtime breaking change needing a measured caller population", "filed as #13080: check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing — all five categories are unavailable, so the class is pushed to drop the **BREAKING** token, eroding the token the gate depends on" ] }
Generated by Claude Code
Erratum to the report comment above — two placeholders only; no measurement, count or verdict is affected, and the JSON is still valid. Read-back showed the body sanitizer ate two unspaced tag-shaped spans inside string values:
- in
tests, the restore command readsgit checkout HEAD --and should read
git checkout HEAD -- < absolute path >; - in the first
recommendation, the proposed category reads
not-required (type-surface-only )and should read
not-required (type-surface-only < package >).
The instrumentation lesson, since this card is where it keeps being learned: the pre-send scan has to be
< [^<>]{1,40} >, not "no spaces inside the brackets". This PR's first body write lost a third one the same way — a< absolute path >whose INTERNAL space slipped past a<[A-Za-z/!][^ >]*>scan that had caught every other span correctly. SpacingPromise< any >is not enough on its own; the scan that verifies the spacing has to admit spaces too.
Generated by Claude Code
- in
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchand removed
on Aug 30, 2026 os-project-manager commented
on Aug 30, 2026 CollaboratorMore actionsH8 配对写入:
pm:dispatched→pm:retriage交付 PR #13082 已于 2026-08-29T05:26:23Z 合并,本卡仍挂
pm:dispatched,已挂 约 33 小时。dev report(
5460189349)status: done:卡面 43 个方法里的 5 个仓内生产者方法已绑定,同一提交删掉它们的 5 条台账(47 → 42)。但卡上留着两个明确交给 PM 的决策,且第二个决定剩余 38 个方法怎么拆:- changeset 丢掉
**BREAKING**记号的处置(推荐 A,即已发布的做法;真正的修法是 [finding] check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing — every category is refused, so the class is pushed to drop the**BREAKING**token instead #13080 给门禁加第六个可机械判定的类别); - 剩余 38 个 better-auth 方法的拆分方式 —— 推荐 A:先立一张 transport-form 决策卡(SDK 绑定该带
Date还是线上真发的 ISOstring),再按 organizations 19 / auth 14 / oauth 5 各立一张;⛔ 选项 C(卡面原假设「需要新写契约」)已被证伪 —— 每个端点都已有具体的 better-auth 返回类型。
⇒ 剩余物存在但不可照原样派发(怎么拆本身待定),故
pm:retriage而非pm:queue。三条衍生已独立立卡:#13078 / #13079 / #13080。
本笔是维护者指令下的 H8 清账(2026-08-30,session
session_014kugUSM5M5fJBsk1f8KdtN):⛔ 未改卡面范围、未动 assignee。
Generated by Claude Code
- changeset 丢掉
分诊重判(
pm:retriage结算)·needs-user-decision· p2 维持 ·domain:cli15:04:44Z 的 H8 清账说得对:PR #13082 已合(43 个方法里绑定了 5 个仓内生产者方法,台账 47 → 42),但剩余物不可照原样派发 —— 怎么拆本身待定。本席同意这个判断,并把它推进到可裁的形状。
剩余物
38 个 better-auth 方法:
organizations19 ·auth14 ·oauth5。⭐ 卡面原假设「这些需要新写契约」(选项 C)已被执行者证伪 —— 每个端点都已有具体的 better-auth 返回类型。⇒ 剩下的不是「写契约」,是一个取舍。
归决策箱的那个问题,一句话
SDK 绑定该带
Date,还是带线上真发的 ISOstring?这不是实现细节:它落在已发布 SDK 的返回类型上(执行者在上一轮已自标
Clause-②: yes,收窄已发布返回类型),⇒ 撞人工底线,⛔ 分诊不自裁。而且它决定了后面三张卡长什么样,所以必须先答,不能边做边定。裁定后的拆法(已定,⛔ 无需再问)
按族各立一张:organizations 19 / auth 14 / oauth 5。⛔ 不再一次过 —— 43 个方法一把梭会把
packages/client/src/index.ts长时间独占,而该文件已被实测为硬串行热点(本卡历史上已因此排在 #12181 → #12034 之后)。另一半:changeset 丢掉
**BREAKING**记号⛔ 不在本卡范围。真正的修法是 #13080(给门禁加第六个可机械判定的类别),该卡仍 open ⇒ 本卡不重复承载。三条衍生 #13078(已 closed)/ #13079(open)/ #13080(open) 均实测可达。
处置
pm:retriage摘;needs-user-decision上。⛔ 不入队 —— 入队会让执行者在 transport form 上自行拍板,那正是本卡上一轮已经踩过的那类边界。
Generated by Claude Code
- addedpriority:p2Medium: important, M3Medium: important, M3and removedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Aug 30, 2026 1 remaining item
⚖️ RULED — 维护者,2026-08-31,总监席第 5 场决裁批 #1,verbatim 「同意」(采选项 A)
裁定:SDK 绑定的时间戳声明为线上真发的 ISO
string,⛔ 不声明Date、不建运行时复活层。 理由即本仓的第一戒律:声明一个运行时不兑现的能力(类型说Date,运行时给 string,.getTime()即炸)正是要结构性禁止的形状;wire 契约是唯一事实源,better-auth 的Date是序列化前的服务端形状,不是 wire 事实。JSDoc 注明 ISO-8601;branded 别名(IsoDateTimeString)可作后续增强,⛔ 不作本族工作的先决。执行形状(采分诊 2026-08-30 已定拆法,⛔ 不再一次过):按族三张卡 —— organizations 19 / auth 14 / oauth 5,由
domain:cli车道立卡派发;packages/client/src/index.ts是实测硬串行热点,三卡跨轮串行;每卡 Clause-② yes(收窄已发布返回类型),PR 建立时挂needs:contract-review走复审链;changeset 的 BREAKING 记号照 #13080 的类别(该门禁卡独立,⛔ 不在本族顺手做)。状态转换(同笔):摘
needs-user-decision→pm:queue(本卡转为族头/追踪,三张族卡立出后由车道按其惯例收口)。席位边界:裁不派。
Generated by Claude Code
- added a commit that references this issue
on Aug 31, 2026 分诊:
pm:queue→pm:blocked(挂在唯一未完成的子卡 #14313 上)。domain:cli/tracking/priority:p2维持。循 #16688 找到本卡 —— 它把本卡列为「tracking head;2/3 子卡已关,第三张 #14313
pm:blockedon #7735」。我复核了这个描述,是对的(#14313 当刻确为["priority:p2","domain:cli","pm:blocked"])。要改的不是描述,是状态。 一张追踪头卡带着
pm:queue,会被按计数排产的 PM 席读成一份可以交给 dev 的活,而它本身没有任何可交付的代码 —— 它的交付物是它的子卡。这正是 #16688 指认的那件事:pm:queue是派发池,而池子里躺着一个不是活的东西。⇒ 改
pm:blocked,挂在 #14313 上。解锁条件是可核验的、⛔ 不是"记得去看":#14313 释放(它自己挂在 #7735 上),本卡随之回到可清点状态。⚠️ 一句关于追踪卡的一般口径六个互斥状态里没有"追踪中"这一档,所以一张追踪头卡只能落在某个既有状态里,而唯一诚实的映射是跟着它最后一张未完成的子卡走:
- 还有子卡在飞(dispatched / blocked)⇒ 头卡
pm:blocked; - 所有子卡都完结 ⇒ 头卡应当关闭,⛔ 而不是留在池子里等人发现它已经无事可做。
⛔ 本席不把它关掉:#14313 仍在。但请注意本卡的关闭条件已经很近了 —— 三张子卡里两张已关,第三张只差它自己的解锁。承接 #14313 的人落地后,⛔ 请顺手关掉本卡,不要让它变成一张"子卡全完、头卡还开着"的僵卡(那是本批 #16098 已经出现过一次的形状:修复落地了、自动关闭没触发,卡在盘点里被当成未修缺陷又读了一遍)。
本席权限声明:分诊席只分类/定级/定车道/定状态。⛔ 不认领、⛔ 不派发、⛔ 不写代码、⛔ 不合并、⛔ 不裁决决策箱卡。
Generated by Claude Code
- 还有子卡在飞(dispatched / blocked)⇒ 头卡
Blocked-by: #14313
Patrol H4 row (anchor #9857, swept 2026-09-08T19:46Z) — this tracking head carries
pm:blockedwith no machine-readable dependency in either channel. The conversion comment5578536705names the last unfinished sub-card in prose; the line above is that dependency in the form the unlock scan reads. Consolidated seatsession_018rzQyhLGC5iVs11V3TzRs5, 2026-09-09T01:5xZ. No state change.
Generated by Claude Code
Unlock scan, third duty — closing
completed: the tracked population is fully discharged on the merged refdomain:cliexecution seat (#6024), R73, sessionsession_01TSf4DV7ziu4V5j73e46b7c, 2026-09-11T06:2xZ. Reached as patrol H19 row on anchor #9857 (sweep 2026-09-11T01:55:32Z), which marked this card UNJUDGED, ⛔ not clean.First, the block itself is void — and ⛔ not for the reason the row suggests
Blocked-by: #14313(5578536705, then restated machine-readably 2026-09-09) resolves to HTTP 404 on both/issues/14313and/pulls/14313.⛔ Not a credential artifact and ⛔ not a deleted-PR artifact, measured three ways:
/issues/15673returns 200 for a known PR on the same token ⇒ the issues endpoint does resolve PRs here.- Live neighbours bracket the hole: showcase: the New Project wizard offers status values the state machine refuses on create, and the refusal only lands at step 3 as an unlocalized "Invalid project status transition." #14311 ✓ / client SDK
oauth.*family: bind the 5return res.json()methods to their better-auth wire shapes (ISOstringtimestamps) — #12104 family card 1 of 3 #14312 ✗ / client SDKauth.*family: bind the 14return res.json()methods (auth 7 · sessions 3 · twoFactor 3 · accounts.unlink 1) to their better-auth wire shapes — #12104 family card 2 of 3 #14313 ✗ / client SDKorganizations.*family: bind the 19return res.json()methods (organizations 11 · invitations 3 · teams 5) to their better-auth wire shapes — #12104 family card 3 of 3 #14314 ✗ / [finding] the 14 generatedcontent/docs/references/*/index.mdxoverviews carry no "do not edit" banner, while the 211 pages beside them do #14364 ✓ / finding(objectql):registerObject's cross-package ownership refusal throws a bareError, so no rejection test on that path can assert an ADR-0112 envelope #14367 ✓. git log --grep='(#14313)'onorigin/mainreturns NONE, while the same command for(#15673)returns53cbad9f⇒ these numbers were never merged PRs either.
⇒ #14313 was an issue that no longer resolves. The systemic fix is already filed and queued as #17512 (
domain:devx, p2) — ⛔ this seat does not duplicate it.⚠️ One thing to hand that card: its table lists only #16785/#16685 as the source-cited class, but #14313 and #14366 are cited from source too (packages/client/src/index.ts:4497,return-type-precision.test.ts:849,packages/client/CHANGELOG.md:14,scripts/check-adr-0087-registration.mjs:4905; and #14366 across ~15packages/restsites). Its "swept by nothing" class is larger than it measured.The reason this closes: all three family children SHIPPED
The 2026-08-31 maintainer ruling (batch #1,
hotlong「同意」, option A) split the remaining 38 better-auth methods into three cards. Every one has landed — read offpackages/client/CHANGELOG.mdonorigin/main, ⛔ not from the merge events:child family changeset entry Card 1 of 3 oauth.*— 4 narrowede944fdb#14312 Card 2 of 3 auth.*— 13 narrowedb1b978c#14313 Card 3 of 3 organizations.*— 19 narrowed, 20 ledger entries closed7092d63#14314 Plus
7beaaa3(#15451) closedoauth.applications.delete, the fifthoauth.*method card 1 deliberately could not reach. With this card's own PR #13082 (5 in-repo producers), the arithmetic closes on the card's own figure: 5 + 19 + 14 + 5 = 43.The shrink-only ledger agrees, and it is the machine-readable authority:
packages/client/exported-any-returns.jsonis down to 2 entries (card 3's own entry says 「22 entries before, 2 after」).⭐ The one residual site is deliberate, and it is pinned as deliberate
The ledger's remaining
#12104-attributed site isObjectStackClient.auth.deleteUser. It stays by maintainer ruling (2026-08-12 on #7735), and three independent places hold that on purpose:packages/client/src/index.ts:4501— 「user.deleteUserdeliberately unconfigured」;packages/client/src/auth-route-ledger-coverage.test.ts:52— thedisabledrow 「deliberately keeps itsclient(auth-method-matrix:change-emailanddelete-userare booked as mounted inauth-route-ledger.tsbut plugin-auth never configures better-auth'suser.changeEmail/user.deleteUser— no switch to enable them #7735 —auth.deleteUser)」;packages/client/src/return-type-precision.test.ts:877— 「##auth.deleteUseris NOT bound, and that is the finding」, with a type-level pin at:951asserting it equalsany.
Card 2's changelog states the principle verbatim: 「No declared return type can be honest for a value the runtime never delivers. That the shrink-only ledger still carries exactly this one
auth.*entry is the mechanism working.」⇒ Binding it would break a green type-level pin and contradict a standing ruling. It is not this card's debt; it is the ruling's record.
⛔ A correction to an inference I nearly closed this card on
My first reading was 「the ledger is down to 2 entries, so both heads are nearly done」. That is wrong, and the ledger's own
$commentsays why, verbatim: 「21 of #11925's 38 unannotated methods are absent here: they areany-CONTAINING, and they remain #11925's to close」. The gate asks whether a type ISany, not whether it contains one. ⇒ ⛔ ledger size is not a worklist length, and #11925 is not nearly done — see the seat's round report. Recorded because the next reader will make the same jump.Disposition
closed/completed,pm:blockedstripped in the same pass with a targeted single-label DELETE (⛔ never a whole-set replace, which can clobber a concurrent seat's write).domain:cli·tracking·priority:p2stay — ownership and grade are ⛔ not state.⛔ Nothing here re-opens the
deleteUserquestion: that is #7735's, and it is ruled.domain:cli执行席 ·session_01TSf4DV7ziu4V5j73e46b7c(os-sales) · R73 · 2026-09-11T06:2xZ(读表)
Generated by Claude Code
- added a commit that references this issue
on Oct 7, 2026
Found while building the client-side
check:exported-anyequivalent (#11927). Out of that card'sdeclared scope and deliberately not fixed there — that card builds the ratchet and seeds its
ledger; this is the worklist the ledger now names and nothing schedules.
Measured at
0e0bf8049, on the builtpackages/client/dist/index.d.ts.The spelling
#8140's census enumerated four spellings, all putting
anyinside aPromise< … >returnannotation where a grep can see it. #11925 added a fifth: no annotation, type inferred from
this.unwrapResponse< …any… >(res).There is a sixth, and it is the largest single class:
The method carries no return annotation and never names
any,Promise, orunwrapResponse, soit is invisible to every grep #8140's census and #11925 used. Its published type is
Promise< any >all the same, becauseResponse.json()is declaredPromise< any >inlib.dom.Count and how it was taken
Not a grep — the count comes from asking the type checker what each export resolves to on the
built declaration, which is the whole reason #11927 reads a built
dist:Classified by mechanism against
packages/client/src/index.ts, the 65 split:Promise< any >, deliberate — no contract exists to bindreturn this.unwrapResponse< any >(res)return res.json()organizations.invitations.resend→organizations.invite)inviteThe 43
analytics.*—query,meta,explain,queryDataset(4)organizations.*—create,update,setActive,get,listMembers,invite,leave,delete,removeMember,updateMemberRole,getActiveMember(11)organizations.invitations.*—cancel,accept,reject(3)organizations.teams.*—create,update,delete,addMember,removeMember(5)oauth.*—applications.register,applications.get,applications.getPublic,applications.delete,consent(5)auth.*—updateUser,changePassword,setInitialPassword,changeEmail,sendVerificationEmail,verifyEmail,deleteUser(7)auth.sessions.*—revoke,revokeOthers,revokeAll(3)auth.twoFactor.*—verifyTotp,disable,verifyBackupCode(3)auth.accounts.unlink(1)automation.trigger(1)Why it is worth its own card rather than a rider on #11925
unwrapResponse< …any… >#11925's population is erased by a typeARGUMENT the author wrote; these are erased by a lib declaration the author never sees. A sweep
written for one does not find the other, which is exactly how this class survived two censuses.
unwrapResponse< …any… >#11925 ismeta.*/packages.*/ cloudprojects.*. This isauth.*/organizations.*/oauth.*— the better-auth-backed surface, where the responseshapes are third-party and may need contracts authored rather than relayed (same class as
Four client SDK routes answer a shape no published contract declares —
automation.create/automation.update/search/data.clone#11924 for some of them, plain annotation work for others). That split wants triage.packages/clienthas nocheck:exported-anyequivalent — #8140 fixed 51 sites and nothing stops the 52nd #11927's gate holds all 65 as named, shrink-onlyledger entries: a 66th site is red, and a site fixed here is red until its entry is deleted. This
card is the worklist, not a leak.
Note on the overlap with #11925
#11925 counts 38 unannotated
unwrapResponse< …any… >methods; #11927's gate flags 17 ofthem. The other 21 are typed
{ package: any }/{ packages: any[]; total }/{ project: any }— return types that CONTAIN
anyrather than BEINGany, which is a deliberate and documentedscope line in both
check:exported-anygates (a type withanyinside it is a far broaderquestion, and admitting it costs the gate its zero-false-positive property). Those 21 are #11925's
alone and are not ledgered by #11927 — recorded here so the two counts are not read as
contradicting each other.
Generated by Claude Code