AI Development Methodology

09 — Git workflow

Purpose: define the git practices that keep the project navigable, auditable, and safe — for both human contributors and AI agents operating in parallel. These practices are not aesthetic; each one prevents a specific class of failure.

Every rule here exists because the absence of it has caused real damage. Read them as preventive measures, not preferences.


Branch protection

The rule

The primary branch (master, main, or whatever the project calls it — referred to below as the trunk) is protected.

These rules apply to humans and AI agents identically. No contributor is “trusted enough” to skip them — the rules exist because the failure modes are equally bad regardless of who triggers them.

Why this matters

When to break the rule

Almost never. The exceptions are:

If you find yourself wanting to force-push to recover from a small problem, stop and find a non-destructive path. Reverts, new commits, and PRs are almost always available.


Branch naming

Branches are named by intent and content.

Prefix by intent

feature/<slug>   # new functionality
fix/<slug>       # bug fix
chore/<slug>     # tooling, dependencies, infrastructure
refactor/<slug>  # restructuring without behavior change
docs/<slug>      # documentation-only changes
test/<slug>      # test-only changes
release/<slug>   # release preparation

Slug rules

In practice: feature/add-csv-export, fix/date-parser-overflow, refactor/extract-session-middleware, test/regression-for-bl-0517.

Why named branches matter

Patch-branch convention for authoritative artifacts

When an AI agent (especially an autonomous loop) is allowed to propose a fix to an artifact the project treats as authoritative — methodology docs, design-system tokens, regulatory checklists, upstream spec mirrors — the fix lands on a patch branch, not directly on the trunk.

<area>-patch/YYYY-MM-DD-NN

Examples:

methodology-patch/2026-05-25-01    # first methodology patch of the day
designsystem-patch/2026-05-25-01   # first design-system patch of the day
spec-patch/2026-05-25-02           # second upstream-spec patch of the day
runbook-patch/2026-05-25-01        # first runbook patch of the day

Common area prefixes (pick from these or register your own in the project’s instruction file):

Area prefix Authoritative artifact
methodology The methodology docs themselves (in a self-applying project — see Constraint 1 of templates/AUTONOMOUS_LOOP.md “Tiered autonomy”).
designsystem Design tokens, component spec docs, brand guidelines.
spec Upstream contract mirrors, OpenAPI specs the project ingests.
runbook On-call runbooks, incident-response checklists, escalation maps.
compliance Regulatory checklists, audit manifests, license inventories.

Each project records its allowed area prefixes in CLAUDE.md / AGENTS.md (or equivalent) so the autonomous loop knows which areas it can patch and which it must escalate.

The convention enables three things:

  1. The maintainer reviews diffs, not findings. A patch branch already contains the proposed edit, the CHANGELOG entry, and (where applicable) a cross-AI verification note. Review is yes/no on a concrete change, not translate-finding-into-fix.
  2. The patch is reversible at any point before merge. Branch delete = patch dropped. No trunk state ever touched.
  3. The branch name carries the audit context — area (which authoritative artifact), date (when proposed), sequence number (multiple patches same day get distinct names).

The patch branch follows all other rules in this doc: trunk protection still applies (no force-push, no direct trunk commit), commits still have clear messages, CHANGELOG entry lands in the same commit as the patch. All DoD gates still apply — Gate 4 (documentation updated, see 07_definition_of_done.md “Gate 4 — Documentation updated”) and the diff-verification mode of Gate 5 (see 10_testing_and_verification.md “Three modes”) gate the merge.

The autonomous loop never auto-merges a patch branch — that’s always the maintainer’s gate. See AUTONOMOUS_LOOP.md “Tiered autonomy for authoritative artifacts” for the tier matrix that decides whether a finding becomes a patch branch (T0/T1) or stays as advice (T2/T3).


Commit cadence

The rule

Commit and push after every completed sub-task once tests pass locally. Do not accumulate giant uncommitted change sets.

What counts as a sub-task

Why frequent commits

Why “once tests pass locally”

Committing broken intermediate states bloats the history with noise and breaks bisection. The discipline is: complete a coherent sub-task, run the relevant tests, commit. If a long sub-task naturally produces broken intermediate states, keep them local until the sub-task is consistent, then commit the consistent end state.

When pushing matters

Push the commits often, too. A local-only commit:

Default to push-after-every-commit on the active branch. Branches in flight should always be visible on the remote.


Commit message convention

Follow conventional-commits style:

<type>(<scope>): <subject>

<optional body>

<optional footer>

Types

Type When to use
feat New behavior visible to the user or an external system.
fix Bug fix. The diff demonstrably corrects broken behavior.
chore Tooling, deps, configuration, housekeeping. No user-visible change.
refactor Restructuring code with no behavior change.
docs Documentation changes only.
test Tests added or changed (without changing tested behavior).
release Version bumps, release-tag commits.
perf Performance improvement (provable, with measurement).
revert Reverts a prior commit. Body must name the reverted commit.

Scope

A short, lowercase identifier of what part of the codebase the commit touches: (api), (web), (backlog), (deploy), (auth). Optional but useful for grep.

Subject

Examples

feat(api): add CSV export endpoint for activity report

Operators currently re-key totals manually. The new endpoint streams CSV
matching the on-screen table filter state.

Closes BL-0428.
fix(web): handle empty trip data in summary card

The card threw on `trips.length === 0` because of an unguarded `.reduce`.
Added a guard plus a regression test.

Closes BL-0517.
refactor(auth): extract session validation into shared helper

No behavior change. Three routes were duplicating the same check; now
they all call validateSession() from the shared module.

Short-body types need no ceremony: chore(deps): upgrade typescript to 5.4, docs(methodology): add 09 git workflow.

What not to commit


Lock-file management

Most modern package managers produce a lock file (package-lock.json, pnpm-lock.yaml, yarn.lock, Cargo.lock, Gemfile.lock, composer.lock, poetry.lock, uv.lock). The lock file pins exact versions across all dependencies, ensuring install produces the same tree every time.

Lock files are an underrated source of confusion in AI-collaborated workflows: agents touch dependencies, lock files change, and the resulting diffs are huge and inscrutable.

The rule

Lock files are committed to the repo. They’re not generated artifacts to gitignore. The whole point is reproducibility — the lock file is the dependency snapshot. The exception is for libraries that are themselves installed as dependencies in other projects (where the lock file would interfere with downstream consumers); applications and end-user tools commit the lock file.

When AI agents should update the lock file

Operation Update lock file?
Adding a new dependency (npm install foo) ✓ Yes — the new dependency requires the lock update.
Removing a dependency (npm uninstall foo) ✓ Yes — remove the dead entries.
Explicit upgrade (version bump in package.json, npm update foo) ✓ Yes — that’s the point of the upgrade.
Unrelated work that happened to regenerate the lock file (you ran npm install just to fetch deps) ✗ No — discard the spurious lock-file diff.
Security patch (npm audit fix) ⚠ Only with explicit user OK and only if scoped to the patch — security fixes can cascade unexpectedly.

Discarding spurious lock-file diffs

If you ran npm install for unrelated reasons and the lock file changed:

git checkout -- package-lock.json    # or: git restore package-lock.json

Then re-fetch dependencies from the unchanged lock — most package managers have a “clean install” command for exactly this (npm ci, pnpm install --frozen-lockfile, yarn install --frozen-lockfile, cargo build --locked).

When the diff is huge

A massive lock-file diff usually indicates a dependency cascade — one dependency upgraded transitively forced many others to shift. Read the top of the diff to find the root cause:

Why this matters specifically for AI agents

An agent that runs npm install just to set up the workspace can produce a diff carrying unintended transitive bumps, conflicts with the user’s pinned versions, and hundreds of lines of noise that bury the actual work.

Defensive pattern: agents do not commit lock-file changes unless the work directly involved dependencies. When in doubt, discard the diff and reinstall from the committed lock.


PR discipline

Pull requests are the unit of review. Each PR should be readable, scoped, and traceable.

PR title

PR body

Two sections, always:

## Summary

- <Bullet 1: what changed and why.>
- <Bullet 2: any non-obvious decision.>
- <Bullet 3: any tradeoff worth flagging.>

## Test plan

- [ ] <Specific verification step 1>
- [ ] <Specific verification step 2>
- [ ] <Specific verification step 3>

Summary: one to three bullets on what changed and why — not a diff recap, since the diff is right there. Highlight what the reviewer would otherwise miss.

Test plan: specific, checkable verifications matching the Definition of Done gates relevant to this change — “ran the full suite locally,” “verified the CSV download at /reports respects the date filter,” “verified in both themes,” “updated CHANGELOG and README.” Vague entries (“tested it”) are not a test plan.

Tie to backlog items

Every PR should close at least one BL-### item. Reference it explicitly:

Closes BL-0428.

The word matters — Closes, Fixes, Resolves — pick one and stay consistent. Many tools auto-link these.

Co-author credit

Never include co-author credit unless explicitly agreed. Auto-adding a Co-authored-by: ... line for an AI agent is a project-by-project decision, not a default.

PR size

What not to put in a PR

Multiple unrelated items (one PR closes one, or a few tightly-coupled, items); WIP commits left in; debug code (console.log, commented-out blocks); and everything on the never-commit list — secrets, generated files, unrelated lock-file churn.


Squash, merge, or rebase — picking the trunk-merge strategy

GitHub (and most hosting platforms) offer three ways to land a PR onto the trunk: squash merge, merge commit, or rebase-and-merge. They produce different shapes of history and different tradeoffs.

Strategy What it produces When to prefer
Squash merge One commit on the trunk per PR. The PR’s individual commits are collapsed into a single commit with the PR title + body. Most projects, most of the time. Clean linear history; easy to revert (git revert <commit>); simple git log.
Merge commit The PR’s individual commits land on the trunk plus a merge commit marking the integration point. When the PR’s individual commits are meaningful and you want to preserve them (e.g., a multi-step refactor where each step is independently valuable). Produces a branchy graph.
Rebase and merge The PR’s individual commits land on the trunk as if they had been written there directly. No merge commit. When you want per-commit granularity AND a linear history. Rewrites the PR’s commit hashes — coordinate before adopting if collaborators have pulled the PR branch.

Pick one and stay consistent

Mixing strategies on the same trunk produces a confusing history (some PRs squashed, some preserved, no apparent rule). Decide once per project; the project instruction file should name the choice (“This project uses squash-merge for all PRs to main.”).

Default recommendation

Squash merge is the default for most projects. The benefits (clean trunk, easy revert, simple git log) outweigh the loss of intra-PR commit granularity — which is usually noise (WIP commits, “fix typo”, “address review”) that the squash usefully discards.

Merge-commit or rebase-merge earn their place where per-commit granularity is load-bearing: monorepos generating changelogs per package, long-running PRs whose contributors want multi-commit attribution, or projects where git bisect precision matters at the commit level.

The choice changes only history shape. Review, branch protection, and conventional-commit format apply identically under all three.


Pre-commit hooks

The rule

The project should have hooks for linting, type-checking, and formatting. Never bypass hooks (--no-verify and equivalents) unless the user has explicitly authorized it for that specific commit.

Why hooks exist

Hooks catch the cheap-to-prevent failures: a missing import, an unformatted file, a type error. CI will also catch these, but CI runs minutes later — the local hook saves the round-trip.

What to do when a hook fails

  1. Read the failure. Hooks usually say what went wrong.
  2. Fix the underlying issue. Re-stage the fix and commit again.
  3. Do not bypass. A bypass-and-promise-to-fix-later almost always becomes a bypass-and-forget.

When bypass is allowed

The user has explicitly said “skip hooks for this commit” — usually because the hook itself is broken and fixing it is being separately tracked. In that case:

The agent never decides to bypass on its own. If a hook is failing repeatedly and seems broken, surface the observation; let the user direct.


Never amend published commits

The rule

Once a commit is pushed to a shared branch, it is history. New commits, not --amend.

Why

Amending a published commit changes its hash. Other contributors who have pulled the original commit now have a divergent history. The conflict is hard to resolve cleanly and easy to resolve destructively (force push, lose work).

A new commit, by contrast, advances history without rewriting it. Everyone’s local clones converge naturally.

Local-only amend is fine

A commit that exists only in your local working directory (never pushed) can be amended freely. The hash change has no other reference yet.

Squashing before opening a PR

If you have a long messy local history and want to clean it before opening the PR, squash it locally. Once the PR is open, the commits inside it are part of the shared history — further history rewrites should be done with care and coordination.

Recovering from a bad commit


Worktrees for parallel agents

The problem

When two or more contributors (humans, AI agents, automation) are working in the same checkout of the repo at the same time, dangerous shared state appears:

The solution

Use git worktree to give each contributor (especially each AI agent session) an isolated working tree.

git worktree add <path> <branch>

For example:

git worktree add ../project-agent-a4f2 feature/csv-export
git worktree add ../project-agent-b193 feature/date-fix

Each session operates in its own directory. HEAD-shifts in one tree do not affect the other. Commits, branches, and config are shared (it is the same repository), but the workspace is independent.

When to use a worktree

When not to use a worktree

Command reference

git worktree add <path> <branch>                  # existing branch
git worktree add -b <new-branch> <path> <base>    # new branch
git worktree list
git worktree remove <path>                        # clean state
git worktree remove --force <path>                # DISCARDS uncommitted work
git worktree prune                                # directory already deleted

A recurring failure to clean up worktrees clutters the disk and the worktree list. Build cleanup into the end of the session. Note that remove --force is on the ⚠ (approval-gated) rows of the operation table — it discards uncommitted work in that tree.

Creation and cleanup hazards

Do not create filesystem links from inside a worktree to the parent repo’s directories (especially package directories like node_modules). When the worktree is removed, the link is followed and the target is emptied. This has happened in real projects and is brutally destructive.

If you need shared installation state (e.g., a shared package cache), use the package manager’s native shared-cache feature, not filesystem links.


What AI agents can and can’t do in git — the operation table

One table, three autonomy levels, matching the destructive classes in 11. ✓ = do it without asking. ⚠ = approval-gated: surface the intent and wait for an explicit, scoped yes — then the agent may execute. ✗ = agent-prohibited: a human executes, always; the note says what the operation costs.

Pairs with the decision-ownership matrix in 11_human_roles.md, which covers decisions broadly. This table is git-operation-specific.

Operation Agent autonomy Notes
git status / git diff / git log / git show ✓ Read-only inspection. Anytime.
git add <specific files> ✓ Staging known files. Prefer this over git add . which can stage unintended files.
git commit (on a feature branch) ✓ The agent’s normal flow per commit cadence.
git push origin <feature-branch> ✓ Pushing the agent’s own feature branch.
git fetch / git pull --ff-only ✓ Idempotent sync (not strictly read-only — pull --ff-only updates local refs and the working tree, but only if a fast-forward is possible; the flag prevents accidental merges).
git checkout <branch> (in a worktree the agent owns) ✓ In its own worktree, not the user’s primary checkout.
git worktree add / git worktree remove (own, clean) ✓ Manage agent’s own isolation. remove --force is ⚠ — see below.
git merge <other> (into agent’s own feature branch) ✓ Bringing trunk into feature is normal.
git rebase <upstream> (own feature branch, not pushed-and-shared) ✓ Rewriting your own un-shared history is fine.
gh pr create / gh pr comment / gh pr view ✓ Standard PR workflow on agent’s own branch.
gh issue view / gh issue comment ✓ Read + non-destructive write on issues.
git tag -a <annotated> (release tag) ⚠ Only with the user’s explicit OK for this release. Tags are durable references.
git push origin <tag> ⚠ Same. Tag pushes are public.
gh pr merge (squash / merge / rebase) ⚠ Merging to main is consequential; user-led decision.
gh release create ⚠ Release pages are durable; user-authorized.
git rebase --onto ⚠ Rewrites commits onto a new base; can lose commits if mishandled.
git push --force ✗ Overwrites the remote branch. Work others based on the previous version becomes orphaned.
git push --force-with-lease ✗ Safer than --force but still rewrites; safe only if no one else has pushed since you pulled.
git reset --hard ⚠ Discards uncommitted changes and resets HEAD. Local-only work is gone.
git checkout -- <file> / git restore <file> / git checkout . ⚠ Discards uncommitted changes in the working tree.
git clean -fd / -fdx ⚠ Removes untracked files — and with -x, ignored files too, including .gitignored contents.
git branch -D ⚠ Force-deletes a branch even if unmerged; its commits may become unreachable.
git worktree remove --force ⚠ Removes a worktree along with its uncommitted changes.
git filter-branch / git filter-repo ✗ Rewrites history. Heavy-handed; usually for secret-scrubbing. Always user-led.
rm -rf <repo> ⚠ Removes the working tree.
Any direct push to the trunk ✗ Trunk lands via PR only. See Branch protection.
Production deploys (./deploy prod, etc.) ✗ See Production deploys.

Handling the ⚠ rows (approval-gated)

Handling the ✗ rows (agent-prohibited)

Recovery when damage is done

git reflog shows recent HEAD movements, and lost commits usually live there until garbage collection. Your hosting platform’s mirror may hold the lost remote state. The longer you wait, the lower the chance of recovery — move fast on recovery, slow on the original destructive command.

The principle

Reversibility maps to autonomy. If an operation can be cleanly undone (a revert, a new commit, a reset of the agent’s own feature branch), the agent can do it. If it can’t be cleanly undone (force-push, hard reset, history rewrite, branch deletion, production deploy), the agent waits for the user.

This is the same threshold the decision-ownership matrix in 11_human_roles.md uses — reversibility is the dominant factor in deciding what belongs to the human vs. the agent.

What “with user OK” means in practice

When the table says ⚠ “only with the user’s explicit OK,” the agent doesn’t infer the OK from prior conversation. The agent surfaces the intent (“I’m about to cut release v1.2.3 — tag, push, and create the GitHub release page. Confirm?”) and waits for an explicit yes. The OK applies to that operation, not to future operations of the same kind.


Production deploys

The hard rule

If the project has a production deploy path — a script, a CI workflow, a CLI command — AI agents never run it autonomously. Production is the user’s domain.

This applies regardless of permissions. Even if the agent has the technical capability to run deploy.sh production, it does not. Production deploys carry consequences that an AI cannot fully evaluate: customer impact, data implications, timing relative to incidents, regulatory considerations.

Why this is absolute

A bad production deploy can:

None of these is recoverable by reverting code. They require operational response.

How to document the deploy command

Name the command in the project instruction file, with the prohibition attached to it rather than filed elsewhere — “Production deploy: scripts/deploy.bat production. This command is user-only; AI agents and automated workflows never run it. Staging: scripts/deploy.bat staging.” The warning is unsubtle on purpose: a contributor reading the command learns it is off-limits in the same breath.

What an AI agent can do

What the agent cannot do is push the button. The button is the human’s.


Release tagging and semantic versioning

Commits accumulate continuously; releases are deliberate snapshots. The release-tag commit makes a specific point in history easy to refer back to, easy to deploy from, and easy for adopters to pin against.

When to cut a release

Three reliable triggers:

  1. A coherent batch of changes hits the trunk. Multiple related commits (a feature + its docs + a CHANGELOG entry) bundled into one release rather than one tag per commit.
  2. A milestone is reached. An epic closes, a phase exits, a documented goal lands.
  3. A bug needs to be fixed in a way that adopters will reference. Hot-fix release — see Hot-fix workflow below.

What NOT to release: every commit, every WIP push, every small refactor. Releases should be referenceable.

Semantic versioning (SemVer)

Use SemVer 2.0: MAJOR.MINOR.PATCH.

Bump When to use
MAJOR (X.0.0) Breaking change. Adopters who pinned to the previous major need to actively migrate. For a methodology-style project: a renamed doc, a removed section, a restructure of artifacts adopters link to. For code: a breaking API change.
MINOR (X.Y.0) New feature, new section, new artifact — backward-compatible. Adopters who upgrade get more; nothing they depend on breaks.
PATCH (X.Y.Z) Fix that doesn’t change the surface. Typo, link repair, factual correction, doc clarification.

A useful heuristic: if an adopter who pinned to the previous version would have to do work to upgrade, it’s at least a MINOR; if they’d have to change their existing usage, it’s a MAJOR.

The release commit

A single commit per release on the trunk. The conventional-commit type is release:, no scope needed:

release: vX.Y.Z — <short summary, under 70 chars>

<Body summarizing what's in the release, grouped by category
 (Feat / Fix / Docs / Chore). Bullet style with file references.>

CHANGELOG renamed [Unreleased] -> vX.Y.Z in this commit.

The body should match the corresponding CHANGELOG entry — they’re describing the same thing for two different audiences (git readers vs. CHANGELOG readers).

Annotated tags, not lightweight

git tag -a vX.Y.Z -m "vX.Y.Z - <same short summary as commit subject>"

Annotated tags carry the tagger’s identity, timestamp, and message. Lightweight tags (git tag vX.Y.Z) are just refs to a commit, with no metadata. Annotated tags are the standard for releases. If your project signs tags (git tag -s), use that; otherwise unsigned-annotated is the baseline.

Pushing the release

Two pushes, never combined — git push origin main (the release commit), then git push origin vX.Y.Z (the tag). --follow-tags works if configured, but the two-step form makes the order visible and intentional.

Counts in a release entry carry an as-of marker

A release entry that states a live repository total in the present tense — “the repo now has N tags for N releases” — is false the moment the release’s own tag is pushed, because it was counted before that tag existed. The same is true of any figure the release itself changes: file counts, line totals, doc inventories.

Two rules, both cheap:

Corrections go forward, in a later entry — a published CHANGELOG entry is history and is not rewritten. The failure mode this prevents is specific and recurring: each release corrects its predecessor’s total, states its own the same way, and is wrong again one release later.

Release notes vs. CHANGELOG

Same content, two audiences: CHANGELOG.md is the canonical, durable, structured record; platform release notes are polished for browsing and can carry media the CHANGELOG can’t. Generate the notes from the CHANGELOG entry — never write them twice. Shorter notes that link to the CHANGELOG for detail are fine.

Anti-patterns


Hot-fix workflow

The normal flow is: item → branch → PR → merge → eventual release. A hot-fix bypasses parts of this for genuine emergencies.

When a hot-fix is justified

The bar should be high. Reserve hot-fixes for:

Do NOT hot-fix for: ordinary bugs, feature requests, “while we’re at it” improvements. Those go through the normal flow.

The protocol

  1. Branch from the last released tag, not from main:
    git checkout -b fix/hotfix-<slug> v<last-released-version>
    

    Branching from main would include unreleased work that hasn’t been verified for the production environment. Branching from the tag isolates the hot-fix to a known-good base.

  2. Scope ruthlessly. The hot-fix touches only what’s needed to resolve the immediate problem. Defer everything else — even adjacent improvements — to follow-up items.

  3. Test the fix per the DoD. Status: done requires Test: pass — hot-fixes do not skip this. The verification loop is faster (because the scope is tight), not absent.

  4. File the item with Priority: P0 so the bypass-of-normal-flow is visible in the backlog. The item closes in the same hot-fix PR.

  5. Merge the hot-fix branch to main via PR. The PR is reviewed under hot-fix urgency (faster than normal, not skipped).

  6. Cherry-pick to release branches if you maintain any. If your project has long-lived release branches (e.g., release/v1.x), the hot-fix needs to land on each affected one: git cherry-pick <commit-hash>.

  7. Tag a new patch release (see Release tagging). The hot-fix gets its own version so adopters can pin to “the version that has the fix.”

  8. Archive the item to the originating epic’s ARCHIVE.md — or to a dedicated epics/E00-hotfixes/ epic if your project sees enough hot-fixes to warrant one.

Write down what happened

After the hot-fix lands, the AI-collaborated equivalent of a post-mortem:

This is a strong memory-entry candidate (see 08_lessons_and_memory.md “When to write a new memory” — Trigger 4: validated judgment call after an incident).

What hot-fixes are NOT


Conflict resolution

Merge conflicts are normal. Resolving them carelessly is not.

The rule

Prefer resolving merge conflicts to discarding work. Investigate whose change is whose before picking a side.

The process

  1. Pull the latest from the trunk into your branch. Conflicts surface.
  2. For each conflict: open the file, read both sides, understand what each change was trying to accomplish.
  3. Reconcile. Sometimes you keep one side. Sometimes you keep both (one merged into the other). Sometimes the conflict reveals that two contributors went in different directions and the right answer is to talk.
  4. Run the tests. Conflict resolution can produce syntactically valid but semantically broken code.
  5. Commit the merge. The commit message says “Merge X into Y; reconciled in favor of " so future readers understand what was decided.

Anti-patterns

When the conflict is large

If the merge conflict is so large that you don’t know how to resolve it, stop and surface the situation. A 30-file conflict between two long-lived branches is usually a sign that branches diverged too long; the right answer is a coordinated re-merge, not a heroic single-contributor reconciliation.


Audit trail

Git history is the project’s permanent record. Every commit, every PR, every backlog state change is preserved.

What this enables

git log -p -- <file> gives every change to a file with diffs (why a decision was made); git blame <file> finds the commit behind a specific line; git log --grep="<pattern>" finds every commit for an item or topic; git log --all --source can reconstruct work on deleted branches.

Backlog state changes are commits too

Every change to a BACKLOG.md or ARCHIVE.md — adding an item, flipping a status, acquiring or releasing a lock — is a git commit. This makes the backlog itself fully auditable. See 05_locks_and_parallel_work.md for the lock audit pattern.

Implications

Commit messages are documentation read months later by people without your context; a messy history is a permanent tax on every future reader; and in an incident, git history is usually the first source consulted.


Operational work (deploys, pipelines, runbooks)

Most real projects have operational concerns beyond writing code: deploys, data pipelines, scheduled jobs, monitoring, on-call response. The methodology covers code workflow; operational workflow is project-specific. But certain patterns transfer.

The pattern set

Pattern Why it matters
Document the deploy command(s) in the project instruction file. Future contributors (and the AI) need to know how to ship.
Mark which commands are agent-prohibited (a human runs them). Production deploys, destructive backfills, data resets — these touch real user state, so a user’s approval authorizes the human to proceed, not the agent to act.
Backup before any destructive operation. A backup taken five minutes before a wrong-button-press is the cheapest recovery.
Smoke-test after deploy. A successful deploy that doesn’t smoke-test isn’t proven to have moved real state.
Have a runbook for common ops. Restarting a service, rotating a credential, rolling back a release — write the steps down so they can be followed under pressure.
Separate dev / staging / production environments cleanly. The boundary between these is where most operational incidents happen.
Idempotent batch jobs. A re-run of a stuck job should produce the same end state, not corrupt it.
One-way data flows where possible. If content syncs from dev to prod, never the reverse, the failure modes shrink.

Operational work in the backlog

Operational work is normal backlog work: “rotate the third-party API key,” “investigate Tuesday’s 500 spike,” “build the DR runbook.” Items and epics follow the usual lifecycle — the Test: field just means something operational (“the rotation completed and the smoke test passed”). Adapt the DoD gates to the operation; verifiable completion is the invariant.

The methodology deliberately doesn’t pick your deploy tooling, monitoring stack, incident procedures, or runbook format. It asks you to have them.

The hard rule for operational work

If your project has production state (real users, real data, real money), destructive operations against it are agent-prohibited — a human executes them, and an approval does not move that step to the agent. Automation is fine for backups, deploys to staging, and scheduled jobs that don’t destroy state. Automation that drops tables, rolls back data, or cancels payments needs a human in the loop, not a confirmation prompt in front of a robot.


How the git workflow connects to the rest of the methodology


Common mistakes around git workflow

Mistake Fix
Direct commit to the trunk for “a small thing.” Open a PR. Small PRs land quickly. The exception is not worth the precedent.
Force-pushed to the trunk after a bad merge. Recover via reflog and your hosting platform’s branch backup. Resolve to never do this again.
Generic commit messages (“wip,” “update,” “fix”). Rewrite locally before pushing. After pushing, follow up with better messages or a clean revert+redo.
Long-running branch is many commits behind the trunk. Pull the trunk into the branch regularly. Conflicts grow non-linearly with divergence.
Hooks bypassed without explanation. Bring the bypass to surface. Either fix the underlying issue or file an item for the hook itself.
Stale branches lingering on the remote for months. Periodic cleanup: delete merged branches. Keep unmerged in-progress branches; archive abandoned ones with a tag.
PR with no test plan. Add one. Specific, checkable verifications. The DoD requires it.
PR closes an item but doesn’t reference it. Add Closes BL-###. so the tooling and humans both make the connection.
Co-author trailers added without permission. Remove them. Authorship is consensual.
AI agent ran git reset --hard on the user’s working tree. Surface and confirm before destructive operations. Recover via reflog if possible.
Production deploy run by automation that wasn’t supposed to. Hard rule violation. Audit how it happened; restore boundary.

Authority

The git workflow rules bind every contributor — human or AI. They are not aesthetic preferences; they are preventive measures.

When a user explicitly directs a workflow exception (e.g., “force-push this to recover from yesterday’s bad merge”), the exception applies to that specific operation, not to the rule generally. The next operation reverts to the standard rules unless the user states otherwise.

When a contributor (human or AI) is uncertain whether an operation is allowed, the safe path is to ask. The cost of a question is small; the cost of an unintended destructive operation can be large.

Destructive operations are the most-protected category, and they split in two. Workspace-destructive operations are approval-gated: never autonomous, and the agent may execute after an explicit, scoped, per-operation yes. Production-destructive operations are agent-prohibited: a human executes, with care and a recovery plan, and consent does not transfer that step. The classes are defined once in 11_human_roles.md.


Next: 10 — Testing and verification