thumbgate 1.28.0 → 1.28.1

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.
@@ -231,7 +231,7 @@ const {
231
231
  finalizeSession: finalizeFeedbackSession,
232
232
  } = require('../../scripts/feedback-session');
233
233
 
234
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.28.0' };
234
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.28.1' };
235
235
  const COMMERCE_CATEGORIES = [
236
236
  'product_recommendation',
237
237
  'brand_compliance',
@@ -1430,22 +1430,14 @@ function writeNdjsonResponse(id, payload, error = null) {
1430
1430
  process.stdout.write(`${body}\n`);
1431
1431
  }
1432
1432
 
1433
- /**
1434
- * Default staleness threshold: if a lock is older than this (ms), the holder
1435
- * is considered orphaned even if its PID is still alive — it likely belongs
1436
- * to a defunct Claude Code session whose process was never reaped.
1437
- */
1438
- const LOCK_STALE_MS = Number(process.env.THUMBGATE_LOCK_STALE_MS) || 2 * 60 * 60 * 1000; // 2 hours
1439
-
1440
1433
  /**
1441
1434
  * Acquire a file-system lock to prevent duplicate MCP server instances.
1442
- * Returns { lockFile, cleanupLock } on success, or calls process.exit(1)
1443
- * if another live server holds the lock.
1435
+ * Returns { lockFile, cleanupLock } on success.
1444
1436
  *
1445
- * Staleness reaping: if the lock-holding process is alive but the lock is
1446
- * older than LOCK_STALE_MS, the holder is killed (SIGTERM) and the lock is
1447
- * reclaimed. This prevents orphaned `thumbgate serve` processes from permanently
1448
- * blocking new sessions.
1437
+ * Every live stdio client needs its own MCP process, and those sessions can
1438
+ * legitimately run for many hours. A lock's age is therefore not evidence that
1439
+ * its owner is orphaned. Live owners coexist through per-session locks; only a
1440
+ * lock whose PID is no longer running is reclaimed.
1449
1441
  */
1450
1442
  function acquireLock() {
1451
1443
  const feedbackDir = getFeedbackPaths().FEEDBACK_DIR;
@@ -1458,26 +1450,19 @@ function acquireLock() {
1458
1450
  try { process.kill(lockData.pid, 0); isRunning = true; } catch { /* process is dead */ }
1459
1451
 
1460
1452
  if (isRunning) {
1461
- const lockAge = Date.now() - new Date(lockData.startedAt).getTime();
1462
- if (lockAge > LOCK_STALE_MS) {
1463
- // Orphaned process kill it and take over
1464
- process.stderr.write(`[thumbgate] Lock held by PID ${lockData.pid} is ${Math.round(lockAge / 60000)}m old (threshold: ${Math.round(LOCK_STALE_MS / 60000)}m). Reaping orphaned process.\n`);
1465
- try { process.kill(lockData.pid, 'SIGTERM'); } catch { /* already gone */ }
1466
- } else {
1467
- // Another session's MCP server is running coexist via per-session lock.
1468
- // Each Claude session communicates via its own stdio pipe and needs its own server.
1469
- // SQLite WAL mode handles concurrent access safely.
1470
- process.stderr.write(`[thumbgate] Another MCP server (PID ${lockData.pid}) is running for ${feedbackDir}. Starting concurrent session.\n`);
1471
- const sessionLockFile = path.join(feedbackDir, `.mcp-server-${process.pid}.lock`);
1472
- fs.writeFileSync(sessionLockFile, JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() }));
1473
- const cleanupSessionLock = () => { try { fs.unlinkSync(sessionLockFile); } catch { /* already removed */ } };
1474
- process.on('exit', cleanupSessionLock);
1475
- process.on('SIGTERM', () => { cleanupSessionLock(); process.exit(0); });
1476
- process.on('SIGINT', () => { cleanupSessionLock(); process.exit(0); });
1477
- return { lockFile: sessionLockFile, cleanupLock: cleanupSessionLock };
1478
- }
1453
+ // Another session's MCP server is running — coexist via per-session lock.
1454
+ // Each client communicates via its own stdio pipe and needs its own server.
1455
+ // SQLite WAL mode handles concurrent access safely.
1456
+ process.stderr.write(`[thumbgate] Another MCP server (PID ${lockData.pid}) is running for ${feedbackDir}. Starting concurrent session.\n`);
1457
+ const sessionLockFile = path.join(feedbackDir, `.mcp-server-${process.pid}.lock`);
1458
+ fs.writeFileSync(sessionLockFile, JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() }));
1459
+ const cleanupSessionLock = () => { try { fs.unlinkSync(sessionLockFile); } catch { /* already removed */ } };
1460
+ process.on('exit', cleanupSessionLock);
1461
+ process.on('SIGTERM', () => { cleanupSessionLock(); process.exit(0); });
1462
+ process.on('SIGINT', () => { cleanupSessionLock(); process.exit(0); });
1463
+ return { lockFile: sessionLockFile, cleanupLock: cleanupSessionLock };
1479
1464
  }
1480
- // Stale lock from a dead or reaped process — remove it
1465
+ // Stale lock from a dead process — remove it.
1481
1466
  try { fs.unlinkSync(lockFile); } catch { /* already gone */ }
1482
1467
  process.stderr.write(`[thumbgate] Removed stale lock (PID ${lockData.pid} is no longer running).\n`);
1483
1468
  }
@@ -7,7 +7,7 @@
7
7
  "npx",
8
8
  "--yes",
9
9
  "--package",
10
- "thumbgate@1.28.0",
10
+ "thumbgate@1.28.1",
11
11
  "thumbgate",
12
12
  "serve"
13
13
  ],
package/bin/cli.js CHANGED
@@ -2695,23 +2695,27 @@ function install() {
2695
2695
  }
2696
2696
 
2697
2697
  async function gateCheck() {
2698
- // Explicit emergency escape hatch ONLY. The 2026-06-03 hotfix made this
2699
- // bypass-by-default, which silently disabled ThumbGate's enforcement entirely
2700
- // (the firewall approved everything). Restored 2026-06-04: enforcement runs by
2701
- // default in warn-by-default posture (see gates-engine applyEnforcementPosture);
2702
- // set THUMBGATE_HOTFIX_BYPASS=1 to disable all checks if a gate ever misfires.
2703
- if (process.env.THUMBGATE_HOTFIX_BYPASS === '1') {
2704
- process.stdout.write(JSON.stringify({
2705
- decision: 'approve',
2706
- reason: 'hotfix-bypass-opt-in',
2707
- hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: '' }
2708
- }) + '\n');
2709
- return;
2710
- }
2711
2698
  try {
2712
2699
  const payload = readStdinText();
2713
2700
  const input = payload ? JSON.parse(payload) : {};
2714
2701
  const gatesEngine = require(path.join(PKG_ROOT, 'scripts', 'gates-engine'));
2702
+
2703
+ // The operator bypass only skips advisory and strict-mode gates. Security and
2704
+ // self-protection floors are evaluated before the early approval path.
2705
+ if (process.env.THUMBGATE_HOTFIX_BYPASS === '1') {
2706
+ const hardFloorOutput = gatesEngine.runHardFloor(input);
2707
+ if (hardFloorOutput) {
2708
+ process.stdout.write(hardFloorOutput + '\n');
2709
+ return;
2710
+ }
2711
+ process.stdout.write(JSON.stringify({
2712
+ decision: 'approve',
2713
+ reason: 'operator-bypass-opt-in',
2714
+ hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: '' }
2715
+ }) + '\n');
2716
+ return;
2717
+ }
2718
+
2715
2719
  const output = await gatesEngine.runAsync(input);
2716
2720
  process.stdout.write(output + '\n');
2717
2721
  } catch (err) {
@@ -2749,11 +2753,18 @@ function statuslineRender() {
2749
2753
 
2750
2754
  function hookAutoCapture() {
2751
2755
  syncActiveProjectContext();
2756
+ const { extractPromptEnvelope } = require(path.join(PKG_ROOT, 'scripts', 'feedback-sanitizer'));
2757
+ const rawStdin = readStdinText();
2758
+ const promptEnvelope = extractPromptEnvelope(rawStdin);
2759
+ // stdin on a UserPromptSubmit hook is the JSON payload
2760
+ // {session_id, transcript_path, cwd, prompt, ...}. Persist ONLY the human
2761
+ // `.prompt` field — never the whole stdin object — so session metadata blobs
2762
+ // can't be promoted as lessons.
2752
2763
  const prompt = process.env.CLAUDE_USER_PROMPT
2753
2764
  || process.env.THUMBGATE_USER_PROMPT
2754
2765
  || process.env.CODEX_USER_PROMPT
2755
2766
  || process.env.USER_PROMPT
2756
- || readStdinText().trim();
2767
+ || promptEnvelope.prompt;
2757
2768
  const { evaluatePromptGuard } = require(path.join(PKG_ROOT, 'scripts', 'prompt-guard'));
2758
2769
  const { processInlineFeedback, formatCliOutput } = require(path.join(PKG_ROOT, 'scripts', 'cli-feedback'));
2759
2770
  const { detectFeedbackSignal } = require(path.join(PKG_ROOT, 'scripts', 'feedback-quality'));
@@ -2766,25 +2777,39 @@ function hookAutoCapture() {
2766
2777
  })
2767
2778
  );
2768
2779
 
2769
- recordConversationEntry({
2770
- author: 'user',
2771
- text: prompt,
2772
- source: 'claude_user_prompt',
2773
- });
2774
-
2775
2780
  const guardResult = evaluatePromptGuard(prompt);
2776
2781
  if (guardResult) {
2782
+ recordConversationEntry({
2783
+ author: 'user',
2784
+ text: prompt,
2785
+ source: 'claude_user_prompt',
2786
+ });
2777
2787
  process.stdout.write(`${JSON.stringify(guardResult)}\n`);
2778
2788
  return;
2779
2789
  }
2780
2790
 
2781
2791
  const detected = detectFeedbackSignal(prompt);
2782
2792
  if (!detected) {
2793
+ recordConversationEntry({
2794
+ author: 'user',
2795
+ text: prompt,
2796
+ source: 'claude_user_prompt',
2797
+ });
2783
2798
  return;
2784
2799
  }
2785
2800
 
2786
2801
  const signal = detected.signal;
2787
2802
  const conversationWindow = readRecentConversationWindow({ limit: 8 });
2803
+ const { buildFeedbackSourceIdentity } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
2804
+ const sourceEvent = buildFeedbackSourceIdentity({
2805
+ signal,
2806
+ promptText: prompt,
2807
+ sessionId: promptEnvelope.sessionId || process.env.CLAUDE_SESSION_ID,
2808
+ promptId: promptEnvelope.promptId,
2809
+ projectDir: promptEnvelope.projectDir || CWD,
2810
+ timestamp: promptEnvelope.timestamp,
2811
+ source: 'claude-user-prompt',
2812
+ });
2788
2813
  const result = processInlineFeedback({
2789
2814
  signal,
2790
2815
  context: prompt,
@@ -2793,7 +2818,15 @@ function hookAutoCapture() {
2793
2818
  : undefined,
2794
2819
  whatWentWrong: signal === 'down' ? prompt : undefined,
2795
2820
  whatWorked: signal === 'up' ? prompt : undefined,
2821
+ sourceEvent,
2796
2822
  });
2823
+ if (!result.feedbackResult || !result.feedbackResult.duplicate) {
2824
+ recordConversationEntry({
2825
+ author: 'user',
2826
+ text: prompt,
2827
+ source: 'claude_user_prompt',
2828
+ });
2829
+ }
2797
2830
  process.stdout.write(formatCliOutput(result) + '\n');
2798
2831
  }
2799
2832
 
@@ -3947,53 +3980,6 @@ switch (COMMAND) {
3947
3980
  case 'dispatch-brief':
3948
3981
  dispatchBrief();
3949
3982
  break;
3950
- case 'gate-check': {
3951
- // PreToolUse hook interface: reads tool call JSON from stdin, outputs gate verdict
3952
- // Used by: generate-pretool-hook.sh → npx thumbgate gate-check
3953
- const { run: gateRun, runAsync: gateRunAsync } = require(path.join(PKG_ROOT, 'scripts', 'gates-engine'));
3954
- const { evaluateSelfProtection } = require(path.join(PKG_ROOT, 'scripts', 'self-protection'));
3955
- let stdinData = '';
3956
- process.stdin.setEncoding('utf8');
3957
- process.stdin.on('data', (chunk) => { stdinData += chunk; });
3958
- process.stdin.on('end', async () => {
3959
- try {
3960
- const input = JSON.parse(stdinData);
3961
- const output = await gateRunAsync(input);
3962
- // Self-protection overlay (2026-07-08): the gate engine only denies edits
3963
- // to ThumbGate's own governance files under strict enforcement; by default
3964
- // such edits pass as a clean ALLOW. Surface them (or block in strict) so an
3965
- // agent can't silently disable the firewall. Only overlays when the engine
3966
- // did not already produce a hard deny.
3967
- let verdict = output;
3968
- try {
3969
- const parsed = JSON.parse(output || '{}');
3970
- const alreadyDeny = (parsed.hookSpecificOutput && parsed.hookSpecificOutput.permissionDecision === 'deny')
3971
- || parsed.decision === 'block';
3972
- if (!alreadyDeny) {
3973
- const sp = evaluateSelfProtection(input.tool_name, input.tool_input);
3974
- if (sp && sp.action === 'block') {
3975
- verdict = JSON.stringify({
3976
- decision: 'block',
3977
- reason: sp.message,
3978
- hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: sp.message },
3979
- });
3980
- } else if (sp && sp.action === 'warn') {
3981
- verdict = JSON.stringify({
3982
- hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: sp.message },
3983
- });
3984
- }
3985
- }
3986
- } catch (_e) { /* non-JSON engine output: pass through unchanged */ }
3987
- process.stdout.write(verdict + '\n');
3988
- process.exit(0);
3989
- } catch (err) {
3990
- process.stderr.write(`gate-check error: ${err.message}\n`);
3991
- process.stdout.write(JSON.stringify({}) + '\n');
3992
- process.exit(0);
3993
- }
3994
- });
3995
- break;
3996
- }
3997
3983
  case 'hermes-gate': {
3998
3984
  // Nous Research Hermes Agent `pre_tool_call` shell hook.
3999
3985
  // Hermes pipes each pending tool call as JSON to stdin and reads a decision from stdout;
@@ -4004,7 +3990,7 @@ switch (COMMAND) {
4004
3990
  // Hermes `pre_tool_call` is binary (block or allow) with no warn channel, and the whole point
4005
3991
  // of wiring it is to gate, so we run STRICT enforcement by default — otherwise ThumbGate's
4006
3992
  // warn-by-default posture would pass every deny through and the hook would block nothing.
4007
- // Opt out with THUMBGATE_HERMES_WARN_ONLY=1; THUMBGATE_HOTFIX_BYPASS=1 still disables checks.
3993
+ // Opt out with THUMBGATE_HERMES_WARN_ONLY=1; the operator bypass also leaves ordinary gates advisory.
4008
3994
  // Wire it in ~/.hermes/config.yaml — see adapters/hermes/config.yaml.
4009
3995
  if (process.env.THUMBGATE_HERMES_WARN_ONLY !== '1' && process.env.THUMBGATE_HOTFIX_BYPASS !== '1') {
4010
3996
  process.env.THUMBGATE_STRICT_ENFORCEMENT = '1';
@@ -4016,8 +4002,22 @@ switch (COMMAND) {
4016
4002
  process.stdin.on('end', async () => {
4017
4003
  try {
4018
4004
  const payload = JSON.parse(hermesStdin);
4019
- // Hermes sends snake_case tool_name/tool_input gates-engine reads these directly.
4020
- const verdict = await hermesGateRun({ tool_name: payload.tool_name, tool_input: payload.tool_input });
4005
+ const hermesToolName = String(payload.tool_name || '');
4006
+ const canonicalToolNames = {
4007
+ terminal: 'Bash',
4008
+ process: 'Bash',
4009
+ execute_code: 'Bash',
4010
+ patch: 'Edit',
4011
+ write_file: 'Write',
4012
+ read_file: 'Read',
4013
+ };
4014
+ const verdict = await hermesGateRun({
4015
+ tool_name: canonicalToolNames[hermesToolName] || hermesToolName,
4016
+ tool_input: {
4017
+ ...(payload.tool_input || {}),
4018
+ hermes_tool_name: hermesToolName,
4019
+ },
4020
+ });
4021
4021
  let parsed = {};
4022
4022
  try { parsed = JSON.parse(verdict); } catch (_e) { parsed = {}; }
4023
4023
  const hso = parsed.hookSpecificOutput || {};
@@ -342,7 +342,7 @@
342
342
  "id": "self-protect-config",
343
343
  "layer": "Execution",
344
344
  "toolNames": ["Edit", "Write", "MultiEdit"],
345
- "pattern": "(?:config/gates/|config/budget\\.json|\\.thumbgate/|thumbgate\\.json)",
345
+ "pattern": "(?:config/gates/|config/(?:budget|enforcement|mcp-allowlists)\\.json|\\.thumbgate/config\\.json|thumbgate\\.json)",
346
346
  "action": "block",
347
347
  "message": "Self-protection: agent cannot modify ThumbGate configuration, gate rules, or budget settings.",
348
348
  "severity": "critical",
@@ -372,7 +372,7 @@
372
372
  "id": "self-protect-hooks-disable",
373
373
  "layer": "Execution",
374
374
  "toolNames": ["Edit", "Write", "Bash"],
375
- "pattern": "(?:settings\\.json|settings\\.local\\.json).*(?:hooks|PreToolUse|PostToolUse)",
375
+ "pattern": "(?:\\.claude/settings(?:\\.local)?\\.json|\\.codex/config\\.toml|scripts/hook-[^/]+\\.(?:js|sh))",
376
376
  "action": "block",
377
377
  "message": "Self-protection: agent cannot modify hook registrations.",
378
378
  "severity": "critical",
@@ -2,14 +2,11 @@
2
2
  "repo": "IgorGanapolsky/ThumbGate",
3
3
  "repositoryUrl": "https://github.com/IgorGanapolsky/ThumbGate",
4
4
  "homepageUrl": "https://thumbgate.ai",
5
- "githubDescription": "Agent governance: thumbs become Pre-Action Checks that flag repeat mistakes and hard-block catastrophic classes before code or systems change.",
6
- "metaDescription": "ThumbGate is the pre-action governance runtime for AI coding agents (Claude Code, Cursor, Codex, Gemini CLI, Amp, Cline, OpenCode): 👍 thumbs up and 👎 thumbs down feedback become history-aware lessons stored locally; Enterprise adds shared lessons and org visibility. PreToolUse checks flag and log risky tool calls hard-blocking catastrophic classes by default and warning on the rest, with strict mode to hard-block every rule.",
5
+ "githubDescription": "ThumbGate Pre-Action Checks derive rules from repeated failures, flag risky tool calls, hard-block detected secret leaks, and block matches in strict mode.",
6
+ "metaDescription": "ThumbGate is the local pre-action governance runtime for AI coding agents (Claude Code, Cursor, Codex, Gemini CLI, Amp, Cline, OpenCode): accepted 👍 thumbs-up and 👎 thumbs-down feedback becomes history-aware local lessons; repeated failures can become prevention rules. PreToolUse checks flag and log risky tool calls, hard-block detected secret leaks, and block matching actions in strict mode. Pro is for individual operators; Enterprise rollout is scoped after intake.",
7
7
  "topics": [
8
8
  "thumbgate",
9
9
  "pre-action-checks",
10
- "save-llm-tokens",
11
- "reduce-llm-cost",
12
- "ai-cost-optimization",
13
10
  "mcp",
14
11
  "mcp-server",
15
12
  "ai-agents",
@@ -4,7 +4,7 @@
4
4
  "pages": [
5
5
  {
6
6
  "route": "/",
7
- "sentinel": "Stop paying for the same AI mistake twice",
7
+ "sentinel": "Catch AI agents before they spend money",
8
8
  "description": "Home page hero (lifeblood — never regress)"
9
9
  },
10
10
  {
@@ -0,0 +1,38 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash|Edit|Write",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node",
10
+ "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/hook-pre-tool-use.js"]
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node",
21
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/cli.js", "hook-auto-capture"]
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "SessionStart": [
27
+ {
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "node",
32
+ "args": ["${CLAUDE_PLUGIN_ROOT}/bin/cli.js", "session-start"]
33
+ }
34
+ ]
35
+ }
36
+ ]
37
+ }
38
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.28.0",
4
- "description": "ThumbGate self-improving agent governance: thumbs-up/down turns every mistake into a prevention rule and blocks repeat patterns. 36 pre-action checks, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
3
+ "version": "1.28.1",
4
+ "description": "ThumbGate Pre-Action Checks derive rules from repeated failures, flag risky tool calls, hard-block detected secret leaks, and block matches in strict mode.",
5
5
  "homepage": "https://thumbgate.ai",
6
6
  "repository": {
7
7
  "type": "git",
@@ -46,6 +46,7 @@
46
46
  "scripts/bayes-optimal-gate.js",
47
47
  "scripts/belief-update.js",
48
48
  "scripts/billing.js",
49
+ "scripts/checkout-attribution-reference.js",
49
50
  "scripts/bot-detection.js",
50
51
  "scripts/build-metadata.js",
51
52
  "scripts/classifier-routing.js",
@@ -235,6 +236,7 @@
235
236
  "scripts/workspace-evolver.js",
236
237
  "scripts/xmemory-lite.js",
237
238
  ".claude-plugin/plugin.json",
239
+ "hooks/hooks.json",
238
240
  ".claude/commands/",
239
241
  "commands/",
240
242
  ".well-known/",
@@ -280,9 +282,11 @@
280
282
  "public/learn.html",
281
283
  "public/lessons.html",
282
284
  "public/numbers.html",
285
+ "public/partner-intake.html",
283
286
  "public/pricing.html",
284
287
  "public/pro.html",
285
288
  "public/assets/brand/",
289
+ "!public/assets/brand/github-social-preview.png",
286
290
  "public/assets/claude-thumbgate-statusbar.svg",
287
291
  "public/assets/codex-thumbgate-statusbar-test.svg",
288
292
  "public/assets/legal-intake-control-flow.svg",
@@ -383,7 +387,7 @@
383
387
  "social:prospect:bluesky": "node scripts/social-bluesky-prospecting.js",
384
388
  "social:prospect:bluesky:dry": "node scripts/social-bluesky-prospecting.js --dry-run",
385
389
  "social:reply-publish:bluesky:dry": "node scripts/social-reply-monitor-bluesky.js --publish-approved --dry-run",
386
- "test": "npm run test:python && npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:billing-setup && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:platform-limits && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:post-everywhere-channels && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:release-window && npm run test:check-congruence && npm run test:tool-registry && npm run test:repeat-metric && npm run test:noop-detect && npm run test:action-receipts && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:memory-scope-readiness && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operational-dashboard && npm run test:operator-artifacts && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:mcp-tool-annotations && npm run test:mcp-oauth && npm run test:mcp-oauth-flow && npm run test:plan-gate && npm run test:ai-component-inventory && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:entitlement && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:imperative-detector && npm run test:audit-pr-bot-contamination && npm run test:stripe-bootstrap-saas-catalog && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:statusline-cache-aggregate && npm run test:public-repo-hygiene && npm run test:no-internal-orchestration-leaks && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:lesson-semantic-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:predictive-credible-range && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:lesson-canonical && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:xss-checkout-escape && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-archived-product-guard && npm run test:postgres-guard && npm run test:checkout-bot-guard && npm run test:checkout-pro-confirmation-gate && npm run test:pricing-page-telemetry && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:telemetry-tracked-link-slug && npm run test:prompt-eval && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:numbers-page && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:competitive-positioning-marketing && npm run test:medium-weekly && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity && npm run test:token-savings-dashboard && npm run test:cursor-wiring && npm run test:pretooluse-injection && npm run test:recent-corrective-context && npm run test:durability-step && npm run test:mailer && npm run test:brand-assets && npm run test:enforcement-teeth && npm run test:bayes-optimal-gate && npm run test:swarm-coordinator && npm run test:session-report && npm run test:agent-reasoning-traces && npm run test:judge-reward && npm run test:llm-behavior-monitor && npm run test:prompting-os && npm run test:single-use-credential-gate && npm run test:structured-prompt-driven && npm run test:require-evidence-gate && npm run test:rule-validator && npm run test:bluesky-atproto && npm run test:social-reply-monitor-bluesky && npm run test:bluesky-delete-replies && npm run test:architect-kit-memory-bridge && npm run test:sonar-review-hotspots && npm run test:actionable-remediations && npm run test:gemini-embedding-policy && npm run test:agent-design-governance && npm run test:public-core-boundary && npm run test:hook-stop-verify-deploy && npm run test:hook-stop-anti-claim && npm run test:plausible-server-events && npm run test:activation-tracker && npm run test:activation-onboarding && npm run test:unified-revenue-rollup && npm run test:conversion-rate-stats && npm run test:external-customer-audit && npm run test:telemetry-export && npm run test:stripe-checkout-diagnostic && npm run test:stripe-business-identity-probe && npm run test:revenue-observability-doctor && npm run test:public-bundle-ratchet && npm run test:pack-runtime-integrity && npm run test:hook-self-protection && npm run test:self-protect-enforcement && npm run test:stripe-payment-link-update && npm run test:ci-cd-hygiene-audit && npm run test:verify-marketing-pages-deployed && npm run test:install-email-capture && npm run test:install-shim && npm run test:hook-runtime-subcommands && npm run test:implementation-notes && npm run test:daily-block-cap && npm run test:free-to-paid-conversion-units && npm run test:metrics-real-endpoint && npm run test:cli-trial-and-help && npm run test:cost-cli && npm run test:silent-failure-cluster && npm run test:proof:truth && node --test tests/adaptive-reliability.test.js && npm run test:mcp-oauth-reviewer && npm run test:dfcx-gate && npm run test:dfcx-gate-server && npm run test:vertex-scorer && npm run test:dashboard-chat && npm run test:gitar-integration && npm run test:secret-redaction && npm run test:discoverable-skills && npm run test:discoverable-skill-skills && npm run test:sync-telemetry && npm run test:leak-scanner && npm run test:team-sync && npm run test:eval-rag && npm run test:async-eval-observability && npm run test:letta-adapter && npm run test:policy-engine-adapter && npm run test:tool-contract-validator && npm run test:check-update && npm run test:hermes-gate && npm run test:memory-provider-enforcement-bridge && npm run test:publisher-credential-guards && npm run test:reddit-browser-notification-watch && npm run test:payment-rails && npm run test:cursor-marketplace-doctor && npm run test:okara-money-promo-automation",
390
+ "test": "npm run test:python && npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:billing-setup && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:platform-limits && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:post-everywhere-channels && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:release-window && npm run test:check-congruence && npm run test:tool-registry && npm run test:repeat-metric && npm run test:noop-detect && npm run test:action-receipts && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:memory-scope-readiness && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operational-dashboard && npm run test:operator-artifacts && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:mcp-tool-annotations && npm run test:mcp-oauth && npm run test:mcp-oauth-flow && npm run test:plan-gate && npm run test:ai-component-inventory && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:entitlement && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:imperative-detector && npm run test:audit-pr-bot-contamination && npm run test:stripe-bootstrap-saas-catalog && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:statusline-cache-aggregate && npm run test:public-repo-hygiene && npm run test:no-internal-orchestration-leaks && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:lesson-semantic-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:predictive-credible-range && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:lesson-canonical && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:xss-checkout-escape && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-archived-product-guard && npm run test:postgres-guard && npm run test:checkout-bot-guard && npm run test:checkout-pro-confirmation-gate && npm run test:pricing-page-telemetry && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:telemetry-tracked-link-slug && npm run test:prompt-eval && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:numbers-page && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:competitive-positioning-marketing && npm run test:medium-weekly && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity && npm run test:token-savings-dashboard && npm run test:cursor-wiring && npm run test:pretooluse-injection && npm run test:recent-corrective-context && npm run test:durability-step && npm run test:mailer && npm run test:brand-assets && npm run test:enforcement-teeth && npm run test:bayes-optimal-gate && npm run test:swarm-coordinator && npm run test:session-report && npm run test:agent-reasoning-traces && npm run test:judge-reward && npm run test:llm-behavior-monitor && npm run test:prompting-os && npm run test:single-use-credential-gate && npm run test:structured-prompt-driven && npm run test:require-evidence-gate && npm run test:rule-validator && npm run test:bluesky-atproto && npm run test:social-reply-monitor-bluesky && npm run test:bluesky-delete-replies && npm run test:architect-kit-memory-bridge && npm run test:sonar-review-hotspots && npm run test:actionable-remediations && npm run test:gemini-embedding-policy && npm run test:agent-design-governance && npm run test:public-core-boundary && npm run test:hook-stop-verify-deploy && npm run test:hook-stop-anti-claim && npm run test:plausible-server-events && npm run test:activation-tracker && npm run test:activation-onboarding && npm run test:unified-revenue-rollup && npm run test:conversion-rate-stats && npm run test:external-customer-audit && npm run test:telemetry-export && npm run test:stripe-checkout-diagnostic && npm run test:stripe-business-identity-probe && npm run test:revenue-observability-doctor && npm run test:public-bundle-ratchet && npm run test:pack-runtime-integrity && npm run test:hook-self-protection && npm run test:self-protect-enforcement && npm run test:never-bypass-branch-protection && npm run test:stripe-payment-link-update && npm run test:ci-cd-hygiene-audit && npm run test:verify-marketing-pages-deployed && npm run test:install-email-capture && npm run test:install-shim && npm run test:hook-runtime-subcommands && npm run test:implementation-notes && npm run test:daily-block-cap && npm run test:free-to-paid-conversion-units && npm run test:metrics-real-endpoint && npm run test:cli-trial-and-help && npm run test:cost-cli && npm run test:silent-failure-cluster && npm run test:proof:truth && node --test tests/adaptive-reliability.test.js && npm run test:mcp-oauth-reviewer && npm run test:dfcx-gate && npm run test:dfcx-gate-server && npm run test:vertex-scorer && npm run test:dashboard-chat && npm run test:gitar-integration && npm run test:secret-redaction && npm run test:discoverable-skills && npm run test:discoverable-skill-skills && npm run test:sync-telemetry && npm run test:leak-scanner && npm run test:team-sync && npm run test:eval-rag && npm run test:async-eval-observability && npm run test:letta-adapter && npm run test:policy-engine-adapter && npm run test:tool-contract-validator && npm run test:check-update && npm run test:hermes-gate && npm run test:memory-provider-enforcement-bridge && npm run test:publisher-credential-guards && npm run test:reddit-browser-notification-watch && npm run test:payment-rails && npm run test:service-checkout-price-integrity && npm run test:cursor-marketplace-doctor && npm run test:plugin-hooks-manifest && npm run test:okara-money-promo-automation",
387
391
  "test:python": "python3 -m pytest tests/*.py",
388
392
  "test:check-update": "node --test tests/check-update.test.js",
389
393
  "test:hook-stop-verify-deploy": "node --test tests/hook-stop-verify-deploy.test.js",
@@ -444,7 +448,7 @@
444
448
  "test:memory-dedup": "node --test tests/memory-dedup.test.js",
445
449
  "test:lesson-db": "node --test tests/lesson-db.test.js",
446
450
  "test:lesson-rotation": "node --test tests/lesson-rotation.test.js",
447
- "test:feedback-quality": "node --test tests/feedback-quality.test.js",
451
+ "test:feedback-quality": "node --test tests/feedback-quality.test.js tests/feedback-event-idempotency.test.js",
448
452
  "test:sync-version": "node --test tests/sync-version.test.js",
449
453
  "test:release-window": "node --test tests/release-window.test.js",
450
454
  "test:team-sync": "node --test tests/team-sync.test.js",
@@ -512,11 +516,11 @@
512
516
  "test:training-export": "node --test tests/training-export.test.js tests/databricks-export.test.js",
513
517
  "test:deployment": "node --test tests/deployment.test.js tests/deploy-policy.test.js tests/publish-decision.test.js tests/changeset-check.test.js tests/release-notes.test.js tests/sonarcloud-workflow.test.js tests/package-boundary.test.js tests/public-package-boundary.test.js",
514
518
  "test:operational-integrity": "node --test tests/operational-integrity.test.js tests/sync-branch-protection.test.js",
515
- "test:workflow": "node --test tests/parallel-workflow.test.js tests/workflow-contract.test.js tests/positioning-contract.test.js tests/docs-claim-hygiene.test.js tests/thumbgate-scope.test.js tests/workflow-runs.test.js tests/workflow-sprint-intake.test.js tests/revenue-pack-utils.test.js tests/sales-pipeline.test.js tests/github-outreach.test.js tests/enterprise-story.test.js tests/guide-conversion-path.test.js tests/buyer-intent-revenue-assist.test.js",
519
+ "test:workflow": "node --test tests/parallel-workflow.test.js tests/workflow-contract.test.js tests/positioning-contract.test.js tests/docs-claim-hygiene.test.js tests/thumbgate-scope.test.js tests/workflow-runs.test.js tests/workflow-sprint-intake.test.js tests/revenue-pack-utils.test.js tests/apollo-acquisition.test.js tests/sales-pipeline.test.js tests/github-outreach.test.js tests/enterprise-story.test.js tests/guide-conversion-path.test.js tests/buyer-intent-revenue-assist.test.js",
516
520
  "test:sales-pipeline": "node --test tests/sales-pipeline.test.js",
517
- "test:billing": "node --test tests/billing.test.js tests/stripe-sync-product-images.test.js",
521
+ "test:billing": "node --test tests/billing.test.js tests/stripe-sync-product-images.test.js tests/checkout-attribution-reference.test.js",
518
522
  "test:billing-setup": "node --test tests/billing-setup-redaction.test.js",
519
- "test:cli": "node --test tests/analytics-report.test.js tests/agent-design-governance.test.js tests/codex-self-heal.test.js tests/creator-campaigns.test.js tests/cli.test.js tests/codex-bridge-script.test.js tests/dependabot-changeset.test.js tests/dispatch-brief.test.js tests/feedback-normalize.test.js tests/install-mcp.test.js tests/install-scope-docs.test.js tests/pr-manager.test.js tests/pro-local-dashboard.test.js tests/published-cli.test.js tests/revenue-status.test.js tests/stripe-live-status.test.js tests/creator-dev-and-prune.test.js",
523
+ "test:cli": "node --test tests/analytics-report.test.js tests/agent-design-governance.test.js tests/codex-self-heal.test.js tests/creator-campaigns.test.js tests/cli.test.js tests/codex-bridge-script.test.js tests/dependabot-changeset.test.js tests/dispatch-brief.test.js tests/feedback-normalize.test.js tests/feedback-lesson-sanitization.test.js tests/install-mcp.test.js tests/install-scope-docs.test.js tests/pr-manager.test.js tests/pro-local-dashboard.test.js tests/published-cli.test.js tests/revenue-status.test.js tests/stripe-live-status.test.js tests/creator-dev-and-prune.test.js",
520
524
  "test:evolution": "node --test tests/workspace-evolver.test.js",
521
525
  "test:watcher": "node --test tests/jsonl-watcher.test.js",
522
526
  "test:autoresearch": "node --test tests/autoresearch.test.js",
@@ -637,13 +641,16 @@
637
641
  "test:product-feedback": "node --test tests/product-feedback.test.js",
638
642
  "test:feedback-root-consolidator": "node --test tests/feedback-root-consolidator.test.js",
639
643
  "social:publish:launch": "node scripts/social-analytics/publish-thumbgate-launch.js",
644
+ "social:publish:latest-release-article": "node scripts/social-analytics/publish-latest-release-article.js",
645
+ "social:publish:latest-release-fallbacks": "node scripts/social-analytics/publish-latest-release-fallbacks.js",
646
+ "social:verify:latest-release": "node scripts/social-analytics/verify-latest-release-posts.js",
640
647
  "social:schedule:campaign": "node scripts/social-analytics/schedule-thumbgate-campaign.js",
641
648
  "social:install:growth": "node scripts/social-analytics/install-growth-automation.js",
642
649
  "social:reconcile:campaign": "node scripts/social-analytics/reconcile-thumbgate-campaign.js",
643
650
  "social:sync:launch-assets": "node scripts/social-analytics/sync-launch-assets.js",
644
651
  "social:engagement:audit": "node scripts/social-analytics/engagement-audit.js",
645
652
  "test:install-growth-automation": "node --test tests/install-growth-automation.test.js",
646
- "test:publish-thumbgate-launch": "node --test tests/publish-thumbgate-launch.test.js",
653
+ "test:publish-thumbgate-launch": "node --test tests/publish-thumbgate-launch.test.js tests/publish-latest-release-article.test.js tests/latest-release-campaign-ops.test.js",
647
654
  "test:reconcile-thumbgate-campaign": "node --test tests/reconcile-thumbgate-campaign.test.js",
648
655
  "test:schedule-thumbgate-campaign": "node --test tests/schedule-thumbgate-campaign.test.js",
649
656
  "test:social-reply-monitor": "node --test tests/social-reply-monitor.test.js tests/reddit-monitor-launchd.test.js",
@@ -762,12 +769,15 @@
762
769
  "eval:observability": "node scripts/async-eval-observability.js",
763
770
  "test:memory-provider-enforcement-bridge": "node --test tests/memory-provider-enforcement-bridge.test.js",
764
771
  "test:payment-rails": "node --test tests/payment-rails.test.js",
772
+ "test:service-checkout-price-integrity": "node --test tests/service-checkout-price-integrity.test.js",
765
773
  "test:publisher-credential-guards": "node --test tests/publisher-credential-guards.test.js",
766
774
  "test:reddit-browser-notification-watch": "node --test tests/reddit-browser-notification-watch.test.js",
767
775
  "cursor:marketplace:doctor": "node scripts/cursor-marketplace-doctor.js",
768
776
  "cursor:marketplace:doctor:json": "node scripts/cursor-marketplace-doctor.js --json",
769
777
  "test:cursor-marketplace-doctor": "node --test tests/cursor-marketplace-doctor.test.js",
778
+ "test:plugin-hooks-manifest": "node --test tests/plugin-hooks-manifest.test.js",
770
779
  "test:hook-self-protection": "node --test tests/hook-self-protection.test.js",
780
+ "test:never-bypass-branch-protection": "node --test tests/never-bypass-branch-protection.test.js",
771
781
  "test:self-protect-enforcement": "node --test tests/self-protect-enforcement.test.js",
772
782
  "test:pack-runtime-integrity": "node --test tests/pack-runtime-integrity.test.js",
773
783
  "test:entitlement": "node --test tests/entitlement.test.js",
@@ -230,7 +230,7 @@
230
230
  </div>
231
231
  <div class="screen">
232
232
  <div class="message">Before I run this migration, check whether this repeats a rejected deploy pattern.</div>
233
- <div class="message"><strong>ThumbGate:</strong> Require evidence first: test output, rollback path, and the exact affected tables. Then install the local gate so future risky actions are flagged and logged before execution (and hard-blocked for catastrophic classes or under strict mode).</div>
233
+ <div class="message"><strong>ThumbGate:</strong> Require evidence first: test output, rollback path, and the exact affected tables. Then install the local gate so future risky actions are flagged and logged before execution (and hard-blocked for secret exfiltration and guardrail-tampering by default, or under strict mode).</div>
234
234
  <code class="code">thumbs down: agent claimed the release was published before checking npm and CI</code>
235
235
  <code class="code">npx thumbgate init --agent codex</code>
236
236
  </div>
@@ -239,7 +239,7 @@
239
239
 
240
240
  <section aria-labelledby="what-ships">
241
241
  <h2 class="section-title" id="what-ships">What ships today</h2>
242
- <p class="section-copy">This is the ChatGPT-facing distribution page. It keeps the claim precise: ChatGPT is the discovery, advice, checkpointing, and typed-feedback surface. The enforcement — flag and log by default, hard-block for catastrophic classes or under strict mode — still runs in the local agent or CI lane.</p>
242
+ <p class="section-copy">This is the ChatGPT-facing distribution page. It keeps the claim precise: ChatGPT is the discovery, advice, checkpointing, and typed-feedback surface. The enforcement — flag and log by default, hard-block for secret exfiltration and guardrail-tampering by default, or every rule under strict mode — still runs in the local agent or CI lane.</p>
243
243
  <div class="grid">
244
244
  <article class="card">
245
245
  <h3>Live GPT entrypoint</h3>
@@ -78,7 +78,7 @@
78
78
  </div>
79
79
  <div class="card">
80
80
  <h3>Promote repeat failures to PreToolUse gates</h3>
81
- <p>When the same agent mistake shows up twice, ThumbGate distills it into a prevention rule and flags the next attempt at the tool-call boundary — hard-blocking it for catastrophic classes or under strict enforcement — with the rule that fired in the agent's reasoning trace, so Codex chooses a safer plan instead of being told to "be more careful."</p>
81
+ <p>When the same agent mistake shows up twice, ThumbGate distills it into a prevention rule and flags the next attempt at the tool-call boundary — hard-blocking it for secret exfiltration and guardrail-tampering by default, or every rule under strict enforcement — with the rule that fired in the agent's reasoning trace, so Codex chooses a safer plan instead of being told to "be more careful."</p>
82
82
  </div>
83
83
  <div class="card">
84
84
  <h3>Audit trail enterprise procurement requires</h3>
@@ -256,7 +256,7 @@ $ npx thumbgate feedback-self-test
256
256
  </div>
257
257
  <div class="tile">
258
258
  <h3>Checks before action</h3>
259
- <p>Pre-Action Checks evaluate commands, file edits, publishes, merges, and other high-risk actions before execution. Bad repeats are flagged and logged before they burn time or tokens — and hard-blocked for catastrophic classes (secret exfiltration, destructive deletes) or when strict enforcement is enabled.</p>
259
+ <p>Pre-Action Checks evaluate commands, file edits, publishes, merges, and other high-risk actions before execution. Bad repeats are flagged and logged before they burn time or tokens — and hard-blocked for secret exfiltration and guardrail-tampering by default (destructive deletes, force-push, and supply-chain warn by default) or when strict enforcement is enabled.</p>
260
260
  </div>
261
261
  </section>
262
262
 
@@ -252,7 +252,7 @@ footer { padding: 32px 0 44px; color: var(--muted); }
252
252
  <option>We need an independent proof checklist first</option>
253
253
  </select>
254
254
  <button type="submit" data-revenue-cta data-cta-id="diagnostic_page_submit" data-cta-placement="intake">Submit for diagnostic scope</button>
255
- <p class="hint">Ready to pay now? Use the $499 diagnostic checkout above. If the workflow needs fit review first, submit it here and we will route you to the diagnostic or sprint only when the scope is real.</p>
255
+ <p class="hint" data-diagnostic-payment-hint>Ready to pay now? Use the $499 diagnostic checkout above. If the workflow needs fit review first, submit it here and we will route you to the diagnostic or sprint only when the scope is real.</p>
256
256
  </form>
257
257
  </div>
258
258
  <section>
@@ -295,6 +295,28 @@ footer { padding: 32px 0 44px; color: var(--muted); }
295
295
  <script>
296
296
  (function () {
297
297
  const search = new URLSearchParams(window.location.search);
298
+ const paidCta = document.querySelector('[data-cta-id="diagnostic_hero_paid"]');
299
+ const inboundSource = search.get('utm_source') || search.get('source');
300
+ const isAiventyxTraffic = String(inboundSource || '').trim().toLowerCase() === 'aiventyx';
301
+ if (paidCta && isAiventyxTraffic) {
302
+ paidCta.remove();
303
+ const paymentHint = document.querySelector('[data-diagnostic-payment-hint]');
304
+ if (paymentHint) {
305
+ paymentHint.textContent = 'Submit the workflow here. Aiventyx will collect payment on its checkout once the scope is accepted.';
306
+ }
307
+ } else if (paidCta) {
308
+ const href = new URL(paidCta.getAttribute('href'), window.location.origin);
309
+ const passthrough = /^(utm_(source|medium|campaign|content|term)|source|acquisition_id|visitor_id|session_id|visitor_session_id|install_id|trace_id|creator|community|post_id|comment_id|campaign_variant|offer_code|referrer_host)$/i;
310
+ for (const [key, value] of search.entries()) {
311
+ if (!passthrough.test(key) || !value.trim()) continue;
312
+ href.searchParams.set(key, value.trim());
313
+ }
314
+ if (inboundSource && inboundSource.trim()) {
315
+ href.searchParams.set('utm_source', inboundSource.trim());
316
+ }
317
+ paidCta.setAttribute('href', `${href.pathname}${href.search}${href.hash}`);
318
+ }
319
+
298
320
  const form = document.querySelector('[data-diagnostic-intake-form]');
299
321
  if (form) {
300
322
  for (const [key, value] of search.entries()) {