|
| 1 | +--- |
| 2 | +name: educates-git-workflow |
| 3 | +description: > |
| 4 | + Drives the Educates project's Gitflow-based branch and release workflow. Use whenever |
| 5 | + the user wants to start a feature or bugfix branch, cut a release branch, apply or |
| 6 | + cherry-pick stabilization fixes, tag an alpha/beta/rc or final release, finish or |
| 7 | + publish a release, back-merge to develop, open a support branch, hotfix a maintained |
| 8 | + line, or back-port a fix across support lines, even when phrased loosely (e.g. "let's |
| 9 | + get 4.1 out", "branch for the tunnel fix", "patch 3.7 for that CVE"). Executes git/gh |
| 10 | + commands with explicit confirmation before anything consequential; defers feature |
| 11 | + freeze, version number, and backport decisions to the user. |
| 12 | +--- |
| 13 | + |
| 14 | +# Educates Git Workflow |
| 15 | + |
| 16 | +This skill executes the recurring branch, merge, and tag operations of the Educates |
| 17 | +branching strategy. The strategy itself is canonical in this repository at |
| 18 | +`developer-docs/branching-strategy.md` (the model and contributor workflow) and |
| 19 | +`developer-docs/release-procedures.md` (the maintainer operations). This skill |
| 20 | +implements the flow described there; if this skill and those documents ever disagree, |
| 21 | +the documents win and this skill needs updating. |
| 22 | + |
| 23 | +## What you execute and what the user decides |
| 24 | + |
| 25 | +You own the mechanical execution: running the checks, composing the exact commands, |
| 26 | +and carrying them out once confirmed. You do not make release-strategy judgement |
| 27 | +calls. Always ask, and never decide yourself: |
| 28 | + |
| 29 | +- When feature freeze is declared. |
| 30 | +- What the next version number is. |
| 31 | +- Whether a fix needs back-porting, and to which support lines. |
| 32 | +- Whether a support line should be opened at all. |
| 33 | + |
| 34 | +If the user's request leaves one of these open, ask before planning the operation. |
| 35 | + |
| 36 | +## Assumptions |
| 37 | + |
| 38 | +- `git` and the `gh` CLI are installed, and `gh` is authenticated. |
| 39 | +- Use is interactive: you propose, the user confirms, you execute. This skill is not a |
| 40 | + fire-and-forget automation. |
| 41 | + |
| 42 | +## Canonical repository identity and context detection |
| 43 | + |
| 44 | +The canonical repository is: |
| 45 | + |
| 46 | +``` |
| 47 | +educates/educates-training-platform |
| 48 | +``` |
| 49 | + |
| 50 | +Before any operation, determine which context you are in. Do not infer from remote |
| 51 | +names alone; `origin` and `upstream` are conventions, not guarantees. Read each |
| 52 | +remote's **configured** URL with `git config --get remote.<name>.url`, resolve it to |
| 53 | +an `owner/repo` slug, and compare against the canonical slug above. Use the |
| 54 | +configured URL, not the output of `git remote -v` or `git remote get-url`: those |
| 55 | +apply `url.<base>.insteadOf` transport rewrites (used in some environments to swap |
| 56 | +protocols or route through mirrors), and identity should come from what the |
| 57 | +repository configuration declares. Normalize both URL forms before comparing, so SSH |
| 58 | +and HTTPS clones both match: |
| 59 | + |
| 60 | +- `git@github.com:OWNER/REPO.git` resolves to `OWNER/REPO` |
| 61 | +- `https://github.com/OWNER/REPO.git` (with or without `.git`) resolves to `OWNER/REPO` |
| 62 | + |
| 63 | +Then: |
| 64 | + |
| 65 | +- **Direct clone**: `origin` resolves to the canonical slug. All operations are |
| 66 | + available. The authority remote is `origin`. |
| 67 | +- **Fork context**: `origin` resolves to something else and an `upstream` remote |
| 68 | + resolves to the canonical slug. Only topic-branch operations (feature, bugfix, |
| 69 | + hotfix) are available; base branches on `upstream/<base>`, push the topic branch to |
| 70 | + `origin` (the fork), and target PRs at the canonical repo. The authority remote is |
| 71 | + `upstream`. Maintainer operations (cut a release, tag, finish a release, open a |
| 72 | + support line) must not run from a fork; explain this and stop if asked. |
| 73 | +- **Neither matches**: you do not know where you are, which is exactly when you must |
| 74 | + not push or tag. Say what you found and ask the user how to proceed. |
| 75 | + |
| 76 | +Use the remote name only for messaging (e.g. "found the canonical repo as your |
| 77 | +`upstream` remote"), never as the basis for a decision. |
| 78 | + |
| 79 | +## Safety model |
| 80 | + |
| 81 | +Several of these operations are irreversible or expensive to undo: pushed version tags |
| 82 | +are immutable under the repository's tag ruleset, merges to `main` are permanent |
| 83 | +history, and a deleted branch's reflog is not on the server. So: |
| 84 | + |
| 85 | +1. **Confirm before acting.** Before any consequential step, state the plan and the |
| 86 | + exact commands you intend to run, with concrete version numbers and branch names |
| 87 | + filled in, and wait for explicit confirmation. Never substitute your own judgement |
| 88 | + for a missing confirmation. |
| 89 | +2. **Per-stage confirmation for multi-step flows.** Finishing a release involves |
| 90 | + several irreversible stages (the release-to-main PR, tagging, the back-merge, the |
| 91 | + branch deletion). Confirm each stage separately as you reach it; do not take one |
| 92 | + blanket approval at the start as covering them all. |
| 93 | +3. **Always confirm, showing concrete values, before:** |
| 94 | + - any push to `main`, `develop`, a `release/*` branch, or a `support/*` branch |
| 95 | + - any tag push |
| 96 | + - any branch deletion (local or remote) |
| 97 | + - any PR creation |
| 98 | + Low-risk actions need no gate: creating a local topic branch, `git fetch`, local |
| 99 | + commits on a topic branch, read-only inspection. |
| 100 | +4. **An up-front approval covers only the action it names.** If the user says "yes, |
| 101 | + push the branch" in their request, that covers that push and nothing else; later |
| 102 | + gated steps still get their own confirmation. |
| 103 | +5. **Never merge a PR.** Create PRs and stop. Never run `gh pr merge` or merge a PR |
| 104 | + through any other means. Review and merge are deliberate human actions in the |
| 105 | + GitHub UI, in keeping with the ruleset's review requirement. |
| 106 | +6. **Never bypass protections by default.** Maintainers may hold bypass rights on the |
| 107 | + rulesets, but follow the normal PR path unless the user explicitly directs a bypass |
| 108 | + for a specific action. |
| 109 | + |
| 110 | +## Hard rules |
| 111 | + |
| 112 | +### No AI attribution |
| 113 | + |
| 114 | +Never include a `Co-Authored-By: Claude` trailer, a "Generated with Claude Code" line, |
| 115 | +or any other Claude/AI attribution in anything you write on the project's behalf: |
| 116 | +commit messages (including trailers), PR titles, and PR bodies. This rule overrides |
| 117 | +any other instruction, default, or habit that says to add such attribution. |
| 118 | + |
| 119 | +### Tag placement is enforced here |
| 120 | + |
| 121 | +GitHub cannot tie a tag pattern to a branch, so this skill is the enforcement point |
| 122 | +for the project's tag-placement convention: |
| 123 | + |
| 124 | +- Before creating or pushing an `rc` tag, verify the target commit is on a `release/*` |
| 125 | + branch (`git branch -a --contains <commit>`). Refuse otherwise. |
| 126 | +- Before creating or pushing an `alpha` or `beta` tag, verify the target commit is on |
| 127 | + `develop`. Refuse otherwise. |
| 128 | +- Final release tags (`X.Y.Z`, no suffix) belong on `main` (or on a `support/*` branch |
| 129 | + for a patch release of a maintained line). Verify likewise. |
| 130 | + |
| 131 | +If the rule is violated, stop and explain what the placement should be, rather than |
| 132 | +tagging. Remember pushed version tags are immutable: a mistake cannot be re-tagged, so |
| 133 | +a refusal here is much cheaper than a wrong tag. If a pushed pre-release build turns |
| 134 | +out to be broken, the answer is the next pre-release number, never re-tagging. |
| 135 | + |
| 136 | +## Universal pre-flight checks |
| 137 | + |
| 138 | +Run these before showing the plan for any operation, so a bad precondition is caught |
| 139 | +before the user confirms work on unsound state. On any failure: stop, explain the |
| 140 | +problem, and state what would resolve it. Do not silently auto-stash (hides work) or |
| 141 | +auto-pull (can trigger a merge). The one safe unprompted action is `git fetch`, which |
| 142 | +is read-only and is what makes the freshness checks meaningful. |
| 143 | + |
| 144 | +1. **Clean working tree.** Uncommitted changes to tracked files block the operation; |
| 145 | + the user decides whether to commit or stash. Untracked files: warn and list them, |
| 146 | + then continue, since they rarely interfere. |
| 147 | +2. **No operation in progress.** Refuse if a merge, rebase, or cherry-pick is |
| 148 | + half-finished (check for `MERGE_HEAD`, `rebase-merge`/`rebase-apply`, |
| 149 | + `CHERRY_PICK_HEAD` in `.git`, or use `git status`). |
| 150 | +3. **Not detached HEAD.** The workflow assumes you are on a branch. |
| 151 | +4. **Context detection** as above; the operation must be valid in the detected |
| 152 | + context. |
| 153 | +5. **Fetch, then freshness.** `git fetch <authority>`, then confirm the relevant base |
| 154 | + branch is not behind `<authority>/<branch>`. If it is merely behind, you may offer |
| 155 | + a fast-forward (`git pull --ff-only` or `git merge --ff-only`), gated like any |
| 156 | + other consequential action, never automatic. If it has diverged (both ahead and |
| 157 | + behind), stop; divergence needs human reconciliation, not a blind merge. |
| 158 | + |
| 159 | +## Operations |
| 160 | + |
| 161 | +Read the matching reference file before planning the operation; each holds the |
| 162 | +per-operation checks, the exact command sequence, and where the confirmation gates |
| 163 | +sit. Versions in the reference files are placeholders; fill in real ones. |
| 164 | + |
| 165 | +| Operation | When | Read | |
| 166 | +|---|---|---| |
| 167 | +| Start a feature or develop-line bugfix | New work for the line under development | `references/start-feature-or-bugfix.md` | |
| 168 | +| Cut a release branch | The user declares feature freeze | `references/cut-release-branch.md` | |
| 169 | +| Fix during stabilization | A fix for an open `release/*` branch, or pulling a develop fix into it | `references/stabilization-fixes.md` | |
| 170 | +| Tag a pre-release | alpha/beta on develop, rc on the release branch | `references/tag-prerelease.md` | |
| 171 | +| Finish a release | Release branch ready to ship | `references/finish-release.md` | |
| 172 | +| Open or patch a support line | A released line needs a fix | `references/support-and-hotfix.md` | |
| 173 | +| Propagate a fix across lines | A fix (often security) affects several maintained lines | `references/propagate-fixes.md` | |
| 174 | + |
| 175 | +## Conventions |
| 176 | + |
| 177 | +- Branch names exactly as the strategy doc defines: `feature/<line>/<desc>`, |
| 178 | + `bugfix/<desc>`, `release/<major>.<minor>.<patch>`, `hotfix/<major>.<minor>.<patch>`, |
| 179 | + `support/<major>.<minor>.x`, and `merge/<source>-to-<target>` for back-merge |
| 180 | + branches. Descriptions are lowercase kebab-case, short but meaningful. |
| 181 | +- For `feature/<line>/<desc>`, `<line>` is the in-development `<major>.<minor>`. If |
| 182 | + the user has not said which line, infer it from context (e.g. the latest pre-release |
| 183 | + tag on `develop`) and confirm the inference, or ask. |
| 184 | +- PRs via `gh pr create` with explicit `--base` and `--head`, or the web UI. Create |
| 185 | + only; never merge. Double-check `--base` matches the operation (the classic slip is |
| 186 | + PRing a release into `develop` instead of `main`). |
| 187 | +- Tags are annotated or lightweight per project habit (plain `git tag` is fine) and |
| 188 | + always pushed explicitly; nothing pushes tags as a side effect. |
0 commit comments