Umbrella #628 scope item 6 requires dependencies to be durable structured
state carrying source, target, type, blocking condition, completion
condition, current state, and evidence. Nothing stored any of that.
Dependency knowledge existed only as a per-run computation:
allocator_dependencies re-parsed the Depends: declaration out of every
issue body on every allocation, _allocator_candidates_from_gitea resolved
each reference against live issue state, and the result collapsed into two
in-memory WorkCandidate fields that classify_skip consumed and discarded.
Three consequences followed. Nothing could answer "what is waiting on #N"
without re-listing every open issue and re-parsing every body, so the
reverse edge automatic resumption needs did not exist in any form. Only
issue-blocked-by-issue was expressible, leaving the other six #628
relationships with nowhere to live. And no observation was recorded, so a
transient lookup failure and a real block were indistinguishable after the
fact.
Add the store:
- dependency_edges table under schema v4. Creating the table is itself the
v3 to v4 migration: additive, idempotent, and it never touches the
existing tables. Uniqueness is (scope, source, target, edge_type), so
re-observation updates one row rather than appending duplicates.
- dependency_graph.py owns the vocabulary: the seven #628 relationship
types, the three states, and fail-closed normalization for both plus
endpoint kinds. An unrecognized value writes nothing rather than landing
as unqueryable free text. Evidence is sanitized before storage, so no
credential or endpoint URL can be persisted or read back.
- upsert_dependency_edge, list_dependency_edges, and
record_dependency_edge_observation on ControlPlaneDB. Filtering by target
makes reverse lookup a single query. State transitions append to the
existing events table rather than a parallel audit table.
- The allocator persists what it already resolved. States map one-to-one
from the resolver's met/unmet/unavailable partitions, so nothing is
re-classified and unavailable evidence is never recorded as met.
- gitea_list_dependency_edges exposes stored edges read-only, gated on
gitea.read, and is added to the documented inventory the #781 drift guard
checks.
Selection is deliberately untouched: classify_skip still consumes the
in-memory dependency_unmet field. The write is best-effort and reports
failures through reasons, so a broken or absent store leaves allocation
behaving exactly as it did before — proven by allocating the same candidate
set through a store whose writes all raise and comparing the selection,
skip set, and candidate count.
Automatic blocking and resumption (#628 item 7), non-issue edge creation,
and defect auto-linking are later slices; this one only makes the graph
durable and queryable.
Tests: 31 new cases covering fresh-schema creation, a real v3-to-v4
migration with row retention, idempotent re-migration, enum rejection,
upsert idempotence, forward and reverse lookup, scope isolation, transition
events, redaction at rest, live allocation-run ingestion, write-failure
tolerance, and the tool's permission gate.
Verification: 4126 passed in the branch worktree. The 11 failures in
test_commit_payloads, test_issue_702_review_findings_f1_f6, test_mcp_server,
test_post_merge_moot_lease, and test_reconciler_supersession_close reproduce
identically on a clean detached checkout of master at 300e8acd, so they are
pre-existing and proven by baseline run, not introduced here.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
5.8 KiB
Registered MCP tool inventory
This is the canonical list of tools the Gitea-Tools MCP server registers. It
exists because documentation and the registered inventory drifted: the workflow
documented a gitea_edit_issue tool that no namespace had ever registered, so a
mutation could be planned against a tool that did not exist and only fail at
execution time (#781).
The rule
Documentation must never name a tool an actor cannot reach.
Two guards enforce it, both in tests/test_issue_781_edit_issue_tool.py:
- The list below must equal the registered tool set exactly — sorted, no duplicates, nothing missing in either direction. Adding a tool without documenting it fails, and documenting a tool without registering it fails.
- Every backticked
gitea_*/mcp_*identifier inskills/**/*.mdmust be a registered tool. Module and script names that share the prefix are listed explicitly inmcp_tool_inventory.NON_TOOL_IDENTIFIERSrather than being waved through by a looser pattern.
Updating this file
When you add or remove an @mcp.tool(), regenerate the block below:
PYTEST_CURRENT_TEST=1 venv/bin/python -c "
import mcp_server, mcp_tool_inventory
print(mcp_tool_inventory.render_inventory_block(
mcp_server.mcp._tool_manager._tools))
"
Replace everything between the markers with that output. Do not hand-edit individual entries — the generator and the guard share one ordering rule.
Registered tools
Namespaces (gitea-tools, gitea-reviewer, gitea-merger, gitea-reconciler)
register the same tool set; what differs per namespace is the execution profile
that gates each call, not which tools exist.
gitea_abandon_workflow_leasegitea_acquire_conflict_fix_leasegitea_acquire_merger_pr_leasegitea_acquire_reviewer_pr_leasegitea_activate_profilegitea_adopt_merger_pr_leasegitea_adopt_workflow_leasegitea_allocate_next_workgitea_assess_already_landed_reconciliationgitea_assess_conflict_fix_classificationgitea_assess_conflict_fix_pushgitea_assess_gitea_operation_pathgitea_assess_master_paritygitea_assess_mcp_namespace_healthgitea_assess_pr_sync_statusgitea_assess_review_merge_state_machinegitea_assess_reviewer_pr_leasegitea_assess_terminal_label_hygienegitea_assess_work_issue_duplicategitea_assess_worktree_cleanup_integritygitea_audit_configgitea_audit_stable_branch_contaminationgitea_audit_worktree_cleanupgitea_authorize_reconciliation_cleanup_phasegitea_authorize_review_correctiongitea_capability_stop_terminal_reportgitea_capture_branches_worktree_snapshotgitea_check_pr_eligibilitygitea_cleanup_merged_pr_branchgitea_cleanup_obsolete_reviewer_comment_leasegitea_cleanup_post_merge_moot_leasegitea_cleanup_stale_claimsgitea_cleanup_stale_review_decision_lockgitea_cleanup_terminal_pr_labelsgitea_close_issuegitea_commit_filesgitea_consume_irrecoverable_decision_lock_provenancegitea_create_issuegitea_create_issue_commentgitea_create_labelgitea_create_prgitea_delete_branchgitea_diagnose_review_decision_lockgitea_diagnose_reviewer_pr_lease_handoffgitea_diagnose_terminalgitea_dry_run_pr_reviewgitea_edit_issuegitea_edit_prgitea_expire_workflow_leasesgitea_get_authenticated_usergitea_get_current_usergitea_get_filegitea_get_pr_review_feedbackgitea_get_profilegitea_get_runtime_contextgitea_get_shell_healthgitea_heartbeat_reviewer_pr_leasegitea_inspect_workflow_leasegitea_issue_irrecoverable_provenance_authorizationgitea_list_dependency_edgesgitea_list_issue_commentsgitea_list_issuesgitea_list_labelsgitea_list_profilesgitea_list_prsgitea_list_workflow_leasesgitea_load_review_workflowgitea_lock_issuegitea_mark_final_review_decisiongitea_mark_issuegitea_merge_prgitea_mirror_refsgitea_observability_link_issuegitea_observability_list_projectsgitea_observability_reconcile_incidentgitea_post_heartbeatgitea_quarantine_contaminated_reviewgitea_reclaim_expired_workflow_leasegitea_reconcile_already_landed_prgitea_reconcile_issue_claimsgitea_reconcile_merged_cleanupsgitea_reconcile_superseded_by_merged_prgitea_record_irrecoverable_decision_lock_provenancegitea_record_pre_review_commandgitea_record_shell_spawn_outcomegitea_record_stable_branch_push_attemptgitea_release_merger_pr_leasegitea_release_reviewer_pr_leasegitea_release_workflow_leasegitea_resolve_task_capabilitygitea_resume_review_draftgitea_review_prgitea_route_task_sessiongitea_save_review_draftgitea_scan_already_landed_open_prsgitea_sentry_get_issue_eventsgitea_sentry_link_gitea_issuegitea_sentry_list_issuesgitea_sentry_reconcile_issuegitea_sentry_watchdoggitea_set_issue_labelsgitea_submit_pr_reviewgitea_update_pr_branch_by_mergegitea_validate_review_final_reportgitea_view_issuegitea_view_prgitea_whoamigitea_workflow_dashboardmcp_check_workflow_skill_preflightmcp_get_control_plane_guidemcp_get_skill_guidemcp_list_project_skills
Issue-content editing
gitea_edit_issue is the only path that changes an issue's title or body. It
PATCHes the issue endpoint, refuses a pull-request number, sends only the fields
the caller named, and proves the result by read-after-write — including that
state, labels, assignees, and milestone did not move.
gitea_edit_pr remains pull-request-only. The two paths never merge: a single
tool that accepted either kind would make the narrower capability reachable
through the wider one.