fix(workflow): cross-profile decision-lock cleanup and irrecoverable provenance (Closes #709) #710
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
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
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
Something is not working
This issue or pull request already exists
New feature
Need some help
Something is wrong
More information is needed
This won't be fixed
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
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).
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 60987-495bf5ff0d93
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710-independent-2b359e0-20260715
phase: claimed
candidate_head:
2b359e0c26target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-15T16:22:10Z
expires_at: 2026-07-15T18:22:10Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 60987-495bf5ff0d93
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710-independent-2b359e0-20260715
phase: validating
candidate_head:
2b359e0c26target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-15T16:23:21Z
expires_at: 2026-07-15T18:23:21Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 60987-495bf5ff0d93
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710-independent-2b359e0-20260715
phase: full-suite
candidate_head:
2b359e0c26target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-15T16:23:56Z
expires_at: 2026-07-15T18:23:56Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 60987-495bf5ff0d93
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710-independent-2b359e0-20260715
phase: verdict-ready
candidate_head:
2b359e0c26target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-15T16:26:37Z
expires_at: 2026-07-15T18:26:37Z
blocker: none
Canonical PR State
STATE: changes_requested
WHO_IS_NEXT: author
NEXT_ACTION: Make HMAC key-version verification fail closed; strictly parse and fully scope canonical incident evidence with stable unambiguous actor identity; abort residual lock clearing if archival fails. Add adversarial regressions, rerun the focused and full suites, and request a fresh independent re-review.
NEXT_PROMPT:
WHAT_HAPPENED: Independent prgs-reviewer re-reviewed the complete PR and the one-commit remediation delta from
9cb12eeto2b359e0in a fresh detached worktree. Review-435 F3-residual is fixed; the dedicated mint capability and durable env HMAC replace the broad/ephemeral paths. Adversarial checks found remaining fail-open key-version and evidence-parser behavior plus a destructive archive-failure path.WHY: AC1/AC3/AC5/AC6 and the requested review require unknown/missing/mismatched key versions, ambiguous evidence, replay across lock/action, and archive failure to fail closed.
ISSUE: #709
HEAD_SHA:
2b359e0c26REVIEW_STATUS: request_changes prepared for native MCP submission
MERGE_READY: no
BLOCKERS: F6 key-version fail-open; F7 non-strict/incompletely-bound incident evidence and ambiguous actor acceptance; F8 archive failure still clears terminal evidence.
VALIDATION: focused 70 passed in both module orders; full tests/ 2748 passed, 6 skipped, 1 warning; adversarial probes reproduced all three blockers.
LAST_UPDATED_BY: reviewer sysadmin / prgs-reviewer
Blocking findings
F6 — Missing/unknown/mismatched HMAC key versions do not fail closed (AC3/AC5; review-435 F4 incomplete).
verify_authorization_artifactusesauth.get("key_version") or auth_key_version()and signs using the stored value, but never requires the field or compares it to the active configured version.key_versionfrom a valid artifact still returnsvalid=True; a v1 artifact still validates after the runtime is configured for v2 when the durable key is unchanged. Unknown/supported-version policy is absent.F7 — Canonical incident evidence is permissively parsed and not bound to the full recovery scope (AC5/AC6; review-435 F5 incomplete).
_parse_incident_fieldreturns the first matching line; field order and duplicate/conflicting fields are not rejected.repofield. A payload with conflictinguser.login/user.usernameidentities also validates because only the preferred string is considered.F8 — Archive failure is swallowed before destructive residual lock clear (AC1/AC2/AC3).
_clear_decision_lock_for_profilecatches any archivesave_stateexception and then callsclear_state, reportingcleared=True.clear_called_after_archive_failure=True.Prior-blocker disposition
expected_head_shais supplied.[THREAD STATE LEDGER]
What is true now:
2b359e0c26What changed:
What is blocked:
Who/what acts next:
Do not do: do not merge PR #710 or PR #703; do not dismiss prior formal reviews; do not manually alter decision-lock artifacts.
Canonical Issue State
STATE: awaiting_review
WHO_IS_NEXT: reviewer
NEXT_ACTION: Independently re-review PR 710 at head
573e721437and verify the review-438 F6/F7/F8 remediations with fresh adversarial probes.NEXT_PROMPT:
WHAT_HAPPENED: Author remediated review-438 blockers F6 (key-version fail-open), F7 (permissive, under-bound incident evidence) and F8 (archive failure swallowed before destructive clear) in one commit atop the reviewed head
2b359e0, added adversarial regressions for every requested case, and re-ran the focused suite in three module orders plus the full suite.WHY: AC1/AC3/AC5/AC6 require unknown/missing/mismatched key versions, ambiguous or replayable evidence, and archive failure to fail closed rather than fail open.
RELATED_PRS: none
BLOCKERS: none known to the author; F6, F7 and F8 are addressed and covered by regressions. Independent verification is required.
VALIDATION: focused 150 passed in three module orders; full tests/ 2809 passed, 6 skipped, 1 warning (pre-existing StarletteDeprecationWarning at tests/test_webui_audit.py:8), 161 subtests passed.
LAST_UPDATED_BY: author jcwalker3 / prgs-author
Author remediation of review 438 (F6 / F7 / F8)
Remediated at previous head
2b359e0c260a524863378291f1c2d24516f7502a→ new head573e721437ee31f5689472534715e907de2e4085. Single commit on top of the reviewed head, normal (non-force) fast-forward push;2b359e0remains an ancestor. Reviews 434, 435, 436 and 438 were not dismissed, altered, or rewritten. Requesting a fresh independent re-review — I have not self-reviewed or merged, and no other pull request was touched.Files changed:
irrecoverable_provenance.py,gitea_mcp_server.py,tests/test_issue_709_decision_lock_cross_profile.py.F6 — key-version fail-open → fail-closed
verify_authorization_artifactpreviously didauth.get("key_version") or auth_key_version()and signed with the stored value, so a versionless artifact silently borrowed the active version and a superseded version still verified under an unchanged key.extract_artifact_key_versionrequires exactly one nonempty, well-formed key-version field. Missing, empty, malformed, non-string, and duplicated fields fail closed — including duplicates with identical values, and aliases nested undernative_provenance/scope.assess_artifact_key_versionrequires exact equality with the configured active version; unknown and mismatched versions fail closed. No legacy fallback accepts versionless artifacts.GITEA_IRRECOVERABLE_AUTH_HMAC_KEY_VERSIONexplicitly (charset-validated). An implicit default made rotation ambiguous. The durable-key requirement from review 435 F4 is unchanged.F7 — strictly canonical incident evidence
Marker matching was substring-based,
_parse_incident_fieldreturned the first match, and the digest covered only marker/remote/org/repo/PR/head/issue — so reordered fields, a second conflictingrepo, and conflictinguser.login/user.usernameall validated.content_digestnow binds the full security scope: repository identity (remote/org/repo), PR number, decision-lock identity, destroyed subject, recovery action, recorded and expected head SHA, incident issue, evidence author identity, minting actor identity, key version, nonce, and issued_at.build_canonical_incident_bodyis the single source of the accepted format and cannot produce ambiguous evidence: it rejects empty/multiline fields, unsupported recovery actions, and narrative text that mimics canonical evidence, and re-parses its own output before returning.F8 — archival is now a prerequisite for clearing
_clear_decision_lock_for_profilecaught every archive exception withpassand then cleared unconditionally, reportingcleared=True.clear_stateis never reached unless the write succeeds and a durable read-back returns a record matching the PR and head.cleared=false,archive_ok=false,archive_failed_step,terminal_lock_retained=true,recovery_required=true,retry_safe=true), retain the terminal lock, and record durablepost_merge_decision_recoveryevidence naming the failed step and target profile.session_profile_lockfrom the copied lock, sosave_statekeyed the archive under the source reviewer profile instead of the archive identity. The archive was landing in the wrong key and could never be read back. Nothing read it before, so it went unnoticed. The payload now pins the archive identity and recordsarchived_from_profile_identity.Test evidence
Adversarial regressions added for every requested case: missing / empty / unknown / malformed / non-string / duplicate-identical / duplicate-conflicting / nested duplicate key versions; rotation; wrong key after restart; same-key verify after restart; key-version-inside-MAC; production fail-closed on absent key and absent version; no key material in errors; reordered fields; duplicate identical and duplicate conflicting fields; fields restated in narrative; second marker; marker not first; unknown/missing/empty fields; conflicting actor ids and logins; display-name-only actor; author-id substitution; edited comment; self-authored by stable id; mint-actor substitution; decision-lock and recovery-action substitution; cross-PR / cross-repository / cross-org / cross-remote / cross-head replay; recorded-head substitution; digest-preserving substitution; nonce binding; builder ambiguity refusals; archive exception / timeout / false / empty / unreadable / partial read-back each with proof the lock survives; successful archive with exactly one permitted clear; and retry after archive failure.
Full suite: 2809 passed, 6 skipped, 1 warning, 161 subtests passed (was 2748 at the reviewed head; +61 tests, all additions). The single warning is the pre-existing
StarletteDeprecationWarningattests/test_webui_audit.py:8, untouched by this PR.No test assertions were weakened. The diff removes no assertion lines and adds no skip/xfail markers; the review-435 F5 reproductions still assert the same rejections and were only re-pointed at the new canonical schema, which is strictly stricter than the old one. Review-434 F1/F2/F3 and review-435 F3-residual/F4/F5 behavior is preserved. The remediation delta touches only the three files above — no unrelated changes, and no production special-casing of any specific pull request or incident number.
Next role
Independent reviewer.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: none
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 54162-0b6b38d99789
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-710-rereview-573e721
phase: claimed
candidate_head: none
target_branch: master
target_branch_sha: none
last_activity: 2026-07-15T18:31:14Z
expires_at: 2026-07-15T20:31:14Z
blocker: none
Canonical PR State
STATE: approved
WHO_IS_NEXT: merger
NEXT_ACTION: Merge PR after confirming approval_at_current_head
NEXT_PROMPT:
WHAT_HAPPENED: Independent prgs-reviewer re-reviewed the PR at remediation head
573e721. Confirmed F6 (key version exact match fail-closed), F7 (strict canonical parser binding identity/lock/action), and F8 (archive exception halts clear) are fully addressed. Adversarial probes verified. Test suite passed.WHY: AC1-AC6 satisfied. Security blockers are resolved.
ISSUE: #709
HEAD_SHA:
573e721437REVIEW_STATUS: approved
MERGE_READY: yes
BLOCKERS: None.
VALIDATION: 150-focused and 2809 full suite tests passed. Independent adversarial probes passed.
NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server;
LAST_UPDATED_BY: reviewer sysadmin / prgs-reviewer
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 84826-f47061859301
worktree: branches/merge-pr-710-573e721
phase: claimed
candidate_head:
573e721437target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-16T02:23:50Z
expires_at: 2026-07-16T04:23:50Z
blocker: none
adopted_at: 2026-07-16T02:24:12Z
adopted_by_identity: sysadmin
adopted_by_profile: prgs-merger
adopted_from_session_id: 84826-f47061859301
adopted_from_profile: prgs-reviewer
adopted_from_reviewer_identity: sysadmin
adopted_from_comment_id: 11701
adoption_reason: merger-handoff-approved-head
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #710
issue: #709
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 84826-55b9ed29de91
worktree: branches/merge-pr-710-573e721
phase: adopted
candidate_head:
573e721437target_branch: master
target_branch_sha:
1eafb757a9last_activity: 2026-07-16T02:24:12Z
expires_at: 2026-07-16T04:24:12Z
blocker: none