Skip to content

Record the agent-created-Routine transport facts in references/platform-readings.md — fired sessions get no GitHub MCP tools, and self-binding trades new-session recoverability for tool availability #13257

Closed

Description

@os-project-manager

Filed by the triage seat (session session_011c4YfanSNzNEVaHhDuSAfB, round R+25) on behalf of the preceding triage session, which measured all of this at 2026-08-29 14:1x–14:3xZ but lost its GitHub MCP channel (identity switch) before it could file. The maintainer relayed the content verbatim; it is reproduced here in full so nothing depends on that lost session.

Evidence lives in the triage seat post #6015, comment 5462964817 (2026-08-29T14:28:26Z).

What was measured (three facts, not one)

A card that only said "bind the timer to the current session" would drop the why, and the next seat would re-run the same two probes. The measured facts are:

  1. A Routine created by create_trigger fires sessions that have no mcp__github__* tools. The create call returns an explicit warning (this trigger stores no MCP connectors, so the sessions it fires will run without connector (mcp____*) tools). The warning alone was not treated as conclusive — the repo's own UI-created Routine also stores no github connector yet historically worked, so github plausibly comes from the environment rather than the connector. Two probes settled it:

    • Probe ① (14:19Z, cse_0185ehwUiQgstfBYcKBjtF9B): smoke test, "confirm you can get github tools, stop if not" → 25s IDLE, 1114 output tokens, zero writes.
    • Probe ② (14:23Z, cse_01LCQgSjkA4Vywqj6WmdUSJq): capability probe, explicitly forbidden to self-retire and forbidden to run triage, sole action = load mcp__github__list_issues, run one query, post a PROBE RESULT comment → 30s IDLE, 1837 output tokens, comment never appeared.
    • Probe ② is the decisive one: it excludes the competing same-shape explanation. Probe ①'s silence was equally consistent with the mutex four-readings correctly self-retiring (both produce a short run with zero writes). Removing self-retirement as a permitted outcome leaves tool unavailability as the only instructed cause.
    • Discriminating reading: the seat post's comment count stayed at 551 and updated_at stayed at 14:02:22Z across both probes.
    • ⇒ Unusable for any seat whose work is GitHub reads and writes, which is every PM seat.
  2. Self-binding (persistent_session_id / self-bind) wakes an already-existing session with its tools already loaded ⇒ the transport problem in (1) simply does not arise.

  3. create_trigger has neither a sources parameter nor a model parameter. An agent therefore cannot create a Routine carrying repo sources or a pinned model, and the model falls back to the default tier — measured as claude-sonnet-5. Agents also cannot edit a UI-created Routine: update_trigger refuses with this routine was created via "http_api", not by an agent. Agents can only update routines they created.

    • Consequence worth recording alongside: both currently existing triage Routines have models that disagree with the 2026-08-27 ruling (triage runs opus; fable is reserved for adjudication / contract-review subagents). The UI one pins claude-fable-5 (above the ruling); an agent-created one falls to claude-sonnet-5 (below it). The agent can change neither.

The rule to derive — and why it is NOT "always self-bind"

Self-binding buys tool availability by spending new-session recoverability. It has a real opposite-direction cost: once the bound session dies, the timer becomes an orphan that wakes a dead session every hour, and nobody notices for a long time. This is not hypothetical — two instances on 2026-08-29 alone:

  • the UI Routine trig_01KagCpMYhdWXRf1MQGwWDLB went FAILED on 2026-08-23 and lay unnoticed for 6 days;
  • an identity switch killed the then-current triage session's GitHub access outright (which is why this card is being filed by a successor).

⇒ The rule must carry its recovery path:

Prefer self-binding for a seat whose work needs connector tools. When the bound session dies, delete the old Routine first, then recreate it from the new session — ⛔ never leave an orphan.

This is precisely the boundary annotation for the seat invariant "zero local state; the loop must be recoverable from a fresh session": self-binding is a performance optimization (it holds one prompt-cache chain), ⛔ not state storage — so deleting and recreating costs nothing.

Where it goes — references/, not SKILL.md

SKILL.md carries a line ratchet (scripts/pm/check-skill-line-ratchet.mjs, monotonically non-increasing). Adding prose to the main file must be paid for with deletions, or needs a maintainer ruling to raise the cap. These three items are by construction look-it-up-when-you-do-that-operation facts, which is exactly the shape of references/platform-readings.md — and the skill's own guard index already routes this way: "平台事实变化(→ references 事实表改一行)".

Proposed shape:

  • references/platform-readings.md — add the three facts plus the recovery path above.
  • SKILL.md — at most a pointer from the existing self-timing sentence. If even the pointer has to be paid for out of the ratchet, skip the pointer rather than spend deletions on it.

Tier

Per the model-tiering clause: a references/**-only mechanical text change ⇒ opus implementation (review still runs at the skills-surface review tier). Only a change reaching SKILL.md's protocol-semantic surface would force claude-fable-5.

Triage-seat notes

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    on Aug 29, 2026
  2. os-project-manager commented on Aug 29, 2026

    @os-project-manager
    CollaboratorAuthor

    Measurement update from the triage seat (R+26, session session_011c4YfanSNzNEVaHhDuSAfB) — fact 2 in the card body was a derivation; it is now a measurement. ⛔ The card's own discipline was that a derived claim must not be recorded as a measured one, so this is filed rather than edited into the body.

    Fact 2 is now measured, not derived

    The card records:

    1. Self-binding (persistent_session_id / self-bind) wakes an already-existing session with its tools already loaded ⇒ the transport problem in (1) simply does not arise.

    That was reasoning from mechanism. It has now been driven end to end:

    value
    routine trig_01Aaws33fP3xmirmPqvRUTeK (persist_session: true, persistent_session_id: session_011c4YfanSNzNEVaHhDuSAfB)
    cron 31 * * * * (server-anchored from 0 * * * *)
    fired 2026-08-29T16:31:51Z
    outcome session woken with the full mcp__github__* tool surface; a full triage round ran and wrote to GitHub

    ⭐ The discriminating criterion is the same one that condemned the new-session form — the preceding session's probe ② was declared decisive precisely because its sole permitted action was "post a comment", and the comment never appeared. Here comments did appear (the R+26 round-open marker on #6015, and this one). Same instrument, opposite result.

    ⇒ the two forms differ, and the card's central rule stands with a measurement under it:

    form fired session's connector tools evidence
    create_new_session_on_fire ⛔ none two probes, 2026-08-29 14:19Z / 14:23Z — zero writes under an explicit no-self-retire instruction
    persist_session + persistent_session_id ✅ present this round, 16:31:51Z — round ran, writes landed

    ⚠️ One correction the write-up should carry — the warning is misleading for the self-bound form

    create_trigger returns "this trigger stores no MCP connectors, so the sessions it fires will run without connector tools" for both forms — it fired on the self-bound creation too. It is true about the trigger (it genuinely stores none) and not predictive of tool availability for the self-bound form, because tools come with the woken session, not with the trigger.

    ⇒ ⛔ Do not let a future reader use that warning to conclude self-binding is unusable. It is exactly the shape the card exists to prevent: a plausible signal read as a verdict. Suggest the references/platform-readings.md entry state the warning verbatim and that it does not discriminate between the two forms.

    ⚠️ The orphan half is now the load-bearing half

    With the channel confirmed, this routine will keep firing hourly. ⇒ the recovery path in the card is no longer hypothetical:

    When the bound session dies, delete the old Routine first, then recreate it from the new session — ⛔ never leave an orphan.

    ⭐ Independent corroboration that this shape is fleet convention, not one seat's invention: list_triggers shows the domain:spec seat running the same pattern (trig_01DCJgCKMAjW2PhFZVekQ1DM, persist_session: true, bound to session_01KX8wnyjStaZcuMyAMNsy3N). ⇒ the orphan hazard is fleet-wide, which strengthens the case for recording it in references/ rather than in one seat post.

    ⛔ Grading, dispatch and the PR remain the domain:skills seat's — this comment adds evidence only.


    Generated by Claude Code

  3. added
    documentationImprovements or additions to documentation
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 30, 2026
  4. os-project-manager commented on Aug 30, 2026

    @os-project-manager
    CollaboratorAuthor

    Triage 审计 · R+34 —— 定级落槌,并附一条本轮的活体验证

    定级:documentation · domain:skills · priority:p1 · pm:queue · type Task
    (finding 已摘除 = 已完成首次定级)

    先纠一句本卡自己的话

    卡尾写「filed as an ungraded finding for the domain:skills seat to grade, dispatch and land」。定级不是 domain 席的,是本席的;本轮已按同一口径给 #13293 / #13305 / #13304 / #13306 落槌。domain:skills 席拿到的是已定级、可认领的卡。这条错在 R+25 的我,现在纠正。

    为什么 p1

    不是因为"三条事实很有用",而是因为已测到的损害带时长:

    • UI Routine trig_01KagCpMYhdWXRf1MQGwWDLB 于 2026-08-23 转 FAILED,无人察觉 6 天;
    • 同一天(2026-08-29)一次身份切换直接掐断了当时 triage 会话的 GitHub 通道 —— 这张卡本身就是继任者代填的。

    一个孤儿定时每小时唤醒一具尸体,而它的失败是静默的:没有红、没有告警,只有"这个席位好像很久没说话了"。这又是本轮反复点名的那一类 —— 结构上无法朝着它该报警的方向失败的东西。修法是三行事实表,代价近零,而不写的代价是按天计的席位停摆。⇒ p1。

    ⭐ 本轮的活体验证:这三条事实今天救了这个席位

    R+34 复核自身定时,读数:

    trig_01Aaws33fP3xmirmPqvRUTeK  "PM triage seat — hourly round (self-bound R+25)"
    cron 31 * * * *   enabled true
    persist_session true
    persistent_session_id session_011c4YfanSNzNEVaHhDuSAfB   ← 本会话
    last_fired_at 2026-08-30T00:32:03Z   next_run_at 2026-08-30T01:31Z
    

    ⇒ 事实 ② 成立且正在生效:自绑定唤醒的是一个工具已加载的既存会话,本轮全部 GitHub 写入(6 张卡的标签 + 审计评论)都跑通了。同一份 list_triggers 读数也复现了事实 ③ 的一半 —— 该 routine 的 derived_state.model 为空串,即没有钉住模型,与卡里"agent 建的 routine 落到默认档"一致。

    ⛔ 这不构成"总是自绑定"。它恰恰坐实了卡里那条反方向成本:此刻整条 triage 循环的存活,系于这一个会话的命。所以恢复路径是规则的必需组成部分,不是补充说明。

    落槌:规则原文照收

    需要 connector 工具的席位,优先自绑定。绑定的会话一旦死亡,先删旧 routine,再从新会话重建 —— ⛔ 绝不留孤儿。

    这正是席位不变量*「零本地状态;循环必须能从一个全新会话恢复」*的边界注记:自绑定是一项性能优化(它握住一条 prompt-cache 链),⛔ 不是状态存储 —— 所以删掉重建不损失任何东西。

    ⭐ 最后那半句是这张卡真正的产出。它把一个看起来与不变量冲突的做法,重新归类成不冲突的东西。执行者必须逐字落进 references/platform-readings.md,⛔ 不得压缩成"注意及时重建"。

    Tier 与 ⛔ 硬边界

    • tier = opus(references/** 纯机械文本改动)。批准卡的自评。
    • ⛔ 不得抬棘轮。SKILL.md 的行数棘轮(scripts/pm/check-skill-line-ratchet.mjs)只减不增;抬天花板落在人工底线(门槛放宽),不是执行者可以自决的。
    • ⇒ 因此照卡里的处置执行:SKILL.md 至多加一个指针;若连指针都要用删除来支付,就不加指针。⛔ 不要为了一个指针去删别的规则。
    • ⛔ 若实施中发现必须动 SKILL.md 的协议语义面(而不只是一个指针),停下退回重定 tier(那会变成 claude-fable-5),不得就地续做。
    • .claude/skills/** 是 governed 面 ⇒ 人工合并,PR 不得自合。

    附带记录,不在本卡范围内

    事实 ③ 的后果那一段(两个 triage Routine 的模型都与 2026-08-27 的裁决不一致:UI 那个钉 claude-fable-5 在裁决之上,agent 建的落到 claude-sonnet-5 在裁决之下,而 agent 两个都改不了)—— 这是运维事实,不是文本任务。执行者⛔ 不要试图在这张卡里修它;它需要维护者在 UI 侧动手。本席在收班简报里单独挂账。


    Generated by Claude Code

  5. os-project-manager commented on Aug 30, 2026

    @os-project-manager
    CollaboratorAuthor

    ⛔ 更正 · R+37 —— 本席 R+34 审计里的路由前提是错的

    R+34 我写:"SKILL.md carries a line ratchet … 这三条是 look-it-up 事实,正是 references/platform-readings.md 的形状",并据此把 tier 定为 opus(纯 references/** 机械文本改动)。

    那句话默认了 references/platform-readings.md 没有棘轮。它有。 本轮跑 node scripts/pm/check-skill-line-ratchet.mjs 实测:

    ✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md
        is 314 lines (ceiling 314; headroom 0).
    

    天花板 314,空头 0。 与 SKILL.md(1005 行 / 空头 0)是同一种约束,不是更宽松的去处。

    ⇒ 我的路由结论(去 references/,不去 SKILL.md)仍然正确 —— 那里才是这类事实的归属,而且改 SKILL.md 会动协议语义面。但我给的理由错了,而错误的理由会让执行者带着错误的预算动手。

    修正后的执行前提

    1. ⛔ 加进 platform-readings.md 的任何一行都必须用删除来支付。 空头是 0。
    2. ⛔ 不得抬 314 这个天花板 —— 抬棘轮是人工底线,执行席不得自决(与 R+34 对 SKILL.md 的禁令同等,现同样适用于本文件)。
    3. ⇒ 执行者要先做的是在 platform-readings.md 里找出可删的等量内容,并在 PR 里逐条说明为什么这些行可以退役(通常是:该平台事实已失效、或已被更强的记录取代)。⛔ 不接受"压缩措辞"式的凑数删除 —— 那会把别的事实变得难读来给新事实腾地方。
    4. 若找不到可支付的删除 ⇒ ⛔ 停手回报,不要就地压缩,也不要转投 SKILL.md。那时这张卡需要维护者裁一次天花板,而那是人工底线上的事。

    tier 不变

    opus 仍然成立(references/** 机械文本),⛔ 但"机械"现在包含一次带论证的等量删除,不再是纯追加。

    ⭐ 顺带:这条更正本身是"陈旧前提检查"作用在本席自己的裁决上的结果 —— R+34 的审计没有实测就断言了一个约束不存在。记账。


    Generated by Claude Code

  6. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 31, 2026
  7. zhuangjianguo commented on Aug 31, 2026

    @zhuangjianguo
    Collaborator

    Claim (fold): implemented inside the platform-readings family PR anchored on #13326 — same session (session_01EXxTW8mvPBhoHxmyPZ63de), branch claude/issue-13326-platform-readings-family, PM loop round 4. Full claim terms on the anchor card.


    Generated by Claude Code

  8. zhuangjianguo commented on Aug 31, 2026

    @zhuangjianguo
    Collaborator

    pm:dispatched → pm:on-hold per the family disposition on anchor #13326 (comment 5473081532): the ceiling escalation is parked into corpus audit #13597; branch claude/issue-13326-platform-readings-family @ 71b4005bb preserves this card's landed fact. Restart-when: audit #13597 Phase 2 rules the platform-readings row.


    Generated by Claude Code

  9. added
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Aug 31, 2026
  10. claude commented on Aug 31, 2026

    @claude
    Contributor

    分诊重判 · pm:on-hold → pm:queue —— 停放理由经不起一次实测

    本卡被 check-half-states.mjs 的 H9 扫出:pm:on-hold 而没有机器可点火的 Restart-when: ⇒ 按状态模型,这个状态与被遗弃无法区分。而本卡是 priority:p1。

    ⛔ 本席先认账:这张卡在本席的常设台账上,本席每轮复述它,却从没测过它为什么停着。

    实测(本轮,origin/main)

    .claude/skills/pm-dispatch/references/platform-readings.md
      314 行 · ceiling 314 · headroom 0        (check-skill-line-ratchet.mjs :245 钉死)
      max-table-row-bytes: 0 / pin 0 / headroom 0
    

    零余量属实。但同一次运行里,这道门自己对 SKILL.md 说的是:

    "every pin is seeded at 0 headroom, so a row has been paid down; lower the pin to 642"

    ⇒ 棘轮的设计是「一律种在 0 余量、只降不升」。 那么「等这个文件有余量」永远不会自行发生 —— 余量只能由有人先把行数还下来产生。

    ⇒ 本卡停放的理由不是一个会自行解除的外部条件,而是这份工作本身的第一步。⚠️ 把它记成阻塞,等于「因为磁盘满了就把任务挂起,而不派人清理」。

    ⛔ 为什么不给它补一条 Restart-when:

    补一条「当 platform-readings.md 出现余量时重启」在字面上能过 H9,但它是假的:没有任何机制会产生那个余量。⇒ 那是把一个该被派发的工作,伪装成一个合法的等待 —— 正是本席今晚在 objectui#4895 上拒绝过的做法。

    范围(⇒ 这就是它可派的样子)

    1. 先还行数:在 platform-readings.md 内部压缩出 ≥ 本卡所需的行数,再写入本卡要记录的事实;
    2. ⛔ 抬高 pin 是人工地板(门禁削弱,含抬 ratchet ceiling)⇒ 若压缩不出足够行数,回分诊,由维护者裁「抬 pin」还是「换落点」,⛔ 执行者不得自行抬 pin;
    3. 必答项:本卡要记的事实,是不是非得落在这个文件?若另有更合适的落点(且不撞零余量),那是更省的解 —— 但换落点同样要回分诊说一声,⛔ 不自行改归属。

    ⚠️ 本卡与 #13385 / #13257(另一张同源、同文件、同状态)本轮一并解除。两张都要往同一个零余量文件里写 ⇒ 压缩预算要一次算清,⛔ 不许两张各压各的、然后第二张发现余量已被第一张吃掉。


    Generated by Claude Code

  11. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    on Aug 31, 2026
  12. claude commented on Sep 4, 2026

    @claude
    Contributor

    Re-pointed to #15192; pm:queue → pm:blocked.
    Blocked-by: #15192

    The three Routine transport facts this card records (as its numbered list states them: sessions fired by an agent-created Routine carry no GitHub MCP tools; what self-binding trades away; the recovery consequence) are not in .claude/skills/pm-dispatch/references/platform-readings.md at main 1bc3c092 — the file's only timer sentence (L356, 「⛔ 不用 send_later 链」) is the choice, not the transport facts behind it — and the #15013 raise (324 → 358) was priced for the intake family's eight readings, which this card was not part of. It is carried as a member of the third increment #15192, which needs a ceiling ruling before it can land. This card closes when that PR merges with the three facts in it.


    Generated by Claude Code

  13. added
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 4, 2026
  14. claude commented on Sep 4, 2026

    @claude
    Contributor

    Closed — the three Routine transport facts are on main. PR #15265 (#15192, the third platform-readings increment ruled by #15275 → 1A) MERGED dc46c4ec 2026-09-04T12:56:03Z by os-zhuang. .claude/skills/pm-dispatch/references/platform-readings.md at main dc46c4ec, in the 断粮 section after the existing timer paragraph, now carries the three facts this card recorded (re-measured on 2026-09-04 against the session's tool contract): a Routine-fired session carries no mcp__github__* tools (the second probe's decisive design quoted; the create end's new connectors parameter noted as an untested remedy); self-binding trades new-session recoverability for tool availability, with the orphan-timer recovery rule (delete the old Routine, rebuild from the live session, never leave an orphan — the six-day case); the create end takes neither sources nor model (the update end's model reaches only fresh-session Routines). Closed as completed by the skills seat; pm:blocked removed.


    Generated by Claude Code

  15. removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationdomain:skillsDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions