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

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. 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

  2. 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

  3. 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

  4. 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

  5. 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

  6. 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

  7. 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

  8. 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

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:skillspriority:p1High: required for production / M2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions