fix: resolve conflicts for PR #490

Merge prgs/master into feat/issue-470-preflight-contract, keeping stacked-PR
lock base support (#484) and explicit task binding on lock_issue preflight (#470).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-08 03:59:51 -04:00
co-authored by Claude Opus 4.8
19 changed files with 1491 additions and 10 deletions
+33
View File
@@ -286,6 +286,28 @@ heartbeat timestamp. An active same-issue/same-operation lease blocks duplicate
work. An expired lease still blocks takeover until a recovery review records why
the prior work is abandoned, completed, or unsafe to continue.
**Stacked PRs (#484).** By default the lock worktree must be base-equivalent to
`master`/`main`/`dev` — ordinary work is unchanged. A *stacked* PR (deliberately
based on another unmerged PR's branch) is an explicit, opt-in path: pass
`stacked_base_branch` **and** `stacked_base_pr` to `gitea_lock_issue`. The lock
fails closed unless that branch is owned by a live **open** PR whose number
matches `stacked_base_pr`, so arbitrary or stale branches cannot be used as
bases. When approved, the lock payload records
`approved_stacked_base = {branch, pr_number, verified_open}` and the worktree may
be base-equivalent to that branch instead of master. `gitea_create_pr` then
allows `base = <that branch>` only when it matches the recorded approval, the
dependency PR is **still open**, and the PR body documents the stack:
- `Stacked on PR #<X> / issue #<Y>`
- `Base branch: <feature-branch>`
- `Head branch: <this-issue-branch>`
- `Do not merge before PR #<X>` (merge ordering)
- retarget/rebase to `master` after the dependency lands, if required
Stacked support never bypasses the issue lock — the base is recorded *on* the
lock and re-verified at PR time. A merged/closed dependency base fails closed;
retarget onto `master` or re-lock against a live base.
**Do not manually seed `/tmp/gitea_issue_lock.json` or any lock file as a normal
recovery path.** That global slot is deprecated and can clobber unrelated live
leases (#438). After an MCP restart, call `gitea_lock_issue` again — own-branch
@@ -303,6 +325,17 @@ shared state and manual writes can clobber another session's live lease. Use
3. Operator override only when explicitly authorized — record
`External-state mutations` and `operator override proof` in the final report.
**Adoption proof in the live lock response (#477):** when `gitea_lock_issue`
adopts an existing own branch, the response carries an `adoption` block with
citable fields — `adoption_decision` (`ADOPT`), `adopted` (`true`),
`adopted_branch`, `adopted_branch_head`, `matcher_summary` (boundary-safe reason
the branch qualified), `competing_branch_check`, and `safe_next_action`. A normal
lock instead returns an `adoption_check` block with `adoption_decision`
(`NO_MATCH`) and `adopted: false`, so a non-adoption response can never be misread
as claiming adoption. Recovery reports should quote the live lock response
`adoption`/`adoption_check` block directly instead of inferring adoption from
separate offline checks.
`gitea_create_pr` rejects lock files that lack sanctioned `lock_provenance`
metadata. Final-report validation blocks handoffs that hide lock read/write/delete
under `External-state mutations: none` or mix author PR creation with reviewer