Agile Workflow — Issues, PRs, Autonomous Merge, Releases, Villages

The unified development process for every Tawa repo. If your Claude Code session loads tawa-docs/.claude/rules/tawa-agile-workflow.md, it will follow this flow automatically.

Ground Rules

  • Git host is Forgejo only — everything lives at git.tawa.pro. No GitHub, no mcp__github__* tools. Git operations go through mcp__jci-mcp__git_* or the git CLI.
  • Remotes are always HTTPS (https://git.insureco.io/org/repo.git). Never switch to SSH to fix auth — run tawa login, see Auth failures.
  • main is PR-only. Never commit to main; it advances solely through a merged PR — one the agent merges itself (see Autonomous Merge below).

Issue-Driven Development (required)

Every feature, bug fix, or non-trivial task must have a git issue before code is written.

Create issue → branch → implement → PR → self-merge → close via commit message
  1. Check for an existing issue first (git_issue_list); create one if missing (git_issue_create).
  2. Reference the issue number in the commit: feat: add tagline to branding (#42).
  3. Auto-close from the commit body with fix #N, close #N, or resolve #N.

Only truly trivial one-liners are exempt.

The Feature Loop

  1. Create issue first — git_issue_create
  2. Plan — planner agent breaks down the work
  3. TDD — tdd-guide agent, tests written first (RED → GREEN → refactor, 80%+ coverage)
  4. Branch — in your own worktree off origin/main: claw/<task>-<shortid>
  5. Code review — code-reviewer agent after writing code
  6. Commit & push — explicit paths, reference the issue (feat: add X (#N))
  7. Open PR — git_pr_create
  8. Rebase & verify green — git fetch origin && git rebase origin/main, then run the repo's typecheck / lint / tests. This is the gate that replaces human review.
  9. Self-merge — git_pr_merge. Automatic; do not wait for a human (see below).
  10. Cut release — git_release_create after a deploy-worthy batch

Autonomous Merge — Agents Self-Merge Their PRs

Every agent runs the full pipeline end-to-end and merges its own PR. The merge is the default terminal step, not a handoff. The human ("vibe coder") does not review or click merge — they tell the agent to test before merging, and a green, rebased PR merges itself. Agents are pulled in only on a major error.

Do NOT wait for the human:

  • Never end a turn at "PR is open, ready for you to merge." A PR you opened is yours to merge.
  • Never ask "want me to merge this?" for a routine change — merging is the assumed intent.
  • When agents fan out, the orchestrator serializes merges (one PR at a time, each rebased on the prior merge) and merges them itself — nothing is parked for a person.

Escalate instead of merging — STOP and surface to the human ONLY when:

  • A merge/rebase conflict you cannot cleanly and confidently resolve.
  • Typecheck / lint / tests / build fail and you can't green them.
  • A deploy-gate or smoke-test failure, or a red post-merge signal.
  • The change is destructive, irreversible, or outside the task's stated scope (schema drops, data migrations, mass deletions, secret rotation, force-push to a protected branch).
  • A security-sensitive change you're not confident is correct.
  • Genuinely ambiguous requirements where the wrong guess is expensive.

"I'm slightly unsure the feature is perfect" is not a major error — that's what the issue, the diff, and the next iteration are for. Outside the list above, proceed to merge.

Permissions & enforcement (already in place):

  • Claude Code's global settings allow Bash(*) and the Forgejo PR tools (git_pr_create, git_pr_merge, git_pr_get, git_pr_list) with no prompt.
  • Branch protection on main (required_approvals=0) enforces PR-only mechanically while still letting the agent self-merge — that is exactly why approvals are 0.
  • Release tags are protected too: only the accounts on a repository's tag rule can create, move or delete a tag matching v* or *-v*. See Release Management.
  • Both are applied by the tawa CLI (0.27.0+). tawa git create and tawa sample --git protect main and release tags on the repository they create, with no further step. For a repository that already exists, run tawa git protect <owner/repo> — a dry run that prints what would change — then add --apply. tawa git protect --all sweeps every repository in the org (--org <name>, default insureco); --all --apply prints how many repositories need changes and asks first (--yes skips the question). --main or --tags limits it to one kind of rule.

Correction (2026-09-29): this section used to name forgejo-protect-main as what enforces PR-only main. No such command exists in the tawa CLI, so anyone who went looking for it found nothing and may have left a repository unprotected. Use tawa git protect (tawa 0.27.0+) instead. Forgejo 14 has tag protection per repository only, with no org-wide setting, which is why the CLI applies it repository by repository.

Deploy is still a separate, single-owner step. After the merge(s) land on origin/main, one agent runs tawa deploy --prod once. Autonomous merge changes the merge gate, not the ship gate. See deploy-pipeline.

Labels — the board's source of truth

Type labels: feature, bug, ux, infra, debt, phase

Status labels → Kanban columns (moving a card = swap the status:* label):

LabelColumnColor
status:backlogBacklog#64748b
status:in-progressIn Progress#2563eb
status:reviewIn Review#d97706
status:blockedBlocked#dc2626
(closed, no label)Doneautomatic

Labels live on the org, not on each repo

Forgejo labels are per-repo, and nothing creates them when a repo is made — so until 2026-09-18 most repos had none, and the first labelled issue on any of them failed with Label "feature" does not exist in <repo>. Labels there: (none). Agents then hand-built a label set one repo at a time, with drifting colors.

The ten labels above now exist once, on the insureco org, and jci-mcp resolves org labels alongside a repo's own (jci-mcp#126). Every insureco repo — including one created five minutes ago — accepts them with zero labels of its own. There is nothing to bootstrap.

git_label { action: "list",   org: "insureco" }                      # the shared taxonomy
git_label { action: "create", org: "insureco", name, color }         # add to it — every repo gets it
git_label { action: "create", repo: "insureco/my-svc", name, color } # one repo only
git_issue_labels_list { repo }                                       # what an issue here can carry; each label marked scope: org | repo
  • A missing standard label is an org problem. Add it to the org, never to the repo.
  • Repo-level labels are for something genuinely specific to one repo. A repo label shadows an org label of the same name, so a repo-level bug with its own color silently replaces the shared one there.
  • An unknown name still fails loudly, on issues and PRs, listing both the repo's and the org's labels. That is deliberate: the old behaviour dropped unknown labels and reported success, leaving issues with no labels at all.
  • A few repos (jci-mcp, bio-vault, iec-insurebio) still carry repo-level copies that predate this. They are harmless. Do not delete them casually — deleting a label strips it from every issue that carries it.

Sprint Rhythm

  1. Plan — review open issues, pick the sprint's work, label with milestone
  2. Build — one issue per branch/feature, commit often
  3. Ship — the agent self-merges its own PR (rebase → verify green → git_pr_merge), cuts a release, closes issues. No human merge gate.
  4. Retrospect — which issues stayed open, and why?

Commit message format

<type>: <description> (#N)

Types: feat, fix, refactor, docs, test, chore, perf, ci. Always link the issue when one exists. Attribution is disabled globally.

Release Management

Cut a release after every meaningful, deployed batch. Check existing releases first (git_release_list), then git_release_create.

BumpMeaning
Patch v1.0.xBug fixes, minor tweaks
Minor v1.x.0New features, UI additions
Major vX.0.0Breaking changes, rewrites

Who can create a release tag

Every repository created with tawa git create or tawa sample --git (tawa 0.27.0+), and every repository tawa git protect --apply has been run on, carries tag rules for v* and *-v*. Only the accounts on the rule's allow-list can create, move or delete a matching tag. Forgejo has no admin bypass: a site or org admin who is not on the list is refused like anyone else.

Default allow-list
Org-owned repositorythe person who ran the command, plus the org's Owners team, plus its runners team when the org has one
Personal repositorythe person who ran the command

iec-admin is on the list only when it was the account that ran the command; it is never added to a repository where it is only the builder's read collaborator. Each run prints who is allowed. --allow-user, --allow-team and --pattern (all repeatable), or release_tags in ~/.tawa/config.yaml, replace the defaults.

Patterns are Forgejo globs matched against the whole tag name, and * matches any characters. v* matches v1.2.3 and also any other tag that starts with v, such as vendor-thing; *-v* matches agent-v0.7.0 and any tag containing -v. A tag you did not mean as a release can therefore be protected too.

When a tag push or git_release_create is refused

The repository's tag rule does not include the account you are using. Do not work around it (a different account, a tag name chosen to dodge the pattern, deleting the rule). Check the rule instead:

tawa git protect <owner/repo> --tags     # dry run: prints the current rule and who it allows

A rule whose allow-list is not the default is reported as DIFFERS and left alone; the line says when it leaves you out. If you should be on it, ask an owner of the repository to add you (--allow-user, then --apply --update). Changing someone else's rule to include yourself is an owner's decision, not a workaround.

tawa git protect refuses to write a rule that would leave the person running it unable to create matching tags, unless --lock-me-out is passed. If your git username is not known, it makes no rule at all; run tawa login.

Documentation & Decisions

Use the wiki to capture the why — architecture decisions, non-obvious "why X instead of Y" choices, onboarding context (data model, auth flow, deploy process). Tools: git_wiki_create / git_wiki_update. Write a wiki page when a new pattern is introduced or a non-obvious decision is made; update it when behavior changes.

Villages — Cross-Repo System Tracking

The ecosystem is organized into Villages — high-level system components spanning multiple repos. All 15 Villages reaching v1 constitutes Voyager (Ecosystem Release 1).

  • Issues live in the repo where the code lives; a Forgejo org-level Project (one per Village) is the cross-repo board.
  • Each constituent repo tracks its own v1 milestone (git_milestone_create).
  • Each Village gets a lightweight {village}-wiki repo (has_issues: false) for roadmap + ADRs.
  • When all repos in a Village hit v1 → cut Village v1 release → Voyager milestone advances.
LayerTool
Village roadmap & ADRsWiki on {village}-wiki repo
Village v1 targetv1 milestone per repo
Cross-repo boardForgejo org Project (one per Village)
Ecosystem releasegit_release_create on the voyager tracking repo

Auth failures — run tawa login

tawa login is how a machine gets its Forgejo credential. One call authenticates, mints a PAT, and stores it for both the tawa CLI (~/.tawa/forgejo-credentials.json) and git (the OS credential helper). With saved credentials present it runs non-interactively.

tawa login --save
git ls-remote origin HEAD     # verify, without touching the credential store

There is nothing to do afterwards. Do not hand-mint a PAT and store it yourself.

Superseded (2026-09-20). This page used to say "do NOT reflexively re-login", and told you to mint a stable git-cli-* PAT through the Forgejo admin CLI and store it by hand so it would sit outside the tawa-cli- cull. That guidance is stale. Do not follow it. It read as a prohibition rather than a caveat: an agent avoided tawa login, worked around it, and then minted a redundant token nobody needed.

What is actually true, verified against iec-builder/src/routes/users.ts on 2026-09-22: deleteStaleTokens(username, machineId) deletes only this machine's tokens, those named tawa-cli-<machineId>-*, and deletes nothing at all when the caller sends no machineId. A login on one machine never revokes another machine's token, so a laptop, a server and a cloud-runner session can all stay logged in as the same user at once. tawa login also rewrites the git credential in the same call, so this machine ends up holding a working credential. The separate stable PAT solved a problem that never existed.

Scopes — the one real limit. The token tawa login mints carries write:repository, write:organization and write:user. That covers git and the Forgejo /pulls API. It does not carry write:issue, so creating an issue with it returns 403, and logging in again will not change that. Use the git_issue_* MCP tools for issues — they authenticate as the MCP server rather than as your local token.

⚠️ Never test a token by putting it in a git URL (https://user:$TOK@host/…) — the credential helper silently overwrites the stored credential with whatever the URL carried.

Commits Are the Only Durable Save

Uncommitted edits live only in the working tree — a branch switch, rebase, or another agent's operation can discard them with no recovery. Commit as soon as a change is coherent, to your claw/<task>-<shortid> branch (never main), staging explicit paths (git add <paths> — never git add -u/-a, which sweep up other agents' uncommitted edits on a shared checkout). Small WIP commits are fine — they squash at merge; lost work cannot.

There is no "Stop hook auto-commits git add -u and pushes to main" safety net — that older behavior is removed and superseded. Any automated commit must be branch-aware: refuse on main, stage explicit paths, and go through a self-merged PR.

The One-Liner

No work without an issue → plan → tests first → branch → commit referencing #N → PR → rebase + verify green → agent self-merges → cut a release per batch. Human is pulled in only on a major error. Issues live with the code; Villages aggregate cross-repo; Wikis hold the "why."

Common Mistakes

  • Starting work with no issue (only trivial one-liners are exempt).
  • Ending at "PR is open, ready for you to merge" — a PR you opened is yours to self-merge.
  • Asking "should I merge?" for a routine, green, rebased PR — merging is the assumed intent.
  • Committing to main directly, or using git add -u/-a on a shared checkout.
  • Merging without running the repo's tests first — the test pass is what replaces review.
  • Committing without a #N reference when an issue exists.
  • Creating feature / status:* labels on a repo because an issue create said they were missing — they live on the org. See Labels live on the org.
  • Switching a remote to SSH to dodge an auth failure.
  • Looking for forgejo-protect-main — it does not exist. Use tawa git protect (0.27.0+).
  • Running tawa git protect --all --apply without first reading the dry run.
  • Working around a refused release tag instead of checking the rule with a dry run. There is no admin bypass; only the accounts on the allow-list can tag.
  • Trusting the sweep summary's "unchanged" count: it currently counts a repository with a DIFFERS rule as unchanged (tawa-cli#128). Read the per-repository DIFFERS lines.
  • Hand-minting a Forgejo PAT, or avoiding tawa login when a push 401s — tawa login is the supported fix and rewrites the git credential itself. See below.
  • Using GitHub / mcp__github__* tools — everything is Forgejo at git.tawa.pro.
  • Tracking cross-repo work as issues in a "meta" repo — issues live where the code lives; aggregate them with a Village Project instead.

Last updated: October 6, 2026