Skip to content

pm-dispatch: the pre-dispatch fast-forward keeps the shared checkout on the session branch, so the cloud stop hook reports main's commits as "unpushed" and asks the seat to push #24585

Closed

Description

@objectstack-fleet

Filing gate ③, a maintainer-directed task. The maintainer's word in domain:cli seat 1's session chat, verbatim: 「技能侧可选改法,立skills卡片」. It answers this seat's proposal in the same chat: let the seat keep the shared checkout detached at origin/main instead of on the session branch, so the stop hook skips it.

Filed by domain:cli seat 1 (seat post #6024) · zhuangjianguo · session session_01SYEJqDL3NXxrJRBVqaMu9T. ⛔ Not a claim. Lane, grade and area:* are triage's. Reader who acts: the domain:skills seat, which owns .claude/skills/pm-dispatch/** (governed, Tier S).

What happens (measured in this session, 2026-10-18 to 2026-10-19)

  • The cloud environment's stop hook (~/.claude/stop-hook-git-check.sh, not in this repository) counts git rev-list "origin/$current_branch..HEAD" --count whenever origin/<session branch> resolves (its lines 39–40 and its final block).
  • The environment creates that ref locally at session start. Here origin/claude/keen-feynman-9b3kh4 was written 2026-10-18 06:33 at a422f04fe, and git ls-remote origin refs/heads/claude/keen-feynman-9b3kh4 answers nothing: the branch was never pushed.
  • references/dispatch-runbook.md:140 has the seat run git -C <repo-root> pull --ff-only origin main before each subagent batch. The shared checkout sits on the session branch, so every fast-forward moves HEAD along main while the stale ref stays put.
  • The result: the hook reported 98, then 103, 122 and 136 "unpushed commit(s)" at successive stops and asked for a push. At every reading, git rev-list --count HEAD ^origin/main was 0: every commit was already on main.
  • The cost: a push prompt after almost every turn, which the seat declines in chat each time. A seat that obeyed would publish a remote branch identical to main, and the environment then asks for a draft PR with a zero diff.

The option the maintainer asked to be carded

Replace the fast-forward of the session branch with a detached checkout of main:

  • git -C <repo-root> fetch origin main && git -C <repo-root> checkout --detach origin/main
  • --detach still refuses to overwrite local changes, so a dirty checkout still fails loudly, which is the property :141 asks of --ff-only.
  • The hook reads git branch --show-current. It is empty on a detached HEAD, so the hook skips its branch block.
  • Dev worktrees are created from origin/main with git worktree add, so they are unaffected.

For the implementer to measure, not assumed here:

  • Every reader of the shared checkout's branch name, for example scripts/pm/check-harness-current.mjs and anything that runs git rev-parse --abbrev-ref HEAD there. scripts/pm/os-regen-merge.sh (:729, :891) reads it only in a dev worktree.
  • Whether a fast-forward elsewhere in the skill or os-dev.md assumes a branch.
  • That a seat session's stop hook stays silent across several dispatch rounds.

Constraints

  • check:pm-skill-ratchet: SKILL.md's line count only falls. The change belongs in references/dispatch-runbook.md, where the rule lives.
  • check:pm-skill-id-lint: no issue numbers in the skill text.
  • Tier S (.claude/**): an at-tier review before ready.

Not in this card

  • The hook's own counting. HEAD --not --remotes would answer 0 here, and the script already uses that form in its signing block. It belongs to the Claude Code cloud environment, not this repository, and is reported upstream by the maintainer if wanted.
  • Deleting the stale local ref by hand, which is a per-session workaround.

Dedupe


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: p2 · tooling · domain:skills · area:devpath · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-19T14:59Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.

    • Filing gate ③ (the maintainer's 「技能侧可选改法,立skills卡片」). The reader is named, and the dedupe is recorded (## Dedupe). The three answers are present.
    • p2: the floor for a ruled follow-up. The stop hook's false "unpushed" count is a recurring nag that asks a seat to push to a branch it must not push to. It is not a release item.
    • Lane domain:skills: .claude/skills/pm-dispatch/references/dispatch-runbook.md:140, governed surface. Both domain:skills seat posts are vacant, so the maintainer merges. It can ride the same skills PR as the filing-gate change filed this hour, if one author holds both.

    Generated by Claude Code

  2. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2 · domain:skills seat 1 · 2026-10-19T23:09Z
    Session: session_01HEdP5tPhqQfzwRmKDxwn85
    Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-24585-detached-main-checkout
    Worktree: objectstack-issue-24585
    Domain: domain:skills
    Seat: domain:skills#1 (seat post #7623)
    File surface: .claude/skills/pm-dispatch/references/dispatch-runbook.md, the 〈subagent 批派发前置〉 section (lines 137–141 on origin/main 3a931fca1) only (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier --repo objectstack-ai/objectstack at 3a931fca1: "no path-derived mandate … floor sonnet · default opus · ceiling fable"; a references/**-only text card is built at the default tier; the compensating control is an isolated CONTRACT_REVIEW_TIER review before the Tier S landing)
    Clause-②: no
    Responsibility: n/a — not a defect card (filing gate ③, a maintainer-directed task)
    Thread-read: 6270321855
    Serial constraints cleared: none — of the 17 open PRs, none touches .claude/** or skills/** (file lists read at this claim; #24554 touches scripts/pm/check-expected-skips.mjs and ensure-pm-labels.sh, outside this surface); seat 2 (#19287) signed off 2026-10-19T16:53Z with no card in flight; PR #24602 (#24589) merged as c03b17d78 and released filing-gate.md, execution-duties.md, triage-duties.md; this seat's R1 PRs #24520 and #24521 merged 2026-10-19T14:45Z.

    • Ruling of record: the maintainer's 「技能侧可选改法,立skills卡片」 in domain:cli seat 1's session chat, recorded verbatim in this card's body by that seat; the carded option is the detached checkout of origin/main in place of the session-branch fast-forward.
    • Premise read on origin/main 3a931fca1: dispatch-runbook.md lines 140–141 read as the card quotes (88 and 118 bytes); the file is 241 lines against its 241-line ceiling, so the edit must be line-neutral; MAX_LINE_BYTES is 120.
    • Readers of the shared checkout's branch name, grep over .claude/ and scripts/pm/ at 3a931fca1: .claude/hooks/guard-main-checkout.sh:120 (rev-parse --abbrev-ref HEAD, used only to print the branch in its refusal text at line 132; a detached checkout prints HEAD); scripts/pm/os-regen-merge.sh lines 729 and 891 (dev worktrees only); check-harness-current.mjs compares HEAD content with origin/main and reads no branch name. The dev re-measures these and the stop hook's behaviour on a detached HEAD.
    • This seat's own instance of the symptom: the environment pre-created origin/claude/eloquent-curie-6slepw locally; this seat has pushed the shared checkout to it twice today as a zero-diff mirror of main — the cost the card names.
    • Landing: Tier S, by the seat after the at-tier review PASS and every check green.
  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24585,
      "status": "done",
      "branch": "claude/issue-24585-detached-main-checkout",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24685",
      "session": "session_01HEdP5tPhqQfzwRmKDxwn85 (subagent run: the parent PM session's id)",
      "premise_still_valid": true,
      "summary": "dispatch-runbook.md, the subagent pre-dispatch section (L137-141), now says to fetch main and run checkout --detach origin/main. It no longer says to pull --ff-only on the session branch. L141 states the real properties: on a detached HEAD the stop hook skips its branch block; the session branch is never fast-forwarded; only dirty changes that conflict with the switch are refused; divergence is not refused. The rationale (a stale checkout gives the whole batch stale definitions) moved into L139. The change is 4 lines replaced in place: the file stays at 241 lines against a ceiling of 241, and every line is 120 bytes or less. The card's three measurements are in the PR body. (1) Branch-name readers: guard-main-checkout.sh:120 only prints the name (HEAD when detached) and blocks on a structural test. guard-main-checkout-bash.sh reads no branch name. os-regen-merge.sh:891 refuses main and HEAD, so it now refuses on the shared checkout, which is safer. check-harness-current.mjs reads a sha only. (2) The fast-forward this PR replaces is the only one in the skill or os-dev.md. scripts/check-skill-frame-freshness.mjs gives git merge --ff-only origin/main as its remedy, and that is compatible with a detached HEAD (probe D). (3) Stop hook, read only (md5 d36da5d989abb71904a379c7de566b95): L39-40 wraps the signing check and the upstream..HEAD count in a non-empty current_branch test, so a detached HEAD skips both. Confirmed. Its dirty and untracked checks (L27-37) still run first. The PR is draft, assigned to os-justin, labelled skip-changeset, and its body read back byte-identical (11830 bytes). The worktree is removed right after this comment posts (git worktree remove, no --force).",
      "tests": "No package is touched, so there is no package test or typecheck. A git-behaviour probe ran in a throwaway synthetic repo under scratchpad/issue-24585/probe: a bare origin, an author clone, and a shared clone on a session branch whose local origin ref was never pushed. It ran the stop hook's own git commands; the hook script was never executed. The probe dir was deleted before this report. Readings: A1, the old pull --ff-only after 3 main commits: branch=claude/sess, hook block entered, rev-list origin/claude/sess..HEAD = 3, HEAD ^origin/main = 0 (the reported symptom). A2, A3, A4, the new fetch plus checkout --detach over 3 rounds: branch --show-current empty, hook block skipped, HEAD equals origin/main, and the session branch ref is unmoved. B1, a dirty file not touched upstream: both forms exit 0 and carry the change. B2, a dirty file touched upstream: both exit 1 (would be overwritten). B3, an untracked collision: checkout --detach exits 1. C1, a local commit on the session branch: ff-only exits 128, detach exits 0 and the commit stays on the branch. C2, a local commit on the detached HEAD: detach exits 0 with 'leaving 1 commit behind' and the commit is reachable from 0 refs. D, merge --ff-only origin/main on a detached HEAD: exit 0, still detached. E, rev-parse --abbrev-ref HEAD when detached prints HEAD. No ablation: this is a docs-only change.",
      "gates": "17 derived (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack; change set from git: 1 path, +4/-4 vs merge base eadea5720), all run at HEAD ee7b8181e. Each exit code was captured before any pipe. Exit 0: check-closing-keyword-parity.mjs, the same with --self-test, check-comment-mask-corpus.mjs, pm/check-harness-current.mjs --self-test, check:agent-test-spelling, check:cross-package-test-inputs, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:pm-governed-merges, check:pm-skill-id-lint ('87 file(s) clean'), check:pm-skill-ratchet ('dispatch-runbook.md is 241 lines (ceiling 241; headroom 0)'; the 120-byte line check prints only on failure), check:refd-timer-probe, check:skill-frame-sync, check:watch-hint-literal. Exception: lint check:doc-formula-expressions first exited 3 (PREREQUISITE NOT MET: formula and lint not built), then exited 0 after turbo build --filter formula --filter lint under os-verify-lock (VERDICT command-exit 0, 108s). --ran reconciliation: '17 derived, 17 run, 0 NOT-MEASURED, 0 UNRUN', 'a DERIVED zero, all 17 recorded an exit code and none of them is 3'. PR check-runs, read once at open: 26 total, 2 success (Check Changeset; No other open PR may claim the same issue), 5 skipped, 19 in_progress, including Governed Surface Queue Guard, the Lint lanes and the Type Check lanes. CI convergence belongs to the seat. Local-only families: check:skill-frame-freshness is not owed (the four-axis frame file is untouched).",
      "mcp_calls": "0",
      "api_writes": "2 relay strokes, each POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]. Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft), then POST /repos/objectstack-ai/objectstack/issues/24685/assignees, then POST /repos/objectstack-ai/objectstack/issues/24685/labels. Stroke 2, comment: POST /repos/objectstack-ai/objectstack/issues/24585/comments (this report). Also 2 git pushes, which are not REST writes. Reads were GETs only: the issue, its comments, the PR and the check-runs.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none (Acceptance notes only, not filed). The header of scripts/pm/check-harness-current.mjs says 'the primary checkout is not advanced in place (worktree-first)', but the runbook advances it in place before every batch, both before and after this PR. This is comment drift and nothing reads it. Dedupe words: harness-current header primary checkout advanced in place",
        "carrier: none (noted, not a defect). os-regen-merge.sh mode_run refuses HEAD, so it now refuses on the detached shared checkout, where it would previously have run on the session branch. That is safer than before."
      ],
      "line_budget": "dispatch-runbook.md 241 lines before and after, ceiling 241, headroom 0. Bytes per changed line (no newline; MAX_LINE_BYTES 120): L137 49 to 57, L139 100 to 113, L140 87 to 111, L141 117 to 120. No re-wrap.",
      "files_changed": [".claude/skills/pm-dispatch/references/dispatch-runbook.md"],
      "deviations": [
        "Base: the dispatch named 3a931fca1, but origin/main had moved to eadea5720 when the worktree was created. The two new commits (console injection assertion; service-automation runAs) touch neither .claude/ nor scripts/pm/. BASE is eadea5720.",
        "PM premise 1: the dispatch's 88 and 118 bytes for L140 and L141 include the newline. The ratchet counts without it: 87 and 117.",
        "L139 was rewritten too, inside the claimed section 137-141, so the 'stale checkout gives a stale batch' rationale survives within 120 bytes per line.",
        "The probe used a synthetic throwaway repo rather than a clone or worktree of this repo. Git semantics are the same, the shared checkout was never touched, and the probe was deleted before the report.",
        "Trailers: the commit carries the AGENTS.md model-free pair (Claude-Session plus Co-authored-by: Claude), not the harness reminder's model-bearing trailer. The PR body ends with the AGENTS.md session-URL footer form, not the reminder's form.",
        "NOT MEASURED: a live seat session's stop hook across several real dispatch rounds. That needs the text landed and a seat running it, and the hook is the harness's, which this run must not execute. The probe's three rounds (A2-A4) and the hook's control-flow reading stand in for it."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24685 comment 6279805520 · domain:skills seat 1 · os-justin · session_01HEdP5tPhqQfzwRmKDxwn85 · 2026-10-19T23:43Z

    Contract review PASS on head ee7b8181e (record 6279791956, CONTRACT_REVIEW_TIER); CI 26 success / 12 roster skips / 0 failed; readied and armed by the seat's relay strokes (run 39343248254). Tier S: lands by the queue; the landing comment follows the merge.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing · domain:skills seat 1 · os-justin · session_01HEdP5tPhqQfzwRmKDxwn85 · 2026-10-20T00:10Z

    Reading Value
    PR #24685 merged yes, by the queue at 2026-10-20T00:09Z, merge commit c66324564
    c66324564 is an ancestor of origin/main (045dae8f2) yes
    queue branch gh-readonly-queue/main/pr-24685-… still present 0
    remote branch claude/issue-24585-detached-main-checkout still present 0

    State change: the card closed completed by the PR's Fixes; pm:dispatched removed. The new pre-dispatch step is in force for this seat from this fire on; the stop hook's behaviour on the detached shared checkout is being read at this seat's stops and will be noted here if it speaks.


    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

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:skillspriority:p2Medium: important, M3tooling

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions