- Bind review decision state to in-process session (pid + profile lock); drop /tmp file path.
- Fail closed on review_pr.py CLI; route live reviews through gated MCP tools only.
- Require operator_authorized on review correction; allow re-mark after correction.
- Validate remote/org/repo on mark and submit; wire review mutation proof into build_final_report.
- Add security regression tests for spoofed locks, correction flow, and CLI bypass.
Refs #211
Reviewer agents could post probe APPROVE/REQUEST_CHANGES reviews while
testing lock paths, polluting PR audit trails. Add a review decision lock
seeded by gitea_resolve_task_capability(review_pr) that requires
gitea_mark_final_review_decision and final_review_decision_ready=True
before gitea_submit_pr_review performs a live mutation.
Add gitea_dry_run_pr_review for read-only submission validation,
gitea_authorize_review_correction for operator-approved fixes, and
assess_review_mutation_final_report for final-report proof. One live
review mutation per run unless correction is explicitly authorized.
Add server-side duplicate detection in gitea_create_issue that re-queries
open and recently closed issues at mutation time, blocks normalized title
matches unless the operator explicitly approves a split, and validates LLM
duplicate-search summaries via review_proofs.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
PR closure had no first-class capability: agents could close PRs through
gitea_edit_pr(state=closed) with no close-specific capability proof, and
gitea_resolve_task_capability(close_pr) failed as unknown, leaving the
broad edit path as an untracked close fallback.
Add close_pr to the resolver TASK_MAP (gitea.pr.close, author-side) and
gate gitea_edit_pr(state=closed) on the same operation: without it the
close fails closed before any auth or API call, with reasons and a
structured permission_report; with it the close proceeds and is audited
as a distinct close_pr action carrying the required capability. Reject
invalid state values outright so case variants cannot bypass the gate.
Generalize the profile gate helper (_profile_operation_gate) and document
the PR comment / PR edit / PR close capability split.
Co-Authored-By: Claude Fable 5 <[email protected]>
Add gitea_route_task_session and role_session_router to fail closed when
reviewer tasks start under author-bound MCP sessions. Block author-side
issue creation fallback after wrong_role_stop. Add handoff proof helper and
tests.
Closes#206
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Addresses the sysadmin REQUEST_CHANGES on PR #203 (reviewed head
10d2644790):
1. Lock redesigned; /tmp file removed entirely. The mutation authority is
now an in-process record (_MUTATION_AUTHORITY) plus an environment
session lock (GITEA_SESSION_PROFILE_LOCK) exported at server launch:
- in-process record cannot be spoofed by other local processes, cannot
go stale across sessions, and cannot race concurrent agents;
- the env lock is inherited by child CLI processes, so review_pr.py can
refuse an ad-hoc GITEA_MCP_PROFILE role escalation without any shared
file; a missing env lock (direct operator CLI use) stays allowed;
- silent except-pass writes are gone; an unresolvable profile fails
closed.
2. Standard reviewer workflow unbroken: verify_mutation_authority seeds
itself from the live config-resolved context at the first mutation gate
(approved preflight path whoami -> eligibility -> review/merge), and now
runs as the final gate after eligibility, reusing the identity that
eligibility proved (no extra /user call).
3. Trailing whitespace removed from review_pr.py (git diff --check clean).
4. Module-global verify_mutation_authority no-op bypass removed from
tests/test_mcp_server.py; replaced with a tests/conftest.py autouse
fixture that only resets per-process state (_MUTATION_AUTHORITY,
_IDENTITY_CACHE, session lock env) between tests — the gate itself
stays live in every test.
5. Tests rewritten for the new design: seeding on first verify, unresolved
profile fails closed, remote/profile/identity mismatches fail closed,
session-lock env mismatch rejected, foreign-pid authority reseeded,
unauthorized author->reviewer pivot blocked, authorized pivot allowed;
CLI: mismatch blocked, match allowed, no-lock allowed.
6. Rebased onto current master (c6fd0fd).
Closes#199
Refs #194
Co-Authored-By: Claude Fable 5 <[email protected]>
Extends review_proofs.py with the four #179 proofs, the successor set to
the #173 checkout/inventory proofs:
- assess_capability_evidence: a capability claim (review_pr, merge_pr, ...)
counts only with exact evidence citing gitea_resolve_task_capability
output or equivalent runtime context; no claims at all fails closed.
- assess_sweep_evidence: secret/provenance sweeps must state the exact
command/script/pattern/named method, the scope scanned, and a boolean
result; vague summaries are downgraded and a missing sweep fails closed.
- assess_live_state_recheck: an explicit pre-mutation recheck must prove
the PR is still open, the live head equals the pinned head (full
40-hex), the base branch is unchanged, and blocking review state was
checked and absent; not performing it fails closed.
- assess_role_boundary: a reviewer run using an author namespace (or vice
versa) is clean only with an explicit justification; unreported
namespace usage fails closed.
build_final_report now takes the four proofs as keyword arguments: any
missing or failed proof downgrades the grade, merge_allowed additionally
requires the proven live-state recheck, and a merge performed without it
is a blocked violation. Existing #173 semantics are unchanged otherwise;
gates only get stricter.
tests/test_review_proofs.py adds 29 tests covering the issue's harness
assertions: capability claims without evidence downgraded, vague sweeps
downgraded, missing/stale live-state recheck downgrades and blocks merge
(violation when a merge is claimed anyway), unjustified author-namespace
use downgraded, and the #173 positive baseline preserved.
SKILL.md sections F/G and the review-pr/merge-pr templates now require the
capability evidence, exact sweep, pre-verdict and pre-merge live-state
rechecks, and reviewer-namespace discipline.
Closes#179
Co-Authored-By: Claude Fable 5 <[email protected]>
- Updated mcp_server.py operator guide entries to use jenkins-mcp / glitchtip-mcp names matching mcp-control-plane.
- Added reload/reconnect instructions for clients in notes.
- Added test for 'enabled but no usable tools' negative assertion.
- Updated EXPECTED_SKILLS and assertions in test_operator_guide.py.
Scoped to issue #146. Author profile.
Refs #146
- Upgrade SKILL.md §K compact format to the issue #182 canonical field set
(Task/Repo/Role/Identity/Issue-PR/Branch-SHA/Files/Validation/Mutations/
Current status/Blockers/Next/Safety) plus role-specific field lists for
review/merge, author, and queue/inventory tasks.
- Point the review-pr, merge-pr, and start-issue template handoff lines at
the exactly-titled Controller Handoff section with their role fields.
- Add review_proofs.assess_controller_handoff(): reports without the exact
section are 'missing' (downgraded), present-but-partial are 'incomplete'
with the absent fields listed, and role extras are enforced per role.
- Add TestControllerHandoff (8 tests) including a SKILL.md doc-contract
test so the documented requirement cannot silently rot.
The handoff supplements the full report; full-report validation rules are
unchanged.
Closes#182
Co-Authored-By: Claude Fable 5 <[email protected]>
- Replace raw patch("sys.stdout") and contextlib.redirect_stderr with pytest MonkeyPatch or contextlib redirect scoped to main calls in CLI tests (create_*, prs, python_cli, manage_labels, merge_pr, review_pr).
- Add regression tests in test_review_proofs.py for stdout/stderr remaining usable.
- Ensures normal pytest summary output without junitxml workaround; improves review validation reporting per #173.
- No behavior change to production code or gates.
Refs #178
Adds author_proofs.py, the author-side counterpart of review_proofs.py
(#173), turning the branch-drift incident from PR #176 into fail-closed
gates:
- verify_branch_for_commit: the current branch must equal the intended
feature branch and may never be a protected branch (master, main,
develop, development, dev); missing state fails closed.
- detect_branch_drift: any branch or HEAD change between validation time
and commit time — including an external branch switch in a shared
worktree, which is treated as an expected event to detect — blocks the
commit until reconciled.
- verify_push_target: a push requires the local branch, remote target
branch, and intended issue branch to all match, none protected.
- assess_protected_branch_commit: an accidental protected-branch commit
must never be pushed, requires repair, and the repair must be reported;
pushing the accident or silently continuing is a violation.
- build_commit_push_report: final-report block carrying the branch proof
before commit and before push; any failed proof, drift, or violation
makes the status blocked.
tests/test_author_proofs.py (27 tests) covers the issue's harness
assertions: commit on master blocked; drift between validation and commit
blocked; push target mismatch blocked; shared-worktree branch switch
detected; repair path cannot silently continue without reporting.
SKILL.md section E and templates/start-issue.md now require recording the
validation-time branch/HEAD, the branch proof before commit, the branch
proof before push, and non-silent accident repair. No review/merge/
permission gate is weakened.
Closes#177
Co-Authored-By: Claude Fable 5 <[email protected]>
Adds review_proofs.py with pure, fail-closed helpers for the reviewer-side
blind queue workflow:
- verify_pinned_head_checkout: proves HEAD == pinned PR head (full 40-hex,
no prefix matches) and diff base == PR base; any mismatch blocks
review/merge.
- assess_inventory_completeness: exhaustive "only PRs found" claims require
every configured repo listed with stated filters, proven pagination, and
reported open-PR totals.
- assess_validation_report: validation results are unclaimable unless the
exact command was stated and its output read; missing pass/fail/skip
counts, unjustified ignored paths, or unexplained deviation from the
canonical command downgrade the report to weak.
- assess_self_review_contamination: contamination must be evidence-backed;
bare same-session claims (either way) yield "unknown" with a
choose-another-PR-or-stop action, never an assumed verdict.
- build_final_report: distinguishes identity eligibility, author/reviewer
distinctness, session contamination, validation-on-pinned-head, merge,
and issue verification; grades "A" only when every proof is present,
downgrades otherwise, and marks a merge without allowing proofs as a
blocked violation.
tests/test_review_proofs.py covers all seven harness assertions from the
issue (unchecked-out pinned head blocks merge; multi-repo inventory;
pagination; missing counts flagged weak; evidence-required contamination;
unread output cannot claim validation passed; ignored paths need
justification).
SKILL.md section F and templates/review-pr.md now require the checkout,
inventory, validation-evidence, and contamination proofs and the
distinguished final report. No review/merge safety gate is weakened.
Closes#173
Co-Authored-By: Claude Fable 5 <[email protected]>
Covers:
1. Stale prior handoff merged but live open
2. Prior open but live closed
3. list_prs and view_pr disagree
4. Merge commit missing after claimed merge
5. Linked issue remains open after claimed merge
Uses live mocks for list/view with staleness fields (#166).
Proves coverage; no src changes or gate weakening.
Refs #170
- Add gitea_get_pr_review_feedback: read-only MCP-native discovery of
formal PR review verdicts (APPROVED / REQUEST_CHANGES / COMMENT) with
latest-state-per-reviewer, blocking change-request and approval
summaries, reviewed-head vs current-head staleness, dismissed/PENDING
handling, credential redaction of review bodies, no endpoint URLs
without the reveal opt-in, and a fail-closed gitea.read gate that
returns feedback_not_attempted with a structured #142 permission
report and zero API calls.
- Add task/role guards to gitea_resolve_task_capability: new
stop_required and task_role_guidance outputs, and a distinct
address_pr_change_requests author task, so a review/merge request
under an author profile returns explicit STOP guidance instead of
silently degrading into author-side mutations, and author-side
remediation is explicitly barred from review verdicts and merges.
- Add build_validation_report: validation reporting helper that
distinguishes passed / failed / skipped / not-run, requires the exact
output for failures, rejects vague statuses, and never implies
full-suite success unless the full-suite command passed.
- Document task/role alignment (review vs author-fix vs merge vs
comment workflows with required identity, allowed/forbidden actions,
and stop conditions), MCP-native review feedback discovery, and
validation reporting discipline in docs/llm-workflow-runbooks.md.
- Add tests/test_review_feedback.py (22 tests) covering discovery,
staleness, redaction, URL hiding, fail-closed gating, role guards for
author/reviewer profiles, and validation report semantics.
Closes#167
Co-Authored-By: Claude Fable 5 <[email protected]>
- Inventory report now includes 'Repository: org/repo' name
- Inventory failure path redacts exception via _redact() per project pattern; no raw exception text leaked
- Added/updated tests covering:
* repository name present in inventory output
* inventory failure output is redacted
* raw exception text does not leak
* author profiles can perform read-only PR inventory
* author profiles still cannot approve/request_changes/merge or bypass gates
Preserves all hard gates and author restrictions. No other scope.
Refs #164
- Updated jenkins-readonly and glitchtip-readonly skills in _PROJECT_SKILLS:
- Descriptions and notes now use actual server names (jenkins-mcp, glitchtip-mcp)
- Explain historical -readonly skill names vs registration in mcp-control-plane #55
- Note gated trigger in jenkins-mcp (see #56), partial filing in glitchtip (see #57)
- Updated docs/safety-model.md: naming note, corrected mutation gating claims for trigger/filing
- Updated docs/architecture/jenkins-readonly-build-status-design.md: naming note, updated Phase 1 to account for gated trigger
- No behavior change to skill status/availability; no secrets exposed
- AC: stale naming explained, actual names documented, Jenkins claims corrected, GlitchTip filing as partial, no over-claiming
Closes#154
- mcp_get_control_plane_guide now instructs to ALWAYS resolve task capability first (gitea_resolve_task_capability), verify identity/profile, use correct namespace.
- Added hard stops for resolve-first, namespace separation, issue.comment distinct from pr.comment.
- Added/updated skills: gitea-resolve-task-capability (new), and steps/notes in issue-authoring, pr-creation, pr-review, pr-merge, issue-comments to call resolver first.
- Guide docstring and guidance updated for AC: do not hardcode, resolve before act, reviewer for review/merge, author for authoring, issue comments require gitea.issue.comment.
- Tests: 4 new covering guide for author/reviewer, missing issue-comment, static-profile safe next action; EXPECTED_SKILLS updated.
- No secrets/URLs; synthetic tests; no prod behavior change.
Closes#144