Commit Graph
45 Commits
Author SHA1 Message Date
sysadmin 9ea2707289 feat(author): harden reporting and claim capability per #183
- Add 'mark_issue' to TASK_MAP in resolve_task_capability for exact mutation proof (closes gap on unknown treated as allowed).
- Add assess_controller_handoff and integrate into build_final_report (downgrades missing handoff).
- Add tests for handoff and update existing.
- Update SKILL.md and review-pr.md to mandate exact 'Controller Handoff' section, exact sweep evidence, PR head SHA in reports.
- Author profile used throughout; no review/merge.

Refs #183
2026-07-05 15:35:32 -04:00
sysadmin 3ff3affa64 Harden URL redaction in inventory/review failure output (Issue #171) 2026-07-05 14:17:52 -04:00
sysadmin 29f04d003a Merge pull request 'Harden LLM role/task alignment and PR review feedback discovery' (#169) from feat/issue-167-review-feedback-discovery into master 2026-07-05 12:17:43 -05:00
sysadminandClaude Fable 5 a4060c59cc feat: harden LLM role/task alignment and PR review feedback discovery (#167)
- 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]>
2026-07-05 12:59:54 -04:00
sysadmin 194301359e feat: harden PR queue reconciliation against stale/conflicting state (#166)
- Add updated_at (list_prs), + merged_at/merge_commit_sha/closed_at/updated_at (view_pr) for staleness detection in tool output and inventory.
- Surface updated_at in gitea_review_pr inventory report.
- Strengthen gitea-pr-review skill steps, _COMMON_WORKFLOWS, _GUIDE_RULES with explicit live reconciliation checklist, 'do not trust prior handoffs', stop on conflict.
- Add dedicated live queue reconciliation runbook section in docs/llm-workflow-runbooks.md.
- Add passthrough tests and assertions for new fields.
- All gates, redaction, author/reviewer separation untouched.
- Tests pass, py_compile clean, diff clean, secret sweep clean.

Closes #166
2026-07-05 12:45:05 -04:00
sysadmin a02d990bc5 Merge pull request 'Allow read-only PR queue inventory under author profiles while preserving hard review/merge gates' (#165) from feat/issue-164-pr-queue-inventory-author into master 2026-07-05 11:38:47 -05:00
sysadmin 88296f0262 fix(pr-queue-inventory): address REQUEST_CHANGES on #165
- 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
2026-07-05 12:29:53 -04:00
sysadmin 56d87db263 Merge pull request 'Update operator runbooks and roadmap docs for Jenkins/GlitchTip MCP status' (#163) from feat/issue-154-update-runbooks-roadmap-docs into master 2026-07-05 11:20:50 -05:00
sysadmin 485ba22c8f Allow read-only PR queue inventory under author profiles while preserving hard review/merge gates 2026-07-05 12:14:26 -04:00
sysadmin 82d6bf286a Merge pull request 'Update Gitea operator guide and skill registry for role-aware task routing' (#162) from feat/issue-144-operator-guide-updates into master 2026-07-05 10:27:58 -05:00
sysadmin e7556f5177 Merge pull request 'feat: structured permission failure reports on gated tools (#142)' (#161) from feat/issue-142-structured-permission-failures into master 2026-07-05 10:24:32 -05:00
sysadmin 7102f27d20 docs: update operator runbooks and roadmap docs for Jenkins/GlitchTip MCP status (#154)
- 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
2026-07-05 10:59:50 -04:00
sysadmin 2592c6ba7e feat: update operator guide and skill registry for role-aware task routing (#144)
- 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
2026-07-05 06:29:27 -04:00
sysadminandClaude Opus 4.8 e80508313a feat: integrate mock server with gitea-tools remote resolution and correct verdict parser
- Fix allowedTools options inside run_compliance to support prefixless and prefixed tool access
- Implement sys.executable resolution to allow compliance runner to run safely inside git worktrees
- Dynamically load GITEA_MCP_CONFIG JSON profiles inside gitea_auth.py to support 'mock' remote name
- Implement gitea_url helper inside gitea_auth.py to automatically handle HTTP and HTTPS scheme differences for localhost/loopback mock targets, preventing wrong SSL version errors
- Fix verdict.py escaping mismatch by normalizing stream-json block output structure before matching strings, allowing PR view decision points to be verified correctly

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-05 04:12:13 -04:00
sysadminandClaude Fable 5 abe952b4f1 feat: structured permission failure reports on gated tools (#142)
Every gated denial now carries a 'permission_report' explaining itself:
requested/missing/required operation, active profile and identity (when
already resolved), the profile's allowed operations, which configured
profiles could perform the operation (names only), whether runtime
switching is supported, whether a different MCP namespace/session is
required, and the exact safe next action.

New fail-soft helper _permission_block_report builds the report only
after a gate has refused — it adds guidance to denials, never widens a
permission, performs no network I/O, and degrades to a minimal
fail-closed report if the profile itself cannot be resolved. Wired into
gitea_check_pr_eligibility (and via it gitea_submit_pr_review,
gitea_merge_pr, and the legacy gitea_review_pr wrapper) and into the
issue-comment gate consumers gitea_list_issue_comments and
gitea_create_issue_comment. Input errors (e.g. empty comment body) are
not permission failures and get no report.

Tests cover: missing gitea.issue.comment, author attempting review and
merge, reviewer attempting an authoring operation, unknown/missing
profile fail-closed, switching-enabled guidance, eligible calls carry
no report, and no secret/URL material in any report.

Closes #142

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 03:44:35 -04:00
sysadmin 8ef9129e44 feat: add read-only Gitea task capability resolver tool (#141) 2026-07-05 03:21:51 -04:00
sysadminandClaude Fable 5 c349b98206 docs: note in-place mutation contract on _with_optional_url
Subagent review (read-only) found no blockers; this addresses its one
LOW note by documenting that the helper mutates the passed dict and
must receive a freshly-built one.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-04 17:06:41 -04:00
sysadmin 552f538d97 fix: redact Gitea web links from MCP PR output 2026-07-04 16:46:55 -04:00
sysadmin 10a29d1bd5 style: remove trailing whitespace in mcp_server.py and test_runtime_clarity.py 2026-07-04 14:57:21 -04:00
sysadmin a0e7d3360e feat: implement profile activation and runtime identity clarity (#131) 2026-07-04 02:04:07 -04:00
sysadminandClaude Fable 5 6a8a9d99b7 feat: add operator guide and project skills discovery MCP tools (#128)
Add three read-only capability-discovery tools so new LLM sessions can
learn the workflow rules and available project skills from the MCP
server instead of long pasted operator prompts:

- mcp_get_control_plane_guide: active profile, authenticated identity
  (fail-soft; unresolved identity returns STOP instructions),
  allowed/forbidden operations, profile-aware guidance (author profiles
  are told review/approve/merge is forbidden; reviewer profiles are told
  review/merge requires eligibility checks and a pinned head SHA; mixed
  profiles get a misconfiguration warning), and the standing rules: hard
  stops, fail-closed behavior, head-SHA pinning, merge confirmation,
  redaction, author/reviewer/merger separation, profile switching, and
  identity verification.
- mcp_list_project_skills: registry of ten project workflows (issue
  authoring, PR creation, PR review, PR merge, issue comments, profile
  switching, redaction/security review, Jenkins read-only, GlitchTip
  read-only, release/operator) with description, when-to-use, required
  operations, status, and per-profile availability. Unimplemented
  services are listed as designed-not-implemented rather than omitted.
- mcp_get_skill_guide: step-by-step guide per skill; unknown names fail
  closed with the list of valid names.

All three are read-only and change no existing gate or permission.
Normal output contains no endpoint URLs or keychain IDs; the guide
includes the server host only under GITEA_MCP_REVEAL_ENDPOINTS=1.

Tests (tests/test_operator_guide.py, 17 new): profile-aware guidance
for author/reviewer, unresolved-identity STOP, read-only behavior,
redaction defaults and reveal opt-in, rules coverage, registry
completeness and profile awareness, unimplemented-service marking,
fail-closed unknown skill names.

Docs: llm-workflow-runbooks.md now tells new sessions to call the guide
tools first.

Closes #128

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 19:49:11 -04:00
sysadminandClaude Fable 5 5aeb51f132 feat: add Gitea issue comment list/create MCP tools (#126)
Add gitea_list_issue_comments and gitea_create_issue_comment so
discussion/design workflows can read and post issue comments through
the MCP layer instead of direct API scripts.

- List requires gitea.read; create requires gitea.issue.comment —
  gated separately from the gitea.pr.* review/merge family, fail closed.
- Issue comments never touch PR review endpoints.
- LLM-safe output: comment id/author/timestamps/body only; web links
  appear solely under the GITEA_MCP_REVEAL_ENDPOINTS admin opt-in.
- Create operations are audit-logged (create_issue_comment) and errors
  are redacted before being raised.
- Tests cover list/create success, permission blocks (including PR
  review permissions not granting issue comments), forbidden-overrides,
  empty body, missing issue with redacted error, endpoint separation,
  and reveal opt-in.
- Document issue comments versus PR reviews in
  docs/gitea-execution-profiles.md.

Closes #126

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 19:07:36 -04:00
sysadmin e880a210ec feat: extend whoami and profile metadata for environments (#104)
Closes #104
2026-07-03 18:11:35 -04:00
sysadminandClaude Fable 5 e0861bcb03 feat: operation-name normalization table with fail-closed enforcement (#106)
Promote the #103 minimal alias map to the documented public table
GITEA_OPERATION_ALIASES and add the #106 enforcement layer:

- normalize_operation(op, service): canonical namespaced names; legacy
  spellings accepted only via the explicit table; unknown, ambiguous,
  and cross-service names fail closed.
- check_operation(op, allowed, forbidden, service): normalizes BOTH the
  requested operation and the profile lists before any membership
  check; forbidden always overrides allowed; unnormalizable allowed
  entries grant nothing and unnormalizable forbidden entries deny the
  request, so normalization can never silently widen permissions;
  empty/missing allowed list denies everything.
- gitea_check_pr_eligibility now routes its capability check through
  check_operation, fixing the mismatch where canonical namespaced
  profile ops (gitea.pr.merge) never matched the raw action (merge)
  and namespaced forbidden entries were never enforced.
- Document the normalization table and enforcement rules in
  docs/gitea-execution-profiles.md, replacing the stale 'enforcement
  out of scope' caveat.
- tests/test_op_normalization.py: full #106 matrix (27 tests) —
  qualified/legacy allowed and forbidden, unknown, ambiguous, service
  mismatch, forbidden-overrides-allowed, empty/missing allowed,
  duplicates after normalization, no permission widening, and
  eligibility integration proving normalization happens before
  enforcement. Existing v1/env unqualified behaviour stays compatible.

Closes #106

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 03:35:03 -04:00
sysadminandClaude Fable 5 ff920a6496 feat: load profiles.json v2 contexts shape with enabled enforcement and LLM-safe output (#120)
Support the canonical contexts-shape version 2 config (contexts / profiles /
projects / rules) alongside the existing environments shape and v1:

- Require a boolean 'enabled' on every context, profile, service, and
  project. Disabled entries are surfaced in audits but fail closed at
  selection/resolution — never a silent fallback to another profile,
  service, or credential source.
- Resolve the active identity from GITEA_MCP_PROFILE via the existing
  select_profile path; profile base_url falls back to the context's enabled
  gitea block.
- Add resolve_service() and project_for_path() for context service and
  project-to-context resolution (internal use; fail closed on disabled).
- get_auth_header now propagates ConfigError when GITEA_MCP_CONFIG is set
  instead of silently degrading to Basic auth.
- Hide endpoint URLs and keychain ids from normal LLM-facing output:
  gitea_whoami / gitea_get_profile report logical names and auth status
  only; new gitea_audit_config tool reports enabled/disabled state and safe
  one-line service summaries. The GITEA_MCP_REVEAL_ENDPOINTS opt-in (and
  'python3 gitea_config.py audit --reveal-endpoints' locally) restores
  endpoints and auth source names for admin diagnostics; token values are
  never printed on any path.
- Ship gitea-mcp.v2-contexts.example.json (synthetic values) and validate
  it in tests.

Implements #120

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 02:19:39 -04:00
sysadmin 472e6850fe fix: close implements tracker gap and clarify closing keywords (#110) 2026-07-02 19:46:21 -04:00
sysadminandClaude Fable 5 8120486109 fix: surface skipped tracker cleanup on merge read-back failure (#98)
gitea_merge_pr ran cleanup_in_progress_for_pr inside the same try as the
post-merge read-back GET; a read-back failure silently skipped tracker
cleanup, leaving only merge_commit=null and no cleanup_status at all, so
status:in-progress could stay stuck while the merge read as full success.

Split the block: read-back failure now returns an explicit
cleanup_status='skipped (merge read-back failed)', and an unexpected
cleanup exception returns 'skipped (cleanup error: <redacted>)' instead of
masking merge_commit. Cleanup still never blocks a performed merge, the
happy-path API call sequence is unchanged, and _redact keeps credentials
out of surfaced errors.

Add regression tests: read-back failure => merge still performed, explicit
skip status, no tracker DELETE traffic; cleanup exception => surfaced and
redacted.

Fixes #98.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-02 16:15:47 -04:00
sysadminandClaude Opus 4.8 cfe3ff6755 fix: add shared API pagination and failure handling (#67)
Harden gitea_auth.api_request: add a per-request timeout (env
GITEA_HTTP_TIMEOUT), convert timeouts and DNS/network failures
(URLError/TimeoutError) into clear RuntimeErrors, give 502/503/504 an
explicit 'upstream unavailable' message, convert malformed success JSON
into a clean error, and redact credential-like substrings from all error
text. Preserves the success path and existing 429 retry/backoff.

Add shared gitea_auth.api_get_all: page-based pagination that tolerates
missing/malformed metadata (relies on page length, not Link/X-Total-Count
headers), honors an optional overall limit, and caps pages. Wire it into
the read-only list tools gitea_list_issues, gitea_list_prs, and
gitea_list_labels (return shape unchanged).

Add tests/test_api_reliability.py (18 cases) and update the three list-tool
tests to the new call path. No auth/profile/merge/review/tracker behavior
changed. No modular #65 refactor.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-02 13:27:06 -04:00
sysadmin 642adf4705 feat: add aliases for gitea_whoami identity lookup (fixes #9)
Issue #9 requested getAuthenticatedUser and getCurrentUser in addition to whoami.
This adds the two aliased MCP tools and their corresponding unit tests.
2026-07-02 06:15:36 -04:00
sysadmin dbfa0fe188 fix: finalize PR 57 tracker cleanup safety 2026-07-02 06:00:06 -04:00
sysadmin b3728c54ce fix: target label delete and move helpers before entry point 2026-07-02 05:55:29 -04:00
sysadmin 4afada098c feat: automatically release status:in-progress on close and merge (#56) 2026-07-02 05:50:10 -04:00
sysadminandClaude Opus 4.8 7bcdd44fe5 fix: validate gitea_edit_pr no-fields before authentication (#43)
gitea_edit_pr called _auth() (and resolved the remote) before checking whether
any editable field was provided, so a pure validation error (no fields) surfaced
as a RuntimeError "no credentials" in environments without Gitea auth — making
test_edit_pr_no_fields_raises depend on credentials/network/env.

Move the payload build + no-fields ValueError ahead of _resolve/_auth/URL setup.
Behavior is unchanged when fields are provided (same _resolve → _auth → audited
PATCH path). No change to auth, retry/backoff, audit, config profiles, or
worktree helpers.

Tests: add test_edit_pr_no_fields_validates_before_auth asserting the no-fields
path raises ValueError and calls neither get_auth_header nor api_request (even
with auth mocked to None). Existing edit-PR tests unchanged.

Full suite passes with no Gitea credentials (287 passed, 0 failures) — the
no-fields test no longer depends on the environment.

Closes #43.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-02 03:08:39 -04:00
sysadminandClaude Opus 4.8 c3c48fb7c2 feat: audit-log Gitea MCP mutating actions with profile metadata (#18)
Add durable, opt-in audit logging for every mutating Gitea MCP action so an
operator can see which execution profile and authenticated Gitea user
performed (or was blocked from / failed) each mutation.

- New gitea_audit.py: pure, no-network module — recursive secret redaction
  (token/password/authorization keys; token/Basic/Bearer value runs),
  build_event (timestamp, action, result, profile, audit label, authenticated
  username, repo, issue/PR, target branch, head SHA, redacted request
  metadata), and an append-only JSON Lines sink.
- mcp_server.py: _audit helper + _audited context manager (simple mutations)
  and an _audit_pr_result decorator (gated review/merge tools, reading their
  own result dict) wired into create_issue, create_pr, edit_pr, close_issue,
  commit_files, delete_branch, create_label, set_issue_labels, mark_issue
  (label/unlabel), gitea_submit_pr_review, and gitea_merge_pr.
- Outcomes recorded as allowed/blocked/failed/succeeded; blocked and failed
  eligibility checks are logged, not just successes.

Off by default: records are written only when GITEA_AUDIT_LOG is set. When it
is unset every audit path short-circuits — no records, no extra API calls — so
existing tool behaviour and API call sequences are unchanged. Auditing never
raises; sink writes are best-effort. Tokens are never written.

Docs: README env table + audit note, .env.example placeholder.
Tests: tests/test_audit.py (19 cases) — redaction, event build, sink writes,
per-tool success/failure/blocked records, secret-free output, off-by-default
no-op, and audit-failure-never-breaks-action.

Closes #18

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-01 22:20:51 -04:00
sysadminandClaude Opus 4.8 f04cf44975 feat: gate gitea_merge_pr behind identity/profile/eligibility + confirmation (#16)
Replace the ungated gitea_merge_pr with a gated merge workflow. This is now
the only merge path the MCP server exposes; the merge API is called only
after every safety gate passes.

Gates (fail-closed at each step):
  1. Merge method is merge | squash | rebase.
  2. Explicit confirmation: confirmation must equal "MERGE PR <n>" (without it,
     zero API calls are made).
  3. Reuse gitea_check_pr_eligibility (#14) with action 'merge': proves the
     authenticated identity, the active profile (and that it allows merge), the
     PR author, blocks self-merge, requires the PR open, and fails closed when
     the PR is not mergeable or mergeability is unknown.
  4. Optional expected_head_sha: refuse if the PR head moved.
  5. Optional expected_changed_files: refuse if the PR's changed file set differs.
  6. Redundant self-merge block (auth user == PR author).

The force/ignore-checks option was removed — Gitea's own mergeable signal
(which reflects branch-protection required reviews/checks) must be positive,
so required approval/check state is honoured, never bypassed.

Output reports performed, authenticated user, profile name, PR author, PR
number, head SHA checked, merge method, gates passed/blocked, and merge
result / merge commit — never a token, auth header, or credential. Error text
is scrubbed via _redact.

Surface audit: no ungated merge path remains. The /merge endpoint appears only
inside gitea_merge_pr; gitea_review_pr fails closed on merge=True before any
API call; gitea_submit_pr_review has no merge parameter and 'merge' is not a
reviewable action. Tests assert all three.

Tests cover: merge succeeds only when all gates pass; self-author blocked;
unknown identity/profile blocked; profile without merge permission blocked;
missing/wrong confirmation blocked (no API call); head-SHA mismatch blocked;
changed-files mismatch blocked; closed PR blocked; non-mergeable blocked;
unknown mergeability fail-closed; no merge call when gates fail; invalid merge
method rejected; output and error redaction; and the no-ungated-merge-path audit.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-01 15:03:49 -04:00
sysadmin 6c992a4fa7 fix: close gitea_review_pr ungated bypass (#15) 2026-07-01 14:42:00 -04:00
sysadminandClaude Opus 4.8 f05e58c847 feat: add gated gitea_submit_pr_review review actions (#15)
Add gitea_submit_pr_review, the only tool that submits a Gitea PR review.
It performs a review mutation (comment / approve / request_changes) only
after every safety gate passes, and never merges.

Gates (fail-closed at each step):
  1. Validate action is comment | approve | request_changes.
  2. Reuse gitea_check_pr_eligibility (#14) for authenticated-user lookup,
     active-profile lookup, PR-author lookup, self-approval block, and the
     profile-allowed-operation check. approve requires 'approve' eligibility,
     request_changes requires 'request_changes', comment requires 'review'.
  3. Redundant self-approval block (auth user == PR author).
  4. Optional expected_head_sha: refuse if the PR head has moved.
  5. Only then POST /repos/{owner}/{repo}/pulls/{n}/reviews (formal review
     endpoint, so approvals/change-requests carry real review state).

Output reports action, whether performed, authenticated user, profile name,
PR author, PR number, head SHA checked, and reasons — never a token, auth
header, or credential. Error text is scrubbed via _redact as defence in depth.

Merge is intentionally not implemented (belongs to #16).

Tests cover: self-author approve blocked, approve/request_changes/comment
succeed only when eligible, unknown identity fail-closed, disallowed profile
op blocked, head-SHA mismatch blocked, no mutation when gates fail, invalid
action rejected, and secret redaction in output and error paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-01 14:31:34 -04:00
sysadminandClaude Opus 4.8 fbbbd5359e feat: add read-only gitea_check_pr_eligibility (#14)
Add a read-only MCP tool that decides whether the current authenticated
identity + active runtime profile is eligible to review, approve,
request_changes, or merge a specific PR. Evaluation only — it never
reviews, approves, requests changes, merges, or mutates anything.

Inspects: authenticated username (/user), active profile metadata
(allowed/forbidden operations), and PR facts (author, state, head SHA,
mergeability). Returns {eligible, requested_action, authenticated_user,
profile_name, pr_author, pr_state, head_sha, mergeable, reasons}.

Fail-closed rules:
- unknown action / unknown remote -> not eligible
- action not in allowed ops, or in forbidden ops -> not eligible
- identity undetermined -> not eligible
- authenticated user == PR author -> cannot approve/merge
- PR not open -> not eligible
- merge requires a positive mergeable signal

No token/auth-header exposure. No review/approve/request-changes
mutation. No merge mutation. No multi-token switching. No
Jenkins/Ops/GlitchTip/Release/deploy behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-01 13:54:49 -04:00
sysadminandClaude Opus 4.8 38c96d5815 feat: add read-only gitea_get_profile discovery tool (#13)
Add a read-only MCP tool that reports the active runtime execution
profile so an LLM can inspect what the current process is configured to
do before deciding whether to attempt an action later.

- gitea_get_profile: returns profile_name, allowed/forbidden operation
  categories, audit_label, token_source_name (a NAME, never a value),
  base_url, remote, resolved server, and — optionally — the verified
  authenticated username. Identity resolution fails soft and marks
  identity_status (verified/unknown/unavailable/not_resolved); the
  profile config is always returned. Never mutates Gitea.
- gitea_auth.get_profile(): extended with forbidden_operations,
  audit_label, token_source_name from env (non-secret metadata).
- .env.example / README: document the new optional metadata vars and
  the discovery tool.
- tests: metadata parsing, verified/unavailable/unknown identity paths,
  skip-identity, and secret-redaction.

Read-only. No token exposure. No multi-token switching. No PR
eligibility, review, or merge workflow. No Jenkins/Ops/GlitchTip/
Release/deploy behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-01 13:41:14 -04:00
sysadminandClaude Opus 4.8 e31612027d feat: support separate Gitea MCP runtime profiles via env config (#19)
Allow the same MCP server to run as separate MCP entries, each with its
own token and profile name, so roles stay task-scoped (the profile is
the role, not the LLM).

- gitea_auth.get_profile(): reads GITEA_PROFILE_NAME,
  GITEA_ALLOWED_OPERATIONS, GITEA_BASE_URL as non-secret metadata.
  Never reads/returns/logs the token.
- gitea_whoami now surfaces the safe profile metadata (name + allowed
  operations) alongside identity; token still never exposed.
- .env.example: placeholder-only template for a runtime profile.
- .gitignore: track .env.example while keeping real .env* ignored.
- README: document multiple env-configured MCP entries.
- tests: profile defaults/parsing, token-never-included, whoami surfaces
  profile without leaking token.

One token + one profile per process. No multi-token switching in a
single runtime. No approve/merge/eligibility workflow. No
Jenkins/Ops/GlitchTip/Release/deploy behavior. No real secrets.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-01 13:21:59 -04:00
sysadminandClaude Opus 4.8 03e28c159e feat: add read-only gitea_whoami authenticated-user lookup (#11)
Add a read-only MCP tool that calls Gitea's authenticated-user
endpoint (GET /api/v1/user) and returns safe identity metadata only:
username, display name, user id, email, server, and remote.

This lets future review/merge workflows prove which Gitea account the
MCP server is authenticated as, so self-review/self-merge can be
detected before acting — the blocker discovered during PR #8 dogfooding.

- Never returns the token, Authorization header, password, or secrets.
- Fails closed with a clear error if identity cannot be determined.
- No mutation; no profile switching; no review/approve/merge behavior.

Tests: identity mapping, secret-redaction, fail-closed, unknown-remote.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-01 12:42:37 -04:00
sysadmin 989856a007 feat: add PR review and edit tools to CLI and MCP server 2026-06-26 06:24:19 -04:00
sysadmin 82fcd5a4bc feat: expand MCP server tools for PR and label management, add helper CLI scripts
Closes #7
2026-06-24 00:14:47 -04:00
sysadmin 51296c88a3 refactor: rename auth.py to gitea_auth.py and ignore env files 2026-06-21 22:27:40 -04:00
sysadmin b7e195e426 feat: add MCP server + shared auth module (#7, #1)
- New: mcp_server.py — FastMCP stdio server exposing 7 tools:
  gitea_create_issue, gitea_create_pr, gitea_close_issue,
  gitea_list_issues, gitea_view_issue, gitea_mark_issue,
  gitea_mirror_refs
- New: auth.py — shared authentication and API helpers
  (get_credentials, get_auth_header, api_request, repo_api_url)
- Refactored: create_pr.py, create_issue.py, manage_labels.py
  to use shared auth module (eliminates credential duplication)
- New: tests/test_mcp_server.py — 17 tests for all MCP tools
- Updated: tests/test_credentials.py — now tests auth.py directly
- Updated: tests/test_create_issue.py — adapted for refactored imports
- New: requirements.txt — frozen venv deps (mcp[cli], pytest)
- Updated: README.md — MCP server as primary interface
- Config: added gitea-tools to mcp_config.json

Closes #1. Resolves #2, #5. Relates to #7.
2026-06-21 20:08:07 -04:00