mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-21 01:53:33 +00:00
fe05c170ea
* feat(mcp): v0.2 catalog read-only tools (workspace, collection, project, role, search) (TASK-980)
Second commit of TASK-970's 3-stage rollout (PLAN-969). Adds 5 read-only
tools to the v0.2 catalog. v0.1 cmdhelp walker stays live alongside;
TASK-981 retires it.
Tools added:
- pad_workspace: list, members, invite, storage, audit-log
- pad_collection: list, create
- pad_project: dashboard, next, standup, changelog
- pad_role: list, create, delete
- pad_search: query (dispatches to "item search"; cross-workspace FTS)
Catalog adjustments from DOC-978's original list:
- Dropped pad_workspace.action: get — no equivalent CLI command exists.
- Dropped pad_collection.action: schema — schema is in `collection list`
output; can be added later if dogfooding shows demand.
Latent bug fix in fan-out handler:
- makeFanOutHandler now strips the catalog's `action` routing key from
the input map before invoking the action handler. Required because
some CLI commands (workspace audit-log) declare their own `--action`
flag — without stripping, BuildCLIArgs would silently emit
`--action audit-log` instead of the user's filter value. The bug was
latent in TASK-979 (pad_meta's actions don't dispatch) and surfaces
in TASK-980 with workspace audit-log.
Custom handler for workspace.audit-log:
- The CLI's `--action <filter>` flag would still collide if exposed
directly. Schema exposes it as `action_filter`; actionWorkspaceAuditLog
renames to `action` before dispatch. All other audit-log flags flow
through unchanged.
Tests:
- TestReadOnlyCatalog_AllToolsRegistered locks the new catalog entries.
- TestReadOnlyCatalog_ActionsMatchCmdhelp verifies every passThrough
cmdPath resolves in cmdhelp (catches drift at test time).
- TestPadWorkspaceAuditLog_RenamesActionFilter / _ForwardsWithoutFilter
pin the audit-log rename behavior in both directions.
- TestMakeFanOutHandler_StripsActionFromInput pins the strip behavior
so no future passThrough can leak the routing key.
Parent: TASK-980 → TASK-970 → PLAN-969.
* fix(mcp): tighten v0.2 catalog tests per Codex review (round 1)
Two P3 findings on test gaps in TASK-980's catalog_readonly_test.go:
1. TestReadOnlyCatalog_ActionsMatchCmdhelp checked a hardcoded `expected`
table against a hardcoded `liveCmdhelpDoc`. Catalog action drift
(rename, removal, addition) and CLI command renames could pass
silently because both halves were under test control. Now the test
does three-way validation:
- Every expected cmdPath resolves in liveCmdhelpDoc (catches typos
in our own table — the original check).
- Every catalog action (modulo inline-handling tools like pad_meta)
has an expected entry (catches new actions added without coverage).
- Every expected entry has a real catalog action (catches stale test
entries that outlive the action).
2. TestReadOnlyCatalog_AllToolsRegistered only verified expected names
were present; an accidental 7th tool would pass silently. Now fails
on unexpected entries AND on Catalog length mismatch.
A `skipTools` set lets us exclude pad_meta (whose actions are inline,
not dispatched) and document the exclusion. TASK-981 will extend the
expected{} map for pad_item.
Parent: TASK-980 → TASK-970 → PLAN-969.
* fix(mcp): exercise catalog actions through fake dispatcher per Codex review (round 2)
Codex P2: TestReadOnlyCatalog_ActionsMatchCmdhelp + AllToolsRegistered
were tightened in round 1, but they still didn't catch a class of drift —
e.g. flipping pad_search.query from passThrough([]string{"item","search"})
to passThrough([]string{"some","other"}) would pass the bijection check
because the action name still matches. The expected{} table was never
exercised against the actual handler.
Add TestReadOnlyCatalog_ActionsDispatchExpectedCmdPath: invokes every
catalog action through a fake dispatcher with a maximal input fixture
(satisfies all required positionals across the read-only surface) and
asserts the captured cmdPath matches the expected table.
Closes the catalog → dispatch drift hole. Now if anyone changes a
passThrough cmdPath without updating expected{}, this test fails
loudly with the actual dispatched path printed in the error.
Parent: TASK-980 → TASK-970 → PLAN-969.