geode-gitflow · diff
git:20260825.9516eab to git:20260906.dc9349e
54 added, 60 removed. Audit A to A.
---
name: geode-gitflow
description: GEODE branch strategy and PR rules. feature → develop → main merge flow, Pre-PR Quality Gate (CI guardrails + docs-sync loop), Post-PR CI ratchet (gh pr checks --watch mandatory), Korean PRs, assignee settings. Triggers on "branch", "git", "pr", "merge", "커밋", "풀리퀘스트".
---
# GEODE Git & PR Workflow
+ Apply only the stages authorized by the current request. Review or local edits
+ do not authorize commit, push, merge, cleanup, reinstall, or runtime restart.
+ The [canonical workflow](../../../docs/workflow.md) and `AGENTS.md` own current
+ worktree and main-maintained tracking exceptions.
+
## Merge Flow (mandatory)
**feature → develop → main** order. Direct push to main prohibited — must go through PR.
```
feature/xxx ──PR──→ develop ──PR──→ main
```
For releases (version stamp bump + CHANGELOG promote), the release branch lands on **develop first**, then a straight pass-through develop → main PR ships to production. See `## Release Flow` below — this rotation eliminates the post-release backmerge that the older pattern required.
## Full Workflow
> **Principle 1**: Every work unit **starts with worktree open (alloc) and ends with worktree close (free)**.
> No direct `git checkout feature/*` in the main repo. No exceptions.
>
> **Principle 2**: develop merge uses a **queue approach** — one at a time. Rebase next worktree after merge.
```
0. ★ Frontier Research (for new infrastructure features)
DISCOVER → COMPARE → DECIDE → DOCUMENT
1. worktree open + feature branch creation ← alloc
2. Code changes (within worktree)
3. ★ Pre-PR Quality Gate (iterate)
4. Commit (code + docs together)
5. PR creation (feature → develop)
6. ★★ Post-PR CI Ratchet (mandatory)
7. merge (feature → develop) ← queue: one at a time
8. develop → main PR creation (batchable)
9. ★★ Post-PR CI Ratchet (mandatory)
10. merge (develop → main)
11. ★★★ Docs-Sync Final Verification
12. worktree close + branch deletion ← free
```
> **Step 0 applicability**: Mandatory for new infrastructure features (Gap, architecture changes).
> Can be skipped for simple bug fixes, documentation updates, or repeating existing patterns.
### Develop Merge Queue (when running parallel worktrees)
When multiple worktrees are open simultaneously, manage develop merges as a sequential queue.
```
Worktree A (fix/xxx) ──→ PR → CI pass → merge #1 ──┐
│ develop updated
Worktree B (fix/yyy) ──→ PR → CI pass ──→ rebase ──→ merge #2 ──┐
│
Worktree C (fix/zzz) ──→ PR → CI pass ──→ rebase ──→ merge #3 ──┘
│
develop → main PR (batch)
```
**Queue rules:**
- Only one merge to develop at a time (conflict prevention)
- After merge, next waiting worktree rebases onto develop then pushes
- Re-run CI after merge (code changed due to rebase)
- develop → main can batch multiple features
```bash
# Queue order management — rebase next worktree
cd .claude/worktrees/<next-task-name>
git fetch origin develop
git rebase origin/develop
git push --force-with-lease
# → CI re-triggered → confirm pass → merge
```
### Concurrent-session drift & CI-trigger recovery
Two failure modes surface when **another session merges to develop while your
feature PR is open** (e.g. a Tau2-promotion or scheduled routine running in
parallel). Both are expected under multi-session work — recognise and recover,
don't re-investigate from scratch each time.
**A. PR goes `CONFLICTING` / `DIRTY` — usually a CHANGELOG collision.** The
other merge added its own top entry (often under `## [Unreleased]`) or bumped
the version, so your top-of-CHANGELOG insert conflicts. Recover:
```bash
git fetch origin
git merge origin/develop # resolve on the feature branch (squash-merge flattens it later)
# CHANGELOG.md is the usual (often only) conflict.
```
- - **Fold, don't stack.** Absorb the concurrent `[Unreleased]` / entry INTO your
- release version's own `### Added/Changed/Fixed` — a release captures *all*
- unreleased work since the last version. Never leave a `## [Unreleased]`
- heading on a branch bound for main (CLAUDE.md forbids it).
+ - **Preserve concurrent entries.** Ordinary fixes stay under `[Unreleased]`.
+ Only an authorized release promotes entries into a versioned section and
+ leaves a fresh `[Unreleased]` heading for subsequent work.
- **Re-check the version number isn't already taken.** If the other session
bumped `pyproject`/CHANGELOG to the number you picked, bump past it
(`grep -m1 '^version' <(git show origin/develop:pyproject.toml)` before
resolving — see [[feedback_concurrent_session_version_collision]]).
- **Regenerate the derived SoT after editing CHANGELOG.md** — the version
fan-out is not just the 5 text files: `node site/scripts/sync-stats.mjs`
(rebuilds `changelog.ts` / `sot.ts` / `llms.txt`) + `uv run python
scripts/check_llms_version.py --fix`, else the CI version ratchet blocks the
merge on a stale `changelog.ts`.
- Re-run the gates, `git add -A`, `git commit` (completes the merge), push.
**B. A fresh PR attaches 0 CI checks (webhook miss).** `gh pr checks <N>` prints
"no checks reported" and stays that way. Confirm it's a *miss*, not slowness:
```bash
gh api "repos/<owner>/<repo>/commits/$(git rev-parse HEAD)/check-runs" --jq .total_count # 0 = nothing attached
```
If `total_count` is 0 minutes after opening (Actions enabled, other PRs' runs
present), re-fire the trigger:
```bash
gh pr close <N> && gh pr reopen <N> # fires a `reopened` event → CI re-triggers
```
Verify attachment via the `check-runs` API, **not** `gh pr checks` alone (it
errors identically on "not yet run" and "will never run"). Don't leave a monitor
spinning on the repeating "no checks" error — diagnose the trigger first.
---
## Release Flow (rotation — eliminates backmerge)
Pre-2026-05-23 GEODE used the canonical gitflow pattern: release branch off
develop → merge to main → backmerge main → develop. That created a one-way
push of version stamps + CHANGELOG promotes into main, leaving develop's
stamps stale until the backmerge PR landed. Every release cycle paid the
cost of a 6-file backmerge PR, **AND** CHANGELOG conflicts when
develop moved while the release PR was in flight (we hit this 4 times
across PR #1499, #1504, #1506).
The current pattern rotates the order so release stamps land on develop
first:
```
develop ──cut──→ release/vX.Y.Z (stamp bump + CHANGELOG promote)
│
└─PR─→ develop (1) ← release branch absorbs back
│
└─PR─→ main (2) ← straight pass-through
```
**Step (1)** — release/* → develop PR. Carries the 5-location stamp bump
(`pyproject.toml`, `CLAUDE.md`, `README.md`, `README.ko.md`, CHANGELOG
header) and the `## [Unreleased]` → `## [X.Y.Z]` promote. Insert a fresh
empty `## [Unreleased]` above the just-promoted section so the next batch
of feature PRs has somewhere to land. Develop is now content-equivalent to
the eventual main state.
**Step (2)** — develop → main PR. No new commits beyond merge — it just
moves main's tip up to develop's. Abbreviated PR body (Summary +
Verification only) is fine here.
**No release-generated backmerge step.** Develop already has every release
commit that main has. After release, the two are content-identical modulo
gitflow merge-commit asymmetry. Main-maintained tracking transactions are a
separate source of drift and use the deliberate pre-sync below.
```bash
# ── Release flow (rotation) ──
# 1. Cut release branch
git fetch origin
git worktree add .claude/worktrees/release-vX.Y.Z -b release/vX.Y.Z origin/develop
# 2. Bump stamps + CHANGELOG promote + add fresh [Unreleased]
# (edit 5 files; see geode-changelog skill for [Unreleased] ratchet rules)
# 3. PR release → develop
gh pr create --base develop --head release/vX.Y.Z \
--title "release: vX.Y.Z — <summary>" \
--body "<release notes>"
# → CI ratchet → merge
# 4. PR develop → main (straight pass-through)
gh pr create --base main --head develop \
--title "release: vX.Y.Z (develop → main)" \
--body "<abbreviated body>"
# → CI ratchet → merge → Pages workflow fires
```
### Deliberate main-to-develop pre-sync
There is no automatic backmerge workflow. Before every `develop -> main`
- promotion, fetch both protected branches and compare their content. If main
- has unique tracking work and the sync is conflict-free, open a CI-gated PR
+ promotion, fetch both protected branches and compare their content and ancestry.
+ If main has commits not in develop and the sync is conflict-free, open a CI-gated PR
directly from the current `main` head to `develop`. Do not put that clean sync
behind the trusted sync-branch prefix: a merge from current main may simply
fast-forward and therefore has no two-parent head.
When conflict resolution is required, create
`sync/main-into-develop-<task>` from current develop and make an explicit merge
commit from current main. The sync head must have first parent exactly
`refs/remotes/origin/develop` and second parent exactly
`refs/remotes/origin/main`. CI grants main-ledger trust only to that
same-repository graph. Pull-request events do not rerun merely because the
unrelated main branch advances, so fetch and run the same resolver immediately
before merge:
```bash
git fetch origin
uv run python scripts/resolve_architecture_roadmap_trust.py \
--event-mode pull_request \
--target-branch develop \
--head-ref "sync/main-into-develop-<task>" \
--head-repo mangowhoiscloud/geode \
--repository mangowhoiscloud/geode \
--head-sha "$(git rev-parse HEAD)" \
--require-trust main
```
If the resolver fails, recreate the sync head from the new tips and rerun CI;
never merge the previously green but now-stale PR.
### Docs pipeline compatibility
The rotation pattern preserves every existing docs / release workflow
trigger:
| Workflow | Trigger | Behavior under rotation |
|----------|---------|-------------------------|
| `pages.yml` | `push: main` (paths include `CHANGELOG.md`, `pyproject.toml`, `CLAUDE.md`) | Fires when develop → main pass-through PR merges. Same moment as before. |
| `petri-publish.yml` | `push: main, develop` | Fires on develop merge AND main merge. Unchanged. |
| `release.yml` | `workflow_dispatch` (manual, default `ref: main`) | Manual trigger unchanged. The `version` input matches both `pyproject.toml` and `CHANGELOG.md` `## [X.Y.Z]` header. |
| `site/scripts/sync-stats.mjs` | invoked by `pages.yml` build | Counts CHANGELOG `## [X.Y.Z]` headers excluding `[Unreleased]`. Rotation's fresh-`[Unreleased]` block is correctly skipped. |
No release workflow needs editing for the rotation. Main-maintained tracking
work still follows the explicit pre-sync above.
---
## Step 0: ★ Frontier Research (mandatory pre-implementation research)
> Applicability: Mandatory for new infrastructure features (Gap, architecture changes). Can skip for simple bug fixes.
Investigate implementations in frontier harnesses (Claude Code, Codex CLI, OpenClaw, Aider, autoresearch, etc.),
create a comparison matrix, and document design decisions.
```
DISCOVER (investigate harnesses via parallel Agents)
→ COMPARE (feature × harness matrix)
→ DECIDE (Option A/B/C + selection rationale)
→ DOCUMENT (docs/plans/research-<topic>.md)
```
> Open source (Codex, Aider, autoresearch, OpenClaw) — verify source directly via `gh api`.
> Closed source (Claude Code only) — official docs/secondary sources — state verification limitations.
---
## Step 0: One-time machine setup
- `.pre-commit-config.yaml` defines 17 hooks, but they only run once the git hook
+ The hooks in `.pre-commit-config.yaml` only run once the git hook
is installed — and a fresh clone has no `.git/hooks/pre-commit`. Worktrees share
the main repo's hooks directory, so installing once covers every worktree.
```bash
uv run pre-commit install # verify: ls .git/hooks/pre-commit
```
- Even installed, pre-commit covers 2 of the 17 gates CI enforces; `scripts/preflight.sh`
- is what closes the rest. Install both.
+ Installed pre-commit hooks do not cover the full CI contract. Use
+ `scripts/preflight.sh` for the current local pre-PR checks and confirm required
+ GitHub checks on the actual PR head before merge.
## Step 1: Worktree Open (alloc)
**Every work unit** starts by opening a worktree. No exceptions.
```bash
# Fetch the canonical integration head; do not move a checkout held by another
# worktree or reuse a dirty main checkout.
git fetch origin
git worktree add .claude/worktrees/<task-name> \
-b feature/<branch-name> origin/develop
cd .claude/worktrees/<task-name>
# → Steps 2~11 all performed within this worktree
```
**Worktree rules:**
- `.claude/worktrees/` is in `.gitignore`
- No `git checkout` within worktree (HEAD conflict)
- No `git checkout feature/*` in main repo — access only via worktree
- Leak check: `git worktree list` to find unclosed worktrees
---
## ★ Pre-PR Quality Gate (mandatory loop before commit)
**After code changes, this loop must pass before commit/PR.**
```
Code changes complete
│
▼
┌─────────────────────────────────────────┐
│ Step 1: CI Guardrails (all must pass) │
│ │
- │ scripts/preflight.sh │ → runs every gate CI enforces
- │ scripts/preflight.sh --fast │ → same minus tests + site build
+ │ scripts/preflight.sh │ → local pre-PR gates; inspect skips
+ │ scripts/preflight.sh --fast │ → skips tests + site build
│ │
│ Any failure → fix → re-run Step 1 │
│ │
│ ※ Detailed inspection lenses: │
│ code-review-workflow │
│ (structure/deps/security/migration/ │
│ performance) │
└────────────────┬────────────────────────┘
│ All passed
▼
┌─────────────────────────────────────────┐
│ Step 2: Docs Writing (mandatory on │
│ code changes) │
│ │
│ □ Add entry to CHANGELOG.md │
│ [Unreleased] │
│ - Added / Changed / Fixed / Removed │
│ - Can skip if no code changes │
│ │
- │ □ Sync CLAUDE.md metrics (if changed) │
- │ - When Tests, Modules change │
+ │ □ Check generated architecture data │
+ │ - Review drift; do not hand-count │
│ │
- │ □ Update docs/progress.md today's │
- │ date section │
- │ - Completion table + remaining table │
+ │ □ Tracking updates only if in scope │
+ │ - Follow the main-owned workflow │
+ │ - Respect its explicit exceptions │
│ │
│ Omission found → fix → re-run Step 1 │
└────────────────┬────────────────────────┘
│ All complete
▼
┌─────────────────────────────────────────┐
│ Step 3: Commit │
│ │
│ Include code + docs in a single commit │
│ No separate docs-only commits │
│ (maintain consistency) │
│ │
│ git add <code files> CHANGELOG.md ... │
│ git commit -m "<type>: <description>" │
└────────────────┬────────────────────────┘
│
▼
Ready to create PR
```
### Quality Gate Anti-patterns
+ Neither `--fast` nor a skipped local gate establishes a full pass. Preflight
+ does not replace the current required checks in `.github/workflows/ci.yml`;
+ site ESLint is `npm run lint`, distinct from documentation render lint.
+
| Anti-pattern | Result | Correct Approach |
|-------------|--------|------------------|
| Creating PR with CI failures | Wastes reviewer time | Pass all locally before PR |
| Code-only commit, docs in separate PR | CHANGELOG missing, version mismatch | Code + docs in same commit |
| Direct push to main | Gitflow violation, history pollution | Must go through PR |
| Skipping docs-sync | README/CHANGELOG fall behind | Step 2 checklist mandatory |
| **Merging without CI confirmation** | **Broken code enters main** | **gh pr checks --watch mandatory** |
---
## ★★ Post-PR CI Ratchet — Mandatory Before Merge (CRITICAL)
> **Karpathy P4**: Ratchet = advance only on verification pass, rollback on failure.
> Merging a PR without CI green is a **ratchet violation**.
### Absolute Rule
**Before running any merge command, you must check CI status with `gh pr checks`.**
Merge prohibited if CI is still running or has failed.
### Merge Ratchet Loop
```
PR creation complete
│
▼
┌──────────────────────────────────────────────────┐
│ Step A: Wait for CI completion + check results │
│ │
│ gh pr checks <PR#> --watch --repo <owner/repo> │
│ │
│ → All pass → Proceed to Step B │
│ → Any fail → Proceed to Step C │
│ → pending/running → Wait (--watch auto-waits) │
└────────────────┬──────────────────────────────────┘
│
┌────────┴────────┐
▼ ▼
┌──────────────┐ ┌──────────────────────────────┐
│ Step B: │ │ Step C: Failure fix loop │
│ Run Merge │ │ │
│ │ │ 1. gh run view --log-failed │
│ gh pr merge │ │ → Identify failure cause │
│ <PR#> │ │ 2. Fix locally │
│ --merge │ │ 3. Commit + push (same branch) │
│ │ │ 4. CI auto re-triggered │
│ │ │ 5. Return to Step A │
│ │ │ │
│ │ │ (Repeat until pass) │
└──────────────┘ └──────────────────────────────────┘
```
### Merge Command Template (copy and use)
```bash
# ── feature → develop ──
# 1. Create PR
gh pr create --base develop --assignee mangowhoiscloud \
--title "<type>: <description>" \
--body "<detailed body template>"
# 2. ★★ CI Ratchet: Wait for checks to pass (MUST — never skip)
gh pr checks <PR#> --watch --repo mangowhoiscloud/geode
# 3. Merge remotely only after all pass. Do not let gh delete the branch here;
# the guarded free command below owns remote/worktree/local cleanup.
gh api --method PUT repos/mangowhoiscloud/geode/pulls/<PR#>/merge \
-f merge_method=squash
# ── develop → main ──
# 4. Create PR
gh pr create --base main --head develop --assignee mangowhoiscloud \
--title "<type>: <description> (develop → main)" \
--body "<develop → main template>"
# 5. ★★ CI Ratchet: Wait for checks to pass (MUST — never skip)
gh pr checks <PR#> --watch --repo mangowhoiscloud/geode
# 6. Merge only after all pass
gh pr merge <PR#> --merge --repo mangowhoiscloud/geode
```
### CI Failure Fix Loop
```bash
# Check failure logs
gh pr checks <PR#> --repo mangowhoiscloud/geode
gh run view <run_id> --log-failed
# Fix locally → push → CI auto re-runs
# ... fix ...
git add -A && git commit -m "fix: <CI failure cause fix>"
git push
# Check ratchet again
gh pr checks <PR#> --watch --repo mangowhoiscloud/geode
# pass → merge
```
### Common CI Failure Causes and Responses
| Failure | Response |
|---------|----------|
- | `ruff` lint error | `uv run ruff check --fix core/ tests/` + `uv run ruff format core/ tests/` |
+ | `ruff` lint error | Fix the affected files, then rerun the Ruff scopes in `scripts/preflight.sh` |
| `mypy` type error | Fix types, minimize `# type: ignore` |
| `bandit` security warning | Add `# nosec` or to pyproject.toml skips (only when justified) |
| `pytest` failure | Fix test code, add tests for new code |
| `coverage < 75%` | Add tests for modules with insufficient coverage |
---
## PR Writing Rules
| Item | Rule |
|------|------|
| **Language** | **Korean** (both title + body) |
| **Title** | `<type>: <Korean description>` (under 70 chars) |
| **Assignee** | `--assignee mangowhoiscloud` (always) |
| **Base** | feature → `develop`, develop → `main` |
### ★ PR Body Build Rules (CRITICAL — must follow)
> **A weak PR body prevents reviewers from understanding the change intent.**
> A 1-3 line PR body is an **anti-pattern**. Fill all required sections from the template below.
**Before generating PR body, you must:**
1. Check full diff with `git diff develop...HEAD`
2. Classify all changed **files** into core/secondary/docs
3. Write a one-line **why** rationale for each file change
4. Copy test result numbers from **actual execution output** (no XXXX placeholders)
5. Use HEREDOC format (prevents line break/markdown breakage)
### Anti-pattern vs Correct PR Body
| Anti-pattern (prohibited) | Correct Approach |
|---------------------------|------------------|
| `"progress hooks"` (3 words) | Write summary + changes + impact scope + QG in full |
| `"develop → main merge. X changes."` (1 line) | Include PR numbers, CI confirmation results for develop→main too |
| Summary only without listing changed files | Per-file AS-IS → TO-BE + one-line rationale |
| `XXXX passed` (placeholder) | `2168 passed` (actual number) |
- | Skipping Quality Gate checklist | All 5 CI tools + 4 docs items checked |
+ | Skipping Quality Gate checklist | Report actual commands, skipped checks, and required PR check results |
## PR Body Detailed Template (feature → develop)
**All sections are required. If not applicable, state "N/A".**
```markdown
## Summary
<!-- Required. 2-3 lines. "What" + "why" changed. Include background motivation. -->
<Core of the change in 2-3 sentences. What problem existed and how this PR solves it.>
## Changes
### Core Changes (Code)
<!-- Required. List all changed files without omission. -->
- `filepath:line-range`: Change content — AS-IS → TO-BE
- Rationale: One-line explanation of why this change was made
### Secondary Changes (Code)
<!-- If N/A, state "None" -->
- `filepath`: Rename/format/type fixes etc.
### Documentation/Config Changes
<!-- Required. If code changed, CHANGELOG must be included. -->
- `CHANGELOG.md`: Items added to [Unreleased] > Fixed/Added/Changed
- `CLAUDE.md`: Updated items (if applicable)
- `pyproject.toml`: Dependency/config changes (if applicable)
## Impact Scope
<!-- Required. -->
- **Affected modules**: <specific paths like core/cli, core/ui>
- **Backward compatibility**: Maintained / Broken (if broken, attach migration guide)
- **Test changes**: Added N / Modified N / Deleted N
## Design Decisions
<!-- Required for structural changes. For simple bug fixes, state "Simple fix, no design decisions needed." -->
- Why was approach B chosen over approach A?
- If referencing frontier harness cases: link `docs/plans/research-<topic>.md`
- Alternative comparison: Option A (pros/cons) vs Option B (pros/cons) → selection rationale
## Pre-PR Quality Gate (required — paste actual execution results)
<!-- Why one script instead of a command list: CI enforces 17 gates while this
checklist listed 5, and 3 of those 5 were documented at a NARROWER scope
than CI runs them (`mypy core/` vs CI's `mypy core/ evals/ evolve/ scripts/`).
A branch could pass the checklist verbatim and still fail CI — 5 of 60
sampled July 2026 failures were exactly that scope gap. Keep preflight.sh
as the single source; if CI gains a gate, add it there, not here. -->
- [x] `scripts/preflight.sh` — **all gates passed** (paste the final line)
- [x] `pytest -m "not live"` — **N passed** in Xs
- [x] CHANGELOG.md [Unreleased] entry added
- [x] README.md metric consistency verified
- [ ] CLAUDE.md sync (if applicable)
- - [x] docs/progress.md today's date section updated
+ - [ ] Main-maintained tracking update (only if requested, through its own workflow)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
```
## PR Body Template (develop → main)
```markdown
## Summary
develop → main merge. <1-2 line summary of main changes. What features/fixes are included.>
## Included Changes
<!-- Required. List all feature PRs with numbers and titles. -->
- #number `<type>: <title>` — One-line summary of core change
- #number `<type>: <title>` — One-line summary of core change
## Change Metrics
- **Files**: N files changed
- **Tests**: N passed (compared to previous +N/-N)
- **Modules**: N (specify if changed)
## Testing
- [x] Full CI passed (`gh pr checks --watch` confirmed)
- [x] feature → develop CI pass confirmed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
```
### gh pr create Command — HEREDOC Required
PR body must be passed in **HEREDOC** format. Inline `--body "..."` prohibited.
```bash
# ✅ Correct: HEREDOC
gh pr create --base develop --assignee mangowhoiscloud \
--title "<type>: <description>" \
--body "$(cat <<'PRBODY'
## Summary
...fill full template...
🤖 Generated with [Claude Code](https://claude.com/claude-code)
PRBODY
)"
# ❌ Prohibited: Inline (line breaks broken, content truncated)
gh pr create --body "One line summary"
```
---
## ★★★ Docs-Sync Final Verification (after main merge, before cleanup)
> Performed after the work unit is fully merged to main.
> Even if docs were written in Pre-PR Step 2, metrics may change during the merge process, so final verification is needed.
### Verification Checklist
```
main merge complete (step 10)
│
▼
┌──────────────────────────────────────────────────┐
│ □ README.md metric consistency │
- │ - modules: find core/ -name "*.py" | wc -l │
- │ - tests: uv run pytest --co 2>&1 | wc -l │
- │ - tools count, version │
+ │ - architecture_baseline.py --check │
+ │ - pytest --collect-only: use collection summary │
+ │ - reviewed inventory and version │
│ │
│ □ CLAUDE.md metric consistency │
│ - Verify Tests, Modules using same criteria │
│ │
│ □ CHANGELOG.md [Unreleased] omission check │
│ - Are changes merged to main recorded? │
│ │
- │ □ docs/progress.md today's date section exists │
- │ - If missing, add and commit │
+ │ □ In-scope tracking update follows its owner │
+ │ - Main-owned work is a separate transaction │
│ │
│ □ pyproject.toml coverage omit │
│ - Check if new module is in omit breaking │
│ coverage │
│ │
│ → If mismatch found: │
- │ docs(sync) commit → feature → develop → main │
- │ (apply same gitflow loop) │
+ │ Use the owning document's permitted branch │
+ │ and CI-gated workflow; do not edit in place │
│ → If no issues: proceed to step 12 │
│ (workspace cleanup) │
└──────────────────────────────────────────────────┘
```
### Pre-PR Step 2 vs Docs-Sync Final Verification
| Phase | Timing | Role |
|-------|--------|------|
| Pre-PR Step 2 | Before commit | **Write** docs (CHANGELOG entry, CLAUDE.md metrics) |
| Docs-Sync Final Verification | After main merge | **Verify** docs (README metrics, coverage omit, omission check) |
Docs are written in Pre-PR, and the final verification after main merge catches anything missed — a dual-layer structure.
---
## Branch Structure
```
main ─────────────────────────── production (stable, tagged)
│
└── develop ────────────────── integration (CI mandatory)
│
├── feature/<name> ───── Feature development
- ├── hotfix/<name> ────── Emergency fixes (branch from main)
+ ├── hotfix/<name> ────── Runtime fixes (branch from origin/develop)
└── release/v<semver> ── Release preparation
```
## Commit Convention
```
<type>(<scope>): <description>
Types: feat, fix, refactor, test, docs, ci, chore
Scopes: pipeline, scoring, analysis, verification, cli, memory, tools, llm
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
```
## CI Pipeline (GitHub Actions)
```
lint ─────┐
typecheck ─┤
test ──────┼──→ gate (all must pass for merge)
security ──┘
```
```bash
- # Local CI guardrails (Pre-PR)
- uv run ruff check core/ tests/
- uv run ruff format --check core/ tests/
- uv run mypy core/
- uv run bandit -r core/ -c pyproject.toml
- uv run pytest tests/ -m "not live" -q
+ # Local pre-PR gates; report any skipped checks, not just the exit code.
+ scripts/preflight.sh
# GitHub CI ratchet (Post-PR, mandatory before merge)
gh pr checks <PR#> --watch --repo mangowhoiscloud/geode
```
## Step 12: Worktree Close (free)
**After feature merge completion**, worktree must be closed. Run the canonical
guarded cleanup from outside the target worktree:
```bash
cd ~/workspace/geode
uv run python scripts/check_repo_hygiene.py free-merged-worktree \
--pr <feature-pr> \
--worktree .claude/worktrees/<task-name>
```
The command performs the leak check and prune. A refusal is a blocking signal;
inspect the reported dirty, owner, ancestry, or tree mismatch instead of using
manual `--force` cleanup.
## Worktree Allocation (Workflow Step 0)
Record on the Progress Board, then allocate the worktree.
```bash
# 1) Record Backlog → In Progress on Progress Board (from main)
# 2) Allocate Worktree
git fetch origin
git worktree add .claude/worktrees/<task-name> \
-b feature/<branch-name> origin/develop
# Note: the target path IS the worktree checkout root; `.owner` is gitignored
# (see /.owner in .gitignore) so the convention does not pollute feature branches.
echo "session=$(date -Iseconds) task_id=<task-name>" > .claude/worktrees/<task-name>/.owner
```
On completion (after the PR merges) tear down all three stale artifacts —
remote branch, worktree, local branch — per [Post-Merge Cleanup](#post-merge-cleanup-mandatory-after-every-merge)
below. A PR reporting "No commits between" is a content/branch-selection issue,
not permission to push protected branches directly.
## PR Body Template (MANDATORY)
```
## Summary
<1-3 bullet points: what changed and why>
## Why
<Problem statement — what broke, what was missing, what user reported>
## Changes
| File | Change |
|------|--------|
| `path/to/file.py` | description of change |
## GAP Audit (if applicable)
| Item | Status | Notes |
|------|--------|-------|
| ... | Implemented / Dropped / Already exists | ... |
## Verification
- [ ] ruff check clean
- [ ] mypy clean
- [ ] pytest pass (count)
- [ ] E2E unchanged (if applicable)
## Reference
<Source: frontier codebase, PR, issue, serve log, etc.>
```
Minimum required sections: **Summary**, **Why**, **Changes**, **Verification**.
develop → main PRs may use abbreviated form (Summary + Verification only).
| Change | Cascading Updates |
|--------|-------------------|
| New tool | `definitions.json` + handlers + E2E |
| LLM adapter | `core/llm/router/` + `core/llm/providers/` + E2E |
## Post-Merge Cleanup (MANDATORY after every merge)
A squash-merged feature PR leaves three stale artifacts behind: the remote
branch, worktree, and local branch. `git branch -d` cannot recognize a squash
merge and is therefore not the cleanup gate. Run the repository command from
outside the target worktree:
Never run `gh pr merge ... --delete-branch` from a linked worktree. GitHub CLI
may switch that checkout to the PR base branch and fast-forward it as part of
local branch cleanup, violating the one-branch-per-worktree contract. Use the
remote-only API merge above, leave the linked checkout untouched, then free it
from the repository root:
```bash
cd ~/workspace/geode
uv run python scripts/check_repo_hygiene.py free-merged-worktree \
--pr <feature-pr> \
--worktree .claude/worktrees/<task-name>
```
The command fails before mutation unless the PR is `MERGED`, its final head
tree equals the merge tree, the checked-out local branch is an ancestor of
that final head, the remote has not advanced, the checkout is clean, and
`.owner.task_id` matches the worktree directory. Only after those proofs does
it remove remote branch → worktree → squash-only local branch and prune. A
refusal must be investigated; never delete another session's owner-protected
worktree manually.
- ## Rebuild & Restart (Workflow Step 7)
-
- After merging to main, rebuild CLI and serve to update the runtime to the latest code.
-
- ```bash
- # 1) Stop any running geode serve daemon(s). Use `pgrep -f`, NOT
- # `ps aux | grep "geode serve"` — ps aux truncates the long python path
- # before "geode serve", so the grep silently matches nothing, the kill is a
- # no-op, and stale daemons survive a "rebuild" and then fight over
- # ~/.geode/cli.sock (the multi-serve pathology behind the 2026-06-09
- # model-resolution bug: banner shows one daemon's model, calls route through
- # another). `geode serve stop` / `geode doctor` already use pgrep -f
- # internally (core/cli/cmd_lifecycle.py, core/cli/doctor.py); this manual
- # line should match.
- pkill -f "geode serve" || true # no-op if none running; verify: pgrep -f "geode serve"
+ ## Rebuild & Restart (only when authorized)
- # 2) Reinstall CLI as editable + sync dependencies.
- # The [audit] extra (inspect_ai) is REQUIRED — the seed-generation pilot's
- # petri_audit tool and the self-improving loop's audit subprocess both need
- # it. Omitting it makes the pilot fail loudly ("petri_audit aborted —
- # install the [audit] extra") instead of measuring; pre-fix it silently
- # emitted all-zero dim_means (PR-PILOT-PETRI-AUDIT-WIRING, 2026-06-01).
- uv tool install -e ".[audit]" --force
- uv sync --extra audit
+ A merge does not authorize changing the user's global installation or running
+ services. When deployment or restart is explicitly in scope:
- # 3) Verify version + restart serve
- geode version # Confirm version match
- geode serve & # Restart in background
- ```
+ 1. Resolve the intended installation, checkout, process identity, and owner.
+ Inspect the current lifecycle implementation in
+ `core/cli/commands/lifecycle.py` before choosing a stop/restart operation;
+ a process-name match alone does not establish ownership.
+ 2. Stop only the confirmed in-scope process. Do not use a broad `pkill -f`,
+ terminate another session, or hide a failed stop with `|| true`.
+ 3. Install only the requested channel and required extras. Editable global
+ installation and the `[audit]` extra are not defaults for ordinary runtime
+ work; follow the [distribution contract](../geode-distribution/SKILL.md)
+ when installation is part of the task.
+ 4. Verify version, process identity, and the requested runtime smoke result
+ before reporting completion. If ownership or restart authority is unclear,
+ stop at the local/merged artifact and ask for direction.
## Progress Board (Workflow Step 8)
Update project tracking from main. Backlog → In Progress → Done.