thumbgate 1.27.12 → 1.27.14

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 (133) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.well-known/llms.txt +2 -1
  3. package/.well-known/mcp/server-card.json +1 -1
  4. package/README.md +2 -4
  5. package/adapters/claude/.mcp.json +2 -2
  6. package/adapters/mcp/server-stdio.js +1 -1
  7. package/adapters/opencode/opencode.json +1 -1
  8. package/adapters/policy-engine/ethicore-guardian-client.js +68 -0
  9. package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +260 -0
  10. package/bin/cli.js +78 -259
  11. package/config/gate-templates.json +0 -228
  12. package/config/gates/claim-verification.json +0 -18
  13. package/package.json +35 -25
  14. package/public/assets/brand/thumbgate-logo-transparent.svg +22 -0
  15. package/public/assets/brand/thumbgate-mark-inline-v3.svg +19 -0
  16. package/public/assets/brand/thumbgate-mark.svg +11 -5
  17. package/public/blog.html +0 -30
  18. package/public/brand/thumbgate-mark.svg +9 -5
  19. package/public/chatgpt-app.html +2 -2
  20. package/public/compare.html +2 -1
  21. package/public/dashboard.html +1 -1
  22. package/public/federal.html +1 -1
  23. package/public/index.html +95 -216
  24. package/public/learn.html +59 -35
  25. package/public/lessons.html +1 -1
  26. package/public/numbers.html +2 -2
  27. package/public/pro.html +7 -7
  28. package/scripts/agent-readiness.js +142 -0
  29. package/scripts/aws-blocks-guardrails.js +228 -0
  30. package/scripts/cli-schema.js +22 -10
  31. package/scripts/dashboard-chat.js +2 -1
  32. package/scripts/document-intake.js +1 -49
  33. package/scripts/durability/step.js +3 -3
  34. package/scripts/gate-stats.js +5 -11
  35. package/scripts/gates-engine.js +0 -49
  36. package/scripts/gemini-embedding-policy.js +2 -1
  37. package/scripts/hook-stop-anti-claim.js +116 -184
  38. package/scripts/hosted-config.js +0 -12
  39. package/scripts/lesson-search.js +1 -15
  40. package/scripts/llm-client.js +187 -5
  41. package/scripts/plausible-domain-config.js +3 -1
  42. package/scripts/seo-gsd.js +240 -1
  43. package/scripts/tool-registry.js +2 -2
  44. package/scripts/vector-store.js +44 -0
  45. package/scripts/workspace-evolver.js +62 -2
  46. package/src/api/server.js +340 -131
  47. package/public/assets/brand/thumbgate-mark-inline.svg +0 -15
  48. package/public/compare/adopt-ai.html +0 -219
  49. package/public/compare/agentix-labs.html +0 -197
  50. package/public/compare/ai-experience-orchestration.html +0 -216
  51. package/public/compare/anthropic-claude-for-legal.html +0 -260
  52. package/public/compare/anthropic-containment.html +0 -280
  53. package/public/compare/arcade.html +0 -175
  54. package/public/compare/arcjet.html +0 -239
  55. package/public/compare/bumblebee.html +0 -307
  56. package/public/compare/claude-code-hooks.html +0 -294
  57. package/public/compare/databricks-unity-ai-gateway.html +0 -215
  58. package/public/compare/fallow.html +0 -351
  59. package/public/compare/heidi.html +0 -233
  60. package/public/compare/mem0.html +0 -342
  61. package/public/compare/oak-and-sparrow-gatekeeper.html +0 -289
  62. package/public/compare/rein.html +0 -236
  63. package/public/compare/sigmashake.html +0 -256
  64. package/public/compare/speclock.html +0 -342
  65. package/public/guides/agent-harness-optimization.html +0 -342
  66. package/public/guides/agentic-web-governance.html +0 -406
  67. package/public/guides/ai-agent-governance-sprint.html +0 -415
  68. package/public/guides/ai-agent-pre-action-approval-gates.html +0 -401
  69. package/public/guides/ai-agent-workflow-migration-checklist.html +0 -392
  70. package/public/guides/ai-deployment-readiness.html +0 -415
  71. package/public/guides/ai-mode-ads-agent-governance.html +0 -401
  72. package/public/guides/ai-search-topical-presence.html +0 -342
  73. package/public/guides/autoresearch-agent-safety.html +0 -342
  74. package/public/guides/background-agent-governance.html +0 -358
  75. package/public/guides/best-tools-stop-ai-agents-breaking-production.html +0 -363
  76. package/public/guides/browser-automation-safety.html +0 -342
  77. package/public/guides/chatgpt-ads-trust.html +0 -353
  78. package/public/guides/claude-code-feedback.html +0 -339
  79. package/public/guides/claude-code-prevent-repeated-mistakes.html +0 -161
  80. package/public/guides/claude-code-skills-guardrails.html +0 -343
  81. package/public/guides/claude-desktop.html +0 -356
  82. package/public/guides/code-knowledge-graph-guardrails.html +0 -365
  83. package/public/guides/codex-cli-guardrails.html +0 -339
  84. package/public/guides/cursor-agent-guardrails.html +0 -339
  85. package/public/guides/cursor-prevent-repeated-mistakes.html +0 -161
  86. package/public/guides/database-agent-safety.html +0 -406
  87. package/public/guides/deepseek-v4-runtime-guardrails.html +0 -346
  88. package/public/guides/developer-machine-supply-chain-guardrails.html +0 -358
  89. package/public/guides/gcp-mcp-guardrails.html +0 -147
  90. package/public/guides/gemini-cli-feedback-memory.html +0 -339
  91. package/public/guides/gpt-5-5-model-evaluation.html +0 -358
  92. package/public/guides/internal-ai-engineering-stack-guardrails.html +0 -348
  93. package/public/guides/long-running-agent-context-management.html +0 -346
  94. package/public/guides/mcp-tool-governance.html +0 -401
  95. package/public/guides/multica-thumbgate-setup.html +0 -134
  96. package/public/guides/native-messaging-host-security.html +0 -342
  97. package/public/guides/policy-engine-pre-action-gates.html +0 -346
  98. package/public/guides/pre-action-checks.html +0 -342
  99. package/public/guides/pretooluse-hooks-vs-advisory-prompt-rules.html +0 -342
  100. package/public/guides/prompt-tricks-to-workflow-rules.html +0 -365
  101. package/public/guides/proxy-pointer-rag-guardrails.html +0 -352
  102. package/public/guides/rag-precision-tuning-guardrails.html +0 -352
  103. package/public/guides/reasoning-compression-guardrails.html +0 -346
  104. package/public/guides/relational-knowledge-ai-recommendations.html +0 -342
  105. package/public/guides/roo-code-alternative-cline.html +0 -339
  106. package/public/guides/semantic-programmatic-seo-guardrails.html +0 -352
  107. package/public/guides/seo-agent-skills-guardrails.html +0 -344
  108. package/public/guides/stop-repeated-ai-agent-mistakes.html +0 -342
  109. package/public/learn/ac-dc-runtime-enforcement.html +0 -277
  110. package/public/learn/agent-harness-pattern.html +0 -181
  111. package/public/learn/agent-identity-connector-governance.html +0 -146
  112. package/public/learn/agent-swarms-shared-gates.html +0 -173
  113. package/public/learn/agentic-enterprise-context-brain.html +0 -117
  114. package/public/learn/agentic-os-team-governance.html +0 -146
  115. package/public/learn/ai-agent-governance.html +0 -158
  116. package/public/learn/ai-agent-persistent-memory.html +0 -211
  117. package/public/learn/anthropomorphic-claim-gates.html +0 -180
  118. package/public/learn/background-agent-control-layer.html +0 -184
  119. package/public/learn/claude-code-goal-with-rubrics.html +0 -205
  120. package/public/learn/codex-role-plugins-need-governance.html +0 -125
  121. package/public/learn/cost-aware-agent-gate-routing.html +0 -173
  122. package/public/learn/databricks-unity-ai-gateway-runtime-governance.html +0 -157
  123. package/public/learn/deterministic-agent-workflows.html +0 -185
  124. package/public/learn/feedback-loop-vs-decision-layer.html +0 -283
  125. package/public/learn/from-prototype-to-production.html +0 -223
  126. package/public/learn/learn.css +0 -51
  127. package/public/learn/mcp-pre-action-checks-explained.html +0 -172
  128. package/public/learn/pretix-stripe-connect-marketplaces.html +0 -161
  129. package/public/learn/regulated-agent-execution-boundary.html +0 -196
  130. package/public/learn/spec-driven-development.html +0 -168
  131. package/public/learn/stop-ai-agent-force-push.html +0 -134
  132. package/public/learn/vibe-coding-safety-net.html +0 -142
  133. package/scripts/reddit-browser-notification-watch.js +0 -230
@@ -16,11 +16,10 @@
16
16
  * ThumbGate dogfood — we are the prevention-rule generator and a perfect
17
17
  * customer for our own gate.
18
18
  *
19
- * Wires through .claude/settings.json Stop hooks list. Always exits 0 and
20
- * emits a Claude-hook `decision:"block"` JSON payload when a final-response
21
- * violation can be evaluated before the response is accepted. Transcript-only
22
- * runs still surface a reminder for the next turn when the host has already
23
- * written the assistant response.
19
+ * Wires through .claude/settings.json Stop hooks list. Always exits 0
20
+ * (informational): the goal is to surface a system reminder in the next
21
+ * turn so the agent corrects mid-conversation rather than to hard-block
22
+ * the turn that already happened.
24
23
  *
25
24
  * Stdin: Claude Code passes the hook payload as JSON on stdin. We read
26
25
  * `transcript_path` to locate the JSONL session log and scan the last
@@ -44,8 +43,20 @@ const CLAIM_PATTERNS = [
44
43
  /\beverything\s+(?:is\s+)?(?:done|working|ready)\b/i,
45
44
  /\b(?:github|repo|repository)\s+(?:about|metadata|description|topics?)\b.*\b(?:updated|verified|fixed|match(?:es|ed)?)\b/i,
46
45
  /\b(?:about|metadata|description|topics?)\b.*\b(?:updated|verified|fixed|match(?:es|ed)?)\b.*\b(?:github|repo|repository)\b/i,
47
- /\b(?:money|payment|charge|checkout|revenue|price|pricing|invoice|billing|tax|sales tax|inventory|stock|permission|access|customer[-\s]facing)\b.*\b(?:correct|accurate|verified|valid|matches|working|fixed|resolved|calculated|configured)\b/i,
48
- /\b(?:correct|accurate|verified|valid|matches|working|fixed|resolved|calculated|configured)\b.*\b(?:money|payment|charge|checkout|revenue|price|pricing|invoice|billing|tax|sales tax|inventory|stock|permission|access|customer[-\s]facing)\b/i,
46
+ // Added 2026-06-11 after a cross-project failure analysis: these completion
47
+ // claims ("all green / stable / verified / race over / tests pass") were
48
+ // asserted without proof and slipped past the original set. The proof-gate
49
+ // below suppresses them whenever the SAME turn ran a verification tool, so a
50
+ // "verified" claim backed by a test/curl/Read stays silent.
51
+ /\b(?:all\s+)?(?:tests?|checks?|ci)\s+(?:are\s+)?(?:now\s+)?passing\b/i,
52
+ /\ball\s+(?:tests?|checks?)\s+pass(?:ed)?\b/i,
53
+ /\bverified\b/i,
54
+ /\bconfirmed\b/i,
55
+ /\b(?:is|are|it'?s|now)\s+stable\b/i,
56
+ /\ball\s+clear\b/i,
57
+ /\bgood\s+to\s+go\b/i,
58
+ /\brace\s+(?:is\s+)?over\b/i,
59
+ /\bno\s+longer\s+racing\b/i,
49
60
  ];
50
61
 
51
62
  // Proof-of-verification patterns. If the SAME turn included one of these
@@ -69,63 +80,97 @@ const PROOF_PATTERNS = [
69
80
  /\bshopify\b/,
70
81
  /\bsquare\b/,
71
82
  /\bquickbooks\b/,
72
- /\bgh\s+api\b/,
73
- /\bls\b/,
74
- /\bcat\b/,
75
83
  /Read\s*\(/, // Claude Code Read tool call
76
84
  /Bash\s*\(/, // Claude Code Bash tool call
77
85
  ];
78
86
 
79
- const POSITIVE_FEEDBACK_PATTERNS = [
80
- /\bthumbs?\s*up\b/i,
81
- /👍/,
82
- /\bthank(s| you)\b/i,
83
- /\bgood\b/i,
84
- /\bgreat\b/i,
85
- /\bperfect\b/i,
86
- /\bok(?:ay)?\b/i,
87
+ const COMMERCIAL_CLAIM_SUBJECTS = [
88
+ 'money',
89
+ 'payment',
90
+ 'charge',
91
+ 'checkout',
92
+ 'revenue',
93
+ 'price',
94
+ 'pricing',
95
+ 'invoice',
96
+ 'billing',
97
+ 'tax',
98
+ 'sales tax',
99
+ 'inventory',
100
+ 'stock',
101
+ 'permission',
102
+ 'access',
103
+ 'customer facing',
87
104
  ];
88
105
 
89
- const LOW_VALUE_CLOSEOUT_PATTERNS = [
90
- /^(?:good|great|perfect|ok(?:ay)?|thanks?|thank you)[.!,\s-]*(?:$|\b)/i,
91
- /\buse\s+\w+\s*\/\s*\w+\b/i,
92
- /\bsounds good\b/i,
106
+ const COMMERCIAL_CLAIM_STATES = [
107
+ 'correct',
108
+ 'accurate',
109
+ 'verified',
110
+ 'valid',
111
+ 'matches',
112
+ 'working',
113
+ 'fixed',
114
+ 'resolved',
115
+ 'calculated',
116
+ 'configured',
93
117
  ];
94
118
 
95
- const SUBSTANTIVE_CLOSEOUT_PATTERNS = [
96
- /\b(?:evidence|verified|tested|proof|result|residual risk|next state|next action|timestamp|source|url|file|line|passed|failed|blocked|unknown)\b/i,
97
- /\b(?:node --test|npm run|curl|gh |git |pytest|playwright|screenshot|diff --check)\b/i,
98
- /https?:\/\//i,
99
- /\/[\w.-]+\/[\w./-]+/,
100
- /`[^`]+`/,
119
+ const GREEN_CLAIM_PHRASES = [
120
+ 'all green',
121
+ 'all tests green',
122
+ 'all checks green',
123
+ 'all the tests green',
124
+ 'all the checks green',
125
+ 'all tests are green',
126
+ 'all checks are green',
101
127
  ];
102
128
 
103
- function readTranscriptEntries(transcriptPath) {
104
- if (!transcriptPath || !fs.existsSync(transcriptPath)) return [];
129
+ function normalizeClaimText(text) {
130
+ return String(text || '')
131
+ .toLowerCase()
132
+ .replace(/[-_]+/g, ' ')
133
+ .replace(/[^a-z0-9]+/g, ' ')
134
+ .replace(/\s+/g, ' ')
135
+ .trim();
136
+ }
137
+
138
+ function containsPhrase(normalizedText, phrase) {
139
+ return ` ${normalizedText} `.includes(` ${phrase} `);
140
+ }
141
+
142
+ function findTokenListClaim(text, subjects, states, label) {
143
+ const normalized = normalizeClaimText(text);
144
+ if (!normalized) return null;
145
+ const subject = subjects.find((candidate) => containsPhrase(normalized, candidate));
146
+ const state = states.find((candidate) => containsPhrase(normalized, candidate));
147
+ return subject && state ? `${label}: ${subject} ${state}` : null;
148
+ }
149
+
150
+ function findGreenClaim(text) {
151
+ const normalized = normalizeClaimText(text);
152
+ return GREEN_CLAIM_PHRASES.find((phrase) => containsPhrase(normalized, phrase)) || null;
153
+ }
154
+
155
+ function readLastAssistantTurn(transcriptPath) {
156
+ if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;
105
157
  let content;
106
158
  try {
107
159
  content = fs.readFileSync(transcriptPath, 'utf8');
108
160
  } catch {
109
- return [];
161
+ return null;
110
162
  }
111
- return content
112
- .trim()
113
- .split('\n')
114
- .map((raw) => {
115
- try {
116
- return JSON.parse(raw);
117
- } catch {
118
- return null;
119
- }
120
- })
121
- .filter(Boolean);
122
- }
123
-
124
- function readLastAssistantTurn(transcriptPath) {
125
- const entries = readTranscriptEntries(transcriptPath);
163
+ const lines = content.trim().split('\n');
126
164
  // Walk backwards to find the last assistant message
127
- for (let i = entries.length - 1; i >= 0; i--) {
128
- const entry = entries[i];
165
+ for (let i = lines.length - 1; i >= 0; i--) {
166
+ const raw = lines[i].trim();
167
+ if (!raw) continue;
168
+ let entry;
169
+ try {
170
+ entry = JSON.parse(raw);
171
+ } catch {
172
+ continue;
173
+ }
129
174
  if (entry.type === 'assistant' && entry.message) {
130
175
  return entry.message;
131
176
  }
@@ -133,22 +178,6 @@ function readLastAssistantTurn(transcriptPath) {
133
178
  return null;
134
179
  }
135
180
 
136
- function readPreviousUserText(transcriptPath) {
137
- const entries = readTranscriptEntries(transcriptPath);
138
- let seenAssistant = false;
139
- for (let i = entries.length - 1; i >= 0; i--) {
140
- const entry = entries[i];
141
- if (!seenAssistant && entry.type === 'assistant') {
142
- seenAssistant = true;
143
- continue;
144
- }
145
- if (seenAssistant && entry.type === 'user' && entry.message) {
146
- return extractText(entry.message);
147
- }
148
- }
149
- return '';
150
- }
151
-
152
181
  function extractText(message) {
153
182
  if (!message || !Array.isArray(message.content)) return '';
154
183
  return message.content
@@ -175,24 +204,16 @@ function extractToolUseSummary(message) {
175
204
  .join('\n');
176
205
  }
177
206
 
178
- function wordCount(text) {
179
- return String(text || '').trim().split(/\s+/).filter(Boolean).length;
180
- }
181
-
182
- function hasPositiveFeedback(text) {
183
- return POSITIVE_FEEDBACK_PATTERNS.some((p) => p.test(text || ''));
184
- }
185
-
186
- function isLowValueCloseout(text, toolUseSummary = '') {
187
- const normalized = String(text || '').trim();
188
- if (!normalized) return false;
189
- if (toolUseSummary.trim()) return false;
190
- if (wordCount(normalized) > 45) return false;
191
- if (SUBSTANTIVE_CLOSEOUT_PATTERNS.some((p) => p.test(normalized))) return false;
192
- return LOW_VALUE_CLOSEOUT_PATTERNS.some((p) => p.test(normalized));
193
- }
194
-
195
207
  function findClaim(text) {
208
+ const commercialClaim = findTokenListClaim(
209
+ text,
210
+ COMMERCIAL_CLAIM_SUBJECTS,
211
+ COMMERCIAL_CLAIM_STATES,
212
+ 'commercial truth',
213
+ );
214
+ if (commercialClaim) return commercialClaim;
215
+ const greenClaim = findGreenClaim(text);
216
+ if (greenClaim) return greenClaim;
196
217
  for (const p of CLAIM_PATTERNS) {
197
218
  const m = text.match(p);
198
219
  if (m) return m[0];
@@ -204,79 +225,6 @@ function hasProof(combined) {
204
225
  return PROOF_PATTERNS.some((p) => p.test(combined));
205
226
  }
206
227
 
207
- function extractPayloadText(payload) {
208
- if (!payload || typeof payload !== 'object') return '';
209
- const candidates = [
210
- payload.response,
211
- payload.assistant_response,
212
- payload.assistantResponse,
213
- payload.final_response,
214
- payload.finalResponse,
215
- payload.text,
216
- payload.output,
217
- payload.message,
218
- ];
219
- for (const candidate of candidates) {
220
- if (typeof candidate === 'string' && candidate.trim()) return candidate;
221
- if (candidate && typeof candidate === 'object') {
222
- const extracted = extractText(candidate);
223
- if (extracted.trim()) return extracted;
224
- }
225
- }
226
- return '';
227
- }
228
-
229
- function extractPayloadPreviousUserText(payload) {
230
- if (!payload || typeof payload !== 'object') return '';
231
- const candidates = [
232
- payload.previous_user_text,
233
- payload.previousUserText,
234
- payload.user_prompt,
235
- payload.userPrompt,
236
- payload.prompt,
237
- ];
238
- for (const candidate of candidates) {
239
- if (typeof candidate === 'string' && candidate.trim()) return candidate;
240
- if (candidate && typeof candidate === 'object') {
241
- const extracted = extractText(candidate);
242
- if (extracted.trim()) return extracted;
243
- }
244
- }
245
- return '';
246
- }
247
-
248
- function buildResponseQualityReason() {
249
- return [
250
- 'ThumbGate response-quality gate: final response answered positive feedback',
251
- 'with a low-value social closeout instead of silence-level brevity or an evidence checkpoint.',
252
- 'Positive feedback after operational work should trigger either no extra noise,',
253
- 'a compact evidence checkpoint, or a concrete next-state update.',
254
- 'Do not reply with generic "Good / Great / Use X/Y" filler.',
255
- ].join(' ');
256
- }
257
-
258
- function buildResponseQualityReminder() {
259
- return [
260
- '⚠️ ThumbGate response-quality gate: previous turn answered positive feedback',
261
- ' with a low-value social closeout instead of silence-level brevity or an evidence checkpoint.',
262
- ' Positive feedback after operational work should trigger either no extra noise,',
263
- ' a compact evidence checkpoint, or a concrete next-state update.',
264
- ' Do not reply with generic "Good / Great / Use X/Y" filler.',
265
- ].join('\n');
266
- }
267
-
268
- function writeBlock(reason) {
269
- process.stdout.write(JSON.stringify({
270
- decision: 'block',
271
- reason,
272
- hookSpecificOutput: {
273
- hookEventName: 'Stop',
274
- permissionDecision: 'deny',
275
- permissionDecisionReason: reason,
276
- },
277
- }) + '\n');
278
- }
279
-
280
228
  function readStdinSync() {
281
229
  try {
282
230
  return fs.readFileSync(0, 'utf8');
@@ -295,42 +243,34 @@ function main() {
295
243
  }
296
244
 
297
245
  const transcriptPath = payload.transcript_path || process.env.CLAUDE_TRANSCRIPT_PATH;
298
- const directText = extractPayloadText(payload) || process.env.CLAUDE_RESPONSE || '';
299
- const directPreviousUserText = extractPayloadPreviousUserText(payload)
300
- || process.env.CLAUDE_PREVIOUS_USER_TEXT
301
- || process.env.CLAUDE_PREVIOUS_USER
302
- || '';
303
-
304
- if (directText && hasPositiveFeedback(directPreviousUserText) && isLowValueCloseout(directText, '')) {
305
- writeBlock(buildResponseQualityReason());
306
- return;
307
- }
308
-
309
246
  const message = readLastAssistantTurn(transcriptPath);
310
247
  if (!message) return; // no transcript visible; nothing to check
311
248
 
312
249
  const text = extractText(message);
313
250
  const toolUseSummary = extractToolUseSummary(message);
314
- const previousUserText = readPreviousUserText(transcriptPath);
315
-
316
- if (hasPositiveFeedback(previousUserText) && isLowValueCloseout(text, toolUseSummary)) {
317
- process.stdout.write(buildResponseQualityReminder() + '\n');
318
- return;
319
- }
320
-
321
251
  const claim = findClaim(text);
322
252
  if (!claim) return; // no completion claim made; silent
323
253
 
324
254
  const proofText = `${text}\n${toolUseSummary}`;
325
255
  if (hasProof(proofText)) return; // claim backed by proof in same turn
326
256
 
327
- // Surface a system reminder for the NEXT turn. Do not hard-block.
257
+ // Strict mode (THUMBGATE_STRICT_ENFORCEMENT=1): hard-block the stop. Emit a
258
+ // Stop-hook block decision so Claude Code does NOT end the turn — the agent
259
+ // must run the verification (or retract) before it can stop. Default mode
260
+ // stays soft (a reminder for the next turn) so we don't break existing wiring.
261
+ if (process.env.THUMBGATE_STRICT_ENFORCEMENT === '1') {
262
+ const reason = `ThumbGate anti-claim gate (strict): you claimed completion ("${claim}") without a proof tool call in the same message. Run the verification (curl / grep / test / Read) and restate with the proof, or retract — do not end the turn on an unverified claim.`;
263
+ process.stdout.write(JSON.stringify({ decision: 'block', reason }) + '\n');
264
+ return;
265
+ }
266
+
267
+ // Default (soft): surface a system reminder for the NEXT turn. Do not hard-block.
328
268
  const reminder = [
329
269
  '⚠️ ThumbGate anti-claim gate: previous turn claimed completion',
330
270
  ` ("${claim}") without a proof tool call in the same message.`,
331
- ' Per CLAUDE.md anti-lying: never claim "done / live / deployed / fixed"',
332
- ' or commercial truth (money / tax / inventory / permissions / customer-facing state)',
333
- ' without curl / grep / test / source-of-truth output in the SAME turn.',
271
+ ' Per CLAUDE.md anti-lying: never claim "done / live / deployed / fixed /',
272
+ ' verified / all green / stable" or commercial truth (money / tax / inventory /',
273
+ ' permissions / customer-facing state) without curl / grep / test / source-of-truth output in the SAME turn.',
334
274
  ' If the work really is verified, re-state the claim with the proof.',
335
275
  ' If not, retract and run the verification before re-asserting.',
336
276
  ].join('\n');
@@ -354,16 +294,8 @@ if (path.resolve(process.argv[1] || '') === path.resolve(__filename)) {
354
294
  module.exports = {
355
295
  CLAIM_PATTERNS,
356
296
  PROOF_PATTERNS,
357
- POSITIVE_FEEDBACK_PATTERNS,
358
- LOW_VALUE_CLOSEOUT_PATTERNS,
359
297
  findClaim,
360
298
  hasProof,
361
- hasPositiveFeedback,
362
- isLowValueCloseout,
363
- extractPayloadText,
364
- extractPayloadPreviousUserText,
365
- buildResponseQualityReason,
366
299
  extractText,
367
300
  extractToolUseSummary,
368
- readPreviousUserText,
369
301
  };
@@ -13,7 +13,6 @@ const DEFAULT_PRO_PRICE_DOLLARS = PRO_MONTHLY_PRICE_DOLLARS;
13
13
  const DEFAULT_PRO_PRICE_LABEL = PRO_PRICE_LABEL;
14
14
  const DEFAULT_SPRINT_DIAGNOSTIC_PRICE_DOLLARS = 499;
15
15
  const DEFAULT_WORKFLOW_SPRINT_PRICE_DOLLARS = 1500;
16
- const DEFAULT_SNAPSHOT_PRICE_DOLLARS = 97;
17
16
  const GA_MEASUREMENT_ID_PATTERN = /^G-[A-Z0-9]+$/i;
18
17
 
19
18
  function normalizeOrigin(value) {
@@ -130,10 +129,6 @@ function resolveHostedBillingConfig({ requestOrigin } = {}, env = process.env) {
130
129
  const googleSiteVerification = normalizeTrackingId(env.THUMBGATE_GOOGLE_SITE_VERIFICATION);
131
130
  const sprintDiagnosticCheckoutUrl = normalizeAbsoluteUrl(env.THUMBGATE_SPRINT_DIAGNOSTIC_CHECKOUT_URL);
132
131
  const workflowSprintCheckoutUrl = normalizeAbsoluteUrl(env.THUMBGATE_WORKFLOW_SPRINT_CHECKOUT_URL);
133
- const paypalDiagnosticCheckoutUrl = normalizeAbsoluteUrl(env.THUMBGATE_PAYPAL_DIAGNOSTIC_CHECKOUT_URL);
134
- const paypalWorkflowSprintCheckoutUrl = normalizeAbsoluteUrl(env.THUMBGATE_PAYPAL_WORKFLOW_SPRINT_CHECKOUT_URL);
135
- const morSnapshotCheckoutUrl = normalizeAbsoluteUrl(env.THUMBGATE_MOR_SNAPSHOT_CHECKOUT_URL);
136
- const morProvider = String(env.THUMBGATE_MOR_PROVIDER || '').trim();
137
132
 
138
133
  return {
139
134
  appOrigin,
@@ -147,16 +142,10 @@ function resolveHostedBillingConfig({ requestOrigin } = {}, env = process.env) {
147
142
  proPriceLabel,
148
143
  sprintDiagnosticCheckoutUrl,
149
144
  workflowSprintCheckoutUrl,
150
- paypalDiagnosticCheckoutUrl,
151
- paypalWorkflowSprintCheckoutUrl,
152
- morSnapshotCheckoutUrl,
153
- morProvider,
154
145
  sprintDiagnosticPriceDollars: normalizePriceDollars(env.THUMBGATE_SPRINT_DIAGNOSTIC_PRICE_DOLLARS)
155
146
  || DEFAULT_SPRINT_DIAGNOSTIC_PRICE_DOLLARS,
156
147
  workflowSprintPriceDollars: normalizePriceDollars(env.THUMBGATE_WORKFLOW_SPRINT_PRICE_DOLLARS)
157
148
  || DEFAULT_WORKFLOW_SPRINT_PRICE_DOLLARS,
158
- snapshotPriceDollars: normalizePriceDollars(env.THUMBGATE_SNAPSHOT_PRICE_DOLLARS)
159
- || DEFAULT_SNAPSHOT_PRICE_DOLLARS,
160
149
  gaMeasurementId,
161
150
  googleSiteVerification,
162
151
  posthogApiKey,
@@ -170,7 +159,6 @@ module.exports = {
170
159
  DEFAULT_PRO_PRICE_LABEL,
171
160
  DEFAULT_SPRINT_DIAGNOSTIC_PRICE_DOLLARS,
172
161
  DEFAULT_WORKFLOW_SPRINT_PRICE_DOLLARS,
173
- DEFAULT_SNAPSHOT_PRICE_DOLLARS,
174
162
  GA_MEASUREMENT_ID_PATTERN,
175
163
  normalizeAbsoluteUrl,
176
164
  normalizeOrigin,
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('node:path');
4
- const fs = require('node:fs');
5
4
  const { readJSONL, getFeedbackPaths } = require('./feedback-loop');
6
5
  const { buildMemoryLifecycleView, scoreHybridMemoryMatch } = require('./agent-memory-lifecycle');
7
6
  const { loadOptionalModule } = require('./private-core-boundary');
@@ -167,17 +166,6 @@ function resolveLessonPaths(options = {}) {
167
166
  };
168
167
  }
169
168
 
170
- function readPackagedBuiltinLessons() {
171
- if (process.env.THUMBGATE_DISABLE_BUILTIN_LESSONS === '1') return [];
172
- const builtinPath = path.resolve(__dirname, '..', 'config', 'builtin-lessons.json');
173
- try {
174
- const parsed = JSON.parse(fs.readFileSync(builtinPath, 'utf8'));
175
- return Array.isArray(parsed.lessons) ? parsed.lessons : [];
176
- } catch {
177
- return [];
178
- }
179
- }
180
-
181
169
  function readPreventionRuleMatches(queryText, limit = 3, options = {}) {
182
170
  const { PREVENTION_RULES_PATH } = resolveLessonPaths(options);
183
171
  if (!PREVENTION_RULES_PATH) return [];
@@ -493,9 +481,7 @@ function searchLessons(query = '', options = {}) {
493
481
  const sqliteResults = tryFts5Search(query, options);
494
482
  if (sqliteResults) return sqliteResults;
495
483
 
496
- const localMemories = readJSONL(MEMORY_LOG_PATH);
497
- const builtinMemories = options.includeBuiltinLessons === false ? [] : readPackagedBuiltinLessons();
498
- const memories = [...builtinMemories, ...localMemories];
484
+ const memories = readJSONL(MEMORY_LOG_PATH);
499
485
  const feedbackEntries = readJSONL(FEEDBACK_LOG_PATH);
500
486
  const feedbackById = new Map(feedbackEntries.map((entry) => [entry.id, entry]));
501
487
  const parsedLimit = Number(options.limit || 10);