thumbgate 1.30.0 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.well-known/mcp/server-card.json +1 -1
  3. package/README.md +54 -16
  4. package/adapters/claude/.mcp.json +2 -2
  5. package/adapters/forge/forge.yaml +3 -3
  6. package/adapters/mcp/server-stdio.js +105 -10
  7. package/adapters/opencode/opencode.json +1 -1
  8. package/bench/observability-eval-suite.json +2 -2
  9. package/bin/cli.js +168 -31
  10. package/config/evals/generation-quality-golden.json +95 -0
  11. package/config/evals/rag-answer-quality-golden.json +91 -0
  12. package/config/evals/retrieval-hybrid-ablation.json +66 -0
  13. package/config/evals/retrieval-ranking-golden.json +522 -0
  14. package/config/gates/claim-verifiers.example.json +42 -0
  15. package/config/gates/claim-verifiers.json +25 -0
  16. package/config/gates/default.json +217 -50
  17. package/config/mcp-allowlists.json +233 -206
  18. package/config/model-tiers.json +7 -2
  19. package/glama.json +6 -0
  20. package/hooks/hooks.json +1 -1
  21. package/package.json +69 -12
  22. package/public/assets/diagrams/before-after.svg +17 -16
  23. package/public/assets/diagrams/hero-thumbs.svg +68 -0
  24. package/public/assets/diagrams/loop.svg +19 -13
  25. package/public/assets/diagrams/self-improving-thumbs-loop.svg +105 -0
  26. package/public/compare.html +1 -0
  27. package/public/dashboard.html +126 -28
  28. package/public/evaluations.html +1 -1
  29. package/public/index.html +142 -13
  30. package/public/numbers.html +3 -2
  31. package/public/pricing.html +143 -30
  32. package/scripts/a-plus-evidence-scorecard.js +303 -0
  33. package/scripts/agent-readiness.js +110 -0
  34. package/scripts/async-eval-observability.js +36 -11
  35. package/scripts/audit-trail.js +37 -1
  36. package/scripts/auto-promote-gates.js +149 -34
  37. package/scripts/auto-wire-hooks.js +20 -8
  38. package/scripts/cli-schema.js +14 -0
  39. package/scripts/colbert-style-maxsim.js +236 -0
  40. package/scripts/cross-encoder-reranker.js +356 -126
  41. package/scripts/dashboard-chat.js +350 -17
  42. package/scripts/document-intake.js +283 -7
  43. package/scripts/eval-quality-suite.js +204 -0
  44. package/scripts/feedback-loop.js +115 -7
  45. package/scripts/feedback-paths.js +32 -13
  46. package/scripts/feedback-quality.js +53 -0
  47. package/scripts/feedback-schema.js +3 -0
  48. package/scripts/file-ledger-lock.js +130 -0
  49. package/scripts/filesystem-search.js +17 -7
  50. package/scripts/financial-control-plane.js +1514 -0
  51. package/scripts/gates-engine.js +202 -7
  52. package/scripts/gemini-embedding-policy.js +1 -0
  53. package/scripts/harness-tool-names.js +70 -0
  54. package/scripts/hook-runtime.js +15 -3
  55. package/scripts/hook-stop-anti-claim.js +63 -3
  56. package/scripts/human-escalation.js +353 -41
  57. package/scripts/lesson-db.js +16 -5
  58. package/scripts/lesson-embedding-index.js +67 -20
  59. package/scripts/lesson-embedding-maintenance.js +177 -0
  60. package/scripts/lesson-reranker.js +55 -9
  61. package/scripts/lesson-retrieval.js +305 -29
  62. package/scripts/lesson-search.js +22 -8
  63. package/scripts/llm-client.js +304 -15
  64. package/scripts/model-tier-router.js +593 -0
  65. package/scripts/pragmatic-hybrid-search.js +379 -0
  66. package/scripts/provider-action-normalizer.js +11 -4
  67. package/scripts/rag-document-pipeline.js +461 -0
  68. package/scripts/rag-structured-output.js +441 -0
  69. package/scripts/ragas-style-metrics.js +351 -0
  70. package/scripts/request-envelope.js +178 -0
  71. package/scripts/rerank-pipeline.js +370 -0
  72. package/scripts/rerank-quality-eval.js +155 -0
  73. package/scripts/retrieval-hybrid-ablation.js +120 -0
  74. package/scripts/retrieval-quality-tier.js +118 -0
  75. package/scripts/secret-scanner.js +395 -4
  76. package/scripts/self-distill-agent.js +7 -1
  77. package/scripts/self-healing-check.js +25 -0
  78. package/scripts/skill-packs.js +183 -0
  79. package/scripts/slow-loop.js +72 -0
  80. package/scripts/statusline-links.js +1 -1
  81. package/scripts/statusline.sh +8 -1
  82. package/scripts/telemetry-analytics.js +13 -1
  83. package/scripts/thumbgate-search.js +98 -6
  84. package/scripts/tier-budget-guard.js +186 -0
  85. package/scripts/tool-registry.js +141 -5
  86. package/scripts/universal-claim-evaluator.js +767 -0
  87. package/scripts/vector-store.js +154 -17
  88. package/scripts/verify-marketing-pages-deployed.js +85 -3
  89. package/scripts/workflow-sentinel.js +77 -11
  90. package/server.json +44 -0
  91. package/smithery.yaml +17 -0
  92. package/src/api/server.js +196 -13
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thumbgate",
3
3
  "description": "One 👎 becomes a hard rule the agent cannot bypass. Captures thumbs-down feedback, distills it into PreToolUse Pre-Action Checks, enforced across every future Claude Code session.",
4
- "version": "1.30.0",
4
+ "version": "1.34.0",
5
5
  "author": {
6
6
  "name": "Igor Ganapolsky",
7
7
  "email": "ig5973700@gmail.com",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.30.0",
3
+ "version": "1.34.0",
4
4
  "description": "ThumbGate — 👍👎 feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
5
5
  "homepage": "https://thumbgate.ai",
6
6
  "transport": "stdio",
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # ThumbGate
2
2
 
3
+ [![MCP Toplist](https://mcptoplist.com/badge/glama%2FIgorGanapolsky%2FThumbGate.svg)](https://mcptoplist.com/server/glama%2FIgorGanapolsky%2FThumbGate)
4
+
3
5
  <p align="center">
4
6
  <a href="https://thumbgate.ai">
5
7
  <img src="public/assets/brand/thumbgate-icon-512.png" alt="ThumbGate" width="128" height="128" />
@@ -28,6 +30,19 @@ Accepted feedback is stored as local lessons. Repeated concrete failures can bec
28
30
  npx thumbgate init # auto-detects the supported agent and wires its integration
29
31
  ```
30
32
 
33
+ ### MCP / Glama / registry install (stdio)
34
+
35
+ Directories and clients that install ThumbGate as an MCP server must start **stdio MCP**, not the HTTP API:
36
+
37
+ ```bash
38
+ npx -y thumbgate serve
39
+ ```
40
+
41
+ - Equivalent: `npx -y thumbgate mcp`
42
+ - Do **not** use `npm start` for MCP — that launches the hosted HTTP API (`src/api/server.js`), not the agent-facing stdio server.
43
+ - Canonical package metadata: `server.json` (`runtimeHint: npx` + `packageArguments: ["serve"]`), Smithery: `smithery.yaml`, maintainers: `glama.json`.
44
+ - Product name is **ThumbGate** only (npm: `thumbgate`). Retired legacy package aliases are not active product surfaces.
45
+
31
46
  Works with **Claude Code, Cursor, Codex, Gemini CLI, Amp, Cline, OpenCode** and MCP-compatible agents after their integration is configured. Free tier: 2 feedback captures/day (10 total) and up to 3 active auto-promoted prevention rules. [Pro: $19/mo or $149/yr](https://thumbgate.ai/checkout/pro?utm_source=github&utm_medium=readme) is the individual tier for unlimited rules, history-aware lessons, feedback sessions, a personal dashboard, and DPO export. Enterprise is custom and scoped after intake; hosted team sync and a hosted org dashboard are not in the current general-availability runtime.
32
47
 
33
48
  [![CI](https://github.com/IgorGanapolsky/ThumbGate/actions/workflows/ci.yml/badge.svg)](https://github.com/IgorGanapolsky/ThumbGate/actions/workflows/ci.yml)
@@ -256,6 +271,11 @@ evaluation/observability—is maintained in
256
271
  architecture exists, what can fail, how it is measured and secured, how it is
257
272
  deployed, and what evidence is required before claiming it works.
258
273
 
274
+ The full framework decision and one end-to-end RAG request—tenant authorization,
275
+ query transformation, parent-child retrieval, hybrid fusion, reranking,
276
+ structured generation, evaluation, and tracing—are documented in
277
+ [`docs/RAG_PRODUCTION_ARCHITECTURE.md`](./docs/RAG_PRODUCTION_ARCHITECTURE.md).
278
+
259
279
  ### Retrieval & latency: local-first, zero network hops
260
280
 
261
281
  ThumbGate's latency advantage is structural, not a tuned cloud cluster: there is no retrieval service and no model on the enforcement path, so the gate decision never leaves your machine.
@@ -302,22 +322,40 @@ Each recommendation ships with the benchmark commands to run next: feedback-deri
302
322
 
303
323
  ## Install for Your Agent
304
324
 
305
- | Agent | Command |
306
- |-------|---------|
307
- | **Claude Code** | `npx thumbgate init --agent claude-code` |
308
- | **Cursor** | `npx thumbgate init --agent cursor` |
309
- | **VS Code / Open VSX** | [plugins/vscode-extension/README.md](plugins/vscode-extension/README.md) |
310
- | **Antigravity-compatible** | [plugins/antigravity-extension/INSTALL.md](plugins/antigravity-extension/INSTALL.md) |
311
- | **JetBrains** | [plugins/jetbrains-plugin/README.md](plugins/jetbrains-plugin/README.md) |
312
- | **Codex** | `npx thumbgate init --agent codex` |
313
- | **Gemini CLI** | `npx thumbgate init --agent gemini` |
314
- | **Amp** | `npx thumbgate init --agent amp` |
315
- | **Cline** (Roo Code successor) | `npx thumbgate init --agent cline` |
316
- | **OpenCode** | `npx thumbgate init --agent opencode` |
317
- | **Claude Desktop** | [Download extension bundle](https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb) |
318
- | **Any MCP agent** | `npx thumbgate serve` |
319
-
320
- Works with **Claude Code, Cursor, Codex, Gemini CLI, Amp, Cline, OpenCode**, and any MCP-compatible agent. Migrating from Roo Code (sunsetting 2026-05-15)? See [`adapters/cline/INSTALL.md`](./adapters/cline/INSTALL.md).
325
+ Enforcement depends on what the harness lets us intercept, so the table says which
326
+ you get. This distinction is real: with a pre-tool hook ThumbGate stops the action
327
+ before it runs; over MCP only, ThumbGate answers `gate_check` and the agent decides
328
+ whether to obey.
329
+
330
+ | Agent | Command | Enforcement |
331
+ |-------|---------|-------------|
332
+ | **Claude Code** | `npx thumbgate init --agent claude-code` | Hard — PreToolUse hook |
333
+ | **Codex** | `npx thumbgate init --agent codex` | Hard — `pre_tool_use` hook |
334
+ | **Gemini CLI** | `npx thumbgate init --agent gemini` | Hard — PreToolUse hook |
335
+ | **ForgeCode** | `npx thumbgate init --agent forge` | Hard — `pre_tool_use` trigger |
336
+ | **Cursor** | `npx thumbgate init --agent cursor` | Advisory — MCP `gate_check` |
337
+ | **Cline** (Roo Code successor) | `npx thumbgate init --agent cline` | Advisory — MCP `gate_check` + `.clinerules` |
338
+ | **OpenCode** | `npx thumbgate init --agent opencode` | Advisory — MCP `gate_check` |
339
+ | **Any MCP agent** | `npx thumbgate serve` | Advisory — MCP `gate_check` |
340
+ | **Amp** | `npx thumbgate init --agent amp` | Feedback capture only |
341
+ | **Claude Desktop** | [Download extension bundle](https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb) | Advisory — MCP |
342
+ | **VS Code / Open VSX** | [plugins/vscode-extension/README.md](plugins/vscode-extension/README.md) | See plugin README |
343
+ | **Antigravity-compatible** | [plugins/antigravity-extension/INSTALL.md](plugins/antigravity-extension/INSTALL.md) | See plugin README |
344
+ | **JetBrains** | [plugins/jetbrains-plugin/README.md](plugins/jetbrains-plugin/README.md) | See plugin README |
345
+
346
+ **Advisory means the agent can ignore it.** Harnesses without a pre-tool hook expose no
347
+ interception point, so ThumbGate cannot stop the call itself — it returns a verdict the
348
+ agent is instructed to honor. Treat advisory coverage as a strong default, not a
349
+ guarantee, and prefer a hard-enforcement harness for anything irreversible.
350
+
351
+ The gate is **model-agnostic**: verdicts come from deterministic policy evaluation over
352
+ the proposed tool call (`scripts/gates-engine.js`), never from an LLM. Swapping the model
353
+ behind any harness does not change what is allowed.
354
+
355
+ > **Enforcement posture:** ThumbGate ships **warn-by-default** — a matched gate is logged
356
+ > and surfaced, not blocked. Set `THUMBGATE_STRICT_ENFORCEMENT=1` to hard-block. The
357
+ > `gate_check` tool reports `warn` (never `allow`) when a gate matched but posture
358
+ > downgraded it, so an agent is never told a flagged action is fine.
321
359
 
322
360
  ### Install scope: machine-wide vs per-project
323
361
 
@@ -2,13 +2,13 @@
2
2
  "mcpServers": {
3
3
  "thumbgate": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "thumbgate@1.30.0", "thumbgate", "serve"]
5
+ "args": ["--yes", "--package", "thumbgate@1.34.0", "thumbgate", "serve"]
6
6
  }
7
7
  },
8
8
  "hooks": {
9
9
  "preToolUse": {
10
10
  "command": "npx",
11
- "args": ["--yes", "--package", "thumbgate@1.30.0", "thumbgate", "gate-check"]
11
+ "args": ["--yes", "--package", "thumbgate@1.34.0", "thumbgate", "gate-check"]
12
12
  }
13
13
  }
14
14
  }
@@ -9,12 +9,12 @@ version: "1"
9
9
  skills:
10
10
  thumbgate-gate-check:
11
11
  description: "ThumbGate PreToolUse gate — blocks known-bad tool calls"
12
- command: "npx --yes --package thumbgate@1.30.0 thumbgate gate-check"
12
+ command: "npx --yes --package thumbgate@1.34.0 thumbgate gate-check"
13
13
  trigger: pre_tool_use
14
14
 
15
15
  thumbgate-feedback:
16
16
  description: "ThumbGate feedback capture — logs user prompt context"
17
- command: "npx --yes --package thumbgate@1.30.0 thumbgate hook-auto-capture"
17
+ command: "npx --yes --package thumbgate@1.34.0 thumbgate hook-auto-capture"
18
18
  trigger: user_prompt
19
19
 
20
20
  mcp:
@@ -23,6 +23,6 @@ mcp:
23
23
  args:
24
24
  - "--yes"
25
25
  - "--package"
26
- - "thumbgate@1.30.0"
26
+ - "thumbgate@1.34.0"
27
27
  - "thumbgate"
28
28
  - "serve"
@@ -95,6 +95,19 @@ const {
95
95
  listEscalations,
96
96
  requestEscalation,
97
97
  } = require('../../scripts/human-escalation');
98
+ const {
99
+ createPurchaseRequisition,
100
+ getFinancialControlRuntimeOptions,
101
+ getRuntimePrincipal,
102
+ listPurchaseRequisitions,
103
+ reconcilePurchaseLedger,
104
+ reservePurchaseRequisition,
105
+ settlePurchaseRequisition,
106
+ } = require('../../scripts/financial-control-plane');
107
+ const MCP_FINANCIAL_PRINCIPAL = getRuntimePrincipal();
108
+ const MCP_FINANCIAL_OPTIONS = getFinancialControlRuntimeOptions({
109
+ authenticatedPrincipal: MCP_FINANCIAL_PRINCIPAL,
110
+ });
98
111
  const { recordReasoningTrace } = require('../../scripts/agent-reasoning-traces');
99
112
  const { recordToolCall } = require('../../scripts/tool-kpi-tracker');
100
113
  const {
@@ -133,9 +146,7 @@ const {
133
146
  throw error;
134
147
  },
135
148
  }));
136
- const {
137
- searchThumbgate,
138
- } = require('../../scripts/thumbgate-search');
149
+ const { searchThumbgateAsync } = require('../../scripts/thumbgate-search');
139
150
  const {
140
151
  buildMultimodalRetrievalPlan,
141
152
  } = require('../../scripts/multimodal-retrieval-plan');
@@ -317,7 +328,7 @@ const {
317
328
  finalizeSession: finalizeFeedbackSession,
318
329
  } = require('../../scripts/feedback-session');
319
330
 
320
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.30.0' };
331
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.34.0' };
321
332
  const COMMERCE_CATEGORIES = [
322
333
  'product_recommendation',
323
334
  'brand_compliance',
@@ -971,14 +982,14 @@ async function callToolInner(name, args) {
971
982
  return buildSuggestFixResponse(args);
972
983
  case 'retrieve_lessons': {
973
984
  // Cross-encoder reranking: retrieve more candidates, then rerank for precision
974
- const { retrieveWithRerankingSync } = loadOptionalModule(path.join(__dirname, '../../scripts/cross-encoder-reranker'), () => ({
975
- retrieveWithRerankingSync: (toolName, actionContext, options = {}) => retrieveRelevantLessons(
985
+ const { retrieveWithReranking } = loadOptionalModule(path.join(__dirname, '../../scripts/cross-encoder-reranker'), () => ({
986
+ retrieveWithReranking: async (toolName, actionContext, options = {}) => retrieveRelevantLessons(
976
987
  toolName,
977
988
  actionContext,
978
989
  { maxResults: options.maxResults || 5 },
979
990
  ),
980
991
  }));
981
- return toTextResult(retrieveWithRerankingSync(
992
+ return toTextResult(await retrieveWithReranking(
982
993
  args.toolName,
983
994
  args.actionContext || '',
984
995
  {
@@ -987,6 +998,9 @@ async function callToolInner(name, args) {
987
998
  scope: args.scope,
988
999
  requireScope: args.requireScope === true,
989
1000
  includeShared: args.includeShared !== false,
1001
+ metadataFilters: args.filters,
1002
+ queryRewrite: args.queryRewrite !== false,
1003
+ includeRetrievalMeta: args.includeRetrievalMeta === true,
990
1004
  },
991
1005
  ));
992
1006
  }
@@ -1009,11 +1023,13 @@ async function callToolInner(name, args) {
1009
1023
  }
1010
1024
  case 'search_thumbgate':
1011
1025
  enforceLimit('search_thumbgate');
1012
- return toTextResult(searchThumbgate({
1026
+ return toTextResult(await searchThumbgateAsync({
1013
1027
  query: args.query,
1014
1028
  limit: args.limit,
1015
1029
  source: args.source,
1016
1030
  signal: args.signal,
1031
+ metadataFilters: args.filters,
1032
+ queryRewrite: args.queryRewrite !== false,
1017
1033
  }));
1018
1034
  case 'import_document':
1019
1035
  return toTextResult(importDocument({
@@ -1038,6 +1054,61 @@ async function callToolInner(name, args) {
1038
1054
  }
1039
1055
  return toTextResult(document);
1040
1056
  }
1057
+ case 'gate_check': {
1058
+ // Same engine the PreToolUse hook uses, so an MCP client and a hook cannot
1059
+ // disagree about whether an action is allowed.
1060
+ const { runAsync } = require('../../scripts/gates-engine');
1061
+ const { canonicalizeToolCall } = require('../../scripts/harness-tool-names');
1062
+ const canonical = canonicalizeToolCall(args.tool_name, args.tool_input || {});
1063
+ const raw = await runAsync({
1064
+ tool_name: canonical.toolName,
1065
+ tool_input: canonical.toolInput,
1066
+ });
1067
+ let decision = 'allow';
1068
+ let reason = '';
1069
+ let flagged = false;
1070
+ try {
1071
+ const parsed = JSON.parse(raw);
1072
+ const hook = parsed.hookSpecificOutput || {};
1073
+ const verdict = hook.permissionDecision || parsed.decision || '';
1074
+ reason = hook.permissionDecisionReason || parsed.reason || hook.additionalContext || '';
1075
+ // The hook wire format says "deny"; the documented tool contract says "block".
1076
+ if (verdict === 'deny' || verdict === 'block') {
1077
+ decision = 'block';
1078
+ flagged = true;
1079
+ } else if (/\[GATE:/.test(reason)) {
1080
+ // A gate MATCHED but the warn-by-default posture downgraded it. Reporting
1081
+ // "allow" here is how this tool would become theater: .clinerules tells the
1082
+ // agent to abort only on "block", so a matched rm -rf / would have been run
1083
+ // with the warning text ignored. "warn" is the honest third state.
1084
+ decision = 'warn';
1085
+ flagged = true;
1086
+ }
1087
+ } catch (_) {
1088
+ // Unparseable engine output must never read as "allow" — fail closed.
1089
+ decision = 'error';
1090
+ reason = 'gate engine returned unparseable output';
1091
+ }
1092
+ const strict = process.env.THUMBGATE_STRICT_ENFORCEMENT === '1';
1093
+ return {
1094
+ content: [{
1095
+ type: 'text',
1096
+ text: JSON.stringify({
1097
+ decision,
1098
+ flagged,
1099
+ enforcement: strict ? 'strict' : 'warn-by-default',
1100
+ guidance: decision === 'block'
1101
+ ? 'Do NOT run this action. Surface the reason to the user.'
1102
+ : decision === 'warn'
1103
+ ? 'A policy gate matched but enforcement is warn-by-default. Do NOT run this action without explicit user confirmation; show them the reason.'
1104
+ : decision === 'error'
1105
+ ? 'Gate evaluation failed. Treat as unsafe and ask the user.'
1106
+ : 'No policy gate matched.',
1107
+ reason,
1108
+ }, null, 2),
1109
+ }],
1110
+ };
1111
+ }
1041
1112
  case 'feedback_stats':
1042
1113
  return toTextResult(analyzeFeedback(undefined, { humanOnly: true }));
1043
1114
  case 'diagnose_failure':
@@ -1310,6 +1381,20 @@ async function callToolInner(name, args) {
1310
1381
  return toTextResult(requestEscalation(args));
1311
1382
  case 'list_human_escalations':
1312
1383
  return toTextResult(listEscalations({ status: args.status }).slice(0, Number(args.limit || 20)));
1384
+ case 'create_purchase_requisition':
1385
+ return toTextResult(createPurchaseRequisition(args, MCP_FINANCIAL_OPTIONS));
1386
+ case 'list_purchase_requisitions': {
1387
+ const rows = listPurchaseRequisitions(MCP_FINANCIAL_OPTIONS)
1388
+ .filter((entry) => !args.status || entry.status === args.status)
1389
+ .slice(0, Number(args.limit || 20));
1390
+ return toTextResult(rows);
1391
+ }
1392
+ case 'reserve_purchase_requisition':
1393
+ return toTextResult(reservePurchaseRequisition(args, MCP_FINANCIAL_OPTIONS));
1394
+ case 'settle_purchase_requisition':
1395
+ return toTextResult(settlePurchaseRequisition(args, MCP_FINANCIAL_OPTIONS));
1396
+ case 'reconcile_purchase_ledger':
1397
+ return toTextResult(reconcilePurchaseLedger(MCP_FINANCIAL_OPTIONS));
1313
1398
  case 'verify_claim':
1314
1399
  return toTextResult(verifyClaimEvidence(args.claim, { goalContract: args.goalContract }));
1315
1400
  case 'require_evidence_for_claim': {
@@ -1324,6 +1409,9 @@ async function callToolInner(name, args) {
1324
1409
  const missingActions = hasMatchingChecks
1325
1410
  ? Array.from(new Set(verification.checks.flatMap((check) => check.missing || [])))
1326
1411
  : [];
1412
+ const factualMismatches = Array.isArray(verification.universal && verification.universal.checks)
1413
+ ? verification.universal.checks.filter((check) => !check.passed)
1414
+ : [];
1327
1415
  try {
1328
1416
  const { recordAuditEvent } = require('../../scripts/audit-trail');
1329
1417
  recordAuditEvent({
@@ -1335,13 +1423,16 @@ async function callToolInner(name, args) {
1335
1423
  goalContract: verification.goalContract && verification.goalContract.matched
1336
1424
  ? verification.goalContract
1337
1425
  : null,
1426
+ universalParsed: verification.universal ? verification.universal.parsedCount : 0,
1338
1427
  },
1339
1428
  decision: blocking ? 'deny' : 'allow',
1340
1429
  gateId: verification.goalContract && verification.goalContract.matched
1341
1430
  ? 'completion_goal_contract'
1342
- : 'completion_claim',
1431
+ : (factualMismatches.length > 0 ? 'completion_claim_factual' : 'completion_claim'),
1343
1432
  message: blocking
1344
- ? `Completion claim blocked — missing evidence: ${missingActions.join(', ') || 'unknown'}`
1433
+ ? (factualMismatches.length > 0
1434
+ ? `Completion claim blocked — factual mismatch/unconfigured: ${factualMismatches.map((c) => c.message).join('; ')}`
1435
+ : `Completion claim blocked — missing evidence: ${missingActions.join(', ') || 'unknown'}`)
1345
1436
  : `Completion claim verified (${verification.verified ? 'evidence present' : 'no matching gate'})`,
1346
1437
  source: 'completion-gate',
1347
1438
  });
@@ -1355,6 +1446,8 @@ async function callToolInner(name, args) {
1355
1446
  missingActions,
1356
1447
  checks: verification.checks,
1357
1448
  goalContract: verification.goalContract,
1449
+ universal: verification.universal,
1450
+ factualMismatches,
1358
1451
  sessionId: args.sessionId || null,
1359
1452
  });
1360
1453
  }
@@ -1419,6 +1512,7 @@ async function callToolInner(name, args) {
1419
1512
  mcp: args.mcp,
1420
1513
  mcpToolCall: args.mcpToolCall,
1421
1514
  budget: args.budget,
1515
+ financialControl: args.financialControl,
1422
1516
  usage: args.usage,
1423
1517
  }, {
1424
1518
  provider: args.provider,
@@ -1428,6 +1522,7 @@ async function callToolInner(name, args) {
1428
1522
  tokenEstimate: args.tokenEstimate,
1429
1523
  costUsd: args.costUsd,
1430
1524
  budget: args.budget,
1525
+ financialControl: args.financialControl,
1431
1526
  repoPath: args.repoPath,
1432
1527
  baseBranch: args.baseBranch,
1433
1528
  affectedFiles: changedFiles.length > 0 ? changedFiles : undefined,
@@ -7,7 +7,7 @@
7
7
  "npx",
8
8
  "--yes",
9
9
  "--package",
10
- "thumbgate@1.30.0",
10
+ "thumbgate@1.34.0",
11
11
  "thumbgate",
12
12
  "serve"
13
13
  ],
@@ -4,7 +4,7 @@
4
4
  "id": "checkout-link-grounding",
5
5
  "traceId": "trace_checkout_001",
6
6
  "question": "Is the Pro checkout link working?",
7
- "response": "The Pro checkout link is working because the checkout diagnostic confirms Stripe Pro checkout is reachable.",
7
+ "response": "The Pro checkout link is working because the checkout diagnostic confirms Stripe Pro checkout is reachable [context-1].",
8
8
  "retrievedContexts": [
9
9
  "Stripe Pro checkout diagnostic confirms the Pro checkout link is reachable and returns a valid checkout target.",
10
10
  "The workflow sprint checkout is intake-led and should not expose a stale payment link."
@@ -15,7 +15,7 @@
15
15
  "id": "letta-tool-gate",
16
16
  "traceId": "trace_letta_001",
17
17
  "question": "Should Letta execute a force push tool call?",
18
- "response": "ThumbGate blocks force push before execution for Letta tool calls.",
18
+ "response": "ThumbGate blocks force push before execution for Letta tool calls [context-1].",
19
19
  "retrievedContexts": [
20
20
  "ThumbGate's Letta adapter wraps Letta tool calls and runs gate-check before the executor is called.",
21
21
  "Force push to main is a blocked high-risk git action."