github-actions-skip-token-suppresses-whole-push
Wrote the verified solution to ~/SOLUTION.md.
Diagnosed the two failure modes:
- GitHub Actions evaluates skip tokens ([ci skip], [skip ci], [no ci], [skip actions], [actions skip]) on the push tip only and suppresses the entire push. A skip-marked chore commit appended after the code commit hides the code commit's run.
- The match is a case-insensitive substring anywhere in the message, so a re-trigger commit that merely quotes the token is suppressed too.
Verified locally (no GitHub auth needed) with a bare remote whose post-receive hook models GitHub's exact rule:
| Variant | Tip message | Result |
|---|---|---|
| A buggy order | ... [ci skip] chore tip |
SUPPRESSED |
| B reordered | code fix is tip | CREATED |
| C retrigger | bare ci: trigger ... empty commit |
CREATED |
| D prose quote | ... the [ci skip] tip ... |
SUPPRESSED |
Also verified the ci-run-guard.sh preflight (fails on poisoned tip, passes on retrigger commit) and the CI-identical doc-drift recompute command (pytest --collect-only -q --override-ini=addopts=).
paths-ignore instead of skip tokens where possible.git commit --allow-empty -m 'ci: trigger ...' on top of already-pushed history (never rewrite).gh api .../actions/runs?head_sha=<sha> --jq .total_count (0 = suppressed), then inspect with gh run view, reading the failing job's log rather than the headline cancelled siblings.ci-run-guard.sh (pre-push hook) and verify-run.sh (SHA-scoped run verifier).# Evidence - Problem class: github-actions-skip-token-suppresses-whole-push - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-15T23:55:08.787Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "SYMPTOM: after pushing three commits (code fix -> board/closeout metadata -> chart-cache refresh marked [ci skip] on the tip), GitHub Actions produced NO workflow run at all. `gh run list --limit 5` kept showing the previous run as newest, and the API query for the pushed SHA returned total_count=0, so the newly pushed code had no CI evidence whatsoever - which silently blocks any 'tick is green' claim that depends on a CI run. A SECOND, self-inflicted variant: an empty re-trigger commit (git commit --allow-empty) whose MESSAGE merely explained the trap in prose ('... after the [ci skip] tip suppressed the push run') also produced no run, because the skip token is matched anywhere in the commit message, not just as a trailing marker.\n\nROOT CAUSE: GitHub Actions evaluates the skip token ([ci skip] / [skip ci] / [no ci] and friends) on the HEAD commit of a push and suppresses workflow creation for the ENTIRE push, not per-commit. Any repo convention that appends a skip-marked chore commit (cache/graph refresh, docs-only metadata) after the meaningful commit therefore hides the meaningful commit's CI run. The message-substring match means re-trigger commits that merely quote the token are skipped too.\n\nFIX: (1) order metadata commits so the commit you need CI evidence for is the push tip - put skip-marked chores BEFORE the code/board commit; (2) if the tip is already skip-marked and history is already pushed (never rewrite pushed history), add a bare `git commit --allow-empty -m 'ci: trigger verification run for <TASK>'` with a message containing no skip token and push that; (3) ALWAYS verify the run exists for the exact pushed SHA instead of trusting `gh run list` to eventually show one: `gh api \"repos/<org>/<repo>/actions/runs?head_sha=<sha>\" --jq .total_count` (returns 0 when suppressed), then `gh run view <id> --json status,conclusion,jobs`. Classify a red run from the failing job's log, not from the headline conclusion: sibling matrix jobs show `cancelled` when one cell fails, and only the failing cell carries the real signature.\n\nVERIFICATION: with the tip rebuilt as a skip-token-free empty commit, `gh api .../runs?head_sha=<sha> --jq .total_count` returned 1, run 35037277580 reached completed/success across py3.10/3.11/3.12, and `git rev-list --count <remote>/main..HEAD` = 0 after `git fetch`. The intermediate suppressed SHA was confirmed at total_count=0 both before and ~2 minutes after the push, ruling out propagation delay.\n\nADJACENT FAILURE SEEN IN THE SAME WINDOW (same tick, different class, recorded here for discover value): the first run that DID start went red on a repository doc-drift gate because the code change added 7 tests, moving live pytest collection 1462 -> 1469 while README still claimed 1462. A test-tree change must carry its count consumers in the SAME commit: recompute with the CI-identical command (`pytest --collect-only -q --override-ini=addopts=`, and the same pipeline for collected file counts), update every tracked 'N tests pass' / 'N tests across' / 'N test files' claim, replay the workflow's drift-check block verbatim locally, and run any docs-drift script before pushing.", "environment": "GitHub Actions on a GitHub-hosted repo pushed over SSH; local driver is gh CLI + git. Repo is a Python project with a matrix CI (py3.10/3.11/3.12) that includes a README test-count drift gate. Repro needs only: a push whose tip commit message contains a skip token, then an API query for that SHA.", "language": "python", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "github-actions-skip-token-suppresses-whole-push", "provider": "openrouter", "solved_at": "2026-09-15T23:55:08.787Z", "version": ""}Wrote the verified solution to ~/SOLUTION.md.
Diagnosed the two failure modes:
- GitHub Actions evaluates skip tokens ([ci skip], [skip ci], [no ci], [skip actions], [actions skip]) on the push tip only and suppresses the entire push. A skip-marked chore commit appended after the code commit hides the code commit's run.
- The match is a case-insensitive substring anywhere in the message, so a re-trigger commit that merely quotes the token is suppressed too.
Verified locally (no GitHub auth needed) with a bare remote whose post-receive hook models GitHub's exact rule:
| Variant | Tip message | Result |
|---|---|---|
| A buggy order | ... [ci skip] chore tip |
SUPPRESSED |
| B reordered | code fix is tip | CREATED |
| C retrigger | bare ci: trigger ... empty commit |
CREATED |
| D prose quote | ... the [ci skip] tip ... |
SUPPRESSED |
Also verified the ci-run-guard.sh preflight (fails on poisoned tip, passes on retrigger commit) and the CI-identical doc-drift recompute command (pytest --collect-only -q --override-ini=addopts=).
paths-ignore instead of skip tokens where possible.git commit --allow-empty -m 'ci: trigger ...' on top of already-pushed history (never rewrite).gh api .../actions/runs?head_sha=<sha> --jq .total_count (0 = suppressed), then inspect with gh run view, reading the failing job's log rather than the headline cancelled siblings.ci-run-guard.sh (pre-push hook) and verify-run.sh (SHA-scoped run verifier).# Evidence - Problem class: github-actions-skip-token-suppresses-whole-push - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-15T23:55:08.787Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "SYMPTOM: after pushing three commits (code fix -> board/closeout metadata -> chart-cache refresh marked [ci skip] on the tip), GitHub Actions produced NO workflow run at all. `gh run list --limit 5` kept showing the previous run as newest, and the API query for the pushed SHA returned total_count=0, so the newly pushed code had no CI evidence whatsoever - which silently blocks any 'tick is green' claim that depends on a CI run. A SECOND, self-inflicted variant: an empty re-trigger commit (git commit --allow-empty) whose MESSAGE merely explained the trap in prose ('... after the [ci skip] tip suppressed the push run') also produced no run, because the skip token is matched anywhere in the commit message, not just as a trailing marker.\n\nROOT CAUSE: GitHub Actions evaluates the skip token ([ci skip] / [skip ci] / [no ci] and friends) on the HEAD commit of a push and suppresses workflow creation for the ENTIRE push, not per-commit. Any repo convention that appends a skip-marked chore commit (cache/graph refresh, docs-only metadata) after the meaningful commit therefore hides the meaningful commit's CI run. The message-substring match means re-trigger commits that merely quote the token are skipped too.\n\nFIX: (1) order metadata commits so the commit you need CI evidence for is the push tip - put skip-marked chores BEFORE the code/board commit; (2) if the tip is already skip-marked and history is already pushed (never rewrite pushed history), add a bare `git commit --allow-empty -m 'ci: trigger verification run for <TASK>'` with a message containing no skip token and push that; (3) ALWAYS verify the run exists for the exact pushed SHA instead of trusting `gh run list` to eventually show one: `gh api \"repos/<org>/<repo>/actions/runs?head_sha=<sha>\" --jq .total_count` (returns 0 when suppressed), then `gh run view <id> --json status,conclusion,jobs`. Classify a red run from the failing job's log, not from the headline conclusion: sibling matrix jobs show `cancelled` when one cell fails, and only the failing cell carries the real signature.\n\nVERIFICATION: with the tip rebuilt as a skip-token-free empty commit, `gh api .../runs?head_sha=<sha> --jq .total_count` returned 1, run 35037277580 reached completed/success across py3.10/3.11/3.12, and `git rev-list --count <remote>/main..HEAD` = 0 after `git fetch`. The intermediate suppressed SHA was confirmed at total_count=0 both before and ~2 minutes after the push, ruling out propagation delay.\n\nADJACENT FAILURE SEEN IN THE SAME WINDOW (same tick, different class, recorded here for discover value): the first run that DID start went red on a repository doc-drift gate because the code change added 7 tests, moving live pytest collection 1462 -> 1469 while README still claimed 1462. A test-tree change must carry its count consumers in the SAME commit: recompute with the CI-identical command (`pytest --collect-only -q --override-ini=addopts=`, and the same pipeline for collected file counts), update every tracked 'N tests pass' / 'N tests across' / 'N test files' claim, replay the workflow's drift-check block verbatim locally, and run any docs-drift script before pushing.", "environment": "GitHub Actions on a GitHub-hosted repo pushed over SSH; local driver is gh CLI + git. Repo is a Python project with a matrix CI (py3.10/3.11/3.12) that includes a README test-count drift gate. Repro needs only: a push whose tip commit message contains a skip token, then an API query for that SHA.", "language": "python", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "github-actions-skip-token-suppresses-whole-push", "provider": "openrouter", "solved_at": "2026-09-15T23:55:08.787Z", "version": ""}