fix(mcp): surface safe diagnostics for opaque mutation internal_error #732

Merged
jcwalker3 merged 1 commits from fix/mutation-error-diagnostics into master 2026-07-17 19:52:46 -05:00
Owner

Closes #731

Refs #729

Summary

Native mutation tools including create_issue, create_pr, and delete_branch have returned non-retryable internal_error results without an exception class, stage, HTTP status, or actionable detail.

The existing structured-error boundary intentionally suppresses untyped exception information, while the runtime guard prevents standalone reproduction outside a sanctioned MCP process. Together, these controls make the underlying mutation failure undiagnosable.

This change adds bounded, redacted diagnostics. It does not claim to fix the underlying mutation failure.

Changes

  • Adds stage-level annotations for escaping exceptions in:

    • create_issue
    • create_pr
    • delete_branch
  • Adds safe exception_class, redacted detail, and optional mutation_stage fields only for otherwise-untyped internal_error results.

  • Adds a gitea_reason_code contract allowing server exceptions to declare a known structured reason safely.

  • Converts preflight-order violations into the typed preflight_order_violation reason.

  • Leaves existing typed authentication, authorization, configuration, HTTP, and network error paths unchanged.

  • Does not change identity, repository, worktree, role, permission, lock, lease, ancestry, or preservation gates.

Security and redaction

Diagnostic detail is bounded and strips or suppresses:

  • tokens and authorization values;
  • Bearer credentials;
  • secret-like JSON and key/value fields;
  • short password values;
  • URLs and hostnames;
  • absolute filesystem paths;
  • multiline and unbounded exception content.

No raw headers, response bodies, environment variables, credentials, or tracebacks are returned.

Validation

Passing suites:

  • New mutation diagnostics: 18 passed
  • Structured boundary: 25 passed
  • Core server: 209 passed
  • Preflight: 92 passed

Two pre-existing branch_cleanup_guard failures reproduce unchanged on pristine master and are unrelated to this three-file change.

Follow-up validation

After merge and MCP restart, run one eligible delete_branch canary operation.

  • If it succeeds, continue the bounded reconciliation batch.
  • If it fails, capture only the structured mutation_stage, exception_class, redacted detail, and reason code.
  • Do not retry or attempt the remaining branches until the revealed cause is corrected.

Operator bootstrap note

This PR was opened manually because native gitea_create_pr is one of the affected mutation paths. No LLM direct API or CLI fallback was used.

Checklist

  • Author identity and repository binding verified.
  • No secrets, tokens, keychain IDs, or raw service URLs committed.
  • Tests covering the changed behavior pass.
  • git diff --check confirmed clean.
  • Documentation and wiki impact evaluated.
Closes #731 Refs #729 ## Summary Native mutation tools including `create_issue`, `create_pr`, and `delete_branch` have returned non-retryable `internal_error` results without an exception class, stage, HTTP status, or actionable detail. The existing structured-error boundary intentionally suppresses untyped exception information, while the runtime guard prevents standalone reproduction outside a sanctioned MCP process. Together, these controls make the underlying mutation failure undiagnosable. This change adds bounded, redacted diagnostics. It does not claim to fix the underlying mutation failure. ## Changes * Adds stage-level annotations for escaping exceptions in: * `create_issue` * `create_pr` * `delete_branch` * Adds safe `exception_class`, redacted `detail`, and optional `mutation_stage` fields only for otherwise-untyped `internal_error` results. * Adds a `gitea_reason_code` contract allowing server exceptions to declare a known structured reason safely. * Converts preflight-order violations into the typed `preflight_order_violation` reason. * Leaves existing typed authentication, authorization, configuration, HTTP, and network error paths unchanged. * Does not change identity, repository, worktree, role, permission, lock, lease, ancestry, or preservation gates. ## Security and redaction Diagnostic detail is bounded and strips or suppresses: * tokens and authorization values; * Bearer credentials; * secret-like JSON and key/value fields; * short password values; * URLs and hostnames; * absolute filesystem paths; * multiline and unbounded exception content. No raw headers, response bodies, environment variables, credentials, or tracebacks are returned. ## Validation Passing suites: * New mutation diagnostics: 18 passed * Structured boundary: 25 passed * Core server: 209 passed * Preflight: 92 passed Two pre-existing `branch_cleanup_guard` failures reproduce unchanged on pristine master and are unrelated to this three-file change. ## Follow-up validation After merge and MCP restart, run one eligible `delete_branch` canary operation. * If it succeeds, continue the bounded reconciliation batch. * If it fails, capture only the structured `mutation_stage`, `exception_class`, redacted `detail`, and reason code. * Do not retry or attempt the remaining branches until the revealed cause is corrected. ## Operator bootstrap note This PR was opened manually because native `gitea_create_pr` is one of the affected mutation paths. No LLM direct API or CLI fallback was used. ## Checklist * [x] Author identity and repository binding verified. * [x] No secrets, tokens, keychain IDs, or raw service URLs committed. * [x] Tests covering the changed behavior pass. * [ ] `git diff --check` confirmed clean. * [ ] Documentation and wiki impact evaluated.
jcwalker3 added 1 commit 2026-07-17 19:52:29 -05:00
Mutation tools (create_issue, create_pr, delete_branch, issue-lock) returned
reason_code=internal_error / retryable=false with no class, message, HTTP
status, or actionable detail. The #699/#701 tool-error boundary intentionally
collapses every non-typed exception to a fixed "Internal tool error" and drops
the class and message, so a mutation failure is undiagnosable — and the #695
runtime guard blocks standalone reproduction. The true failing stage was
therefore invisible by two independent mechanisms.

Make the failure observable and actionable without weakening the secret-free
contract:

- Boundary (internal_error path ONLY): capture a safe exception class (a Python
  type identifier, never instance text) plus a strictly-redacted `detail`
  (token/Authorization/Bearer credentials, JSON/kv secret values incl. short
  passwords, raw URLs/hostnames, and absolute filesystem paths all stripped;
  whitespace collapsed; length-bounded) and an optional `mutation_stage`. Typed
  auth/authz/network/config/http paths are unchanged and still emit no detail.
- Convert raw exceptions into typed structured errors via a safe
  `gitea_reason_code` attribute contract: server raisers may self-declare a
  known reason and the boundary emits it (fixed message) instead of an opaque
  internal_error. Pre-flight order violations now raise `_PreflightOrderError`
  (a RuntimeError subclass → preflight_order_violation).
- Add a `_mutation_stage` context manager tagging escaping exceptions with the
  stage name (preflight_purity / resolve_auth / api_*), applied to
  delete_branch, create_issue, create_pr.

Fail-closed identity/repo/worktree/role/permission/lock/lease/ancestry gates
are untouched; only error *reporting* changed. New suite
test_mutation_error_diagnostics.py (18 tests) pins the redaction, class capture,
stage tagging, typed conversion, and adversarial no-leak contract. Existing
boundary suite (25), core server (209), and preflight (92) suites stay green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
jcwalker3 merged commit daf60266d0 into master 2026-07-17 19:52:46 -05:00
jcwalker3 deleted branch fix/mutation-error-diagnostics 2026-07-17 19:52:46 -05:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

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