test: fail when hand-written madt_ call strings name an unregistered tool or action #860
No reviewers
Labels
No labels
breaking
bug
documentation
enhancement
epic
good first issue
help wanted
refactoring
resolution/duplicate
resolution/invalid
resolution/wontfix
security
severity/critical
severity/high
severity/low
severity/medium
status/abandoned
status/blocked
status/needs-decision
status/needs-info
status/needs-verification
testing
upstream
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
sixfold-space/madtea!860
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "issue-706-call-strings-drift"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
What was wrong
Hand-written
madt_call strings had no drift guard. The hook scripts inhooks/scripts/and the Go string literals of the MCP server (instructions, help topics, prompts, refusal text) print calls such asmadt_worktrees(action="list"). Nothing checked those strings against the tools the server serves. A renamed or removed tool or action left stale text behind.The first version of the guard found two stale strings and one removed tool name:
internal/mcp/prompts.gopassedaction="get"tomadt_status. That tool takes no action argument.whoamihelp topic ininternal/mcp/tools_help.gonamedmadt_auth_permissions, a tool that no longer exists.It also showed a gap in how I read the tool surface.
madt_logacceptsaction=list, but the guard took its action sets from two registry maps, so it treatedmadt_logas a tool with no action argument. That made the validmadt_log(action="list")strings inhooks/scripts/check-git-cli.shandhooks/scripts/check-git-local.shlook stale.What changed
internal/mcp/call_string_drift_test.go. It scans every file inhooks/scripts/*.shand the string literals (not the comments) of the non-test Go files ininternal/mcp/. It checks eachmadt_reference against the live server with every domain enabled:scanCallStringsandscanGoStringLiteralsfind the references.liveCallSurfacereads the served input schema of each tool. Noactionproperty means no action argument. Anactionenum gives the action set. If there is no enum, it falls back to the action manifest that the tool records in the registry. If there is neither,staleCallStringsreports every call string to that tool, because it cannot check them.staleCallStringsreports a reference to an unregistered tool, an action on a tool with no action argument, and an unknown action.internal/mcp/tools_localgit.go:madt_lognow publishes its action set.logActionsholds the set,logInputSchemaadds it as the enum of theactionproperty, and the handler checks the action against the same slice.internal/mcp/prompts.go: the release prompt now says "Call madt_status" with no action argument.internal/mcp/tools_help.go: thewhoamitopic no longer namesmadt_auth_permissions.madt_log(action="list")hook strings stay as they were, because they are valid.Tests
TestCallStringDriftfails when any scanned text names a tool or action that the live server does not register.TestCallStringDriftBitesplants stale strings (unknown tool, unknown action in the shell, Go and prose forms, an action on a tool with no actions, an unknown action onmadt_log, a tool with an open action set, and a stale string in a Go literal) and checks that the guard reports each one. It also checks that valid text,madt_*and a baremadt_prefix produce no report.TestCallStringAllowlistIsCurrentfails when an entry incallStringNotAToolno longer appears in any scanned text, or names a tool that is now registered.What this does not do
This covers the first two acceptance criteria of #706: a gate check for stale tool and action names in the hook scripts and the MCP text. The third criterion, the worktree guard printing a call that every MCP client can make, is a wording decision. I have not made it here. Refs #706.
./scripts/gate.shpasses on this commit.Refs #706