Skip to content

ci(sync): open consumer sync prs as drafts [TURNWRK-793] - #46

Merged
practical-bot merged 1 commit into
mainfrom
ci/turnwrk-793-free-minutes
Nov 1, 2026
Merged

practical-bot merged 1 commit into
mainfrom
ci/turnwrk-793-free-minutes

Conversation

@jcotillo

@jcotillo jcotillo commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Why

TurnWrk billed 3,127 private hosted min in October 2026 against ~1,983 free. shared is public, so its own minutes are free, but sync-consumers.yml opened every bot PR non-draft, so each force-push to bot/sync-shared ran full CI in the four private consumers.

October, CI runs on bot/sync-shared (measured from the Actions API, per-job minutes rounded up):

consumer runs billed min bot PRs merged
dispatch 25 364 2
clean 21 36 1
restock 21 62 1
turnwrk-cortex 22 52 1
total 89 514 5

89 runs to ship 5 merges.

What changed

  • gh pr create now passes --draft (.github/workflows/sync-consumers.yml). Every consumer's CI skips drafts and runs on ready_for_review, so a sync PR runs CI once, when a human marks it ready.
  • The PR body tells the reviewer to mark it ready for review to run CI before merging.
  • Comment on the create/update block: draft state is never changed on update.
  • Net: +7 / −1 (1 file).

What deliberately did NOT change

  • The update path. It is git push -f plus gh pr edit (title and body). Neither touches draft state. A draft stays a draft, and a PR a human marked ready is never pushed back into draft.
  • The three bot PRs that are open now stay non-draft: dispatch #268, restock #55, turnwrk-cortex #101. Their force-pushes keep running CI until each is merged or closed. Converting them is a one-time gh pr ready --undo per PR on another repo, which I left to a human.
  • Matrix, cron, tree-equality guard (TURNWRK-644), and the stale-PR close path are unchanged.

Proof

Every consumer's CI skips drafts and runs on ready_for_review. I read each default-branch ci.yml through the API:

consumer types: includes ready_for_review every job gated on draft == false
clean (trunk) yes unit, e2e
dispatch yes unit, rules, prod-build, e2e (visual is push-only)
restock yes unit, e2e (e2e.yml has no pull_request trigger)
turnwrk-cortex yes unit, quote-eval

No consumer needed a fix.

The private repos accept drafts on this org's plan: dispatch already has 3 draft PRs.

Expected saving: with drafts, CI runs about once per merged sync (on ready_for_review), so about 5 runs instead of 89. At October's per-run cost that is about 514 − 40 ≈ 470 private min/month, split across the consumers. dispatch is most of it (364 → ~30).

Before and after, per trigger (consumer CI runs per sync PR):

event before after
bot opens PR full CI none (draft)
bot force-push (each 4h cron with drift, each shared push) full CI none (draft)
human marks ready n/a full CI once
force-push after ready full CI full CI (unchanged, human intent)

Verification

$ python3 -c "import yaml;yaml.safe_load(open('.github/workflows/sync-consumers.yml'));print('yaml ok')"
yaml ok
$ actionlint .github/workflows/sync-consumers.yml && echo actionlint ok
actionlint ok
$ npm ci && npm run typecheck && npm run test:run
typecheck 0
 Test Files  78 passed (78)
      Tests  820 passed (820)

The workflow itself can only run on a push to main. It was not triggered from this branch.

Refs: TURNWRK-793

🤖 Generated with Claude Code

Every consumer's ci.yml skips draft PRs and runs on ready_for_review, so bot
force-pushes to bot/sync-shared start no consumer CI until a human marks the
PR ready. October: 89 bot-branch CI runs, 514 private min, for 5 merged syncs.

Refs: TURNWRK-793

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jcotillo jcotillo added the merge:queued Hand this PR to the factory merge queue label Nov 1, 2026

@practical-bot practical-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review

I reviewed the current body and the single-file diff against main.

What is right

  • The only behavioral change is the new gh pr create --draft path in .github/workflows/sync-consumers.yml; the existing update path remains git push -f plus gh pr edit, so updates do not force a draft or ready state. The diff is +7/-1, matching the PR body.
  • The PR body is generated with explicit instructions to mark the sync PR ready before merge, and the implementation does not invoke gh pr ready or otherwise alter draft state after creation.
  • I checked each live consumer workflow through the GitHub API. clean, dispatch, restock, and turnwrk-cortex all include ready_for_review in their pull_request types and gate their pull-request jobs on github.event.pull_request.draft == false. This supports the claimed no-CI-while-draft behavior and the one run when a human marks the PR ready.
  • All three rows from gh pr checks 46 --repo TurnWrk/shared passed on this head: secret scan, typecheck + unit, and firestore rules behavior tests. The typecheck + unit run executed npm ci, typecheck, unit tests, build, and vendored checks; the rules row executed npm run test:rules.
  • The exact shell block extracted from the changed workflow passes bash -n, and git diff --check is clean. gh pr create --help confirms that --draft is the supported flag.

Claim ledger

  • “gh pr create now passes --draft”: PASS — exact diff inspection shows gh pr create --repo "${{ matrix.repo }}" --draft; gh pr create --help reports -d, --draft Mark pull request as a draft.
  • “Every consumer's CI skips drafts and runs on ready_for_review”: PASS — API reads of all four consumer ci.yml files found the trigger and draft guards listed above.
  • “The update path ... neither touches draft state”: PASS — the changed workflow contains only gh pr edit for existing PRs and no draft-state command on that path.
  • “Net: +7 / −1 (1 file)”: PASS — git diff --numstat origin/main...HEAD prints 7 1 .github/workflows/sync-consumers.yml.
  • “The workflow itself can only run on a push to main”: PASS — the workflow trigger is push: branches: [main], plus its declared schedule and workflow_dispatch triggers; it has no pull-request trigger.
  • “yaml ok” and “actionlint ok”: UNVERIFIED — the exact commands were attempted, but this review image has neither python3 nor actionlint installed. The changed shell block passed bash -n; the PR checks on this head were green.
  • “typecheck 0, 78 test files and 820 tests passed”: VERIFIED-BY-CI — run 41334496914 passed the repository's typecheck + unit job on head f02d60d568fa18a74932eb9a6e8d71033e0d6bad; I did not rerun the full suite because CI already proved it on this exact head.

NEEDS VERIFICATION: After merge, confirm the first full comparable week in TurnWrk billing shows private hosted minutes at or below approximately 55% of the October baseline, as required by TURNWRK-793's Done-when. The ticket already carries needs:verification.

REVIEW VERDICT: approve
FINDING: No blocking findings; the draft creation path and all four consumer ready-for-review gates are verified -> DROPPED (no defect)
NEEDS VERIFICATION: First full post-merge comparable week of TurnWrk billing is at or below approximately 55% of the October baseline

@practical-bot
practical-bot merged commit 52f0a92 into main Nov 1, 2026
3 checks passed
@practical-bot
practical-bot deleted the ci/turnwrk-793-free-minutes branch November 1, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge:queued Hand this PR to the factory merge queue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants