Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .github/workflows/devkit-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,21 @@ jobs:
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git push origin --delete "$BRANCH_NAME" 2>/dev/null || true
# NO `git push origin --delete "$BRANCH_NAME"` here (DEC-079).
#
# Deleting the head ref DELETES THE PR WITH IT: GitHub auto-closes any
# open PR whose head branch disappears. That is what closed 18 sync PRs
# across the fleet, including four that were already fully green — the
# e2e had passed, and the work was discarded and re-queued for nothing.
# It also made the `gh pr edit` reuse path below dead code: the delete
# closed the very PR the next line goes looking for, so `existing` was
# always empty and every run created a brand-new PR.
#
# Force-pushing the branch is sufficient and non-destructive: an open PR
# follows its head ref and simply picks up the new commit.
git checkout -B "$BRANCH_NAME"
git commit -m "chore: sync devkit submodule to ${{ steps.bump.outputs.target }}"
git push --force-with-lease origin "$BRANCH_NAME"
git push --force origin "$BRANCH_NAME"

body="Automated devkit sync — bumped \`$DEVKIT_PATH\` to the devkit's latest release (\`${{ steps.bump.outputs.target }}\`)."
existing=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number // empty' 2>/dev/null || true)
Expand Down
19 changes: 19 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,25 @@ while half the fleet was still broken. Collector → `e2e/harness/_artifacts.sh`
harnesses; prune → `e2e/ci/prune-artifacts.sh`, invoked by both workflows. A reusable workflow can
call the script from the consumer's vendored devkit path, so there is still exactly one definition.

**DEC-079 — devkit-sync is on-demand, and never deletes its own branch.**
Two corrections to the sync mechanism, one of policy and one of implementation.

*Policy.* The nightly `schedule:` is removed; the caller is `workflow_dispatch:` only. A submodule
pin is a deliberate dependency upgrade, not something that should land while nobody is watching, and
the schedule measurably cost more than it delivered: **189 runs produced 29 merged bumps** (51 PRs
opened, 18 closed without merging). Worse, from 2026-07-10 it silently no-op'd for three weeks —
every night it opened a PR whose checks sat in `action_required` awaiting approval, so nothing merged
and nothing complained, because a no-op is indistinguishable from "nothing to sync". Deliberate bumps
go through `make update-devkit` or a manual dispatch.

*Implementation.* The push step began with `git push origin --delete "$BRANCH_NAME"`. Deleting a head
ref **deletes the PR with it** — GitHub auto-closes any open PR whose head branch disappears. That is
the actual cause of the 18 closed-unmerged PRs, including four that were **already fully green**: the
e2e had passed and the result was discarded and re-queued for nothing. The same delete also made the
`gh pr edit` reuse path below it dead code — it closed the very PR the next line went looking for, so
`existing` was always empty and every run created a brand-new PR instead of updating one. A force-push
alone is sufficient and non-destructive: an open PR follows its head ref and picks up the new commit.

> **Numbering note:** DEC-070–072 were introduced as code comments only and were never written up
> here. This section documents 073–076; backfilling 070–072 is tracked separately.

Expand Down
18 changes: 14 additions & 4 deletions templates/devkit-sync.caller.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
# devkit-sync — caller template (SPEC DEC-044).
#
# Copied into a consumer plugin repo at .github/workflows/devkit-sync.yml by
# `make link-workflows`. Keeps the repo's vendored devkit submodule
# (tests/_testkit) current by bumping it to the devkit's latest main nightly.
# `make link-workflows`. Bumps the repo's vendored devkit submodule
# (tests/_testkit) to the devkit's latest release within its major channel.
#
# ON DEMAND ONLY (DEC-079). This used to run nightly at 04:00 UTC. A submodule
# pin is a deliberate dependency upgrade, not something that should happen while
# you sleep, and the schedule cost more than it delivered:
# * 189 runs across the fleet produced 29 merged bumps — 51 PRs opened, 18 of
# them closed without ever merging.
# * From 2026-07-10 it silently no-op'd for three weeks: every night it opened
# a PR whose checks sat in `action_required` awaiting approval, so nothing
# merged and nothing complained. A no-op looks exactly like "nothing to sync".
# * It clobbered in-flight work — see the branch-delete note in the reusable
# workflow.
# Bump deliberately instead: `make update-devkit`, or run this workflow manually.
#
# Required REPO-level secrets (the shared sync GitHub App; already present on
# repos that previously used skills-sync — same App):
Expand All @@ -15,8 +27,6 @@
name: devkit-sync

on:
schedule:
- cron: "0 4 * * *" # nightly 04:00 UTC (offset from skills-sync's 03:00)
workflow_dispatch:

permissions:
Expand Down
Loading