From 7997c50a4e74ef01501892520fe372a628b4f0a8 Mon Sep 17 00:00:00 2001 From: Fr-e-d <155310055+Fr-e-d@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:40:46 +0200 Subject: [PATCH] sync: update 3 file(s) in core/ - .gaai/core/contexts/rules/orchestration.rules.md - .gaai/core/workflows/delivery-loop.workflow.md - CHANGELOG.md GAAI-Source-Commit: c5e744e50f4b436dff360797b2d11a066b976231 --- .gaai/core/contexts/rules/orchestration.rules.md | 3 ++- .gaai/core/workflows/delivery-loop.workflow.md | 4 ++-- CHANGELOG.md | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gaai/core/contexts/rules/orchestration.rules.md b/.gaai/core/contexts/rules/orchestration.rules.md index 87c47d85..b4dcbbb1 100644 --- a/.gaai/core/contexts/rules/orchestration.rules.md +++ b/.gaai/core/contexts/rules/orchestration.rules.md @@ -204,7 +204,8 @@ A pre-push hook (`.githooks/pre-push`) enforces this rule at the git level. Scope: **worktree-isolated delivery of code/content** — daemon-spawned story delivery and any manual session making an isolated change in a worktree. The merge path here is the same one the `production`-prohibition and auto-merge clauses above already govern; this subsection does not re-assert them, it adds the lifecycle clauses those clauses omit. The mechanism (idempotent creation, throttled reaping, retries, locks) lives in the daemon scripts; the procedure lives in `delivery-loop.workflow.md §Commit Phase`. This section is the normative authority both implement. - **Governed merge path.** Worktree-originated code/content reaches `staging` only via `gh pr create --base staging --head story/{id}` then squash merge (auto-merge per the clause above). Manual Delivery repairs follow the same path with a dedicated branch name if no story branch is active. **Exempt:** Discovery's backlog/governance mutations push directly to `origin/staging` under the daemon staging lock per §Claim Protocol — that path has no story PR and is unaffected by this clause. -- **Post-merge cleanup.** After a story's PR merges, its remote branch (`gh pr merge --delete-branch`) and its worktree are removed; the daemon also drops the local branch ref. No worktree may persist past its story's terminal state. +- **Post-merge cleanup.** After a story's PR merges, its worktree is removed and its local branch ref is dropped through the landed-or-preserved guard below. No worktree may persist past its story's terminal state. The merge does **not** delete the remote branch — deleting it is a separate, optional step (`git push origin --delete `), never a flag on the merge. +- **The merge is an API-only operation (INVARIANT).** `gh pr merge` MUST target the PR by number with `--repo`, and MUST NOT be given `--delete-branch`. That flag deletes the *local* branch too, which forces `gh` to switch the current checkout onto the base branch first. Run from inside a worktree it makes that worktree squat the shared target-branch ref — which mechanically breaks the main-working-tree invariant above, since a branch can be checked out in only one tree — and it silently attempts a fast-forward of the local target branch. Every local teardown (`worktree remove`, `worktree prune`, `branch -D`) runs against the primary working tree with `git -C`, never from inside the worktree being removed. Pass `--match-head-commit ` so a head that moved after review cannot be merged unseen. - **Orphan reaping is eventually-consistent.** A worktree whose story PR is MERGED/CLOSED, or whose HEAD is an ancestor of `origin/staging`, is an orphan and MUST be reaped. Reaping is periodic/convergent, **not** synchronous — "no orphan subsists" is a convergence guarantee, not an instantaneous one. Do not assert or rely on synchronous orphan removal. - **Data-safety refusal (INVARIANT).** A dirty or still-active worktree is NEVER force-removed. The reaper refuses removal and defers it (skip-and-retry next cycle); committed work is never lost because removal frees only the working dir, never a branch ref. Data safety dominates cleanup. - **Landed-or-preserved branch deletion (INVARIANT).** A story branch is deletable ONLY IF its work is verifiably landed — its PR is MERGED, or the story is `status: done` on the remote backlog, or the branch's local tip matches a remote ref (pushed-but-not-yet-merged) — or it is preserved, never destroyed. Because squash-merges are never reachable from `origin/staging` by commit-SHA ancestry, ancestry-on-staging alone is NEVER a valid landed test. A verification failure (network, `gh`, or unreadable remote state) does not affirm landed and fails closed to preserve. Every story-branch deletion site (recovery, cleanup sweeps, reconcile, orphan reaping) routes through this single guard. When the guard refuses deletion, the branch is preserved by rename (`story/{id}-preserved-`) — never left in place under its original name, so the retry path still gets a fresh `story/{id}` — with an append-only audit entry recording the branch tip; the operator-facing log line is throttled per story id so periodic sweeps do not re-emit every cycle. diff --git a/.gaai/core/workflows/delivery-loop.workflow.md b/.gaai/core/workflows/delivery-loop.workflow.md index be74b544..53ad3954 100644 --- a/.gaai/core/workflows/delivery-loop.workflow.md +++ b/.gaai/core/workflows/delivery-loop.workflow.md @@ -240,9 +240,9 @@ Deterministic bash only — no `claude -p` invocation. Implemented in `handle_co 2. Push story branch with retry-rebase pattern (3 attempts, backoff 2s / 4s / 6s) 3. `gh pr create --base staging --head story/{id}` 4. CI watch (advisory mode if no branch protection) -5. `gh pr merge --squash --delete-branch` +5. `gh pr merge --repo --squash --match-head-commit ` — by number, never `--delete-branch` (see the API-only merge invariant in the rules file) 6. Backlog status → `done` (flock-serialized push to staging) -7. Worktree removal (the *remote* branch is deleted by `--delete-branch` at step 5; the local worktree is removed here, and the reaper drops any surviving local branch ref) +7. Worktree removal via `git -C worktree remove`, never from inside the worktree being removed; the local branch ref is dropped by the landed-or-preserved guard. The *remote* branch is left in place — no step here deletes it. **Normative authority:** the worktree + PR + cleanup invariants are defined in `orchestration.rules.md §Branch Rules → Worktree lifecycle & cleanup`. This sequence is the procedure; the rules file is the authority. The procedure restates only hard safety boundaries (see **Safety boundary** below); it does not define invariants. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ba149c1..d9ff5007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed +- docs: the post-merge cleanup clause prescribed a flag that breaks an invariant - fix: record run identity on the handle instead of inferring it - fix: keep and poll the handle until a terminal receipt - fix: make generated execution plans carry artefact frontmatter