Revalidate MCP actions before dispatch - #198
Conversation
|
The I've completed my review. The change is well-structured and thoroughly tested. Let me summarize my findings. Review SummaryThis PR revalidates approved MCP actions immediately before dispatch, so a stale action (connection/grant/policy changed while awaiting approval) is never sent. The design is sound and consistent with the kernel's capability model, and the test coverage is substantial (action-store, facet, session, auto-approval, Activity). I reviewed the full diff ( Findings — no blocking issues. A few observations, none actionable enough to block:
LGTM! |
cc21770 to
715cc94
Compare
04fb946 to
f043907
Compare
|
The interface declares I've thoroughly traced all the key flows. The existing bonk review already exists with a detailed analysis and LGTM verdict. My independent review confirms the same conclusions:
I found no discrete, actionable problems introduced by this change beyond the by-design observations the existing review already documented (extra remote round-trip per tool call; the compat LGTM! |
715cc94 to
972434f
Compare
f043907 to
b8fffc8
Compare
|
The read path throws I've completed a thorough independent review. My review confirms the design is sound and I found no discrete, actionable problems introduced by this change beyond the by-design observations the two existing bonk reviews already documented (extra remote round-trip per tool call; the defensive
No inline findings to submit. LGTM! |
|
The frontend tests confirm the invalidation reason renders in history and that changing I've completed a thorough independent review. Let me summarize. Review SummaryThis PR revalidates an approved MCP action immediately before dispatch, so a stale action (whose connection generation, tool scope, or effective policy changed while awaiting approval) is never sent. I reviewed the full diff ( Verified correct:
By-design observations (not blocking):
I found no discrete, actionable problems introduced by this change. The earlier bonk comments referencing a LGTM! |
2846cdc to
f5c6473
Compare
b8fffc8 to
90f8ab6
Compare
|
Test coverage is thorough, including the read-path generation-change guard. My independent review confirms the design is sound and the implementation is correct. I traced all the key flows independently:
All callers of the changed signatures ( LGTM! |
90f8ab6 to
b77136c
Compare
Preview:
|
ca9faa6 to
ac3b2f8
Compare
a6394d8 to
d90a456
Compare
ac3b2f8 to
6e32eaa
Compare
d90a456 to
7a2a950
Compare
6e32eaa to
405da7a
Compare
e97c4f4 to
a4c0bb9
Compare
a22b59a to
295d35c
Compare
295d35c to
04bbfee
Compare
a4c0bb9 to
5295e5d
Compare
5295e5d to
638ac69
Compare
04bbfee to
98a2485
Compare
638ac69 to
8624ef7
Compare
98a2485 to
0f0e771
Compare
0f0e771 to
bb4d40c
Compare
| record.state = "rejected"; | ||
| record.appliedAt = new Date(); | ||
| record.resolvedBy = resolvedBy; | ||
| record.invalidationReason = failureReason; | ||
| this.storage.actions.put(record); | ||
| return "stopped"; |
There was a problem hiding this comment.
🟡 Automatically approved requests that get cancelled are logged as if a person decided them
The record for a request that could not be carried out is stamped with the rule owner's name but not with the fact that it was handled automatically (record.resolvedBy = resolvedBy at packages/workshop-backend/src/overseer.ts:2680), so the activity log presents an automatic outcome as that person's own decision.
Impact: In the audit trail and the history panel, a request that a background rule attempted and that was then cancelled reads as "By ", hiding that no human acted.
Why the automatic marker is lost on the new invalidation path
applyPendingAction is documented as the single chokepoint that "guarantees the audit log always records the resolving user and whether it was applied automatically" (packages/workshop-backend/src/overseer.ts:2660-2663). The new pre-dispatch failure branch (packages/workshop-backend/src/overseer.ts:2675-2684) sets state, appliedAt, resolvedBy and invalidationReason, but never autoApproved, even though the drain path calls it with autoApproved = true (packages/workshop-backend/src/auto-approval.ts:119).
The UI derives its badge from that field: const autoApproved = record.type === 'action' && record.autoApproved === true in packages/workshop-frontend/src/Activity.tsx:605, rendering By ${resolvedBy.name} instead of Auto-approved (${resolvedBy.name}'s rule) (packages/workshop-frontend/src/Activity.tsx:620). So an invalidated auto-approval is indistinguishable from a manual approval attributed to the rule enabler.
Note the existing doc comment on ActionLogEntry.autoApproved ("Only ever set alongside state "approved" (there is no automatic rejection)") is also now inaccurate: this PR introduces an automatic non-approval outcome.
Was this helpful? React with 👍 or 👎 to provide feedback.
bb4d40c to
6b09f15
Compare
6b09f15 to
728b985
Compare
An MCP action may wait for approval while its connection, grant, or effective policy changes. This captures the approved connection generation and policy, then rechecks them within the bounded discovery path immediately before
tools/call. A stale action is never dispatched; it is recorded using the existing rejected wire state plus an invalidation reason, and auto-approval rules are cleared so current and rolled-back backends stop the current drain.Invalidation uses a thrown, message-preserved sentinel so older Workshops fail closed. Re-enabling a cleared rule explicitly allows future auto-approval rather than leaving a permanent historical barrier.
Stacked on #199. Verified with 316 MCP tests, 289 backend tests plus integration tests, 152 frontend tests, both MCP connector suites, and full
pnpm lint.