fix(scope): wire author bootstrap scope into workflow scope guard #942

Merged
sysadmin merged 1 commits from fix/issue-941-scope-guard-bootstrap-wiring into master 2026-07-26 06:20:01 -05:00
Owner

Closes #941

The recursive bootstrap deadlock

gitea_bootstrap_author_issue_worktree exists to create an author's first branches/ worktree from a clean control checkout, breaking the lock↔worktree cycle recorded in #892. It failed closed on every call:

Workflow scope guard (#683) [missing_issue_worktree]: author source/test mutation
from the stable control checkout is forbidden; bind an issue-backed worktree
under branches/ first.
exception_class: workflow_scope_guard.ProductionGuardError

Because that is the tool an author needs before it can touch any file, #941 could not be implemented through the canonical path — the defect blocked its own repair. Every other worktree-producing route requires a worktree that does not yet exist: gitea_lock_issue refuses with the #274 "worktree path does not exist" branch, gitea_publish_unpublished_issue_branch requires worktree_path on an already registered clean worktree, gitea_recover_dirty_orphaned_issue_worktree requires source_worktree_path, and gitea_rebind_dirty_same_claimant_author_session requires worktree_path plus a dead owner PID.

Why PR #926 reached only two of the three enforcement paths

PR #926 (which closed #892) made create_issue_bootstrap.bootstrap_permits_control_checkout accept task_scope=author_issue_bootstrap, and wired that canonical decision into the two guards its own description named: the #274 branches-only enforcer and the #604 anti-stomp preflight. Both consume a single server-derived assessment computed once per preflight, which is the #757 "one shared decision" rule.

A third enforcement path was never connected. workflow_scope_guard holds an independent copy of the clean-root author decision:

# workflow_scope_guard.py, before this change
if _cib is not None and _cib.is_create_issue_task(mutation_task):
    create_issue_bootstrap = True
else:
    reasons.append("author source/test mutation from the stable control checkout is forbidden; ...")
    blocker_kind = BLOCKER_MISSING_WORKTREE

The exemption test is a task-name allowlist — CREATE_ISSUE_TASKS = frozenset({"create_issue", "gitea_create_issue"}) — which never contained bootstrap_author_issue_worktree (the literal set at gitea_mcp_server.py:10157). Before this change, grep -c 'bootstrap_permits_control_checkout' workflow_scope_guard.py returned 0.

So on the real path

gitea_bootstrap_author_issue_worktree
  -> verify_preflight_purity
    -> _enforce_issue_scope_guard
      -> workflow_scope_guard.assess_production_mutation_guards
        -> assess_root_source_mutation

the guard raised before assess_author_issue_bootstrap was ever consulted. The #926 waiver was unreachable here, so the #892 deadlock stayed partially present and #931 remained blocked.

A second instance of the identical mistake sat immediately downstream: _enforce_issue_scope_guard also computed require_lock from is_create_issue_task, so once the worktree block cleared, the bootstrap task failed on missing_issue_scope ("no owning issue lock is bound"). No lock can exist at bootstrap time — that is the cycle the tool breaks. This PR fixes both, and fixes them the same way.

How the fix reuses the canonical decision

  • workflow_scope_guard.assess_root_source_mutation gains an optional bootstrap_assessment parameter and, in the clean-root author case, consults create_issue_bootstrap.bootstrap_permits_control_checkout over that evidence. The is_create_issue_task arm is untouched and still evaluated first.
  • workflow_scope_guard.assess_production_mutation_guards forwards the evidence unchanged.
  • _enforce_issue_scope_guard accepts and threads the assessment the #274 and #604 guards already consume, so all three judge identical evidence per #757, and derives the pre-ownership lock waiver from that one canonical decision rather than a second task-name allowlist.
  • verify_preflight_purity passes the already-computed assessment at both of its call sites — the assessment was previously computed one line above the scope-guard call and simply not handed to it.

No task-name allowlist decides the bootstrap case anymore. The module-local list survives only for create_issue, whose #749 behaviour is unchanged.

Why ordinary control-checkout mutation stays forbidden

The new arm delegates to a predicate that is fail-closed by construction. It returns False unless every proof obligation is present and affirmative: the task is create_issue or the author bootstrap task; allowed, proven true and block, not_applicable false; reasons empty; task_scope matching the task class exactly, so create-issue scope cannot license the bootstrap and bootstrap scope cannot license create-issue; bootstrap_path == clean_canonical_control_checkout; no dirty files; under_branches false; base_tips_verified with local and remote tips both recorded, non-empty and equal, re-derived rather than trusted; and the assessment describing exactly the workspace and root being guarded, with that workspace being the canonical control checkout.

Anything else — a missing assessment, a non-dict, a truncated or hand-built one, a dirty root, a drifted head, a mismatched binding, or any other author task — leaves the ordinary block in force. commit_files, lock_issue, and every other author mutation from the control checkout still raise. The evidence is server-derived from inspected repository state and is never accepted from a tool argument, so no caller can assert eligibility it has not proven. Protections tied to #274, #604, #618, and #683 are unchanged; nothing is removed or loosened.

Regression coverage

tests/test_issue_941_scope_guard_bootstrap_wiring.py (24 tests) drives the real enforcer, not the authorization helper in isolation. This distinction is the whole point: #892's own predicate tests all passed while the live bootstrap stayed blocked, because its "integration" test called bootstrap_permits_control_checkout directly and asserted on a hand-built dict. A helper-only test cannot observe this defect.

  • Real path — the bootstrap task and its tool alias pass _enforce_issue_scope_guard from a clean control checkout; a spy asserts the guard actually reaches bootstrap_permits_control_checkout.
  • Fail-closed — missing, malformed, non-dict, wrong task_scope, non-empty reasons, mismatched base tips, unverified tips, mismatched workspace binding, mismatched repo-root binding, and blocked assessments each still raise.
  • Ordinary mutation stays denied — commit_files and lock_issue from the control checkout, cross-task smuggling of valid bootstrap evidence onto commit_files, and a dirty control checkout under the bootstrap task.
  • create_issue unchanged — both literals still allowed from a clean control checkout, still blocked when the checkout is dirty.
  • Guard-level wiring — valid evidence unblocks, absent evidence blocks with missing_issue_worktree, the reconciler exemption survives, and omitting the new parameter preserves prior behaviour.

Pre-fix the suite failed; post-fix it passes:

# before the production change
5 failed, 19 passed
FAILED ...::test_bootstrap_task_passes_scope_guard_from_clean_control
FAILED ...::test_bootstrap_tool_alias_passes_scope_guard
FAILED ...::test_guard_consults_canonical_predicate
# with the live signature:
# ProductionGuardError: Workflow scope guard (#683) [missing_issue_worktree]

# after
24 passed in 1.92s

Validation

Run from the branches/ worktree, not a temporary directory.

PYTHONPATH=. venv/bin/python -m pytest \
  tests/test_issue_941_scope_guard_bootstrap_wiring.py \
  tests/test_issue_892_author_bootstrap_deadlock.py \
  tests/test_issue_683_workflow_scope_guards.py \
  tests/test_issue_757_bootstrap_guard_agreement.py \
  tests/test_create_issue_bootstrap.py \
  tests/test_author_issue_bootstrap.py \
  tests/test_anti_stomp_preflight.py \
  tests/test_root_checkout_guard.py \
  tests/test_issue_618_author_worktree_resolution.py -q
# 218 passed, 47 subtests passed

PYTHONPATH=. venv/bin/python -m pytest tests/ -q
# 28 failed, 5525 passed, 6 skipped, 994 subtests passed in 147.74s

The 28 full-suite failures are the pre-existing baseline, not regressions. The four that touch these guards were each re-run on unmodified master 8c1d22a658ecb5715e3f4db6eb7ebb408830c101 and fail identically there:

FAILED tests/test_preflight_workspace_repo_forwarding.py::...::test_commit_files_forwards_explicit_org_repo
FAILED tests/test_workspace_guard_alignment.py::...::test_declared_branches_worktree_passes_when_mcp_root_differs
FAILED tests/test_workspace_guard_alignment.py::...::test_stable_checkout_still_rejected
FAILED tests/test_reconciler_close_workspace_guard.py::...::test_author_non_create_issue_still_blocked_on_control_checkout
# 4 failed in 2.92s on master, no local changes

A full-suite baseline run on unmodified master was still executing when this PR was opened; the reviewer should treat the 28-vs-28 comparison as verified only for those four until that run is posted as a follow-up comment. The failure count matches the standing repository baseline of 28.

Author bootstrap provenance

The worktree for this PR was created under a one-time, issue-scoped operator authorization, because the defect blocks its own repair. Exactly one git worktree add -b created fix/issue-941-scope-guard-bootstrap-wiring at the verified live master SHA 8c1d22a658ecb5715e3f4db6eb7ebb408830c101, followed immediately by gitea_lock_issue binding. No existing worktree was borrowed, the stable control checkout was not edited, and no remote branch was created by hand. The full record, including the refusal text that forced it, is issue comment 17385 on #941. The authorization was consumed once and is expired.

Every Gitea mutation in this PR went through sanctioned gitea-author capabilities: gitea_allocate_next_work, gitea_lock_issue, gitea_create_issue_comment, gitea_commit_files, gitea_create_pr. No tea, no curl, no raw API, no direct database access, no manual push.

Scope

  • Files: workflow_scope_guard.py, gitea_mcp_server.py, tests/test_issue_941_scope_guard_bootstrap_wiring.py
  • Diff: 3 files, +408 / −2
  • Branch: fix/issue-941-scope-guard-bootstrap-wiring
  • Base: master at 8c1d22a658ecb5715e3f4db6eb7ebb408830c101
  • Head: a09c485fc0eba75b35986a1833dba986be6c2101
  • Worktree: branches/issue-941-scope-guard-bootstrap-wiring
  • Assignment asn-15133423bf614c11, lease lease-ecf79c663f354894

No transport work, no role-permission changes, no refactoring beyond the wiring, and no manual worktree escape hatch added to the code.

Effect on #931

Issue #931 remains blocked and received nothing from this session — no assignment, lease, worktree, branch, commit, or PR. It was explicitly excluded from the allocator candidate set. #931 stays blocked until this PR is independently approved, merged, deployed, and the fleet restarted and commissioned, because the running servers continue to execute the pre-fix guard until then.

Handoff

WHO_IS_NEXT: reviewer — independent review against the #941 acceptance criteria. Do not self-review or self-merge.

Closes #941 ## The recursive bootstrap deadlock `gitea_bootstrap_author_issue_worktree` exists to create an author's first `branches/` worktree from a clean control checkout, breaking the lock↔worktree cycle recorded in #892. It failed closed on every call: ``` Workflow scope guard (#683) [missing_issue_worktree]: author source/test mutation from the stable control checkout is forbidden; bind an issue-backed worktree under branches/ first. exception_class: workflow_scope_guard.ProductionGuardError ``` Because that is the tool an author needs before it can touch any file, #941 could not be implemented through the canonical path — the defect blocked its own repair. Every other worktree-producing route requires a worktree that does not yet exist: `gitea_lock_issue` refuses with the #274 "worktree path does not exist" branch, `gitea_publish_unpublished_issue_branch` requires `worktree_path` on an already registered clean worktree, `gitea_recover_dirty_orphaned_issue_worktree` requires `source_worktree_path`, and `gitea_rebind_dirty_same_claimant_author_session` requires `worktree_path` plus a dead owner PID. ## Why PR #926 reached only two of the three enforcement paths PR #926 (which closed #892) made `create_issue_bootstrap.bootstrap_permits_control_checkout` accept `task_scope=author_issue_bootstrap`, and wired that canonical decision into the two guards its own description named: the #274 branches-only enforcer and the #604 anti-stomp preflight. Both consume a single server-derived assessment computed once per preflight, which is the #757 "one shared decision" rule. A third enforcement path was never connected. `workflow_scope_guard` holds an independent copy of the clean-root author decision: ```python # workflow_scope_guard.py, before this change if _cib is not None and _cib.is_create_issue_task(mutation_task): create_issue_bootstrap = True else: reasons.append("author source/test mutation from the stable control checkout is forbidden; ...") blocker_kind = BLOCKER_MISSING_WORKTREE ``` The exemption test is a task-name allowlist — `CREATE_ISSUE_TASKS = frozenset({"create_issue", "gitea_create_issue"})` — which never contained `bootstrap_author_issue_worktree` (the literal set at `gitea_mcp_server.py:10157`). Before this change, `grep -c 'bootstrap_permits_control_checkout' workflow_scope_guard.py` returned `0`. So on the real path ``` gitea_bootstrap_author_issue_worktree -> verify_preflight_purity -> _enforce_issue_scope_guard -> workflow_scope_guard.assess_production_mutation_guards -> assess_root_source_mutation ``` the guard raised before `assess_author_issue_bootstrap` was ever consulted. The #926 waiver was unreachable here, so the #892 deadlock stayed partially present and #931 remained blocked. A second instance of the identical mistake sat immediately downstream: `_enforce_issue_scope_guard` also computed `require_lock` from `is_create_issue_task`, so once the worktree block cleared, the bootstrap task failed on `missing_issue_scope` ("no owning issue lock is bound"). No lock can exist at bootstrap time — that is the cycle the tool breaks. This PR fixes both, and fixes them the same way. ## How the fix reuses the canonical decision * `workflow_scope_guard.assess_root_source_mutation` gains an optional `bootstrap_assessment` parameter and, in the clean-root author case, consults `create_issue_bootstrap.bootstrap_permits_control_checkout` over that evidence. The `is_create_issue_task` arm is untouched and still evaluated first. * `workflow_scope_guard.assess_production_mutation_guards` forwards the evidence unchanged. * `_enforce_issue_scope_guard` accepts and threads the assessment the #274 and #604 guards already consume, so all three judge identical evidence per #757, and derives the pre-ownership lock waiver from that one canonical decision rather than a second task-name allowlist. * `verify_preflight_purity` passes the already-computed assessment at both of its call sites — the assessment was previously computed one line above the scope-guard call and simply not handed to it. No task-name allowlist decides the bootstrap case anymore. The module-local list survives only for `create_issue`, whose #749 behaviour is unchanged. ## Why ordinary control-checkout mutation stays forbidden The new arm delegates to a predicate that is fail-closed by construction. It returns `False` unless every proof obligation is present and affirmative: the task is `create_issue` or the author bootstrap task; `allowed`, `proven` true and `block`, `not_applicable` false; `reasons` empty; `task_scope` matching the task class exactly, so create-issue scope cannot license the bootstrap and bootstrap scope cannot license create-issue; `bootstrap_path == clean_canonical_control_checkout`; no dirty files; `under_branches` false; `base_tips_verified` with local and remote tips both recorded, non-empty and equal, re-derived rather than trusted; and the assessment describing exactly the workspace and root being guarded, with that workspace being the canonical control checkout. Anything else — a missing assessment, a non-dict, a truncated or hand-built one, a dirty root, a drifted head, a mismatched binding, or any other author task — leaves the ordinary block in force. `commit_files`, `lock_issue`, and every other author mutation from the control checkout still raise. The evidence is server-derived from inspected repository state and is never accepted from a tool argument, so no caller can assert eligibility it has not proven. Protections tied to #274, #604, #618, and #683 are unchanged; nothing is removed or loosened. ## Regression coverage `tests/test_issue_941_scope_guard_bootstrap_wiring.py` (24 tests) drives the real enforcer, not the authorization helper in isolation. This distinction is the whole point: #892's own predicate tests all passed while the live bootstrap stayed blocked, because its "integration" test called `bootstrap_permits_control_checkout` directly and asserted on a hand-built dict. A helper-only test cannot observe this defect. * Real path — the bootstrap task and its tool alias pass `_enforce_issue_scope_guard` from a clean control checkout; a spy asserts the guard actually reaches `bootstrap_permits_control_checkout`. * Fail-closed — missing, malformed, non-dict, wrong `task_scope`, non-empty `reasons`, mismatched base tips, unverified tips, mismatched workspace binding, mismatched repo-root binding, and blocked assessments each still raise. * Ordinary mutation stays denied — `commit_files` and `lock_issue` from the control checkout, cross-task smuggling of valid bootstrap evidence onto `commit_files`, and a dirty control checkout under the bootstrap task. * `create_issue` unchanged — both literals still allowed from a clean control checkout, still blocked when the checkout is dirty. * Guard-level wiring — valid evidence unblocks, absent evidence blocks with `missing_issue_worktree`, the reconciler exemption survives, and omitting the new parameter preserves prior behaviour. Pre-fix the suite failed; post-fix it passes: ``` # before the production change 5 failed, 19 passed FAILED ...::test_bootstrap_task_passes_scope_guard_from_clean_control FAILED ...::test_bootstrap_tool_alias_passes_scope_guard FAILED ...::test_guard_consults_canonical_predicate # with the live signature: # ProductionGuardError: Workflow scope guard (#683) [missing_issue_worktree] # after 24 passed in 1.92s ``` ## Validation Run from the `branches/` worktree, not a temporary directory. ``` PYTHONPATH=. venv/bin/python -m pytest \ tests/test_issue_941_scope_guard_bootstrap_wiring.py \ tests/test_issue_892_author_bootstrap_deadlock.py \ tests/test_issue_683_workflow_scope_guards.py \ tests/test_issue_757_bootstrap_guard_agreement.py \ tests/test_create_issue_bootstrap.py \ tests/test_author_issue_bootstrap.py \ tests/test_anti_stomp_preflight.py \ tests/test_root_checkout_guard.py \ tests/test_issue_618_author_worktree_resolution.py -q # 218 passed, 47 subtests passed PYTHONPATH=. venv/bin/python -m pytest tests/ -q # 28 failed, 5525 passed, 6 skipped, 994 subtests passed in 147.74s ``` The 28 full-suite failures are the pre-existing baseline, not regressions. The four that touch these guards were each re-run on unmodified master `8c1d22a658ecb5715e3f4db6eb7ebb408830c101` and fail identically there: ``` FAILED tests/test_preflight_workspace_repo_forwarding.py::...::test_commit_files_forwards_explicit_org_repo FAILED tests/test_workspace_guard_alignment.py::...::test_declared_branches_worktree_passes_when_mcp_root_differs FAILED tests/test_workspace_guard_alignment.py::...::test_stable_checkout_still_rejected FAILED tests/test_reconciler_close_workspace_guard.py::...::test_author_non_create_issue_still_blocked_on_control_checkout # 4 failed in 2.92s on master, no local changes ``` A full-suite baseline run on unmodified master was still executing when this PR was opened; the reviewer should treat the 28-vs-28 comparison as verified only for those four until that run is posted as a follow-up comment. The failure count matches the standing repository baseline of 28. ## Author bootstrap provenance The worktree for this PR was created under a one-time, issue-scoped operator authorization, because the defect blocks its own repair. Exactly one `git worktree add -b` created `fix/issue-941-scope-guard-bootstrap-wiring` at the verified live master SHA `8c1d22a658ecb5715e3f4db6eb7ebb408830c101`, followed immediately by `gitea_lock_issue` binding. No existing worktree was borrowed, the stable control checkout was not edited, and no remote branch was created by hand. The full record, including the refusal text that forced it, is issue comment `17385` on #941. The authorization was consumed once and is expired. Every Gitea mutation in this PR went through sanctioned `gitea-author` capabilities: `gitea_allocate_next_work`, `gitea_lock_issue`, `gitea_create_issue_comment`, `gitea_commit_files`, `gitea_create_pr`. No `tea`, no `curl`, no raw API, no direct database access, no manual push. ## Scope * Files: `workflow_scope_guard.py`, `gitea_mcp_server.py`, `tests/test_issue_941_scope_guard_bootstrap_wiring.py` * Diff: 3 files, +408 / −2 * Branch: `fix/issue-941-scope-guard-bootstrap-wiring` * Base: `master` at `8c1d22a658ecb5715e3f4db6eb7ebb408830c101` * Head: `a09c485fc0eba75b35986a1833dba986be6c2101` * Worktree: `branches/issue-941-scope-guard-bootstrap-wiring` * Assignment `asn-15133423bf614c11`, lease `lease-ecf79c663f354894` No transport work, no role-permission changes, no refactoring beyond the wiring, and no manual worktree escape hatch added to the code. ## Effect on #931 Issue #931 remains blocked and received nothing from this session — no assignment, lease, worktree, branch, commit, or PR. It was explicitly excluded from the allocator candidate set. #931 stays blocked until this PR is independently approved, merged, deployed, and the fleet restarted and commissioned, because the running servers continue to execute the pre-fix guard until then. ## Handoff **WHO_IS_NEXT: reviewer** — independent review against the #941 acceptance criteria. Do not self-review or self-merge.
jcwalker3 added 1 commit 2026-07-26 05:03:27 -05:00
PR #926 (#892) taught create_issue_bootstrap.bootstrap_permits_control_checkout
to accept task_scope=author_issue_bootstrap and wired that canonical decision
into the #274 branches-only enforcer and the #604 anti-stomp preflight. A third
enforcement path was left unwired.

workflow_scope_guard kept its own copy of the clean-root author decision, gated
on create_issue_bootstrap.is_create_issue_task -- a task-name allowlist that
never contained bootstrap_author_issue_worktree. The real call path

    gitea_bootstrap_author_issue_worktree
      -> verify_preflight_purity
        -> _enforce_issue_scope_guard
          -> workflow_scope_guard.assess_production_mutation_guards

therefore raised ProductionGuardError(missing_issue_worktree) before
assess_author_issue_bootstrap was ever consulted, leaving the #892 deadlock
partially present and blocking issue #931.

Changes:

* workflow_scope_guard.assess_root_source_mutation accepts the server-derived
  bootstrap_assessment and, for the clean-root author case, consults the
  canonical bootstrap_permits_control_checkout decision instead of a local
  task-name allowlist. The create_issue arm is unchanged.
* workflow_scope_guard.assess_production_mutation_guards forwards the evidence.
* _enforce_issue_scope_guard accepts and threads the same assessment the #274
  and #604 guards already consume, so all three judge identical evidence, and
  waives the pre-ownership issue-lock requirement for the bootstrap task via
  that same canonical decision rather than a second task-name allowlist.
* verify_preflight_purity passes the once-computed assessment at both sites.

The waiver cannot widen: the predicate fails closed on missing, malformed,
cross-scope, dirty, drifted, or wrongly bound evidence, so ordinary author
source and test mutation from the control checkout stays forbidden and the
#274/#604/#618/#683 protections are unchanged.

Regression: tests/test_issue_941_scope_guard_bootstrap_wiring.py drives the
real enforcer rather than the authorization helper in isolation, which is why
#892's own predicate tests passed while the live bootstrap stayed blocked.

Closes #941

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Author
Owner

Canonical Issue State

STATE: ready-for-review — full-suite baseline comparison now complete; the PR body's open item is closed. Head unchanged at a09c485fc0eba75b35986a1833dba986be6c2101.
WHO_IS_NEXT: reviewer
NEXT_ACTION: Review PR #942 at exact head a09c485fc0eba75b35986a1833dba986be6c2101 against the #941 acceptance criteria, using the completed baseline evidence below. Do not self-review or self-merge.
NEXT_PROMPT:

Review PR #942 in Scaled-Tech-Consulting/Gitea-Tools using only the sanctioned
gitea-reviewer namespace, pinned to exact head
a09c485fc0eba75b35986a1833dba986be6c2101 on branch
fix/issue-941-scope-guard-bootstrap-wiring, base master at
8c1d22a658ecb5715e3f4db6eb7ebb408830c101. Call get_pr_review_feedback first to
confirm reviews are empty. The full-suite baseline comparison is complete and
recorded in this comment: identical 28-failure sets before and after the fix,
with +24 passed accounted for by the new regression file.

WHAT_HAPPENED: Completed the full-suite baseline comparison that the PR body listed as still executing. Two attempts to baseline from the stable control checkout produced no output at all — pytest tests/ there exits without emitting a single line, in the background and in the foreground, while a single test file from the same directory runs normally (11 passed). Rather than report an unverified comparison, the baseline was instead taken in the identical worktree location by temporarily restoring workflow_scope_guard.py and gitea_mcp_server.py to their 8c1d22a content and holding the new test file aside, running the suite, then restoring all three from the committed head. This also removes the location sensitivity that makes cross-directory baselines unreliable in this repository.
WHY: The PR body promised this comparison and explicitly told the reviewer to treat the 28-vs-28 claim as verified only for four guard-critical failures until the run was posted. It is now verified for all 28.
RELATED_PRS: PR #942 (this PR, open, Closes #941). PR #926 (merged, closed #892). PR #940 (merged, delivered #930).
BLOCKERS: none blocking review. Issue #931 stays blocked until this PR is approved, merged, deployed, and the fleet restarted and commissioned.
VALIDATION: Pre-fix run in the worktree, production files reverted to 8c1d22a and the new test file held aside — 28 failed, 5501 passed, 6 skipped, 994 subtests passed in 182.50s. Post-fix run in the identical location — 28 failed, 5525 passed, 6 skipped, 994 subtests passed in 147.74s. The two FAILED lists were extracted, sorted, and compared with comm: zero entries present only post-fix, zero present only pre-fix. The failure sets are identical, so there are no regressions and no accidental fixes. The pass delta of exactly +24 is accounted for by tests/test_issue_941_scope_guard_bootstrap_wiring.py, which contributes 24 tests. After the comparison, all three files were restored from HEAD; the worktree is clean at a09c485fc0eba75b35986a1833dba986be6c2101 and the control checkout is clean on master at 8c1d22a658ecb5715e3f4db6eb7ebb408830c101. The published branch head is unchanged, so this comment does not invalidate the reviewer pin.
LAST_UPDATED_BY: author / jcwalker3 / prgs-author / session prgs-author-14609-c5ebad14

Baseline comparison

Run (identical worktree location) Result
Pre-fix — production files at 8c1d22a, new test held aside 28 failed, 5501 passed, 6 skipped, 994 subtests
Post-fix — committed head a09c485f 28 failed, 5525 passed, 6 skipped, 994 subtests
Failures present only post-fix (regressions) none
Failures present only pre-fix (accidental fixes) none

The 28 pre-existing failures span test_branch_cleanup_guard, test_commit_payloads, test_dirty_orphan_worktree_recovery, test_issue_702_review_findings_f1_f6, test_issue_781_edit_issue_tool, test_issue_784_dependency_edges, test_mcp_server, test_post_merge_moot_lease, test_pr_ownership_issue_pr_mismatch, test_preflight_workspace_repo_forwarding, test_reconciler_cleanup_integration, test_reconciler_close_workspace_guard, test_reconciler_supersession_close, and test_workspace_guard_alignment. None is introduced or altered by this change.

Four of them assert that ordinary author mutation stays blocked on the control checkout — the property this PR must not weaken. They fail identically before and after, and were additionally re-run on unmodified master in the control checkout, where they also fail (4 failed in 2.92s). Their failure is therefore pre-existing and independent of this change, not evidence that the guard was loosened. The positive proof that ordinary control-checkout mutation still fails closed is in the new regression file: commit_files and lock_issue from a clean control checkout both still raise, valid bootstrap evidence cannot be smuggled onto another task, and a dirty control checkout still blocks the bootstrap task.

Separate observation, not part of this PR

pytest tests/ produces no output whatsoever when invoked from the stable control checkout, in background and foreground, while individual test files from that same directory run normally. That is unrelated to this change — it reproduces on unmodified master — but it makes control-checkout baselines unusable and is worth a separate issue. It is plausibly connected to #927 (collection recursing into branches/), which has an open PR #928. No issue was filed for it from this session, which is scoped to #941 only.

## Canonical Issue State STATE: ready-for-review — full-suite baseline comparison now complete; the PR body's open item is closed. Head unchanged at `a09c485fc0eba75b35986a1833dba986be6c2101`. WHO_IS_NEXT: reviewer NEXT_ACTION: Review PR #942 at exact head `a09c485fc0eba75b35986a1833dba986be6c2101` against the #941 acceptance criteria, using the completed baseline evidence below. Do not self-review or self-merge. NEXT_PROMPT: ```text Review PR #942 in Scaled-Tech-Consulting/Gitea-Tools using only the sanctioned gitea-reviewer namespace, pinned to exact head a09c485fc0eba75b35986a1833dba986be6c2101 on branch fix/issue-941-scope-guard-bootstrap-wiring, base master at 8c1d22a658ecb5715e3f4db6eb7ebb408830c101. Call get_pr_review_feedback first to confirm reviews are empty. The full-suite baseline comparison is complete and recorded in this comment: identical 28-failure sets before and after the fix, with +24 passed accounted for by the new regression file. ``` WHAT_HAPPENED: Completed the full-suite baseline comparison that the PR body listed as still executing. Two attempts to baseline from the stable control checkout produced no output at all — `pytest tests/` there exits without emitting a single line, in the background and in the foreground, while a single test file from the same directory runs normally (11 passed). Rather than report an unverified comparison, the baseline was instead taken in the identical worktree location by temporarily restoring `workflow_scope_guard.py` and `gitea_mcp_server.py` to their `8c1d22a` content and holding the new test file aside, running the suite, then restoring all three from the committed head. This also removes the location sensitivity that makes cross-directory baselines unreliable in this repository. WHY: The PR body promised this comparison and explicitly told the reviewer to treat the 28-vs-28 claim as verified only for four guard-critical failures until the run was posted. It is now verified for all 28. RELATED_PRS: PR #942 (this PR, open, Closes #941). PR #926 (merged, closed #892). PR #940 (merged, delivered #930). BLOCKERS: none blocking review. Issue #931 stays blocked until this PR is approved, merged, deployed, and the fleet restarted and commissioned. VALIDATION: Pre-fix run in the worktree, production files reverted to `8c1d22a` and the new test file held aside — `28 failed, 5501 passed, 6 skipped, 994 subtests passed in 182.50s`. Post-fix run in the identical location — `28 failed, 5525 passed, 6 skipped, 994 subtests passed in 147.74s`. The two `FAILED` lists were extracted, sorted, and compared with `comm`: zero entries present only post-fix, zero present only pre-fix. The failure sets are identical, so there are no regressions and no accidental fixes. The pass delta of exactly +24 is accounted for by `tests/test_issue_941_scope_guard_bootstrap_wiring.py`, which contributes 24 tests. After the comparison, all three files were restored from `HEAD`; the worktree is clean at `a09c485fc0eba75b35986a1833dba986be6c2101` and the control checkout is clean on `master` at `8c1d22a658ecb5715e3f4db6eb7ebb408830c101`. The published branch head is unchanged, so this comment does not invalidate the reviewer pin. LAST_UPDATED_BY: author / jcwalker3 / prgs-author / session prgs-author-14609-c5ebad14 ### Baseline comparison | Run (identical worktree location) | Result | |---|---| | Pre-fix — production files at `8c1d22a`, new test held aside | 28 failed, 5501 passed, 6 skipped, 994 subtests | | Post-fix — committed head `a09c485f` | 28 failed, 5525 passed, 6 skipped, 994 subtests | | Failures present only post-fix (regressions) | **none** | | Failures present only pre-fix (accidental fixes) | **none** | The 28 pre-existing failures span `test_branch_cleanup_guard`, `test_commit_payloads`, `test_dirty_orphan_worktree_recovery`, `test_issue_702_review_findings_f1_f6`, `test_issue_781_edit_issue_tool`, `test_issue_784_dependency_edges`, `test_mcp_server`, `test_post_merge_moot_lease`, `test_pr_ownership_issue_pr_mismatch`, `test_preflight_workspace_repo_forwarding`, `test_reconciler_cleanup_integration`, `test_reconciler_close_workspace_guard`, `test_reconciler_supersession_close`, and `test_workspace_guard_alignment`. None is introduced or altered by this change. Four of them assert that ordinary author mutation stays blocked on the control checkout — the property this PR must not weaken. They fail identically before and after, and were additionally re-run on unmodified `master` in the control checkout, where they also fail (`4 failed in 2.92s`). Their failure is therefore pre-existing and independent of this change, not evidence that the guard was loosened. The positive proof that ordinary control-checkout mutation still fails closed is in the new regression file: `commit_files` and `lock_issue` from a clean control checkout both still raise, valid bootstrap evidence cannot be smuggled onto another task, and a dirty control checkout still blocks the bootstrap task. ### Separate observation, not part of this PR `pytest tests/` produces no output whatsoever when invoked from the stable control checkout, in background and foreground, while individual test files from that same directory run normally. That is unrelated to this change — it reproduces on unmodified `master` — but it makes control-checkout baselines unusable and is worth a separate issue. It is plausibly connected to #927 (collection recursing into `branches/`), which has an open PR #928. No issue was filed for it from this session, which is scoped to #941 only.
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #942
issue: #941
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 825-bf1d3c57e99f
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941
phase: claimed
candidate_head: a09c485fc0
target_branch: master
target_branch_sha: 8c1d22a658
last_activity: 2026-07-26T11:09:15Z
expires_at: 2026-07-26T11:19:15Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #942 issue: #941 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 825-bf1d3c57e99f worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941 phase: claimed candidate_head: a09c485fc0eba75b35986a1833dba986be6c2101 target_branch: master target_branch_sha: 8c1d22a658ecb5715e3f4db6eb7ebb408830c101 last_activity: 2026-07-26T11:09:15Z expires_at: 2026-07-26T11:19:15Z blocker: none
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #942
issue: #941
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 825-bf1d3c57e99f
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941
phase: claimed
candidate_head: a09c485fc0
target_branch: master
target_branch_sha: 8c1d22a658
last_activity: 2026-07-26T11:11:52Z
expires_at: 2026-07-26T11:21:52Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #942 issue: #941 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 825-bf1d3c57e99f worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941 phase: claimed candidate_head: a09c485fc0eba75b35986a1833dba986be6c2101 target_branch: master target_branch_sha: 8c1d22a658ecb5715e3f4db6eb7ebb408830c101 last_activity: 2026-07-26T11:11:52Z expires_at: 2026-07-26T11:21:52Z blocker: none
sysadmin approved these changes 2026-07-26 06:14:08 -05:00
sysadmin left a comment
Owner

Canonical PR State

STATE: approved-at-current-head
WHO_IS_NEXT: merger
NEXT_ACTION: Merge PR #942 into master at exact head a09c485fc0eba75b35986a1833dba986be6c2101 using the sanctioned gitea-merger namespace. This reviewer did not merge and did not modify any tracked file.
NEXT_PROMPT:

Merge PR #942 in Scaled-Tech-Consulting/Gitea-Tools using only the sanctioned
gitea-merger namespace with profile prgs-merger, role merger, identity sysadmin.
Pin to exact head a09c485fc0eba75b35986a1833dba986be6c2101 on branch
fix/issue-941-scope-guard-bootstrap-wiring, base master at
8c1d22a658ecb5715e3f4db6eb7ebb408830c101. Before merging, re-verify that the
head is unchanged, that the reviewer approval at that exact head is present and
not dismissed, that no undismissed REQUEST_CHANGES exists, and that no reviewer
lease is active. Do not re-review and do not amend the branch. After merging,
hand off to the reconciler for post-merge branch cleanup of
fix/issue-941-scope-guard-bootstrap-wiring.

WHAT_HAPPENED: Independent reviewer review of PR #942 at exact head a09c485fc0eba75b35986a1833dba986be6c2101. The diff is one commit against master at 8c1d22a658ecb5715e3f4db6eb7ebb408830c101, touching exactly workflow_scope_guard.py, gitea_mcp_server.py, and tests/test_issue_941_scope_guard_bootstrap_wiring.py (+408 / -2). The change wires the third enforcement path onto the canonical create_issue_bootstrap.bootstrap_permits_control_checkout decision and removes the second task-name allowlist that governed the pre-ownership lock waiver. All nine #941 acceptance criteria were checked against the code and against reviewer-run tests. No blocking finding remains, so the verdict is APPROVE.
WHY: PR #926 taught the canonical predicate to accept task_scope=author_issue_bootstrap and wired it into the #274 branches-only enforcer and the #604 anti-stomp preflight, but workflow_scope_guard kept an independent copy of the clean-root author decision keyed on a task-name allowlist that never contained bootstrap_author_issue_worktree. The waiver was therefore unreachable on the real path and the #892 deadlock stayed partially present. This PR routes that third path — and the downstream require_lock derivation — through the one shared server-derived decision, so all three guards judge identical evidence per #757.
ISSUE: #941
HEAD_SHA: a09c485fc0
REVIEW_STATUS: APPROVED
MERGE_READY: true
BLOCKERS: none
VALIDATION: Reviewer identity sysadmin, profile prgs-reviewer, role reviewer, namespace gitea-reviewer, distinct from author jcwalker3; no self-review. Parity in_parity:true, stale:false, restart_required:false, mutation_safe:true, all heads at 8c1d22a658ec. Control checkout clean under git status --porcelain --untracked-files=all. Review performed from a dedicated detached worktree branches/review-pr942-941 at the reviewed head, clean throughout; no tracked file was modified. Remote refs/heads/fix/issue-941-scope-guard-bootstrap-wiring resolves to the reviewed head and refs/heads/master to the recorded base; merge-base equals the recorded base and git rev-list --count returns 1. gitea_get_pr_review_feedback returned reviews: [] before review work, so no prior approval or change request was superseded. Author assignment asn-15133423bf614c11 and lease lease-ecf79c663f354894 are both released with a dead owner PID; repository-wide active lease count was 0 before this reviewer lease. Reviewer-run tests, all from a branches/ worktree: new regression at head 24 passed; the same file on unmodified base 8c1d22a658ec reproduces 5 failed / 19 passed with the real-path failure being ProductionGuardError: Workflow scope guard (#683) [missing_issue_worktree]; focused sweep across the 941, 892, 683, 757, create-issue-bootstrap, author-issue-bootstrap, anti-stomp, root-checkout-guard, and 618 suites 218 passed with 47 subtests passed; a reviewer-authored harness that computes the assessment through the real _create_issue_bootstrap_assessment rather than mocking it, then drives both the #274 and #683 guards, 5 passed at head with the positive case failing on base. Full-suite baseline equivalence was established by this reviewer rather than assumed: head 28 failed / 5525 passed / 6 skipped / 994 subtests, base 28 failed / 5501 passed / 6 skipped / 994 subtests, and the sorted FAILED name sets diff empty at 28 versus 28. The pass delta of exactly +24 equals the new test file, so no regression is introduced. Issue #931 received no implementation artifact: no local branch, no remote ref matching *931* on prgs, no registered worktree, no pull request in a complete 14-PR open inventory, and the issue remains open, status:ready, unassigned.
NATIVE_REVIEW_PROOF: Submitted through the native gitea-reviewer MCP namespace via gitea_submit_pr_review after gitea_load_review_workflow (source skills/llm-project-workflow/workflows/review-merge-pr.md, hash 263d0a6cb8a6, boundary clean) and gitea_mark_final_review_decision head-scoped to a09c485fc0eba75b35986a1833dba986be6c2101. Native runtime: native_mcp_transport:true, production_native_mcp_transport:true, pytest:false, mode production, transport stdio, entrypoint mcp_server, pid 825, token fingerprint 8700c667bf00f4ef. Reviewer PR lease session 825-bf1d3c57e99f, lease comment 17432, candidate head pinned to the reviewed head. No tea, no curl, no raw API, no direct database access.
LAST_UPDATED_BY: reviewer / sysadmin / prgs-reviewer / session 825-bf1d3c57e99f

Findings

No blocking findings. Severity legend: BLOCKER, MAJOR, MINOR, NIT, OBSERVATION.

Acceptance-criteria verification

AC1 — canonical decision replaces the task-name allowlist. Met. workflow_scope_guard.assess_root_source_mutation (workflow_scope_guard.py:347-359) delegates the clean-root author case to create_issue_bootstrap.bootstrap_permits_control_checkout. A sweep of production call sites shows every path is wired: assess_root_source_mutation has one caller (workflow_scope_guard.py:437), assess_production_mutation_guards one (gitea_mcp_server.py:1742), and _enforce_issue_scope_guard two (gitea_mcp_server.py:1544 and gitea_mcp_server.py:1584), both passing the assessment already computed one line above for the #274 guard. BLOCKER_MISSING_WORKTREE has exactly one raise site (workflow_scope_guard.py:366), now behind the canonical decision. No remaining production use of is_create_issue_task decides the bootstrap case.

AC2 — a correctly scoped bootstrap proceeds from a clean control checkout. Met, verified independently. A reviewer harness computed the assessment through the real _create_issue_bootstrap_assessment from a temporary git control checkout and passed it to the #274 and #683 guards exactly as verify_preflight_purity does. The bootstrap task clears both and the assessment carries task_scope=author_issue_bootstrap with allowed=true.

AC3 — ordinary control-checkout mutation stays forbidden. Met. On real computed evidence, commit_files and lock_issue from the control checkout still raise, a dirty control checkout still blocks the bootstrap task, and a drifted head with remote tip unequal to local tip still blocks it. Cross-task smuggling of valid bootstrap evidence onto commit_files is refused by the predicate's exact task_scope match.

AC4 — missing, malformed, incorrect, or mismatched evidence fails closed. Met. create_issue_bootstrap.py:253-314 returns False for a non-dict, a non-bootstrap task, any missing or contradictory disposition, non-empty reasons, a mismatched task_scope, a bootstrap_path other than clean_canonical_control_checkout, dirty files, under_branches, unverified or unequal base tips re-derived rather than trusted, and any workspace or repo-root binding that is not the canonical control checkout. Each case leaves the ordinary block in force.

AC5 — no general bypass. Met. The waiver is reachable only inside the pre-existing not under_branches and workspace == root and not dirty_src and role == "author" arm and only for the one sanctioned task. Evidence is server-derived from inspected repository state; the new parameters live on internal helpers and are never accepted as an MCP tool argument. The pre-ownership lock waiver at gitea_mcp_server.py:1729-1740 derives from the same decision, and an explicit require_author_lock=True from a caller still overrides it in the fail-closed direction. assess_issue_scope_ownership is untouched, so a session locked to a different issue still blocks as out-of-scope.

AC6 — existing create_issue behaviour unchanged. Met. The is_create_issue_task arm is evaluated first and is unmodified. For create_issue the new conjunct cannot change require_lock, because not is_create_issue is already false.

AC7 — #274, #604, #618, #683 protections not weakened. Met. The change is additive threading plus one elif arm; nothing is removed or loosened. The reconciler exemption, the dirty-root diagnostic block, and the branches-only rule retain their prior behaviour.

AC8 and AC9 — real-path regression that fails before the fix. Met. The new file drives the real enforcer rather than the authorization helper in isolation, and on unmodified master it fails with the exact production refusal, not merely a signature error.

Non-blocking items

  1. NIT — workflow_scope_guard.py:374. The friendlier EXACT_NEXT_ACTION_BOOTSTRAP guidance string is still selected via is_create_issue_task alone, so a dirty control checkout under the bootstrap task returns the generic root-diagnostic next action rather than the bootstrap-specific one. The block itself is correct; only the operator guidance text differs. No change required for this PR.
  2. OBSERVATION — tests/test_issue_941_scope_guard_bootstrap_wiring.py:148. The harness mocks _create_issue_bootstrap_assessment, so the new file does not itself exercise the real assessment production for the bootstrap task, nor the two threading lines added to verify_preflight_purity. This reviewer closed that gap with an independent harness and found the behaviour correct, so it is not actionable here.
  3. OBSERVATION — scope. Proof that the live gitea_bootstrap_author_issue_worktree tool succeeds end to end necessarily awaits merge, deployment, fleet restart, and commissioning, because running servers execute the pre-fix guard until then. The PR body states this correctly and does not overclaim.

Break-glass provenance

The one-time operator authorization recorded in issue comment 17385 was scoped to #941, one branch, one worktree, at the verified live master SHA, and is expired. It did not broaden the delivered change: the diff is the three declared files, adds no manual worktree escape hatch, no role-permission change, no transport work, and no refactoring beyond the wiring.

Reviewer evidence correction to the PR body

The PR body asked the reviewer to treat the 28-versus-28 comparison as verified only for four failures, because the baseline run was still executing at publication. That caveat is now resolved: this reviewer ran both full suites and diffed the sorted failure name sets mechanically, and they are identical.

## Canonical PR State STATE: approved-at-current-head WHO_IS_NEXT: merger NEXT_ACTION: Merge PR #942 into `master` at exact head `a09c485fc0eba75b35986a1833dba986be6c2101` using the sanctioned `gitea-merger` namespace. This reviewer did not merge and did not modify any tracked file. NEXT_PROMPT: ```text Merge PR #942 in Scaled-Tech-Consulting/Gitea-Tools using only the sanctioned gitea-merger namespace with profile prgs-merger, role merger, identity sysadmin. Pin to exact head a09c485fc0eba75b35986a1833dba986be6c2101 on branch fix/issue-941-scope-guard-bootstrap-wiring, base master at 8c1d22a658ecb5715e3f4db6eb7ebb408830c101. Before merging, re-verify that the head is unchanged, that the reviewer approval at that exact head is present and not dismissed, that no undismissed REQUEST_CHANGES exists, and that no reviewer lease is active. Do not re-review and do not amend the branch. After merging, hand off to the reconciler for post-merge branch cleanup of fix/issue-941-scope-guard-bootstrap-wiring. ``` WHAT_HAPPENED: Independent reviewer review of PR #942 at exact head `a09c485fc0eba75b35986a1833dba986be6c2101`. The diff is one commit against `master` at `8c1d22a658ecb5715e3f4db6eb7ebb408830c101`, touching exactly `workflow_scope_guard.py`, `gitea_mcp_server.py`, and `tests/test_issue_941_scope_guard_bootstrap_wiring.py` (+408 / -2). The change wires the third enforcement path onto the canonical `create_issue_bootstrap.bootstrap_permits_control_checkout` decision and removes the second task-name allowlist that governed the pre-ownership lock waiver. All nine #941 acceptance criteria were checked against the code and against reviewer-run tests. No blocking finding remains, so the verdict is APPROVE. WHY: PR #926 taught the canonical predicate to accept `task_scope=author_issue_bootstrap` and wired it into the #274 branches-only enforcer and the #604 anti-stomp preflight, but `workflow_scope_guard` kept an independent copy of the clean-root author decision keyed on a task-name allowlist that never contained `bootstrap_author_issue_worktree`. The waiver was therefore unreachable on the real path and the #892 deadlock stayed partially present. This PR routes that third path — and the downstream `require_lock` derivation — through the one shared server-derived decision, so all three guards judge identical evidence per #757. ISSUE: #941 HEAD_SHA: a09c485fc0eba75b35986a1833dba986be6c2101 REVIEW_STATUS: APPROVED MERGE_READY: true BLOCKERS: none VALIDATION: Reviewer identity `sysadmin`, profile `prgs-reviewer`, role `reviewer`, namespace `gitea-reviewer`, distinct from author `jcwalker3`; no self-review. Parity `in_parity:true`, `stale:false`, `restart_required:false`, `mutation_safe:true`, all heads at `8c1d22a658ec`. Control checkout clean under `git status --porcelain --untracked-files=all`. Review performed from a dedicated detached worktree `branches/review-pr942-941` at the reviewed head, clean throughout; no tracked file was modified. Remote `refs/heads/fix/issue-941-scope-guard-bootstrap-wiring` resolves to the reviewed head and `refs/heads/master` to the recorded base; merge-base equals the recorded base and `git rev-list --count` returns 1. `gitea_get_pr_review_feedback` returned `reviews: []` before review work, so no prior approval or change request was superseded. Author assignment `asn-15133423bf614c11` and lease `lease-ecf79c663f354894` are both `released` with a dead owner PID; repository-wide active lease count was 0 before this reviewer lease. Reviewer-run tests, all from a `branches/` worktree: new regression at head 24 passed; the same file on unmodified base `8c1d22a658ec` reproduces 5 failed / 19 passed with the real-path failure being `ProductionGuardError: Workflow scope guard (#683) [missing_issue_worktree]`; focused sweep across the 941, 892, 683, 757, create-issue-bootstrap, author-issue-bootstrap, anti-stomp, root-checkout-guard, and 618 suites 218 passed with 47 subtests passed; a reviewer-authored harness that computes the assessment through the real `_create_issue_bootstrap_assessment` rather than mocking it, then drives both the #274 and #683 guards, 5 passed at head with the positive case failing on base. Full-suite baseline equivalence was established by this reviewer rather than assumed: head 28 failed / 5525 passed / 6 skipped / 994 subtests, base 28 failed / 5501 passed / 6 skipped / 994 subtests, and the sorted `FAILED` name sets diff empty at 28 versus 28. The pass delta of exactly +24 equals the new test file, so no regression is introduced. Issue #931 received no implementation artifact: no local branch, no remote ref matching `*931*` on `prgs`, no registered worktree, no pull request in a complete 14-PR open inventory, and the issue remains open, `status:ready`, unassigned. NATIVE_REVIEW_PROOF: Submitted through the native `gitea-reviewer` MCP namespace via `gitea_submit_pr_review` after `gitea_load_review_workflow` (source `skills/llm-project-workflow/workflows/review-merge-pr.md`, hash `263d0a6cb8a6`, boundary clean) and `gitea_mark_final_review_decision` head-scoped to `a09c485fc0eba75b35986a1833dba986be6c2101`. Native runtime: `native_mcp_transport:true`, `production_native_mcp_transport:true`, `pytest:false`, mode `production`, transport `stdio`, entrypoint `mcp_server`, pid 825, token fingerprint `8700c667bf00f4ef`. Reviewer PR lease session `825-bf1d3c57e99f`, lease comment `17432`, candidate head pinned to the reviewed head. No `tea`, no `curl`, no raw API, no direct database access. LAST_UPDATED_BY: reviewer / sysadmin / prgs-reviewer / session 825-bf1d3c57e99f ## Findings No blocking findings. Severity legend: BLOCKER, MAJOR, MINOR, NIT, OBSERVATION. ### Acceptance-criteria verification **AC1 — canonical decision replaces the task-name allowlist.** Met. `workflow_scope_guard.assess_root_source_mutation` (`workflow_scope_guard.py:347-359`) delegates the clean-root author case to `create_issue_bootstrap.bootstrap_permits_control_checkout`. A sweep of production call sites shows every path is wired: `assess_root_source_mutation` has one caller (`workflow_scope_guard.py:437`), `assess_production_mutation_guards` one (`gitea_mcp_server.py:1742`), and `_enforce_issue_scope_guard` two (`gitea_mcp_server.py:1544` and `gitea_mcp_server.py:1584`), both passing the assessment already computed one line above for the #274 guard. `BLOCKER_MISSING_WORKTREE` has exactly one raise site (`workflow_scope_guard.py:366`), now behind the canonical decision. No remaining production use of `is_create_issue_task` decides the bootstrap case. **AC2 — a correctly scoped bootstrap proceeds from a clean control checkout.** Met, verified independently. A reviewer harness computed the assessment through the real `_create_issue_bootstrap_assessment` from a temporary git control checkout and passed it to the #274 and #683 guards exactly as `verify_preflight_purity` does. The bootstrap task clears both and the assessment carries `task_scope=author_issue_bootstrap` with `allowed=true`. **AC3 — ordinary control-checkout mutation stays forbidden.** Met. On real computed evidence, `commit_files` and `lock_issue` from the control checkout still raise, a dirty control checkout still blocks the bootstrap task, and a drifted head with remote tip unequal to local tip still blocks it. Cross-task smuggling of valid bootstrap evidence onto `commit_files` is refused by the predicate's exact `task_scope` match. **AC4 — missing, malformed, incorrect, or mismatched evidence fails closed.** Met. `create_issue_bootstrap.py:253-314` returns `False` for a non-dict, a non-bootstrap task, any missing or contradictory disposition, non-empty `reasons`, a mismatched `task_scope`, a `bootstrap_path` other than `clean_canonical_control_checkout`, dirty files, `under_branches`, unverified or unequal base tips re-derived rather than trusted, and any workspace or repo-root binding that is not the canonical control checkout. Each case leaves the ordinary block in force. **AC5 — no general bypass.** Met. The waiver is reachable only inside the pre-existing `not under_branches and workspace == root and not dirty_src and role == "author"` arm and only for the one sanctioned task. Evidence is server-derived from inspected repository state; the new parameters live on internal helpers and are never accepted as an MCP tool argument. The pre-ownership lock waiver at `gitea_mcp_server.py:1729-1740` derives from the same decision, and an explicit `require_author_lock=True` from a caller still overrides it in the fail-closed direction. `assess_issue_scope_ownership` is untouched, so a session locked to a different issue still blocks as out-of-scope. **AC6 — existing create_issue behaviour unchanged.** Met. The `is_create_issue_task` arm is evaluated first and is unmodified. For `create_issue` the new conjunct cannot change `require_lock`, because `not is_create_issue` is already false. **AC7 — #274, #604, #618, #683 protections not weakened.** Met. The change is additive threading plus one `elif` arm; nothing is removed or loosened. The reconciler exemption, the dirty-root diagnostic block, and the branches-only rule retain their prior behaviour. **AC8 and AC9 — real-path regression that fails before the fix.** Met. The new file drives the real enforcer rather than the authorization helper in isolation, and on unmodified master it fails with the exact production refusal, not merely a signature error. ### Non-blocking items 1. NIT — `workflow_scope_guard.py:374`. The friendlier `EXACT_NEXT_ACTION_BOOTSTRAP` guidance string is still selected via `is_create_issue_task` alone, so a dirty control checkout under the bootstrap task returns the generic root-diagnostic next action rather than the bootstrap-specific one. The block itself is correct; only the operator guidance text differs. No change required for this PR. 2. OBSERVATION — `tests/test_issue_941_scope_guard_bootstrap_wiring.py:148`. The harness mocks `_create_issue_bootstrap_assessment`, so the new file does not itself exercise the real assessment production for the bootstrap task, nor the two threading lines added to `verify_preflight_purity`. This reviewer closed that gap with an independent harness and found the behaviour correct, so it is not actionable here. 3. OBSERVATION — scope. Proof that the live `gitea_bootstrap_author_issue_worktree` tool succeeds end to end necessarily awaits merge, deployment, fleet restart, and commissioning, because running servers execute the pre-fix guard until then. The PR body states this correctly and does not overclaim. ### Break-glass provenance The one-time operator authorization recorded in issue comment `17385` was scoped to #941, one branch, one worktree, at the verified live master SHA, and is expired. It did not broaden the delivered change: the diff is the three declared files, adds no manual worktree escape hatch, no role-permission change, no transport work, and no refactoring beyond the wiring. ### Reviewer evidence correction to the PR body The PR body asked the reviewer to treat the 28-versus-28 comparison as verified only for four failures, because the baseline run was still executing at publication. That caveat is now resolved: this reviewer ran both full suites and diffed the sorted failure name sets mechanically, and they are identical.
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #942
issue: #941
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 825-bf1d3c57e99f
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941
phase: released
candidate_head: a09c485fc0
target_branch: master
target_branch_sha: 8c1d22a658
last_activity: 2026-07-26T11:14:21Z
expires_at: 2026-07-26T11:24:21Z
blocker: manual-release

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #942 issue: #941 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 825-bf1d3c57e99f worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941 phase: released candidate_head: a09c485fc0eba75b35986a1833dba986be6c2101 target_branch: master target_branch_sha: 8c1d22a658ecb5715e3f4db6eb7ebb408830c101 last_activity: 2026-07-26T11:14:21Z expires_at: 2026-07-26T11:24:21Z blocker: manual-release
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #942
issue: #941
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 74405-788d0a8e91a5
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941
phase: claimed
candidate_head: a09c485fc0
target_branch: master
target_branch_sha: 8c1d22a658
last_activity: 2026-07-26T11:18:55Z
expires_at: 2026-07-26T11:28:55Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #942 issue: #941 reviewer_identity: sysadmin profile: prgs-merger session_id: 74405-788d0a8e91a5 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr942-941 phase: claimed candidate_head: a09c485fc0eba75b35986a1833dba986be6c2101 target_branch: master target_branch_sha: 8c1d22a658ecb5715e3f4db6eb7ebb408830c101 last_activity: 2026-07-26T11:18:55Z expires_at: 2026-07-26T11:28:55Z blocker: none
sysadmin merged commit aab54d4825 into master 2026-07-26 06:20:01 -05:00
Owner

Stale #332 review-decision lock cleanup (#594)

Status: APPLIED

Manual deletion of session-state files is not the workflow.
This path only clears a lock when the referenced PR is merged/closed.

## Stale #332 review-decision lock cleanup (#594) Status: **APPLIED** - actor: `sysadmin` - profile: `prgs-merger` - timestamp: `2026-07-26T11:20:03.646732+00:00` - last terminal: `approve` on PR #942 - PR state: `closed` (merged=True) - merge_commit_sha: `aab54d4825270f5a5c6f9c1abc1ab09eb4f3e218` - prior live_mutations_count: `1` - prior profile_identity: `prgs-reviewer` Manual deletion of session-state files is **not** the workflow. This path only clears a lock when the referenced PR is merged/closed.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#942