fix(pending-ops): MCP approval of bulk-book works and failed approvals no longer consume the op (#1852)
* fix(pending-ops): MCP approval of bulk-book works and failed approvals no longer consume the op Feedback seq 261545 (deepCFO): approving a bulk_book_transactions op over MCP returned BULK_BOOK_UNAUTHORIZED, yet the op vanished from /pending with nothing booked; the user believed it had been approved. Two defects: 1. The bulk_book_transactions RPC gates on auth.uid(), which is NULL on the cookieless service client every MCP approval runs on, so EVERY API-key approval of a samlingsverifikat was refused. New migration 20260824170000 adds p_user_id, honored only for service_role callers (same gate as match_batch_allocate 20260817150000 and undo_sie_import); the executor passes the approving user, who is now also the actor stamped on the verifikat. pg-real test covers member/spoof/no-JWT/ grants like the precedent. 2. The dispatcher consumed the op on ANY executor error other than 404/ 409. An authorization refusal happens before any side-effect and says nothing about the op, so 401/403 now release the claim back to 'pending'. The executor maps RPC codes through the structured-error registry so 403/404/409 are distinguishable from 400. Every CommitResult carries operation_status (pending | committed | rejected | failed_partial), exposed on gnubok_approve_pending_operation, so agents stop inferring consumption from status 'failed'. Catalog token ceiling 59.95K -> 60K per the documented ratchet protocol. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScVhg6XsDtNXkiEQNV7LaZ * fix(pending-ops): revoke anon explicitly on the service-actor bulk_book signature Default privileges grant EXECUTE on new functions to anon; the pg-real grants test (mirroring match_batch_allocate) caught it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ScVhg6XsDtNXkiEQNV7LaZ --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
Jakob Wennberg
parent
e35714518f
commit
dc92fb5c0c
@@ -198,9 +198,14 @@ describe('tools/list payload size guard', () => {
|
||||
// so strict clients stop failing successful unmatched uploads (seq
|
||||
// 261972). Prose trimmed to the floor first; headroom before the
|
||||
// change was ~19 tokens, so even the trimmed contract crossed.
|
||||
// * 59.95K to 60K with operation_status on gnubok_approve_pending_operation
|
||||
// (feedback seq 261545): a failed approve used to consume the op
|
||||
// silently, and agents inferred "consumed" from status 'failed' both
|
||||
// ways. The enum is the contract; the description is one clause;
|
||||
// headroom before the change was ~15 tokens, so even that crossed.
|
||||
// Long-term answer to growth is leaning harder on gnubok_search_tools: if this
|
||||
// fires again, prefer trimming descriptions or making a tool opt-in via search
|
||||
// before bumping further.
|
||||
expect(approxTokens).toBeLessThan(59_950)
|
||||
expect(approxTokens).toBeLessThan(60_000)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16843,6 +16843,7 @@ export const tools: McpTool[] = [
|
||||
error: { type: 'string' },
|
||||
error_code: { type: 'string' },
|
||||
auto_rejected: { type: 'boolean' },
|
||||
operation_status: { type: 'string', enum: ['pending', 'committed', 'rejected', 'failed_partial'], description: 'pending = not consumed, re-approvable' },
|
||||
},
|
||||
required: ['status', 'operation_id'],
|
||||
},
|
||||
@@ -16956,6 +16957,7 @@ export const tools: McpTool[] = [
|
||||
...(result.error ? { error: result.error } : {}),
|
||||
...(result.code ? { error_code: result.code } : {}),
|
||||
...(result.auto_rejected ? { auto_rejected: true } : {}),
|
||||
...(result.operation_status ? { operation_status: result.operation_status } : {}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user