fix(workflow): cross-profile decision-lock cleanup and irrecoverable provenance (Closes #709) #710
Open
jcwalker3
wants to merge 3 commits from
fix/issue-709-decision-lock-cross-profile into master
pull from: fix/issue-709-decision-lock-cross-profile
merge into: :master
:master
:fix/issue-709-decision-lock-cross-profile
:fix/issue-695-native-transport-quarantine
:fix/issue-698-report-validator-schema
:fix/issue-702-stale-binding-lease-recovery
:fix/issue-699-structured-auth-mcp-errors
:fix/issue-695-native-quarantine-v2
:fix/issue-693-review-decision-lock-recovery
:fix/issue-691-obsolete-reviewer-lease-cleanup
:feat/issue-687-reconciler-branch-delete
:fix/issue-683-workflow-guard-hardening
:chore/issue-681-preserve-review-session-wip
:feat/issue-604-anti-stomp-preflight
:feat/issue-606-sentry-observability
:fix/issue-671-block-stable-branch-push
:fix/issue-675-residual-preflight-remediation
:fix/issue-673-remediate-regressions-part2
:fix/issue-673-remediate-regressions
:feat/issue-603-lifecycle-labels
:fix/issue-627-set-issue-labels-pagination
:feat/issue-601-first-class-leases
:feat/issue-612-incident-bridge
:feat/issue-600-controller-allocator-api
:fix/issue-620-head-scoped-review-locks
:feat/issue-613-allocator-db-substrate
:docs/mcp-stable-control-runtime-policy
:feat/issue-609-prepared-review-verdict-resume
:feat/issue-610-live-remote-parity
:feat/issue-503-reviewer-active-worktree
:feat/issue-470-preflight-contract
:feat/issue-440-lock-recovery
:feat/issue-440-branch-recovery
:feat/issue-458-queue-fail-closed-copy
:feat/issue-400-early-duplicate-work-gate
:feat/issue-308-reconcile-inventory-pagination
:feat/issue-308-reconcile-pagination-proof
:docs/issue-261-agent-temp-artifact-cleanup
:feat/issue-262-map-commit-files
:fix/infra-stop-conflict-marker-false-positive
:feat/issue-139-role-aware-task-routing
:feat/issue-188-continuation-selection-wall
:feat/issue-189-continuation-mode-proofs
:feat/issue-232-refresh-wiki-proof-heads
:feat/issue-210-block-workspace-edits
:feat/issue-204-exact-issue-lock
:docs/issue-80-label-taxonomy
:docs/issue-79-safety-boundary-updates
Labels
Clear labels
allocator
anti-stomp
architecture
bug
chore
codex
concurrency
contamination
control-plane
dashboard
database
design
documentation
enhancement
gitea
glitchtip
important
incident
incident-bridge
integration
jenkins
labels
leases
mcp
mcp-health
mcp-menu
multi-project
mutating
nice-to-have
observability
portability
preflight
protected-branch
queue
read-only
reconnect
recovery
refactor
release
reliability
resumable-review
reviewer
roadmap
safety
security
self-hosted
sentry
stale-runtime
status:blocked
status:in-progress
status:pr-open
status:ready
terminal-lock
testing
tracker
type:bug
type:feature
type:feature
type:guardrail
visibility
workflow
workflow-hardening
workflow-hardening
Controller-owned work allocator
Prevent concurrent LLM session stomping
Architecture / structural design
OpenAI Codex client / workflow session surface
Concurrent session safety
Workflow or session contamination incident
MCP control-plane coordination and allocation authority
MCP operational dashboard/queue view
Internal coordination storage (SQLite/Postgres)
Design / investigation, no implementation
Docs / runbooks
New feature or improvement
Gitea MCP workflow
GlitchTip integration
Operational or process incident requiring durable audit trail
Sentry-to-Gitea incident bridging
Integration testing
Jenkins integration
Label taxonomy management
Lease adopt/release/expire lifecycle
MCP server / tooling
MCP namespace and runtime health
MCP menu surface
Work spanning multiple monitoring projects or Gitea repos
Mutating action; requires gating
Observability, metrics, traces, error reporting
Cross-platform / portability
Shared preflight gates before mutation
Protected branch / stable-branch policy concern
Work queue visibility and allocation
Read-only, no mutation
MCP client reconnect/reload recovery path
Recovery paths for stale/foreign leases
Code refactor / restructure
Release / versioning
Reliability / failure handling
Persist and resume prepared review verdicts across sessions
Reviewer workflow tooling
Roadmap / umbrella issue
Safety rails and fail-closed mutation guards
Security / trust boundary
Self-hosted infrastructure integration
Sentry error monitoring integration
Stale backend daemon / runtime-vs-master parity failures
Issue is blocked
Issue is being worked on
Issue has an open pull request
Issue is ready for work
Terminal review lock (#332) path
Tests / test coverage
Issue tracker hygiene / meta
Bug or defect
Feature or enhancement
Feature or enhancement
Safety gate or guardrail
Workflow state visibility for LLMs/operators
Cross-tool workflow
LLM workflow coordination hardening
LLM workflow coordination hardening
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Scaled-Tech-Consulting/Gitea-Tools#710
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Closes #709. Implements the workflow repair identified by Incident #700 comment 11489 / PR #703 comment 11495.
What this PR repairs (future behavior)
init_review_decision_lockrefuses to replace unresolved terminal evidence with an empty init even whenforce=True.post_merge_decision_recovery(applied=false).gitea_record_irrecoverable_decision_lock_provenancerecords absence of proof only (applied=false,historical_cleanup_proven=false). Never fabricates historical cleanup. Confirmation-gated + operator_authorized.Explicit non-claims
applied=true/last_terminal_pr=696/pr_merged=truefor past cleanup.Files
stale_review_decision_lock.pymcp_session_state.pygitea_mcp_server.pytask_capability_map.pytests/test_issue_709_decision_lock_cross_profile.pyValidation
Links
Next role
Independent REVIEWER — do not self-review or merge. Do not merge PR #703 from this workstream.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 40918-d0d14c123645
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710
phase: claimed
candidate_head:
ec5cf67771target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-14T05:06:25Z
expires_at: 2026-07-14T07:06:25Z
blocker: none
Canonical PR State
STATE: changes_requested
WHO_IS_NEXT: author
NEXT_ACTION: Bind irrecoverable-provenance authorization to a non-forgeable artifact, implement and fail-close the merger acceptance path, and scope cross-profile cleanup to repository plus head; add regression tests and request a fresh independent review.
NEXT_PROMPT:
WHAT_HAPPENED: Independent reviewer confirmed scope (5 files, single commit, generic), ran focused (32 passed) and full (2710 passed, 6 skipped, 1 warning, 161 subtests) suites, and found three blocking defects in the #709 recovery/cleanup design.
WHY: AC5 requires recovery authorization that cannot be self-asserted and a precise, implemented merger acceptance condition; AC1/AC6 require cross-profile cleanup scoped to repository plus head. The implementation does not meet these.
ISSUE: #709
HEAD_SHA:
ec5cf67771REVIEW_STATUS: request_changes posted to gitea
MERGE_READY: no
BLOCKERS: F1 self-assertable operator_authorized; F2 no implemented merger acceptance path; F3 cross-repo isolation gap in post-merge cleanup.
VALIDATION: focused 32 passed; full 2710 passed, 6 skipped, 1 warning (pre-existing StarletteDeprecationWarning in tests/test_webui_audit.py, not touched by this PR), 161 subtests passed.
LAST_UPDATED_BY: reviewer sysadmin / prgs-reviewer
Findings (blocking)
F1 — Recovery authorization is self-assertable (AC5, adversarial check C1).
stale_review_decision_lock.py::build_irrecoverable_provenance_recordandgitea_mcp_server.py::gitea_record_irrecoverable_decision_lock_provenance.merger_may_accept = bool(operator_authorized)whereoperator_authorizedis a plain caller-supplied boolean. The only other gates are aconfirmationstring that is fully derivable from the PR number (IRRECOVERABLE DECISION PROVENANCE PR <n>) and agitea.readpermission check.expected_head_shaandincident_refare optional and never validated against the live PR head or canonical incident evidence.operator_authorized=True,head_sha=None,incident_ref=Nonethe record returnsmerger_may_accept=True. An arbitrary reconciler-capable (or any read+comment) caller can self-assert authorization. This is exactly the PR #696 / review-427 threat model.expected_head_shaand reject when it does not equal the live head.F2 — Merger acceptance condition is described but not implemented (AC5, adversarial check C6).
merger_may_accept, theirrecoverable_decision_provenancerecord, orKIND_POST_MERGE_DECISION_RECOVERY. They are write-only forensic artifacts;acceptance_ruleis only a descriptive string.F3 — Cross-profile cleanup does not verify repository or head in its fallback (AC1, AC6, adversarial check C2).
<kind>-<profile>(remote/org/repo intentionally excluded from the filename, validated on load).mcp_session_state.load_state_for_profile(skip_identity_match=True)computesfiltered(the remote/org/repo mismatch reasons) but never uses it; only expiry gates the return, so repository/remote/org are not enforced on cross-profile load.gitea_mcp_server._clear_decision_lock_for_profileclears via a fallback branch whenlast_terminal_mutation.pr_number == pr_number, ignoring head SHA and repository. A same-profile decision lock recorded for a different repository's PR with the same number can be archived and cleared during a merge, destroying unrelated terminal evidence — the exact harm #709 targets.filteredreasons) and require head-SHA match in the fallback clear branch.Non-blocking observations
init_review_decision_lockviaassess_init_overwrite;force=Truecannot destroy terminal evidence.[THREAD STATE LEDGER]
What is true now:
ec5cf67771What changed:
What is blocked:
Who/what acts next:
Do not do: do not merge PR #710 or PR #703; do not self-review; do not treat the irrecoverable record as proof of historical cleanup.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 40918-d0d14c123645
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710
phase: released
candidate_head:
ec5cf67771target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-14T05:11:02Z
expires_at: 2026-07-14T07:11:02Z
blocker: manual-release
Canonical Issue State
STATE:
author-remediation-complete; ready-for-fresh-review
WHO_IS_NEXT:
reviewer
NEXT_ACTION:
Independent prgs-reviewer formal review of PR #710 at head 9cb12ee0f442a89535b6f81712367f1112a3e59e; re-check F1/F2/F3; do not merge PR #703; do not treat review 434 as resolved.
NEXT_PROMPT:
WHAT_HAPPENED:
Author remediations for review 434 pushed as new commit
9cb12ee(does not rewriteec5cf677).F1 mapping (self-assertable auth → server artifact)
irrecoverable_provenance.py;gitea_issue_irrecoverable_provenance_authorization; record tool rejectsoperator_authorized; requires HMAC auth + live head + incident_issue/comment_id; capabilitygitea.decision_lock.irrecoverable_recovery(or reconciler equivalence); confirmation is intent only.F2 mapping (merger consumer)
gitea_consume_irrecoverable_decision_lock_provenance;assess_merger_consumption; merge Gate 8b report+consume; durable idempotentmark_consumed.F3 mapping (exact-scope)
load_state_for_profileenforces remote/org/repo (and missing identity); path traversal/symlink refused;_clear_decision_lock_for_profilerequires head+repo scope — no PR-number-only fallback.Files: irrecoverable_provenance.py (new); gitea_mcp_server.py; mcp_session_state.py; stale_review_decision_lock.py; task_capability_map.py; tests/test_issue_709_decision_lock_cross_profile.py.
WHY:
Review 434 blocked AC5 authorization binding, AC5 merger acceptance, and AC1/AC6 repository+head scoping.
RELATED_PRS:
PR #710 (this PR, head
9cb12ee); PR #703 remains open unmerged untouched.BLOCKERS:
none for fresh review; review 434 not claimed resolved; do not merge #703 from this stream.
VALIDATION:
pytest tests/test_issue_709_decision_lock_cross_profile.py: 46 passed; related native/decision-lock suite: 151 passed; full tests/: 2738 passed, 6 skipped, 1 warning (pre-existing StarletteDeprecationWarning in tests/test_webui_audit.py), 161 subtests; head 9cb12ee0f442a89535b6f81712367f1112a3e59e; root checkout preserved.
LAST_UPDATED_BY:
jcwalker3 / prgs-author
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 19475-9d7deb5c696b
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710-rereview-9cb12ee
phase: claimed
candidate_head:
9cb12ee0f4target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-14T06:02:02Z
expires_at: 2026-07-14T08:02:02Z
blocker: none
Canonical PR State
STATE: changes_requested
WHO_IS_NEXT: author
NEXT_ACTION: Provision a durable non-ephemeral HMAC key so recovery authorizations survive restart and verify in the merger process; make incident evidence authoritative (author identity + canonical content/digest) and restrict minting to the dedicated capability; close the same-repo legacy no-head clear in the primary approve-match branch. Add regression tests and request a fresh independent review.
NEXT_PROMPT:
WHAT_HAPPENED: Independent reviewer re-review at head
9cb12ee. Confirmed scope (6 files incl new irrecoverable_provenance.py, single remediation commit atopec5cf677which remains an ancestor/non-rewritten, generic, no #696/#703/#700 special-casing). Ran focused suite (60 passed) and full suite (2738 passed, 6 skipped, 1 warning, 161 subtests) independently. Prior review-434 F1/F2/F3 are largely remediated, but the new HMAC design and the incident-evidence/mint-authority surface introduce fresh blockers.WHY: AC3/AC5 require recovery authorization that is durable across restart and consumable by the merger, incident evidence that is authoritative and scope-bound, and mint authority limited to the dedicated capability; AC1/AC6 require no PR-number-only clear even for legacy locks.
ISSUE: #709
HEAD_SHA:
9cb12ee0f4REVIEW_STATUS: request_changes posted to gitea
MERGE_READY: no
BLOCKERS: F4 ephemeral per-process HMAC key breaks cross-process/restart consumption; F5 non-authoritative incident evidence + reconciler-equivalence self-mint; F3-residual same-repo legacy no-head approve clear.
VALIDATION: focused 60 passed; full 2738 passed, 6 skipped, 1 warning (pre-existing StarletteDeprecationWarning in tests/test_webui_audit.py:8, not touched by this PR), 161 subtests passed.
LAST_UPDATED_BY: reviewer sysadmin / prgs-reviewer
Review-434 disposition
New / residual blockers
F4 — HMAC signing key is ephemeral per-process; recovery authorization cannot survive restart or cross-process merger consumption (AC3, AC5; HMAC review key-origin/persistence).
F5 — Incident evidence not authoritative + reconciler mint-equivalence permits self-minted recovery authority (AC5; incident-evidence & mint-authority review).
F3-residual — primary approve-match still allows a PR-number-only clear of same-repo legacy locks with no recorded head (AC1, AC6).
if locked and not heads_equal(locked, expected): return Falsethenreturn True— so when the terminal mutation has no recorded head, it returns True on pr_number + action=="approve" alone, and the primary branch archives+clears without a head check. This is inconsistent with the secondary fallback (4640-4659), which refuses the same legacy-missing-head case as recovery_required. Bounded to the same remote/org/repo (cross-repo is blocked by the scoped load), but is exactly the legacy-incomplete-lock destruction #709 targets.Non-blocking observations
[THREAD STATE LEDGER]
What is true now:
9cb12ee0f4ec5cf677(not manually altered)What changed:
What is blocked:
Who/what acts next:
Do not do: do not merge PR #710 or PR #703; do not self-review; do not treat the irrecoverable record as proof of historical cleanup; do not manually dismiss review 434.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 19475-9d7deb5c696b
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710-rereview-9cb12ee
phase: released
candidate_head:
9cb12ee0f4target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-14T06:05:06Z
expires_at: 2026-07-14T08:05:06Z
blocker: manual-release
Canonical Issue State
STATE: author_remediation_pushed_awaiting_independent_rereview
WHO_IS_NEXT: reviewer
NEXT_ACTION: Fresh independent re-review of PR #710 at head
2b359e0only; do not merge #710 or #703NEXT_PROMPT:
WHAT_HAPPENED: Author remediated review 435 REQUEST_CHANGES and pushed commit
2b359e0on fix/issue-709-decision-lock-cross-profile. Isolated worktree branches/author-remediate-pr710-review435. Root master remains1eafb75untouched. F4: durable GITEA_IRRECOVERABLE_AUTH_HMAC_KEY required in production (fail closed; no ephemeral token_bytes); key_version bound into HMAC; cross-process mint/verify regression tests. F5: dedicated gitea.decision_lock.irrecoverable_recovery only (reconciler equivalence removed); incident evidence requires author identity, canonical marker/fields, content_digest, and rejects self-authored evidence. F3 residual: lock_targets_merged_pr_approval requires recorded-head match when expected_head_sha provided.WHY: Review 435 blocked merge on production-dead HMAC, self-mintable recovery authority via reconciler equivalence + weak incident evidence, and primary clear of legacy no-head approve locks.
RELATED_PRS: #710 (this PR, head
2b359e0open unmerged); #703 (open unmerged, no mutation this session); #709 (tracking issue)BLOCKERS: Independent REVIEWER re-review not yet submitted at 2b359e0; review 435 still the last formal verdict until superseded by new-head review.
VALIDATION: Focused tests 70 passed (test_issue_709_decision_lock_cross_profile.py + test_stale_review_decision_lock_cleanup.py). Full tests/ 2748 passed, 6 skipped, 1 pre-existing StarletteDeprecationWarning at tests/test_webui_audit.py:8. PR #703 state open unmerged (updated_at unchanged from prior review session).
LAST_UPDATED_BY: jcwalker3 (prgs-author)
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: none
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 28719-5fa3dc2b48ad
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/reviewer-remediate-pr710-review435
phase: claimed
candidate_head:
2b359e0c26target_branch: master
target_branch_sha: none
last_activity: 2026-07-14T12:03:48Z
expires_at: 2026-07-14T14:03:48Z
blocker: none
Review 435 blockers (F3, F4, F5) have been remediated successfully. Focused regression tests (70/70) and the full test suite (2748 tests) passed. Code changes match the stated remediation plan (HMAC fail-closed, exact-head lock clearing, correct incident evidence bounds).
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.