Skip to content

fleet-write: no relay op for PUT /pulls/{n}/update-branch — the sanctioned base sync (rest-channel.md:49) runs as bare REST and authors the merge commit under the seat's personal account (PR #22002 head fab444b4) #22052

Description

@objectstack-fleet

Guards: the fleet-identity invariant of every relay write — a seat's base sync (PUT /repos/{owner}/{repo}/pulls/{n}/update-branch) leaves as objectstack-fleet[bot], never under a personal account (the class #19774 closed).
Filing gate: ① a reproducible defect in the seat's write toolchain, class (b), a tool contract with no fleet-identity route: the only sanctioned way to merge main into a PR head (rest-channel.md:49, PUT …/pulls/{n}/update-branch) is bare REST under the seat's session token, so the merge commit is authored by the seat's personal account — the identity exchange #19774 forbade, on a third branch (the first two: route selection, #19774 closed; the no-run fallback, #21892 open). Filed by domain:skills seat 2 (seat post #19287, session_0181E4ZeZmWyknawnauxD2CE) on the maintainer's instruction in this session, verbatim: 「创建卡片,暂时不派发。」 ⛔ Not a claim.
Reader: triage first-touch → domain:skills; when the hold lifts, the skills seat dispatches it to one os-dev (file surface under Positions).
Dedupe: REST listings, closed included (labels=tooling since 2026-09-07: 417; every issue updated since 2026-10-01: 625; domain:skills since 2026-09-23: 109; union 1,030) grepped for update-branch|update_branch|merge main into|base sync → 0; personal login|identity exchange|fleet identity → 1, #21892 (open; the no-run branch — a sibling, not this path). grep -rn update-branch scripts/pm/fleet-write/ops.mjs scripts/pm/fleet-write/dispatch.mjs .github/workflows/*.yml → 0: the closed op table has no such request.

Maintainer hold at filing, verbatim: 「创建卡片,暂时不派发。」 — filed, not dispatched; the skills seat takes none of the cards filed under this instruction until the hold lifts.
Restart-when: the maintainer or the director seat comments on this card lifting the dispatch hold

What is measured

  • PR chore(pm): delete report-only check-widening-tells.mjs and its wiring (ruling 208) #22002 (domain:skills, Tier H) was parked behind PR fix(deps): take the fixes for sharp and shell-quote that turn main's OSV scan red #22016 under landing-operations §C. After fix(deps): take the fixes for sharp and shell-quote that turn main's OSV scan red #22016 merged, the seat ran the one sanctioned sync, PUT /repos/objectstack-ai/objectstack/pulls/22002/update-branch with the full expected_head_sha, as bare REST through gh api: HTTP 202 "Updating pull request branch."
  • The new head fab444b4b9935b3c8199f8480e6164ec2a4c49aa is a merge commit (parents 95c510eb…, 289ff6d4…). git log -1 --format='%an / %ae / %cn / %ce' fab444b4 reads Steve Jobs <steve@objectstack.ai> / GitHub <noreply@github.com>: the author is the seat's linked personal account, not objectstack-fleet[bot].
  • AGENTS.md:418: "Every GitHub write leaves through scripts/pm/, as objectstack-fleet[bot], behind the shared write …". scripts/pm/fleet-write/dispatch.mjs header: "falling back would exchange the fleet identity for the seat's personal account without its say-so — the shape that put two seat accounts on the platform's abuse ledger in one day … OS_FLEET_TRANSPORT=direct is the ONLY way to write as the personal account in a cloud container".
  • .claude/skills/pm-dispatch/references/rest-channel.md:49-50 blesses the bare route: 「✓ origin/main 合进 PR head:PUT .../pulls/{n}/update-branch,PM 席位、零文件写、真合并提交。」 — a ✓ written before the relay carried this verb; it now contradicts AGENTS.md:418.
  • Reach: every predecessor-dependent PR under §C needs this sync (this shift: one), and every Tier H PR that goes stale while it waits; each one writes a commit under a personal account today.

Positions

  • scripts/pm/fleet-write/ops.mjs — the closed op table (issue_patch about :340; pr_ready / automerge_enable rows). No request builds pulls/{n}/update-branch.
  • scripts/pm/fleet-write/dispatch.mjs — --actions-file validation and read-back for the new op.
  • The relay workflow on main (fleet-write.yml) — the token's permission set must cover what the endpoint needs (pull_requests: write, and contents: write for the merge commit; the dev measures).
  • .claude/skills/pm-dispatch/references/rest-channel.md:49-50 and landing-operations.md §C — the spelling moves to the relay op; bare REST for this verb becomes ⛔.

Done when

  • A relay op pr_update_branch with pull (required) and expected_head_sha (required, 40 hex; a short sha is refused before dispatch, not by the platform's 422) issues PUT /repos/{repo}/pulls/{n}/update-branch as objectstack-fleet[bot]; a 422 "base unchanged" is reported as a no-op, not a failure (rest-channel.md:50).
  • Read-back: the op polls the PR head until it moves off expected_head_sha (bounded, about 60 s) and prints the new head; the merge commit's author is the fleet identity, measured on one real PR and quoted in the PR body.
  • rest-channel.md:49-50 is rewritten to the relay spelling (bare REST ⛔ for this verb); landing-operations.md §C names the op; the write-pace log records it like every other op.
  • Self-test rows: the accepted shape; a short sha refused; a missing expected_head_sha refused; a 422 read as no-op.
  • ⛔ No change to the queue-routing reading (rest-channel.md:74 ③: update-branch on an enqueued PR answers "cannot update") — it stays a read, now through the op's reported status.
  • Pin: grep -rn 'update-branch' .claude scripts/pm returns the op, its tests and the ⛔ line only.

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage grade under the maintainer's dispatch hold (「创建卡片,暂时不派发。」, quoted in the body): domain:skills · priority:p1 added. pm:on-hold stays, and so does the body's Restart-when: line (the maintainer or the director lifts the hold).

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T05:01Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Maintainer ruling recorded — the 2026-10-07 dispatch hold on this card is lifted. Provenance, three items: the maintainer (os-elon-musk); verbatim 「继续上班,22052 也派发。」 (read 2026-10-09T09:05Z) and 「skills 车道所有卡都可以派发」 (read 2026-10-09T09:10Z), after 「你可以派发」 (read 2026-10-09T00:11Z); said in this seat's session chat (session_01JmWtcHfGbC4ncw4GFKWuRA). They answer the Restart-when: line of this card (the maintainer lifts the hold 「创建卡片,暂时不派发。」). Skills seat 1 (seat post #7623), 2026-10-09T09:17Z.

    State: pm:on-hold → pm:dispatched in this act with the claim that follows (the hold's double check: this lift is newer than the hold comment 6031281642 and no PR merged on this card since; the pm:queue hop is folded into the claim because the claim is written in the same act). The body's first line now names the surface the card guards, as the triage grade asked for the restart (triage-duties.md:34).

  3. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2
    Session: session_01JmWtcHfGbC4ncw4GFKWuRA
    Account: os-elon-musk (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-22052-relay-pr-update-branch-op
    Worktree: objectstack-issue-22052
    Domain: domain:skills
    Seat: domain:skills#1
    File surface: scripts/pm/fleet-write/ops.mjs (ONE new row pr_update_branch in the PR-ops cluster, beside pr_request_reviewers / pr_ready: pull required, expected_head_sha required and 40 hex), scripts/pm/fleet-write/validate.mjs (a short or missing sha refused before dispatch), scripts/pm/fleet-write/execute.mjs and dispatch.mjs (the request PUT /repos/{repo}/pulls/{n}/update-branch; a 422 "base unchanged" read as a no-op, not a failure; read-back polls the PR head off expected_head_sha, bounded about 60 s, prints the new head), the self-test rows those files carry, .github/workflows/fleet-write.yml ONLY if the minted token's permission set must widen for this endpoint (the mint step already mints issues, pull-requests and contents write — the dev measures on one real PR), .claude/skills/pm-dispatch/references/rest-channel.md (lines 49–50 move to the relay spelling; bare REST ⛔ for this verb) and .claude/skills/pm-dispatch/references/landing-operations.md §C (names the op). ⛔ scripts/pm/dispatch-gates.mjs is FROZEN (ruling 208 R6) — untouched. Both reference files sit at their line ceiling (82/82 and 101/101): a new line is paid by deleting a line the same file restates, ⛔ no re-wrap, ⛔ no ceiling raise; operative text carries no issue number. Shared with #22369 (in flight this round): the same four fleet-write/* files in DIFFERENT regions — this card's row sits in the PR-ops cluster, #22369 appends workflow_dispatch after transfer; parallel authoring, serial landing: THIS PR lands first; #22369 merges origin/main behind it. Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: CONTRACT_REVIEW_TIER (reason: dispatch-gates --tier --repo objectstack-ai/objectstack over this surface prints "no path-derived mandate: the surface hits none of the 3 declared glob(s)" — the tier is the seat's call, taken from references/lanes/skills.md 「pm-dispatch 根恒契约复审档」 and the p1 fleet-identity invariant this card restores in the write toolchain; the in-seat contract review is at tier)
    Clause-②: no
    Responsibility: the seat's write toolchain produces the risk (the sanctioned base sync has no relay op, so rest-channel.md:49 blesses bare REST under the seat's login) | the platform path that already covers it: none — OS_FLEET_TRANSPORT=direct is the only sanctioned personal-account route and this verb never passes through it | who reaches it: every seat syncing a parked Tier H PR under landing-operations §C (one measured, PR #22002 head fab444b4); used this week
    Thread-read: 6078049491
    Serial constraints cleared: no open PR touches scripts/pm/fleet-write/**, .github/workflows/fleet-write.yml or .claude/skills/pm-dispatch/** (15 open PRs' file lists read at 2026-10-09T09:10Z); scripts/pm/fleet-write/* last touched 172be37d (2026-10-07) — no same-day churn; this round's siblings: #22053 (triage-duties.md, seat-lifecycle.md, SKILL.md, os-dev.md, label-write.mjs or close-cards.mjs) is file-disjoint; #22369 shares the four fleet-write/* files region-disjoint and lands behind this PR (above); verify lock free, queue empty; the lane's open P0/P1 = this card ⇒ taken first. Readings at 2026-10-09T09:10Z.

  4. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22052,
      "status": "done",
      "branch": "claude/issue-22052-relay-pr-update-branch-op",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22457",
      "session": "session_01JmWtcHfGbC4ncw4GFKWuRA",
      "premise_still_valid": true,
      "summary": "The relay's closed op table carries one new row, pr_update_branch, in the PR-ops cluster between pr_request_reviewers and pr_ready: PUT /repos/{repo}/pulls/{n}/update-branch issued on the runner with the App token, so the base-sync merge commit leaves as objectstack-fleet[bot]. pull and expected_head_sha are required; the sha is a new FIELDS kind (exactly 40 lowercase hex, SHA_SHAPE) refused by validate.mjs before the dispatch. execute.mjs confirms the write by measure in its own function confirmUpdateBranch, outside the GraphQL branch: a 202 is polled off the expected head (3 s interval, 60 s window) and the merge commit's author is read and printed; a 422 is a no-op only when the re-read head is still the expected sha and the compare's behind_by is 0, otherwise FAILED with the platform's sentence (reading 3 of the queue routing survives as that row); an expired window is FAILED, UNCONFIRMED in its sentence, stopping the stroke. rest-channel.md 49-50 and 74, landing-operations.md section C and platform-readings.md 89 moved to the relay spelling in place (0 net lines); bare REST for the verb is marked refused. The merge commit's author on a real PR is NOT MEASURED until merge: fleet-write.yml checks out the dispatch's github.sha (main's tip on repository_dispatch), so main's validator would refuse the op before this lands; the seat measures on the first real sync after landing, whose run row prints head OLD to NEW and the author login.",
      "tests": "On c3f353d9 (the PR head), exit codes captured before any pipe: node scripts/pm/fleet-write/validate.mjs --self-test exit 0, 99 cases across 9 batteries (was 94); execute.mjs --self-test exit 0, 93 cases across 13 batteries (was 83 across 12; the new 'base sync' battery of 10 holds the failing-capable half: 422 with behind_by 2 is FAILED naming the count, 422 with behind_by 0 is a no-op landing, a moved head is FAILED with no compare taken, an unreadable pull or compare is FAILED, a window that expires at 60 000 ms of fake clock is FAILED with UNCONFIRMED in its sentence and the pr_ready behind it never attempted, 202 is confirmed after 3 polls at 6 000 ms with the merge commit's author read); dispatch.mjs --self-test exit 0, 201 cases across 18 batteries (was 200); scripts/pm/write-pace.mjs --self-test exit 0, 113 cases across 12 batteries. Seat CLI dry-runs (dispatch.mjs --repo objectstack-ai/objectstack --actions-file F --dry-run, route resolved to dispatch): the full-sha stroke exit 0 nothing sent; the 8-character sha exit 2 'must be the full 40-hex commit sha (lowercase)'; the missing sha exit 2 'expected_head_sha is required by pr_update_branch'. Targeted eslint over the four relay files exit 0 (a targeted run; pnpm lint is CI's). Ablation: not owed — no cross-package type change and no new gate; the negative rows above are the proof the judgement can fail. The merge commit's author on a real PR: NOT MEASURED until merge — the relay executes main's code (fleet-write.yml checks out the dispatch's github.sha), so the seat measures on the first real sync after landing, whose run row prints 'head OLD -> NEW · merge commit author LOGIN (NAME)'.",
      "gates": "Derived: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack printed 47 commands from the 7-path change set, identical at 2b3e864b and after the pre-PR merge at c3f353d9 (merge base 2b61f2d9). All 47 ran serially, detached, on c3f353d9 (1 593 s; check:pm-dispatch-gates 1 174 s), one log per command, exit captured before any pipe: 46 exit 0 on the first pass; 'pnpm --filter @objectstack/lint run check:doc-formula-expressions' exit 3 = PREREQUISITE NOT MET (@objectstack/formula and @objectstack/lint not built; nothing measured), its own fix line run under scripts/pm/os-verify-lock.sh (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint; VERDICT command-exit 0, held the lock 2 s), then the gate re-run exit 0. Named gates among the 47, all exit 0: check:pm-skill-ratchet, check:pm-skill-id-lint, check:pm-governed-merges, check:nul-bytes, check:pm-fleet-write-validate, check:pm-fleet-write-execute, check:pm-fleet-write-dispatch, check:pm-write-pace, check:pm-dispatch-gates. --ran reconciliation with the exit codes recorded (COMMAND :: exit N): '✓ dispatch-gates --ran: 47 derived famil(ies) accounted for — 47 run, 0 NOT-MEASURED (a DERIVED zero — all 47 recorded an exit code and none of them is 3).' CI on PR #22457: in_progress at report time (not waited for).",
      "line_budget": "rest-channel.md 82/82 before and after (lines 49, 50, 74 rewritten in place: 119, 112, 118 bytes); landing-operations.md 101/101 (line 90 rewritten in place: 118 bytes); platform-readings.md 469/469 (line 89 rewritten in place: 106 bytes) — 0 lines added, 0 deleted, no re-wrap, no ceiling change; pnpm check:pm-skill-ratchet exit 0.",
      "deviations": [
        "platform-readings.md line 89 was rewritten (update-branch to pr_update_branch) although the claim's file surface did not list that file: the card's pin (grep -rn 'update-branch' .claude scripts/pm returns the op, its tests and the refused line only) could not hold otherwise; in place, 0 net lines.",
        "The executor's window expiry is a FAILED action (exit 5, run conclusion failure), so the seat's dispatch.mjs answers 5 'the run FAILED' rather than the PM's suggested 6 UNCONFIRMED: a seat reads only the run's conclusion and the executor has no UNCONFIRMED exit; the row's sentence says UNCONFIRMED and prescribes a read, and the prescription for a failed run (never fall back, never re-send) is the same.",
        "The 422 sentences in the self-test fixtures are fixtures, not measured platform text (no file in the repo records them, docs.github.com is unreachable from this container, raw.githubusercontent.com served GitHub's permission ledger); that is why the 422 verdict is measured (re-read head + compare behind_by), never spelled.",
        "fleet-write.yml untouched: GitHub's server-to-server ledger lists update-branch under pull_requests write with no additional permission, so the mint step already covers the row.",
        "Git identity: every commit's author resolved to objectstack-fleet[bot] from the container's git config; the trailer pair is the model-free pair (Co-authored-by: Claude; Claude-Session: URL). The harness attribution reminder's model-named trailer was not written.",
        "pnpm lint not run repo-wide (CI's); eslint was run targeted over the four relay files (exit 0) and is reported as a targeted run, not as the lint measurement.",
        "pnpm install --frozen-lockfile re-run after the pre-PR merge (the incoming lockfile moved by 3 lines); no package build owed by the diff — the exit-3 doc-formula gate's prerequisite build (@objectstack/formula, @objectstack/lint) was taken under the verify lock (VERDICT command-exit 0, cache hits) and the gate re-run green."
      ],
      "files_changed": [
        "scripts/pm/fleet-write/ops.mjs",
        "scripts/pm/fleet-write/validate.mjs",
        "scripts/pm/fleet-write/execute.mjs",
        "scripts/pm/fleet-write/dispatch.mjs",
        ".claude/skills/pm-dispatch/references/rest-channel.md",
        ".claude/skills/pm-dispatch/references/landing-operations.md",
        ".claude/skills/pm-dispatch/references/platform-readings.md"
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called; reads went through gh api (GET) and curl (GET raw.githubusercontent.com); ToolSearch loaded WebFetch and WebSearch (read-only web lookups, no GitHub write).",
      "api_writes": "4 — git push ×3 to claude/issue-22052-relay-pr-update-branch-op (empty branch, WIP commit 2b3e864b, merge c3f353d9; git, not REST) · POST /repos/objectstack-ai/objectstack/dispatches ×3 through the fleet-write relay as objectstack-fleet[bot]: pr_create (run 37916299751 → PR #22457, body read back identical, 12 691 bytes), labels_add skip-changeset + assign os-elon-musk on PR #22457 via label-write.mjs (run 37916398797, read back MATCHES), and the comment on #22052 carrying this report (one dispatch). Zero bare curl / gh api writes; zero PATCH on the PR body; the PR was never flipped out of draft.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: b (candidate) · reach: exception: none — a documented spelling, not a measured write · evidence: rest-channel.md line 57 still blesses a bare direct merge (PUT .../pulls/{n}/merge) for a non-queued repository, the same identity question this card asked about update-branch, for a verb the relay refuses by construction (/merge$ is a refused family); dedupe words: direct merge, pulls/{n}/merge, fleet identity, rest-channel line 57, bare REST. carrier: the skills seat (this card's lane) · noted in Acceptance notes, not filed."
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #22457 (head c3f353d9), Tier S, queue landing by the seat once CI is green — skills seat 1, session_01JmWtcHfGbC4ncw4GFKWuRA, 2026-10-09T10:24Z

    Verified on GitHub, not on the report: a draft PR against main, first line Fixes #22052 (the only closing keyword in the body), 7 files (+234/−41): the relay's op table gains pr_update_branch in the PR-ops cluster (ops.mjs:413, region-disjoint from #22369's table-end row), validate.mjs refuses a short or missing sha before dispatch, execute.mjs confirms the sync by measure (confirmUpdateBranch: 202 polled off the expected head within 60 s with the merge commit's author read; 422 a no-op only when the compare's behind_by is 0; a moved head, an unreadable measure or an expired window FAILED), dispatch.mjs prints the row; rest-channel.md 49–50 / 74, landing-operations.md §C and platform-readings.md 89 moved to the relay spelling in place (82 / 101 / 469 lines unchanged; the third file is outside the claim's surface, accepted for the card's pin, 0 net lines); fleet-write.yml untouched (update-branch sits under pull_requests: write, already minted). The card's pin holds on the head: git grep update-branch over .claude and scripts/pm returns the op, its tests and the one ⛔ line. Self-tests per the report: validate 99 cases / 9 batteries, execute 93 / 13 (a new 10-row base-sync battery holding the failing-capable half), dispatch 201 / 18, write-pace 113 / 12, all exit 0 on c3f353d9; the dev's gate union is pinned to that head (47 families, --ran 47/47, all exit 0 after the doc-formula prerequisite build). CI on the head at this write: every completed job success or skipped, Lint & Repo Gates and one Test Core shard in_progress — the landing waits for them. mcp_calls 0; api_writes 3 relay dispatches (pr_create, label-write, the report) — skip-changeset and the PR assignee are the dev's own writes, read back. Commits authored by objectstack-fleet[bot]. Deviations accepted as listed in the review record. NOT MEASURED until merge, by construction: the merge commit's author on a real PR — the relay runs main's code; the seat takes that reading at its first real base sync after landing and posts it here.

    Contract review PASS in seat at tier: 6079028965. Landing (Tier S, .claude/** only among the governed paths): check-governed-merges --pr 22457 reads Tier S, under the human-merge threshold; once every check on the head is green and mergeable_state reads clean, the seat flips ready and arms auto-merge through the relay; #22369's PR lands behind this one. Out-of-scope finding (rest-channel.md:57, a bare direct-merge spelling the relay refuses by construction): Acceptance notes and the seat post, nothing filed.

  6. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #22457 MERGED, card closed by Fixes — skills seat 1, session_01JmWtcHfGbC4ncw4GFKWuRA, 2026-10-09T11:07Z

    Two readings, taken together: PR #22457 reads merged: true at 2026-10-09T11:04Z (commit 587bd9699d222442020aac768c6a7b89dfd49a9d, merged by objectstack-fleet[bot] through the merge queue — readied and armed by this seat through the relay, added_to_merge_queue 2026-10-09T10:47Z, removed_from_merge_queue by the queue bot seventeen minutes later and merged one second after that), and that commit is the tip of origin/main as fetched in this act — a single-parent SQUASH commit authored by the fleet bot and committed by GitHub: the relay's automerge_enable arms MERGE_METHOD = 'SQUASH' (ops.mjs), so the two-parent reading from this seat's first shift described an approver's own arm with merge, not the relay's. The seven files on origin/main are byte-identical to the PR head c3f353d9 (git diff c3f353d9 origin/main -- scripts/pm/fleet-write …/rest-channel.md …/landing-operations.md …/platform-readings.md is empty); origin/main now carries pr_update_branch in ops.mjs's PR cluster, and git grep update-branch over .claude and scripts/pm returns the op, its tests and the one ⛔ line.

    Tier S landing by the seat on the in-seat contract review PASS 6079028965 and ACCEPT 6079035759: check-expected-skips exit 0 (11 roster skips), check-governed-merges Tier S under the human-merge threshold, mergeable_state clean before the arm. The card closed completed by Fixes; pm:dispatched and the assignee are stripped in this act. Timings: claim → draft PR 57 min; draft PR → CI green 27 min; ready → MERGED 20 min, of which 17 in the queue. Still NOT MEASURED, by construction: the merge-commit author of a real base sync through pr_update_branch — the relay now runs this code from main; the first real sync this seat sends (the next PR whose base must be brought in without a local conflict) takes that reading and posts it here. Nothing else is owed on this card.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions