Repository navigation
Commit 135054e
fix(pm): fleet-roster reads a shared-account seat's own activity (#28941)
<!-- claude-projects-attribution: {"github_login":"marchtian"} -->
_Requested by **田騏** · [project
thread](https://claude.ai/code/project/chan_01VmuUhXU9SSUzqJYyXSmYSi?thread=cmsg_01VmuUhXU9SSUzqJYyXSmYSi9paTx1Lu8ENYwwssthfK3h)_
Fixes #28906
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:车队总表的「最近活动」改读席位自己的痕迹(座位贴、在飞卡、本席信箱 PR、近 6
小时署名为本席的评论),同账号多席里的高位席不再被误标「疑似撞墙」。
- **为什么改**:一个账号坐同车道多席是规则允许的并发,但高位席没有自己的在飞卡,总表只剩座位贴时间可看,把 server 车道 2、3、5
席误判为撞墙,真撞墙被淹没。
- **风险与回滚**:只改一个只读视图脚本 `scripts/pm/fleet-roster.mjs`,不发布;每次生成多一个分页读(今日约
4 页);回滚即 revert 本 PR。
- **先看**:`scripts/pm/fleet-roster.mjs` 中 `buildRows` 的 `times` 一行与
`signedActivity`;其次 `SIGNATURE_LINE` 镜像及其等值自测。
- **两分钟验证**:`node scripts/pm/fleet-roster.mjs --self-test`,预期 `✓
fleet-roster self-test: 104 cases in 9 batteries pass.`
- **需要你做的**:无,席位落地。
## What changes
`scripts/pm/fleet-roster.mjs` only (+109 / -11).
- **Last activity is the newest of the seat's own traces**: the seat
post's `updated_at`, its in-flight cards, its mailbox pull's
`updated_at` (the mailbox column's pull, already in the
`pulls?state=open` read, so no new call), and every comment of the last
`STALE_HOURS` whose first non-empty line is a seat signature `LANE#N ·
ACCOUNT · SESSION` naming THIS row's lane and seat number. The
attribution is by seat, never by account, so two seats on one account
stay apart.
- **The in-flight column is unchanged**: a seat whose account holds a
lower seat of its lane still reads `= seat N`. `STALE_HOURS` is
unchanged (6).
- **One new read per repo**: `GET /repos/R/issues/comments?since=(now −
6 h)`, paged through `gh-pages.mjs`. The footer of the generated body
names it with the other reads.
- **The signature pattern is mirrored, not imported**: the relay imports
`ROSTER_MARKER` from this file, so it must not load
`check-half-states.mjs` (the same reason `isEpicParent` mirrors H45).
The self-test holds `SIGNATURE_LINE` byte-equal to `SEAT_SIGNATURE_LINE`
(source and flags) and checks `signedSeatOf` against `readSignatureLine`
over a corpus.
## Tests
New self-test battery `one account on two seats of a lane: the second
seat reads its own traces` (13 cases, battery floor 8 → 9). The cases:
- With no trace of its own, seat 2 reads `= seat 1` and its post alone,
and is flagged.
- A fresh mailbox pull lifts the flag. A fresh comment signed
`domain:server#2` lifts it too.
- When the mailbox and the signed comment are both older than
`STALE_HOURS`, the seat is flagged.
- Seat 1's fresh signed comment and fresh mailbox (same account) leave
seat 2 flagged.
- These sign nothing for the seat: another lane's seat 2, a signature
that is not the first non-empty line, and a misspelled signature.
- The newest of the four traces wins.
- The rendered row keeps `= seat 1` without the flag.
- The read window is `STALE_HOURS`, and the footer names the read.
<details><summary>Self-test and suites run on 01dbcd7</summary>
```
node scripts/pm/fleet-roster.mjs --self-test :: exit 0
✓ fleet-roster self-test: 104 cases in 9 batteries pass.
node scripts/pm/fleet-write/dispatch.mjs --self-test :: exit 0 (imports ROSTER_MARKER from this file)
✓ fleet-write/dispatch self-test: 265 cases pass across 24 batteries
pnpm check:pm-record-recognisers :: exit 0 (runs fleet-roster --self-test)
```
</details>
## Acceptance notes
- **The mailbox share counts inbound doorbells.** A mailbox pull's
`updated_at` moves on any comment, including another seat's `To:` line,
so a walled seat that keeps receiving doorbells reads fresh. The card's
Wanted names the mailbox, so it is in. A signed comment is the stricter
trace, because it is attributed to its writer.
- **The comment read stops at `STALE_HOURS`.** The flag is exact. For a
flagged row, a signed comment older than 6 h is not read, so the age
cell can read older than that comment. The read window keeps the cost
bounded: 388 comments in 6 h today against a 5000-row `MAX_PAGES` guard.
A 24 h window measured 1720 comments and 18 pages.
- Only comments on repos in `--repos` are read. A `repo:cloud` seat's
signed comments land on the cloud repo, so those rows still read their
post alone.
- In this session hotcrm answers 403, so the live runs used `--repos
objectstack-ai/objectstack,objectstack-ai/objectui`. The default
three-repo run fails at hotcrm here before and after this change.
## Verification
Live, read-only, before at `origin/main` 9a9823d and after on this
branch:
| seat | post | before (last activity · flag) | after (last activity ·
flag) | after: mailbox pull · newest signed comment |
|:--|:--|:--|:--|:--|
| `domain:server#2` | #20966 | 2026-10-31T02:56Z · 疑似撞墙 |
2026-11-02T04:27Z · none | #28284 04:03Z · 04:27Z |
| `domain:server#3` | #26761 | 2026-11-01T06:46Z · 疑似撞墙 |
2026-11-02T04:07Z · none | #28585 03:49Z · 04:07Z |
| `domain:server#5` | #28677 | 2026-11-01T13:13Z · 疑似撞墙 |
2026-11-02T04:16Z · none | #28678 03:50Z · 04:16Z |
The run flagged 9 rows before and 6 after. The other six flags are
unchanged: `repo:cloud` 1 and 2, `repo:docs.objectos.ai` 1,
`domain:spec` 3 and 5, and `repo:www.objectos.ai` 1. Each server seat's
mailbox and its signed comment are both fresh, so either trace alone
lifts its flag. Seat 1 keeps its 9 in-flight cards, and seats 2, 3 and 5
still read `= seat 1`.
**API cost**: the run made 23 `gh api` calls after the change and 18
before. The 5 new calls are the comment reads: 4 pages on objectstack
and 1 on objectui.
<details><summary>Gates on 01dbcd7 (dispatch-gates --commands, 31
derived)</summary>
All 31 derived commands exited 0. The `--ran` reconciliation reads: `✓
dispatch-gates --ran: 31 derived famil(ies) accounted for — 31 run, 0
NOT-MEASURED (a DERIVED zero — all 31 recorded an exit code and none of
them is 3).`
The 31 are `check-closing-keyword-parity` (+ self-test),
`check-comment-mask-corpus`, `check-declaration-mirrors` (+ self-test),
`check-self-test-wired` (+ self-test),
`check-self-test-workflow-commands` (+ self-test),
`check-whole-set-label-write` (+ self-test), `plan-test-core-shards
--self-test`, `bare-root-worklist --self-test`, and `pnpm check:`
agent-test-spelling, bash32-floor, ci-filter-parity, cli-command-ids,
cross-package-test-inputs, driver-memory-census, entry-guard,
gitlink-declared, nul-bytes, parse-guard, pm-dispatch-gates (fast tier,
1937 cases), pm-record-recognisers, pnpm-filter-targets,
pnpm-flag-spellings, ratchet-remedy-authority, refd-timer-probe,
scripts-symbol-anchors and watch-hint-literal.
Outside the derived set, I ran the rosters under `scripts/`:
- Exit 0: `check-published-list-mirrors`,
`check:engine-double-contract`, `check:i18n-stale-fill` and
`check:pm-label-desc-cap`.
- Exit 3, NOT MEASURED: `check:dts-closure` and
`check:published-readme-exports`. Their prerequisite is built packages,
and this diff touches none.
</details>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>1 parent 62b3ec3 commit 135054e
1 file changed
Lines changed: 109 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
67 | | - | |
68 | | - | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
69 | 78 | | |
70 | 79 | | |
71 | 80 | | |
| |||
283 | 292 | | |
284 | 293 | | |
285 | 294 | | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
286 | 327 | | |
287 | 328 | | |
288 | 329 | | |
| |||
319 | 360 | | |
320 | 361 | | |
321 | 362 | | |
322 | | - | |
| 363 | + | |
323 | 364 | | |
324 | 365 | | |
325 | 366 | | |
| |||
330 | 371 | | |
331 | 372 | | |
332 | 373 | | |
| 374 | + | |
| 375 | + | |
333 | 376 | | |
334 | 377 | | |
335 | 378 | | |
| |||
350 | 393 | | |
351 | 394 | | |
352 | 395 | | |
353 | | - | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
354 | 401 | | |
355 | 402 | | |
356 | 403 | | |
| |||
368 | 415 | | |
369 | 416 | | |
370 | 417 | | |
371 | | - | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
372 | 422 | | |
373 | 423 | | |
374 | 424 | | |
| |||
379 | 429 | | |
380 | 430 | | |
381 | 431 | | |
382 | | - | |
383 | | - | |
| 432 | + | |
384 | 433 | | |
385 | 434 | | |
386 | 435 | | |
| |||
414 | 463 | | |
415 | 464 | | |
416 | 465 | | |
| 466 | + | |
417 | 467 | | |
418 | 468 | | |
419 | 469 | | |
| |||
483 | 533 | | |
484 | 534 | | |
485 | 535 | | |
486 | | - | |
| 536 | + | |
487 | 537 | | |
488 | 538 | | |
489 | 539 | | |
| |||
498 | 548 | | |
499 | 549 | | |
500 | 550 | | |
| 551 | + | |
501 | 552 | | |
502 | 553 | | |
503 | 554 | | |
| |||
552 | 603 | | |
553 | 604 | | |
554 | 605 | | |
555 | | - | |
| 606 | + | |
556 | 607 | | |
557 | 608 | | |
558 | 609 | | |
| |||
606 | 657 | | |
607 | 658 | | |
608 | 659 | | |
| 660 | + | |
609 | 661 | | |
610 | 662 | | |
611 | 663 | | |
612 | 664 | | |
613 | 665 | | |
614 | | - | |
| 666 | + | |
615 | 667 | | |
616 | 668 | | |
617 | 669 | | |
| |||
732 | 784 | | |
733 | 785 | | |
734 | 786 | | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
735 | 833 | | |
736 | 834 | | |
737 | 835 | | |
| |||
0 commit comments