fix(session): close the review gaps the boundary opened

- `SessionSummary.updatedAt`'s wire doc still said "Persisted file mtime",
  which stopped being true for attached sessions.
- The core invariant let `session/inherited` fall through the merge-extensible
  default. It is core-owned, so it gets an explicit case; an unbalanced seed
  legally places it inside an open turn, which the relation permits.
- The Agent Note claimed the boundary reaches disk via `live.pending`/
  `scheduleDrain`. Verified false: the constructor append precedes `enter()`,
  so it never publishes on `session/event` and rides the creation seed instead.
  Attaching is therefore a write where none happened before — recorded, since
  only `load()` stays a pure read.
- The deferred-index proposal asserted this change documented the cold-mtime
  skew on `dsh-host-apiproxy`. It did not; the README entry now exists.
- `firstLiveSeq`'s firehose gap runs through its own seq, not below it.
- The boundary is not always at `firstLiveSeq` (the idempotence guard), so
  consumers scan for the last one.
- `lastActivityTime` excludes by type, so a pickup time still leaks onto a
  synthetic closer when a boundary ends an open turn. Documented.
- Pin the fork claim end-to-end: a child inherits a still-running parent's
  open bracket below its own boundary, while the parent has none. Fails if the
  write moves back to the load path.
- Fix the telemetry title that contradicted its own assertions.

The `/status` call site cannot be pinned the way the other two are: the
command appends its own `command/run` before rendering, so the boundary is
never the log tail there. Its fixture now at least renders over a
boundary-bearing log.
This commit is contained in:
Hypatia May
2026-07-30 13:59:08 +08:00
parent a00786362f
commit 39ebd8f745
26 changed files with 115 additions and 23 deletions
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/proposed/architecture/2026-07-29-durable-last-activity-index.md
2026-07-29-durable-last-activity-index.md: 7d8a83e96d9339054b0d15b2331284c8aecfe65d
2026-07-29-durable-last-activity-index.zh.md: b081aa91cfa51ef8bb87e93f4091431f2d6ddce1
2026-07-29-durable-last-activity-index.md: 2569ce8dcdef4ce85713e4c3905c3a170be00a98
2026-07-29-durable-last-activity-index.zh.md: 64078982bb09b0f94c41d5424d195d371c1a263f
@@ -12,7 +12,7 @@ mtime answers a different question: when the artifact was last written. Every du
The attached projection has a real fix — `lastActivityTime()` skips boundaries — but it needs the event log, and the cold path deliberately does not read one. Reading the log to compute `updatedAt` would defeat the header-only listing that keeps `list()` scaling with session count rather than log size.
The boundary change raised the frequency of this defect and documented it as a known limitation on `dsh-host-apiproxy`. It did not introduce the approximation, and removing the approximation is a durable-format decision, which is why it is scoped here rather than there.
The [boundary change](../../implemented/architecture/2026-07-30-session-inherited-log-boundary.md) raised the frequency of this defect, because a pickup now writes where nothing was written before; `dsh-host-apiproxy`'s README records it under Known Limitations. It did not introduce the approximation, and removing the approximation is a durable-format decision, which is why it is scoped here rather than there.
## Proposal
@@ -12,7 +12,7 @@ mtime 回答的是另一个问题:这份产物上次是什么时候被写入
已附加会话的那个投影有真正的修复办法(`lastActivityTime()` 会跳过边界),但它需要事件日志,而冷路径有意不去读日志。为计算 `updatedAt` 而读取日志,会让只读 header 的列举失去意义,而正是它让 `list()` 的开销随会话数量而非日志体量增长。
边界那次变更提高了这个缺陷的出现频率,并把它作为一项已知限制记录在 `dsh-host-apiproxy` 上。它并没有引入这套近似做法,而移除这套近似是一项持久格式决策,因此它的范围划在本文,而不是那里。
[边界那次变更](../../implemented/architecture/2026-07-30-session-inherited-log-boundary.md)提高了这个缺陷的出现频率,因为一次拾起如今会在此前完全无写入的路径上产生写入;`dsh-host-apiproxy` 的 README 已在 Known Limitations 中记录该项。它并没有引入这套近似做法,而移除这套近似是一项持久格式决策,因此它的范围划在本文,而不是那里。
## 提案