thumbgate 0.9.10

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 (364) hide show
  1. package/.claude-plugin/README.md +134 -0
  2. package/.claude-plugin/bundle/icon.png +0 -0
  3. package/.claude-plugin/bundle/icon.svg +18 -0
  4. package/.claude-plugin/bundle/server/index.js +24 -0
  5. package/.claude-plugin/marketplace.json +36 -0
  6. package/.claude-plugin/plugin.json +21 -0
  7. package/.well-known/mcp/server-card.json +231 -0
  8. package/LICENSE +21 -0
  9. package/README.md +375 -0
  10. package/adapters/README.md +9 -0
  11. package/adapters/amp/skills/thumbgate-feedback/SKILL.md +22 -0
  12. package/adapters/chatgpt/INSTALL.md +83 -0
  13. package/adapters/chatgpt/openapi.yaml +1281 -0
  14. package/adapters/claude/.mcp.json +14 -0
  15. package/adapters/codex/config.toml +9 -0
  16. package/adapters/gemini/function-declarations.json +224 -0
  17. package/adapters/mcp/server-stdio.js +788 -0
  18. package/adapters/opencode/opencode.json +15 -0
  19. package/bin/cli.js +1484 -0
  20. package/bin/memory.sh +64 -0
  21. package/bin/obsidian-sync.sh +20 -0
  22. package/bin/postinstall.js +37 -0
  23. package/config/build-metadata.json +4 -0
  24. package/config/e2e-critical-flows.json +45 -0
  25. package/config/gate-templates.json +77 -0
  26. package/config/gates/claim-verification.json +29 -0
  27. package/config/gates/computer-use.json +39 -0
  28. package/config/gates/default.json +117 -0
  29. package/config/github-about.json +25 -0
  30. package/config/mcp-allowlists.json +135 -0
  31. package/config/model-tiers.json +33 -0
  32. package/config/partner-routing.json +132 -0
  33. package/config/policy-bundles/constrained-v1.json +64 -0
  34. package/config/policy-bundles/default-v1.json +91 -0
  35. package/config/rubrics/default-v1.json +52 -0
  36. package/config/skill-packs/react-testing.json +23 -0
  37. package/config/skill-packs/stripe-integration/references/api-spec.json +1 -0
  38. package/config/skill-packs/stripe-integration/references/webhook-guide.md +3 -0
  39. package/config/skill-specs/pr-reviewer.json +9 -0
  40. package/config/skill-specs/release-status.json +9 -0
  41. package/config/skill-specs/ticket-triage.json +9 -0
  42. package/config/subagent-profiles.json +32 -0
  43. package/config/tessl-tiles.json +29 -0
  44. package/config/thumbgate-settings.managed.json +12 -0
  45. package/openapi/openapi.yaml +1281 -0
  46. package/package.json +283 -0
  47. package/plugins/amp-skill/INSTALL.md +52 -0
  48. package/plugins/amp-skill/SKILL.md +64 -0
  49. package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +22 -0
  50. package/plugins/claude-codex-bridge/.mcp.json +12 -0
  51. package/plugins/claude-codex-bridge/INSTALL.md +43 -0
  52. package/plugins/claude-codex-bridge/README.md +46 -0
  53. package/plugins/claude-codex-bridge/scripts/codex-bridge.js +288 -0
  54. package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +24 -0
  55. package/plugins/claude-codex-bridge/skills/result/SKILL.md +22 -0
  56. package/plugins/claude-codex-bridge/skills/review/SKILL.md +28 -0
  57. package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +27 -0
  58. package/plugins/claude-codex-bridge/skills/setup/SKILL.md +21 -0
  59. package/plugins/claude-codex-bridge/skills/status/SKILL.md +19 -0
  60. package/plugins/claude-skill/INSTALL.md +55 -0
  61. package/plugins/claude-skill/SKILL.md +46 -0
  62. package/plugins/codex-profile/.codex-plugin/plugin.json +43 -0
  63. package/plugins/codex-profile/.mcp.json +12 -0
  64. package/plugins/codex-profile/AGENTS.md +20 -0
  65. package/plugins/codex-profile/INSTALL.md +66 -0
  66. package/plugins/codex-profile/README.md +37 -0
  67. package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +23 -0
  68. package/plugins/cursor-marketplace/CHANGELOG.md +30 -0
  69. package/plugins/cursor-marketplace/LICENSE +21 -0
  70. package/plugins/cursor-marketplace/README.md +124 -0
  71. package/plugins/cursor-marketplace/agents/reliability-reviewer.md +31 -0
  72. package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
  73. package/plugins/cursor-marketplace/commands/capture-feedback.md +33 -0
  74. package/plugins/cursor-marketplace/commands/check-gates.md +25 -0
  75. package/plugins/cursor-marketplace/commands/show-lessons.md +27 -0
  76. package/plugins/cursor-marketplace/hooks/hooks.json +10 -0
  77. package/plugins/cursor-marketplace/mcp.json +12 -0
  78. package/plugins/cursor-marketplace/rules/feedback-capture.mdc +34 -0
  79. package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +30 -0
  80. package/plugins/cursor-marketplace/rules/session-continuity.mdc +28 -0
  81. package/plugins/cursor-marketplace/scripts/gate-check.sh +11 -0
  82. package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +47 -0
  83. package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +31 -0
  84. package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +30 -0
  85. package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +33 -0
  86. package/plugins/gemini-extension/INSTALL.md +92 -0
  87. package/plugins/gemini-extension/gemini_prompt.txt +14 -0
  88. package/plugins/gemini-extension/tool_contract.json +45 -0
  89. package/plugins/opencode-profile/INSTALL.md +57 -0
  90. package/public/assets/instagram-card.png +0 -0
  91. package/public/assets/tiktok-agent-memory.mp4 +0 -0
  92. package/public/blog.html +400 -0
  93. package/public/dashboard.html +1093 -0
  94. package/public/guide.html +317 -0
  95. package/public/index.html +1014 -0
  96. package/public/learn/agent-harness-pattern.html +180 -0
  97. package/public/learn/ai-agent-persistent-memory.html +202 -0
  98. package/public/learn/learn.css +45 -0
  99. package/public/learn/mcp-pre-action-gates-explained.html +172 -0
  100. package/public/learn/stop-ai-agent-force-push.html +134 -0
  101. package/public/learn/vibe-coding-safety-net.html +142 -0
  102. package/public/learn.html +213 -0
  103. package/public/lessons.html +650 -0
  104. package/public/vercel.json +8 -0
  105. package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
  106. package/scripts/a2ui-engine.js +73 -0
  107. package/scripts/access-anomaly-detector.js +12 -0
  108. package/scripts/adk-consolidator.js +266 -0
  109. package/scripts/agent-readiness.js +220 -0
  110. package/scripts/agent-security-hardening.js +227 -0
  111. package/scripts/agentic-data-pipeline.js +847 -0
  112. package/scripts/analytics-report.js +328 -0
  113. package/scripts/analytics-window.js +158 -0
  114. package/scripts/async-job-runner.js +1001 -0
  115. package/scripts/audit-trail.js +398 -0
  116. package/scripts/auto-promote-gates.js +299 -0
  117. package/scripts/auto-wire-hooks.js +312 -0
  118. package/scripts/autonomous-sales-agent.js +39 -0
  119. package/scripts/autoresearch-runner.js +216 -0
  120. package/scripts/background-agent-governance.js +237 -0
  121. package/scripts/behavioral-extraction.js +97 -0
  122. package/scripts/belief-update.js +84 -0
  123. package/scripts/billing.js +2438 -0
  124. package/scripts/bot-detector.js +50 -0
  125. package/scripts/budget-guard.js +173 -0
  126. package/scripts/build-claude-mcpb.js +189 -0
  127. package/scripts/build-metadata.js +97 -0
  128. package/scripts/check-congruence.js +322 -0
  129. package/scripts/cli-feedback.js +135 -0
  130. package/scripts/cli-telemetry.js +87 -0
  131. package/scripts/cloudflare-dynamic-sandbox.js +315 -0
  132. package/scripts/code-reasoning.js +350 -0
  133. package/scripts/codegraph-context.js +466 -0
  134. package/scripts/commercial-offer.js +56 -0
  135. package/scripts/computer-use-firewall.js +250 -0
  136. package/scripts/context-engine.js +694 -0
  137. package/scripts/contextfs.js +1287 -0
  138. package/scripts/conversation-context.js +119 -0
  139. package/scripts/creator-campaigns.js +239 -0
  140. package/scripts/daemon-manager.js +108 -0
  141. package/scripts/daily-digest.js +11 -0
  142. package/scripts/dashboard-render-spec.js +395 -0
  143. package/scripts/dashboard.js +1058 -0
  144. package/scripts/data-governance.js +173 -0
  145. package/scripts/delegation-runtime.js +900 -0
  146. package/scripts/deploy-gcp.sh +44 -0
  147. package/scripts/deploy-policy.js +263 -0
  148. package/scripts/disagreement-mining.js +315 -0
  149. package/scripts/dispatch-brief.js +159 -0
  150. package/scripts/distribution-surfaces.js +44 -0
  151. package/scripts/dpo-optimizer.js +209 -0
  152. package/scripts/ephemeral-agent-store.js +219 -0
  153. package/scripts/eval-harness.js +56 -0
  154. package/scripts/evolution-state.js +241 -0
  155. package/scripts/experiment-tracker.js +267 -0
  156. package/scripts/export-databricks-bundle.js +242 -0
  157. package/scripts/export-dpo-pairs.js +345 -0
  158. package/scripts/export-kto-pairs.js +310 -0
  159. package/scripts/export-training.js +448 -0
  160. package/scripts/failure-diagnostics.js +558 -0
  161. package/scripts/feedback-attribution.js +313 -0
  162. package/scripts/feedback-fallback.js +111 -0
  163. package/scripts/feedback-history-distiller.js +391 -0
  164. package/scripts/feedback-inbox-read.js +162 -0
  165. package/scripts/feedback-loop.js +1887 -0
  166. package/scripts/feedback-paths.js +145 -0
  167. package/scripts/feedback-quality.js +139 -0
  168. package/scripts/feedback-root-consolidator.js +238 -0
  169. package/scripts/feedback-schema.js +426 -0
  170. package/scripts/feedback-session.js +286 -0
  171. package/scripts/feedback-to-memory.js +185 -0
  172. package/scripts/feedback-to-rules.js +163 -0
  173. package/scripts/filesystem-search.js +404 -0
  174. package/scripts/funnel-analytics.js +35 -0
  175. package/scripts/gate-satisfy.js +42 -0
  176. package/scripts/gate-stats.js +116 -0
  177. package/scripts/gate-templates.js +70 -0
  178. package/scripts/gates-engine.js +816 -0
  179. package/scripts/generate-paperbanana-diagrams.sh +99 -0
  180. package/scripts/generate-pretool-hook.sh +40 -0
  181. package/scripts/github-about.js +350 -0
  182. package/scripts/github-outreach.js +65 -0
  183. package/scripts/gtm-revenue-loop.js +520 -0
  184. package/scripts/hallucination-detector.js +226 -0
  185. package/scripts/hf-papers.js +317 -0
  186. package/scripts/history-distiller.js +200 -0
  187. package/scripts/hook-auto-capture.sh +95 -0
  188. package/scripts/hook-stop-pr-thread-check.sh +68 -0
  189. package/scripts/hook-stop-self-score.sh +51 -0
  190. package/scripts/hook-stop-verify-deploy.sh +31 -0
  191. package/scripts/hook-thumbgate-cache-updater.js +48 -0
  192. package/scripts/hook-verify-before-done.sh +20 -0
  193. package/scripts/hosted-config.js +170 -0
  194. package/scripts/hybrid-feedback-context.js +676 -0
  195. package/scripts/install-mcp.js +159 -0
  196. package/scripts/intent-router.js +392 -0
  197. package/scripts/internal-agent-bootstrap.js +490 -0
  198. package/scripts/jsonl-watcher.js +155 -0
  199. package/scripts/lesson-db.js +613 -0
  200. package/scripts/lesson-inference.js +315 -0
  201. package/scripts/lesson-retrieval.js +95 -0
  202. package/scripts/lesson-rotation.js +137 -0
  203. package/scripts/lesson-search.js +644 -0
  204. package/scripts/lesson-synthesis.js +196 -0
  205. package/scripts/license.js +50 -0
  206. package/scripts/local-model-profile.js +383 -0
  207. package/scripts/markdown-escape.js +12 -0
  208. package/scripts/marketing-experiment.js +671 -0
  209. package/scripts/mcp-config.js +149 -0
  210. package/scripts/mcp-policy.js +99 -0
  211. package/scripts/memalign-recall.js +111 -0
  212. package/scripts/memory-firewall.js +222 -0
  213. package/scripts/memory-migration.js +296 -0
  214. package/scripts/meta-policy.js +194 -0
  215. package/scripts/metered-billing.js +16 -0
  216. package/scripts/model-tier-router.js +301 -0
  217. package/scripts/money-watcher.js +71 -0
  218. package/scripts/multi-hop-recall.js +240 -0
  219. package/scripts/natural-language-harness.js +330 -0
  220. package/scripts/obsidian-export.js +712 -0
  221. package/scripts/operational-dashboard.js +103 -0
  222. package/scripts/operational-summary.js +93 -0
  223. package/scripts/optimize-context.js +17 -0
  224. package/scripts/org-dashboard.js +201 -0
  225. package/scripts/partner-orchestration.js +146 -0
  226. package/scripts/per-step-scoring.js +165 -0
  227. package/scripts/perplexity-marketing.js +466 -0
  228. package/scripts/pii-scanner.js +153 -0
  229. package/scripts/plan-gate.js +154 -0
  230. package/scripts/post-everywhere.js +308 -0
  231. package/scripts/post-to-x-retry.sh +22 -0
  232. package/scripts/post-to-x.js +369 -0
  233. package/scripts/pr-manager.js +236 -0
  234. package/scripts/predictive-insights.js +356 -0
  235. package/scripts/principle-extractor.js +162 -0
  236. package/scripts/pro-features.js +40 -0
  237. package/scripts/pro-local-dashboard.js +174 -0
  238. package/scripts/problem-detail.js +53 -0
  239. package/scripts/product-feedback.js +134 -0
  240. package/scripts/profile-router.js +245 -0
  241. package/scripts/prompt-dlp.js +221 -0
  242. package/scripts/prompt-guard.js +83 -0
  243. package/scripts/prove-adapters.js +863 -0
  244. package/scripts/prove-attribution.js +365 -0
  245. package/scripts/prove-automation.js +653 -0
  246. package/scripts/prove-autoresearch.js +304 -0
  247. package/scripts/prove-claim-verification.js +277 -0
  248. package/scripts/prove-cloudflare-sandbox.js +163 -0
  249. package/scripts/prove-data-pipeline.js +410 -0
  250. package/scripts/prove-data-quality.js +227 -0
  251. package/scripts/prove-evolution.js +352 -0
  252. package/scripts/prove-harnesses.js +287 -0
  253. package/scripts/prove-intelligence.js +259 -0
  254. package/scripts/prove-lancedb.js +371 -0
  255. package/scripts/prove-local-intelligence.js +342 -0
  256. package/scripts/prove-loop-closure.js +263 -0
  257. package/scripts/prove-predictive-insights.js +357 -0
  258. package/scripts/prove-runtime.js +350 -0
  259. package/scripts/prove-seo-gsd.js +234 -0
  260. package/scripts/prove-settings.js +279 -0
  261. package/scripts/prove-subway-upgrades.js +277 -0
  262. package/scripts/prove-tessl.js +229 -0
  263. package/scripts/prove-training-export.js +327 -0
  264. package/scripts/prove-workflow-contract.js +116 -0
  265. package/scripts/prove-xmemory.js +332 -0
  266. package/scripts/publish-decision.js +133 -0
  267. package/scripts/pulse.js +80 -0
  268. package/scripts/rate-limiter.js +125 -0
  269. package/scripts/reddit-dm-outreach.js +182 -0
  270. package/scripts/reddit-monitor-cron.sh +26 -0
  271. package/scripts/reflector-agent.js +221 -0
  272. package/scripts/reminder-engine.js +132 -0
  273. package/scripts/revenue-status.js +472 -0
  274. package/scripts/risk-scorer.js +458 -0
  275. package/scripts/rlaif-self-audit.js +129 -0
  276. package/scripts/rubric-engine.js +230 -0
  277. package/scripts/schedule-manager.js +251 -0
  278. package/scripts/secret-scanner.js +414 -0
  279. package/scripts/self-heal.js +147 -0
  280. package/scripts/self-healing-check.js +188 -0
  281. package/scripts/semantic-layer.js +98 -0
  282. package/scripts/seo-gsd.js +1153 -0
  283. package/scripts/settings-hierarchy.js +214 -0
  284. package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
  285. package/scripts/skill-exporter.js +262 -0
  286. package/scripts/skill-generator.js +446 -0
  287. package/scripts/skill-materializer.js +134 -0
  288. package/scripts/skill-packs.js +136 -0
  289. package/scripts/skill-proposer.js +99 -0
  290. package/scripts/skill-quality-tracker.js +284 -0
  291. package/scripts/slo-alert-engine.js +14 -0
  292. package/scripts/slow-loop.js +72 -0
  293. package/scripts/social-analytics/db/schema.sql +32 -0
  294. package/scripts/social-analytics/digest.js +256 -0
  295. package/scripts/social-analytics/generate-instagram-card.js +97 -0
  296. package/scripts/social-analytics/instagram-thumbgate-post.js +73 -0
  297. package/scripts/social-analytics/mcp-server.js +289 -0
  298. package/scripts/social-analytics/normalizer.js +580 -0
  299. package/scripts/social-analytics/notify.js +162 -0
  300. package/scripts/social-analytics/poll-all.js +107 -0
  301. package/scripts/social-analytics/pollers/github.js +195 -0
  302. package/scripts/social-analytics/pollers/instagram.js +253 -0
  303. package/scripts/social-analytics/pollers/linkedin.js +330 -0
  304. package/scripts/social-analytics/pollers/plausible.js +247 -0
  305. package/scripts/social-analytics/pollers/reddit.js +306 -0
  306. package/scripts/social-analytics/pollers/threads.js +233 -0
  307. package/scripts/social-analytics/pollers/tiktok.js +203 -0
  308. package/scripts/social-analytics/pollers/x.js +227 -0
  309. package/scripts/social-analytics/pollers/youtube.js +304 -0
  310. package/scripts/social-analytics/pollers/zernio.js +180 -0
  311. package/scripts/social-analytics/publish-instagram-thumbgate.js +85 -0
  312. package/scripts/social-analytics/publishers/devto.js +122 -0
  313. package/scripts/social-analytics/publishers/instagram.js +317 -0
  314. package/scripts/social-analytics/publishers/linkedin.js +294 -0
  315. package/scripts/social-analytics/publishers/reddit.js +390 -0
  316. package/scripts/social-analytics/publishers/threads.js +275 -0
  317. package/scripts/social-analytics/publishers/tiktok.js +217 -0
  318. package/scripts/social-analytics/publishers/x.js +259 -0
  319. package/scripts/social-analytics/publishers/youtube.js +223 -0
  320. package/scripts/social-analytics/publishers/zernio.js +209 -0
  321. package/scripts/social-analytics/run-digest.js +34 -0
  322. package/scripts/social-analytics/store.js +257 -0
  323. package/scripts/social-analytics/utm.js +143 -0
  324. package/scripts/social-pipeline.js +2628 -0
  325. package/scripts/social-quality-gate.js +18 -0
  326. package/scripts/social-reply-monitor.js +445 -0
  327. package/scripts/status-dashboard.js +155 -0
  328. package/scripts/statusline-lesson.js +16 -0
  329. package/scripts/statusline-tower.js +8 -0
  330. package/scripts/statusline.sh +116 -0
  331. package/scripts/stripe-live-status.js +115 -0
  332. package/scripts/subagent-profiles.js +79 -0
  333. package/scripts/sync-gh-secrets-from-env.sh +70 -0
  334. package/scripts/sync-github-about.js +52 -0
  335. package/scripts/sync-version.js +451 -0
  336. package/scripts/synthetic-dpo.js +234 -0
  337. package/scripts/telemetry-analytics.js +821 -0
  338. package/scripts/tessl-export.js +371 -0
  339. package/scripts/test-coverage.js +120 -0
  340. package/scripts/thompson-sampling.js +417 -0
  341. package/scripts/thumbgate-search.js +189 -0
  342. package/scripts/tool-kpi-tracker.js +12 -0
  343. package/scripts/tool-registry.js +811 -0
  344. package/scripts/train_from_feedback.py +910 -0
  345. package/scripts/user-profile.js +78 -0
  346. package/scripts/validate-feedback.js +580 -0
  347. package/scripts/validate-workflow-contract.js +287 -0
  348. package/scripts/vector-store.js +198 -0
  349. package/scripts/verification-loop.js +291 -0
  350. package/scripts/verify-obsidian-setup.sh +269 -0
  351. package/scripts/verify-run.js +269 -0
  352. package/scripts/webhook-delivery.js +62 -0
  353. package/scripts/weekly-auto-post.js +124 -0
  354. package/scripts/workflow-runs.js +154 -0
  355. package/scripts/workflow-sprint-intake.js +475 -0
  356. package/scripts/workspace-evolver.js +374 -0
  357. package/scripts/x-autonomous-marketing.js +139 -0
  358. package/scripts/xmemory-lite.js +405 -0
  359. package/skills/agent-memory/SKILL.md +97 -0
  360. package/skills/solve-architecture-autonomy/SKILL.md +17 -0
  361. package/skills/solve-architecture-autonomy/tool.js +33 -0
  362. package/skills/thumbgate/SKILL.md +114 -0
  363. package/skills/thumbgate-feedback/SKILL.md +49 -0
  364. package/src/api/server.js +4208 -0
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const { execFileSync } = require('child_process');
8
+
9
+ const ROOT = path.join(__dirname, '..');
10
+
11
+ function resolveProofPaths() {
12
+ const proofDir = process.env.THUMBGATE_PROOF_DIR || path.join(ROOT, 'proof');
13
+ return {
14
+ proofDir,
15
+ reportJson: path.join(proofDir, 'seo-gsd-report.json'),
16
+ reportMd: path.join(proofDir, 'seo-gsd-report.md'),
17
+ };
18
+ }
19
+
20
+ async function run() {
21
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'thumbgate-seo-gsd-proof-'));
22
+ const results = { passed: 0, failed: 0, requirements: {} };
23
+ const { proofDir, reportJson, reportMd } = resolveProofPaths();
24
+
25
+ const checks = [
26
+ {
27
+ id: 'SEO-GSD-01',
28
+ desc: 'buildThumbGateSeoPlan returns GSD stages and four high-ROI public pages',
29
+ fn: () => {
30
+ delete require.cache[require.resolve('./seo-gsd')];
31
+ const { buildThumbGateSeoPlan, HIGH_ROI_QUERY_SEEDS } = require('./seo-gsd');
32
+ const plan = buildThumbGateSeoPlan(HIGH_ROI_QUERY_SEEDS);
33
+
34
+ if (plan.framework !== 'GSD') throw new Error('Framework must be GSD');
35
+ if (plan.capture.totalKeywords !== HIGH_ROI_QUERY_SEEDS.length) {
36
+ throw new Error('Capture stage keyword count is incorrect');
37
+ }
38
+ if (plan.execute.pages.length < 4) throw new Error('Execute stage must publish at least 4 public pages');
39
+ if (!plan.review.recommendedOrder.includes('/compare/speclock')) {
40
+ throw new Error('Recommended order must include /compare/speclock');
41
+ }
42
+ },
43
+ },
44
+ {
45
+ id: 'SEO-GSD-02',
46
+ desc: 'comparison pages stay prioritized ahead of guide pages for bottom-of-funnel demand',
47
+ fn: () => {
48
+ delete require.cache[require.resolve('./seo-gsd')];
49
+ const { THUMBGATE_SEO_PAGE_SPECS } = require('./seo-gsd');
50
+ const publishOrder = THUMBGATE_SEO_PAGE_SPECS.map((page) => page.path);
51
+
52
+ if (publishOrder[0] !== '/compare/speclock') {
53
+ throw new Error('First publish target must be /compare/speclock');
54
+ }
55
+ if (publishOrder[1] !== '/compare/mem0') {
56
+ throw new Error('Second publish target must be /compare/mem0');
57
+ }
58
+ if (new Set(publishOrder).size !== THUMBGATE_SEO_PAGE_SPECS.length) {
59
+ throw new Error('Public SEO paths must be unique');
60
+ }
61
+ },
62
+ },
63
+ {
64
+ id: 'SEO-GSD-03',
65
+ desc: 'renderSeoPageHtml includes schema, thumbs messaging, and proof-backed CTA links',
66
+ fn: () => {
67
+ delete require.cache[require.resolve('./seo-gsd')];
68
+ const { findSeoPageByPath, renderSeoPageHtml } = require('./seo-gsd');
69
+ const page = findSeoPageByPath('/compare/speclock');
70
+ const html = renderSeoPageHtml(page, { appOrigin: 'https://app.example.com' });
71
+
72
+ if (!/"@type": "TechArticle"/.test(html)) throw new Error('TechArticle JSON-LD missing');
73
+ if (!/"@type": "FAQPage"/.test(html)) throw new Error('FAQPage JSON-LD missing');
74
+ if (!/👍 Thumbs up reinforces good behavior/u.test(html)) {
75
+ throw new Error('Thumbs-up proof copy missing');
76
+ }
77
+ if (!/👎 Thumbs down blocks repeated mistakes/u.test(html)) {
78
+ throw new Error('Thumbs-down proof copy missing');
79
+ }
80
+ if (!/Verification evidence/.test(html) || !/Automation proof/.test(html)) {
81
+ throw new Error('Proof CTA links missing');
82
+ }
83
+ },
84
+ },
85
+ {
86
+ id: 'SEO-GSD-04',
87
+ desc: 'seo-gsd CLI writes capture, clarify, organize, execute, review, and page-spec outputs',
88
+ fn: () => {
89
+ execFileSync(process.execPath, [
90
+ 'scripts/seo-gsd.js',
91
+ 'plan',
92
+ '--write',
93
+ `--out-dir=${tmpDir}`,
94
+ ], {
95
+ cwd: ROOT,
96
+ stdio: 'pipe',
97
+ encoding: 'utf8',
98
+ });
99
+
100
+ const expectedFiles = [
101
+ '01-capture.json',
102
+ '02-clarify.json',
103
+ '03-organize.json',
104
+ '04-execute-briefs.md',
105
+ '05-review.json',
106
+ '06-page-specs.json',
107
+ ];
108
+ for (const name of expectedFiles) {
109
+ if (!fs.existsSync(path.join(tmpDir, name))) {
110
+ throw new Error(`Missing generated output ${name}`);
111
+ }
112
+ }
113
+ },
114
+ },
115
+ {
116
+ id: 'SEO-GSD-05',
117
+ desc: 'public sitemap lists the homepage plus every SEO comparison and guide page',
118
+ fn: () => {
119
+ delete require.cache[require.resolve('../src/api/server')];
120
+ const { __test__ } = require('../src/api/server');
121
+ const sitemap = __test__.renderSitemapXml({ appOrigin: 'https://app.example.com' });
122
+
123
+ for (const pathname of [
124
+ '/',
125
+ '/compare/speclock',
126
+ '/compare/mem0',
127
+ '/guides/pre-action-gates',
128
+ '/guides/claude-code-feedback',
129
+ ]) {
130
+ const loc = pathname === '/'
131
+ ? '<loc>https://app.example.com/</loc>'
132
+ : `<loc>https://app.example.com${pathname}</loc>`;
133
+ if (!sitemap.includes(loc)) {
134
+ throw new Error(`Sitemap missing ${pathname}`);
135
+ }
136
+ }
137
+ },
138
+ },
139
+ {
140
+ id: 'SEO-GSD-06',
141
+ desc: 'landing page internally links to the high-intent comparison and guide pages',
142
+ fn: () => {
143
+ const landingHtml = fs.readFileSync(path.join(ROOT, 'public', 'index.html'), 'utf8');
144
+
145
+ if (!/id="compare-guides"/.test(landingHtml)) {
146
+ throw new Error('Landing page compare-guides section missing');
147
+ }
148
+ for (const pathname of [
149
+ '/compare/speclock',
150
+ '/compare/mem0',
151
+ '/guides/pre-action-gates',
152
+ '/guides/claude-code-feedback',
153
+ ]) {
154
+ if (!landingHtml.includes(`href="${pathname}"`)) {
155
+ throw new Error(`Landing page missing internal link for ${pathname}`);
156
+ }
157
+ }
158
+ },
159
+ },
160
+ ];
161
+
162
+ console.log('SEO/GEO GSD - Proof Gate\n');
163
+ console.log('Checking requirements:\n');
164
+
165
+ for (const check of checks) {
166
+ try {
167
+ await check.fn();
168
+ results.passed++;
169
+ results.requirements[check.id] = { status: 'pass', desc: check.desc };
170
+ console.log(` PASS ${check.id}: ${check.desc}`);
171
+ } catch (error) {
172
+ results.failed++;
173
+ results.requirements[check.id] = {
174
+ status: 'fail',
175
+ desc: check.desc,
176
+ error: error.message,
177
+ };
178
+ console.error(` FAIL ${check.id}: ${error.message}`);
179
+ }
180
+ }
181
+
182
+ fs.rmSync(tmpDir, { recursive: true, force: true });
183
+ fs.mkdirSync(proofDir, { recursive: true });
184
+
185
+ const report = {
186
+ phase: '10-seo-gsd',
187
+ generatedAt: new Date().toISOString(),
188
+ passed: results.passed,
189
+ failed: results.failed,
190
+ total: checks.length,
191
+ requirements: results.requirements,
192
+ };
193
+
194
+ fs.writeFileSync(reportJson, JSON.stringify(report, null, 2) + '\n');
195
+
196
+ const markdown = [
197
+ '# SEO/GEO GSD Proof Report',
198
+ '',
199
+ `Generated: ${report.generatedAt}`,
200
+ `Result: ${results.passed}/${checks.length} passed`,
201
+ '',
202
+ '## Requirements',
203
+ '',
204
+ ...Object.entries(results.requirements).map(([id, requirement]) => {
205
+ const checkbox = requirement.status === 'pass' ? '[x]' : '[ ]';
206
+ const errorLine = requirement.error ? `\n - Error: \`${requirement.error}\`` : '';
207
+ return `- ${checkbox} **${id}**: ${requirement.desc}${errorLine}`;
208
+ }),
209
+ '',
210
+ `${results.passed} passed, ${results.failed} failed`,
211
+ '',
212
+ ].join('\n');
213
+
214
+ fs.writeFileSync(reportMd, `${markdown}\n`);
215
+
216
+ console.log(`\nResult: ${results.passed} passed, ${results.failed} failed`);
217
+ console.log(`Report: ${reportJson}`);
218
+
219
+ if (results.failed > 0) {
220
+ process.exitCode = 1;
221
+ }
222
+ }
223
+
224
+ if (require.main === module) {
225
+ run().catch((error) => {
226
+ console.error(error.message || String(error));
227
+ process.exit(1);
228
+ });
229
+ }
230
+
231
+ module.exports = {
232
+ resolveProofPaths,
233
+ run,
234
+ };
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ const ROOT = path.join(__dirname, '..');
9
+ const SETTINGS_PATH = require.resolve('./settings-hierarchy');
10
+ const PROFILE_ROUTER_PATH = require.resolve('./profile-router');
11
+ const HARNESS_PATH = require.resolve('./natural-language-harness');
12
+ const VERIFY_RUN_PATH = require.resolve('./verify-run');
13
+ const SERVER_STDIO_PATH = require.resolve('../adapters/mcp/server-stdio');
14
+ const DASHBOARD_PATH = require.resolve('./dashboard');
15
+ const TOOL_REGISTRY_PATH = require.resolve('./tool-registry');
16
+ const MCP_ALLOWLISTS_PATH = require.resolve('../config/mcp-allowlists.json');
17
+
18
+ function resolveProofPaths() {
19
+ const proofDir = process.env.THUMBGATE_SETTINGS_PROOF_DIR || process.env.THUMBGATE_PROOF_DIR || path.join(ROOT, 'proof');
20
+ return {
21
+ proofDir,
22
+ reportJson: path.join(proofDir, 'settings-report.json'),
23
+ reportMd: path.join(proofDir, 'settings-report.md'),
24
+ };
25
+ }
26
+
27
+ function resetModules() {
28
+ [
29
+ SETTINGS_PATH,
30
+ PROFILE_ROUTER_PATH,
31
+ HARNESS_PATH,
32
+ VERIFY_RUN_PATH,
33
+ SERVER_STDIO_PATH,
34
+ DASHBOARD_PATH,
35
+ TOOL_REGISTRY_PATH,
36
+ MCP_ALLOWLISTS_PATH,
37
+ ].forEach((modulePath) => {
38
+ delete require.cache[modulePath];
39
+ });
40
+ }
41
+
42
+ function writeJson(filePath, payload) {
43
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
44
+ fs.writeFileSync(filePath, JSON.stringify(payload, null, 2));
45
+ }
46
+
47
+ function makeTempProject(structure = {}) {
48
+ const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'thumbgate-settings-project-'));
49
+ for (const [relativePath, payload] of Object.entries(structure)) {
50
+ const fullPath = path.join(projectRoot, relativePath);
51
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
52
+ writeJson(fullPath, payload);
53
+ }
54
+ return projectRoot;
55
+ }
56
+
57
+ function writeReports(results, reportJson, reportMd) {
58
+ fs.mkdirSync(path.dirname(reportJson), { recursive: true });
59
+ fs.writeFileSync(reportJson, JSON.stringify(results, null, 2));
60
+
61
+ const lines = [
62
+ '# Settings Hierarchy Proof',
63
+ '',
64
+ `- Phase: ${results.phase}`,
65
+ `- Timestamp: ${results.timestamp}`,
66
+ `- ${results.passed} passed, ${results.failed} failed`,
67
+ '',
68
+ '## Requirements',
69
+ '',
70
+ ];
71
+
72
+ for (const requirement of Object.values(results.requirements)) {
73
+ lines.push(`- [${requirement.passed ? 'x' : ' '}] **${requirement.id}** ${requirement.desc}`);
74
+ }
75
+
76
+ fs.writeFileSync(reportMd, `${lines.join('\n')}\n`);
77
+ }
78
+
79
+ function withClearedEnv(keys, fn) {
80
+ const previous = new Map(keys.map((key) => [key, process.env[key]]));
81
+ for (const key of keys) {
82
+ delete process.env[key];
83
+ }
84
+
85
+ try {
86
+ return fn();
87
+ } finally {
88
+ for (const [key, value] of previous.entries()) {
89
+ if (value === undefined) {
90
+ delete process.env[key];
91
+ } else {
92
+ process.env[key] = value;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ async function run() {
99
+ const results = {
100
+ phase: '24-settings-hierarchy',
101
+ timestamp: new Date().toISOString(),
102
+ passed: 0,
103
+ failed: 0,
104
+ requirements: {},
105
+ };
106
+ const { reportJson, reportMd } = resolveProofPaths();
107
+
108
+ const checks = [
109
+ {
110
+ id: 'SETTINGS-01',
111
+ desc: 'managed > local > project > user > defaults precedence resolves correctly',
112
+ fn: () => {
113
+ const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'thumbgate-settings-home-'));
114
+ const projectRoot = makeTempProject({
115
+ 'config/thumbgate-settings.managed.json': { mcp: { defaultProfile: 'locked' } },
116
+ '.thumbgate/settings.json': { mcp: { defaultProfile: 'dispatch' } },
117
+ '.thumbgate/settings.local.json': { mcp: { defaultProfile: 'readonly' } },
118
+ });
119
+ writeJson(path.join(homeDir, '.thumbgate', 'settings.json'), {
120
+ mcp: { defaultProfile: 'commerce' },
121
+ });
122
+ const { getSetting } = require('./settings-hierarchy');
123
+ const profile = getSetting('mcp.defaultProfile', { projectRoot, homeDir });
124
+ if (profile !== 'locked') {
125
+ throw new Error(`Expected managed profile override, got ${profile}`);
126
+ }
127
+ fs.rmSync(projectRoot, { recursive: true, force: true });
128
+ fs.rmSync(homeDir, { recursive: true, force: true });
129
+ },
130
+ },
131
+ {
132
+ id: 'SETTINGS-02',
133
+ desc: 'settings status returns origin metadata for resolved fields',
134
+ fn: () => {
135
+ const projectRoot = makeTempProject({
136
+ 'config/thumbgate-settings.managed.json': { dashboard: { showPolicyOrigins: true } },
137
+ });
138
+ const { getSettingsStatus } = require('./settings-hierarchy');
139
+ const status = getSettingsStatus({ projectRoot, homeDir: projectRoot });
140
+ const origin = status.origins.find((entry) => entry.path === 'dashboard.showPolicyOrigins');
141
+ if (!origin || origin.scope !== 'managed') {
142
+ throw new Error('Expected managed origin for dashboard.showPolicyOrigins');
143
+ }
144
+ fs.rmSync(projectRoot, { recursive: true, force: true });
145
+ },
146
+ },
147
+ {
148
+ id: 'SETTINGS-03',
149
+ desc: 'profile routing uses settings fallback when env override is absent',
150
+ fn: () => {
151
+ const projectRoot = makeTempProject({
152
+ 'config/thumbgate-settings.managed.json': { mcp: { defaultProfile: 'dispatch' } },
153
+ });
154
+ try {
155
+ const routed = withClearedEnv(
156
+ ['THUMBGATE_MCP_PROFILE', 'CI', 'GITHUB_EVENT_NAME', 'THUMBGATE_SESSION_TYPE', 'THUMBGATE_SUBAGENT_PROFILE'],
157
+ () => {
158
+ const { routeProfile } = require('./profile-router');
159
+ return routeProfile({ settingsOptions: { projectRoot, homeDir: projectRoot } });
160
+ },
161
+ );
162
+ if (routed.profile !== 'dispatch') {
163
+ throw new Error(`Expected dispatch profile from settings, got ${routed.profile}`);
164
+ }
165
+ } finally {
166
+ fs.rmSync(projectRoot, { recursive: true, force: true });
167
+ }
168
+ },
169
+ },
170
+ {
171
+ id: 'SETTINGS-04',
172
+ desc: 'harness execution is blocked when runtime execution is disabled by settings',
173
+ fn: () => {
174
+ const projectRoot = makeTempProject({
175
+ '.thumbgate/settings.json': { harnesses: { allowRuntimeExecution: false } },
176
+ });
177
+ const { runHarness } = require('./natural-language-harness');
178
+ let blocked = false;
179
+ try {
180
+ runHarness('repo-full-verification', {}, {
181
+ settingsOptions: { projectRoot, homeDir: projectRoot },
182
+ });
183
+ } catch (error) {
184
+ blocked = /disabled by the settings hierarchy/i.test(error.message);
185
+ }
186
+ if (!blocked) {
187
+ throw new Error('Expected harness execution to be blocked by settings');
188
+ }
189
+ fs.rmSync(projectRoot, { recursive: true, force: true });
190
+ },
191
+ },
192
+ {
193
+ id: 'SETTINGS-05',
194
+ desc: 'dashboard and MCP surfaces expose settings status visibility',
195
+ fn: () => {
196
+ const feedbackDir = fs.mkdtempSync(path.join(os.tmpdir(), 'thumbgate-settings-dashboard-'));
197
+ const { generateDashboard } = require('./dashboard');
198
+ const dashboard = generateDashboard(feedbackDir);
199
+ const { TOOLS } = require('./tool-registry');
200
+ const mcpAllowlists = require('../config/mcp-allowlists.json');
201
+
202
+ try {
203
+ if (!dashboard.settingsStatus || !dashboard.settingsStatus.resolvedSettings) {
204
+ throw new Error('Dashboard is missing settingsStatus');
205
+ }
206
+
207
+ const settingsTool = TOOLS.find((tool) => tool.name === 'settings_status');
208
+ if (!settingsTool) {
209
+ throw new Error('settings_status tool is missing from the registry');
210
+ }
211
+
212
+ const defaultAllowlist = mcpAllowlists.profiles && mcpAllowlists.profiles.default;
213
+ if (!Array.isArray(defaultAllowlist) || !defaultAllowlist.includes('settings_status')) {
214
+ throw new Error('settings_status is missing from the default MCP allowlist');
215
+ }
216
+ } finally {
217
+ fs.rmSync(feedbackDir, { recursive: true, force: true });
218
+ }
219
+ },
220
+ },
221
+ {
222
+ id: 'SETTINGS-06',
223
+ desc: 'verify:full includes settings proof and materializes the artifact',
224
+ fn: () => {
225
+ const { buildVerifyPlan, recordVerifyWorkflowRun } = require('./verify-run');
226
+ const plan = buildVerifyPlan('full');
227
+ if (!plan.some((step) => Array.isArray(step.args) && step.args.includes('prove:settings'))) {
228
+ throw new Error('verify:full is missing prove:settings');
229
+ }
230
+ const feedbackDir = fs.mkdtempSync(path.join(os.tmpdir(), 'thumbgate-settings-proof-run-'));
231
+ try {
232
+ const workflowRun = recordVerifyWorkflowRun('full', ROOT, feedbackDir);
233
+ if (!workflowRun.proofArtifacts.some((artifact) => artifact.endsWith(path.join('proof', 'settings-report.json')))) {
234
+ throw new Error('Workflow run is missing settings proof artifact');
235
+ }
236
+ } finally {
237
+ fs.rmSync(feedbackDir, { recursive: true, force: true });
238
+ }
239
+ },
240
+ },
241
+ ];
242
+
243
+ for (const check of checks) {
244
+ try {
245
+ resetModules();
246
+ await check.fn();
247
+ results.passed += 1;
248
+ results.requirements[check.id] = { id: check.id, desc: check.desc, passed: true };
249
+ } catch (error) {
250
+ results.failed += 1;
251
+ results.requirements[check.id] = {
252
+ id: check.id,
253
+ desc: check.desc,
254
+ passed: false,
255
+ error: error.message,
256
+ };
257
+ } finally {
258
+ resetModules();
259
+ }
260
+ }
261
+
262
+ writeReports(results, reportJson, reportMd);
263
+ console.log(`${results.passed} passed, ${results.failed} failed`);
264
+ if (results.failed > 0) {
265
+ process.exitCode = 1;
266
+ }
267
+ }
268
+
269
+ if (require.main === module) {
270
+ run().catch((error) => {
271
+ console.error(error.stack || error.message);
272
+ process.exit(1);
273
+ });
274
+ }
275
+
276
+ module.exports = {
277
+ resolveProofPaths,
278
+ run,
279
+ };