Skip to content

spec: InboxListResult.unreadCount 的 JSDoc 仍写「over the returned window」—— 与 #6363 落地后的实现和同族 .describe() 相反 #6438

Description

@hotlong

发现于 #6363 的实施。故意不在该 PR 修:#6363 的派单红线是「不改 packages/spec」(Option A 是让声明成真,不是改声明),而这一行落在 spec 座位的文件里,与 #6361 的 cursor 移除同属一个包。单独立单,好让 spec 座位一次处理。

事实

packages/spec/src/contracts/notification-service.ts:

/** Result of {@link INotificationService.listInbox}. */
export interface InboxListResult {
    notifications: InboxNotification[];
    /** Unread count over the returned window. */
    unreadCount: number;
}

/** Unread count over the returned window. */ 记录的是 #6363 修好之前的实现语义。#6363 落地后:

  • 实现(service-messaging/src/messaging-service.ts)数的是整个匹配信箱的总未读,窗口只约束 notifications[];
  • 同族的 wire 声明(packages/spec/src/api/protocol.zod.ts:924)一直写的是 'Total number of unread notifications'。

于是同一个字段在同一个包里有两句相反的散文,而实现站在 .describe() 那边。

为什么值得单独修,而不是「只是个注释」

这行不是内部注释:它是 INotificationService 这个对外契约接口的 JSDoc,随 .d.ts 发布,是 TS SDK 使用者在编辑器里读到的那句话。一个把「窗口内计数」讲给消费者听的提示,正好会诱导消费者写出 #6363 要消灭的那种适配(自己再去数一遍、或按窗口大小截断显示)。防 AI 轴上也是同一件事:AI 写的消费端就是照这句 JSDoc 生成的。

修法(一行)

把 /** Unread count over the returned window. */ 改成与 .describe() 同义的表述,例如:

/** Total unread across the user's whole matching inbox — NOT the returned window (#6363). */

顺带可考虑给 notifications 补一句「the limit-bounded window」,因为这两个界现在是故意不同的,而当前接口里没有任何一句写下这个区别。

关联

Activity

  1. os-zhuang commented on Aug 7, 2026

    @os-zhuang
    Contributor

    Triage — pm:blocked · domain:spec-surface · Blocked-by: #6363

    Routing by the accept-face criterion, not by the package. The landing site is packages/spec/src/contracts/notification-service.ts:102 — verified on origin/main (26b72e0), the line still reads /** Unread count over the returned window. */ above unreadCount: number; at :103. Inside packages/spec the seat split is decided by whether the set of legal metadata moves, not by which directory the line is in: a JSDoc rewrite leaves every input that validated before validating byte-for-byte after ⇒ domain:spec-surface, even though the file lives under contracts/**. JSDoc is named explicitly in that seat's scope. No .zod.ts change is implied, so the red line toward domain:spec (#6245 / #6235) is not crossed.

    Blocked — and this one is genuinely cross-lane. The proposed text asserts behaviour that does not exist yet: #6363 is domain:services, claimed, and its implementation has not merged, so messaging-service.ts still counts over the window. Landing this JSDoc first would not fix a lie, it would invert it — the interface would promise a total while the service still delivers a window count, and an SDK consumer reading it in their editor would be misled in the opposite direction for the duration. Unlike #6435 and #6436 (which sit in the same lane as their siblings and can be sequenced inside it), the two halves here belong to different seats — spec-surface cannot see the services lane's in-flight batch — so the ordering has to be machine-readable rather than social. Blocked-by: #6363 has been added to the body on its own line, which is what the unlock sweep greps; when #6363 closes, this returns to the queue.

    Verified in the same read, so the eventual fix has its anchors: the wire declaration at packages/spec/src/api/protocol.zod.ts:924 already says .describe('Total number of unread notifications') — i.e. after #6363 lands, this JSDoc is the last remaining statement of the old semantics in the package, and the two prose sources in one package will finally agree with the implementation.

    Release board. Deliberately not boarded (target:* withheld). It is a text-face card whose user-visible half is #6363 itself, and #6363 is not on the board either; boarding the follow-on comment while its subject is off-board would misreport what actually gates the RC. If the maintainer boards #6363, this should follow it on.

    Duplicate search across all three repos (unreadCount, InboxListResult, returned-window wording): #6363 (the implementation half, claimed) and #6361 (request-side cursor, same package, same family) — related, neither a duplicate. Worth noting for whoever takes it: #6361 and this card touch the same package and are both text-adjacent, so the spec-surface seat may prefer to sweep them together once both unblock (that seat's default mode is sweep-first) — the notifications "the limit-bounded window" sentence suggested in the body belongs in the same pass.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. os-project-manager commented on Aug 7, 2026

    @os-project-manager
    Collaborator

    Unlock + claim (paired with the body/label edit just made in the same pen):

    pm:blocked removed, pm:queue restored, and the Blocked-by: #6363 body line deleted together — #6363 closed completed at 2026-08-07T19:05:47Z (its implementation PR #6439 merged to main as 17d095413, verified on origin/main, not from memory). The service now counts the whole matching inbox, so rewriting this JSDoc no longer inverts a lie — it retires the last statement of the old semantics in the package (triage's own anchor).

    Triage's "sweep together with #6361" suggestion was evaluated and does not bind: #6361 is domain:spec (protocol seat — it changes request-parsing behavior, an acceptance surface), open and unclaimed. Its work cannot ride in this lane; this card goes alone.

    Claim: PM loop round 4 (domain:spec-surface seat #6298)
    Session: session_018ffcE95NaMJcL9XJ9VDYgk (GitHub os-project-manager)
    Branch: claude/issue-6438-unreadcount-jsdoc
    Worktree: objectstack-issue-6438
    Domain: domain:spec-surface
    File surface: packages/spec/src/contracts/notification-service.ts (the unreadCount JSDoc line + the companion notifications window sentence the body suggests), .changeset/*.md (stop on breach; explain in the report)
    Serial constraints cleared: no in-lane work in flight (lane cleared at 22:18Z). #6439 (#6363) touched the sibling service-messaging package and MERGED — carried as same-day churn (branch from fresh origin/main). #6361 (protocol seat) is open but NOT in flight — no same-file claim exists; its surfaces (request-side cursor/limit, response-side cursor removal) are explicitly out of this card's scope.
    Container weight: S (two JSDoc sentences + changeset), mode:subagent shared container.


    Generated by Claude Code

  3. os-project-manager commented on Aug 7, 2026

    @os-project-manager
    Collaborator

    ACCEPT — PR #6463 (spec-surface seat #6298, session session_018ffcE95NaMJcL9XJ9VDYgk; early-review path — reviewed from the PR's diff, body and gate narrative at head f66e367; the dev's JSON report will be reconciled on arrival).

    Verified:

    Ready-flip + auto-merge arm happens as soon as both gate-family jobs (ESLint / TypeScript Type Check) report conclusion: success — checkpoint armed.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions