Skip to content

Trust only our own comments for the state marker#51

Merged
Phlogistique merged 23 commits into
mainfrom
claude/marker-author-trust-3njvdx
Jun 11, 2026
Merged

Trust only our own comments for the state marker#51
Phlogistique merged 23 commits into
mainfrom
claude/marker-author-trust-3njvdx

Conversation

@Phlogistique

Copy link
Copy Markdown
Collaborator

read_state_marker accepted a marker from any comment, so on a public repo anyone able to comment could plant one; if its base= matched, the resume would merge an attacker-chosen commit (fork-pushed objects are reachable by hash in the repo network) into the branch and push it with the action's token. Benign variant: a quote-reply of an old conflict comment resurrects a stale marker, since HTML comments survive quoting and the newest marker wins.

Fix: filter comments to viewerDidAuthor — those posted with the same token the action runs under — which needs no configured identity. The resume test's gh mock rejects comment queries without that filter. Caveat: if the repo switches tokens (e.g. GITHUB_TOKEN → App) while a PR sits in conflict, the old marker is no longer "ours" and the resume takes the safe abandon path.

Also rejects markers with missing fields instead of passing empty values to git (a marker missing squash= used to crash on update-ref and strand the PR under the label); new scenario E covers it.

Stacked on #50 (same function).

🤖 Generated with Claude Code

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX


Generated by Claude Code

claude and others added 13 commits June 9, 2026 20:49
The squash-merge fan-out retargeted every updated child PR onto the
target branch and only afterwards pushed the new heads, batched into a
single non-atomic push together with the merged-branch deletion. If the
push failed (e.g. someone pushed to a child mid-run, rejecting the plain
push) or a pr edit died partway through the loop, set -e aborted the run
with PRs already retargeted but their heads stale - and unlike the
conflict-resume path there is no label to re-trigger the action, so
nothing ever repaired them.

Apply the ordering the resume path already uses: push the updated heads
first, then flip the bases, and delete the merged branch last (deleting
a PR's base branch closes the PR, so every child must be off it first).
A failed push now leaves the PRs untouched on their old base.

The unit test captures the run transcript and asserts the
push -> retarget -> delete order; it fails against the previous code.
Also corrects the README: pushes are plain, not forced, and branch
deletion is its own final step.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
Since #40 the conflict comment's fast-forward step reads `git merge
--ff-only origin/<branch>`, which assert_conflict_comment_merges picks
up with its `^git merge` grep, so the extracted commands never match the
expected conflict merges. Skip the --ff-only line when extracting.

Also trim the new comments in the fan-out push/retarget/delete sequence.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
The fix for the --ff-only line breaking assert_conflict_comment_merges
moved to a separate PR; the e2e job here stays red until that lands and
main is merged back in.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
A head branch can carry several PRs (one per base), so gh calls keyed by
branch name can comment, label, or retarget the wrong one. Every gh call
that acts on a specific PR now uses the PR number: the fan-out carries
number/branch pairs from gh pr list, and the conflict-resolved run gets
PR_NUMBER from the event payload via action.yml.

The payload also already carries the PR's base branch, so the resume
takes it from a new PR_BASE variable instead of querying the API; the
resume test's gh mock no longer answers baseRefName queries, so a
reintroduced lookup fails loudly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
pr_has_conflict_label swallowed gh failures as "no label", so a
transient API error made the resume silently skip a labeled PR, and a
failed comments fetch in read_state_marker read as "no marker", which
made the caller abandon the resume and drop the conflict label for
good. Both now abort the run instead; the label stays on, so the next
push retries.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
read_state_marker accepted a marker from any comment, so anyone able to
comment on a public repo could plant one; if its base matched, the
resume would merge an attacker-chosen commit (fork-pushed objects are
reachable by hash) into the branch and push it with the action's token.
A quote-reply of an old conflict comment could likewise resurrect a
stale marker, since HTML comments survive quoting and the newest marker
wins. Filter the comments to viewerDidAuthor, i.e. those posted with
the same token the action runs under.

Also reject markers with missing fields instead of passing empty values
to git: a marker missing squash= used to crash the run on update-ref
and strand the PR under the label.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
"comments posted with our own token count" parsed as the noun phrase
"token count". Also viewerDidAuthor matches the token's identity, not
the token itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phlogistique added a commit that referenced this pull request Jun 11, 2026
`pr_has_conflict_label` swallowed gh failures as "no label", so a
transient API error made the resume silently skip a labeled PR. Worse, a
failed comments fetch in `read_state_marker` read as "no marker", which
made the caller *abandon* the resume and drop the conflict label for
good — a dead end. Both now abort the run instead; the label stays on,
so the next push retries.

Stacked on #45; #51 builds on this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX

---
_Generated by [Claude
Code](https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
@github-actions

Copy link
Copy Markdown

⚠️ Automatic update blocked by merge conflicts

Resolve them like this:

git fetch origin
git switch claude/marker-author-trust-3njvdx
git merge --ff-only origin/claude/marker-author-trust-3njvdx
git merge origin/claude/fail-loud-resume-reads-3njvdx

Fix the conflicts (for instance with git mergetool), then run git commit before continuing.

git push origin claude/marker-author-trust-3njvdx

Once you push, this action will resume and finish updating this pull request.

@github-actions github-actions Bot added the autorestack-needs-conflict-resolution PR needs manual conflict resolution label Jun 11, 2026
Comment thread update-pr-stack.sh Outdated
Comment thread update-pr-stack.sh Outdated
…jvdx' into claude/marker-author-trust-3njvdx

# Conflicts:
#	update-pr-stack.sh
Comment thread update-pr-stack.sh
Comment on lines +254 to +259
if [[ -z "$OLD_BASE" || -z "$NEW_TARGET" || -z "$SQUASH_HASH" ]]; then
echo "⚠️ State marker on $PR_BRANCH is malformed; cannot resume safely. Removing the label."
abandon_resume "$PR_NUMBER" "ℹ️ autorestack found a malformed state marker on this PR, so it will not update the stack automatically. If this PR still needs its base updated, update its base manually."
return
fi

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a malformed state marker is an internal error, it does not merit a pr comment, just dying

@github-actions github-actions Bot changed the base branch from claude/fail-loud-resume-reads-3njvdx to main June 11, 2026 11:52
@github-actions github-actions Bot removed the autorestack-needs-conflict-resolution PR needs manual conflict resolution label Jun 11, 2026
…x' into claude/marker-author-trust-3njvdx

# Conflicts:
#	update-pr-stack.sh
claude added 2 commits June 11, 2026 11:54
A malformed marker is an internal error: just exit. The label stays on,
so the run stays retryable and the failure is visible in the workflow
instead of a PR comment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
@Phlogistique Phlogistique merged commit bf02a40 into main Jun 11, 2026
3 checks passed
@github-actions github-actions Bot deleted the claude/marker-author-trust-3njvdx branch June 11, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants