git-workflow
Recipe card from the charly-internals plugin (Development — contributor internals).
This card has additional detail pages:
git-workflow — branch-per-change, PR-only agent-validated landing
Section titled “git-workflow — branch-per-change, PR-only agent-validated landing”Every change to an OpenCharly repo lands through ONE discipline: a pull request
that a FRESH pr-validator agent independently validates and merges. A direct
push to main is FORBIDDEN and mechanically disabled — GitHub branch protection
(enforce_admins) + the pre-push-gate block it in every repo. The R10 pass
authorizes OPENING the PR, never a self-merge: the two-step landing separates the
author (who opens the PR) from the fresh evaluator (who validates, merges, tags).
This skill is the mechanics; the project rulebook “Post-Execution Policies” (AGENTS.md / CLAUDE.md) carries the
mandate, /charly-internals:cutover-policy the one-phase rule, /charly-build:migrate
the schema-version/tag coupling, and plugins/internals/agents/pr-validator.md the
evaluator’s own spec.
Non-negotiable invariants
Section titled “Non-negotiable invariants”- No direct push to
main(the project rulebook’s PR-only landing mandate — see “Post-Execution Policies”). Enforced by GitHub branch protection (thecharly/pr-validatorstatus + a PR + linear history +enforce_admins) and thepre-push-gatelocally. Organization-wide apply/verify is owned only byopencharly/.github/scripts/branch-protection.sh. - Never force-push or amend a pushed branch (mandate, same rulebook section). The flow never needs one:
feat/advances only by ADDING commits (the author’s change, any review-round fix commits, then the evaluator’s merge-time version stamp), and the squash-merge collapses them; a stalefeat/catches up withgh pr update-branch(a merge, NOT a rebase-force); tags are add-only. Amend only before the first push. - R10-gated; the merge requires the fresh
pr-validator’s green status. R10 PASS authorizes opening the PR (with pasted evidence); a rule violation or R10 FAIL means no greencharly/pr-validatorstatus and no merge — fix in the same tree, re-run R10, re-push; the status resets and the evaluator re-runs. - Zero warnings is part of R10 (project rulebook R1). A version-mismatch warning clears with
charly box reconcile; any other warning gets/charly-internals:root-cause-analyzerthen a real fix — “warning” is never an accepted end state. - Atomic on
main, never onfeat/. The evaluator’s--squashfolds the author’s change, any review-round fix commits, and the merge-time CalVer rewrite (see “CalVer” inreferences/validator-and-calver.md) into one commit whose message the evaluator composes; thefeat/branch may freely accumulate fix commits across review rounds. Two separate cutovers must never share one PR. - Update the PR; never close-and-recreate (except for work that will not land at all — a disproven premise, an abandoned approach). When a review demands changes, append a commit and push it fast-forward — the status resets and the evaluator re-runs. This is what makes the no-force-push rule livable: because
maingets a squash, a branch carrying five fix commits still lands as one. - Tree-safety before destructive actions (R6). Check
git status+git stash listbefore any destructive working-tree action —git stashdiscards in-progress work;rmon a tracked file is destructive. When the sandbox blocks an action, find a non-destructive alternative rather than working around it. The stash/pop cycle can itself silently un-stage agit rm: a stash taken while a deletion is staged restores the deletion as unstaged onpop, so agit statusright after the cycle that shows the deleted file back as a plain unstaged change (rather than the staged deletion you left) has quietly lost the staging — re-stage it (git rm <path>again, orgit add -u) before committing. A stash/pop round-trip is never a no-op on a mixed add+rm working tree. - Right worktree — pin one absolute path for the whole edit→commit→push sequence. Before branching, staging, or committing, confirm the worktree you are driving is the same one your edits landed in:
git -C <path> rev-parse --show-toplevelmust equal the path you edited, andgit -C <path> status --shortmust list those edits. Under symlinked or near-twin sibling worktrees — a parent dir that is itself a symlink (~/projects→~/Sync/projects), or look-alike names such as…/charlyvs…/<other-worktree>—cd-ing to the wrong sibling makesgit switch -c+git commitrun against a clean tree and report “nothing to commit”, silently landing nothing (or landing in the wrong repo). Never change the path spelling mid-sequence. An unexpected “nothing to commit” right after editing a file is the signature of this mistake — stop and re-verify--show-toplevelbefore retrying (blind retry is an R1 violation). - The universal PR-gate — audit before any PR action, unconditionally. Before opening, updating, or merging any pull request, run the aggregate audit:
gh pr listacross every touched repo +git worktree list+ the live teammate/agent roster. This is a standing preflight, run first every time — never reached for only once something already looks off — because skipping it risks a duplicate PR for scope already covered in flight, a branch update from a stale worktree, or a merge over a still-running validator’s verdict. Full operational detail:/charly-internals:agents“The universal PR-gate”. - Post-commit staging verification. After every commit, re-run
git status --short(expect it empty, or only unrelated untracked paths) andgit show --stat(confirm every intended file is actually listed) — a multi-pathgit addnaming several paths where one is mistyped or stale can commit only the files that did resolve whilegit commitstill succeeds, producing a commit that would not even compile. Habitually re-checkinggit show --stat— not any tooling that fails loudly on its own — is what catches this. - Check-coverage is part of R10. The change must ship the test coverage that proves its functionality (
check:checks for new/changed layers & images, Go tests forcharlycode) AND the live run must have exercised it. A change whose new functionality has no test that would fail without it is not landable. - Every repo is tagged at merge. The superproject, every
box/<distro>,plugins, andpkg/*all mintv<YYYY.DDD.HHMM>on their own merged HEAD — the tag marks the MERGE, decoupled from anycharly.ymlversion:schema field, so a repo needs nocharly.ymlto be tagged. The sole exception is the sdk contract repo, which tags under its own Go-module schemev0.<YYYYDDD>.<HHMM with all leading zeros stripped>(B2 step 0) — not an exemption but a hard Go-module requirement:v<YYYY.DDD.HHMM>is not a valid Go module version (semver forbids a leading-zero segment —0733→733— and amajor ≥ 2would force a/vNmodule-path suffix that breaks everyimport github.com/opencharly/sdk), so the strippedv0.<…>form is mandatory, not a choice. A skipped tag is a defect, not an exemption: the orchestrator verifies the tag landed after each merge (git ls-remote --tags origin v<VER>non-empty) and, if the evaluator skipped it, backfills it add-only on the merged HEAD (git tag -a v<VER> -m "<subject>" <merged-HEAD>+git push origin refs/tags/v<VER>) — tags are immutable, so a backfill only adds one, never moves an existing tag.
Reference Index
Section titled “Reference Index”| Topic | File |
|---|---|
| B1 (the two-step branch-per-change loop, concurrent landings, the cross-repo WIP landing sequence) and B4 (sync to upstream + prune) | references/branch-and-pr-loop.md |
B2 (multi-repo/multi-worktree coordination, per-module verification), B3 (agent teams on one shared tree), B6 (cross-repo @github landing), and B7 (multi-worktree landing + refresh, the canonical end-to-end) |
references/multi-repo-coordination.md |
| B5 (the fresh evaluator + fork/PR path, the two-gate autonomous-landing model), CalVer generation, post-landing cleanliness + report format, and the validation-FAILS recovery sequence | references/validator-and-calver.md |
Cross-References
Section titled “Cross-References”- the project rulebook “Post-Execution Policies” — the mandate this skill operationalizes.
plugins/internals/agents/pr-validator.md— the fresh evaluator’s full spec.opencharly/.github/scripts/branch-protection.sh— the sole organization-wide branch-protection apply/verify owner./charly-internals:cutover-policy— one-phase, atomic-commit, R10-at-the-end./charly-build:migrate—version:↔ tag coupling, per-merge tags, push order./charly-build:reconcile— cross-repo@githubpin alignment used by B6./charly-check:check— the check-coverage gate (R10) every change must satisfy./charly-internals:root-cause-analyzer— run on any FAIL before re-trying.
When to Use This Skill
Section titled “When to Use This Skill”Invoke before any git / gh action that commits, branches, pushes, opens a PR,
or drives the pr-validator merge/tag — and whenever syncing to upstream, applying
branch protection, or pruning branches/worktrees across the main repo and its
submodules.