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,426 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Feedback Schema Validator
4
+ *
5
+ * Implements three reliability patterns:
6
+ * 1. Typed schemas — enforce structure on every feedback memory
7
+ * 2. Action schemas — discriminated union of allowed feedback actions
8
+ * 3. Validation at boundaries — reject bad data before storage
9
+ */
10
+
11
+ const GENERIC_TAGS = new Set(['feedback', 'positive', 'negative']);
12
+ const MIN_CONTENT_LENGTH = 20;
13
+ const MAX_TITLE_DESCRIPTION_LENGTH = 120;
14
+ const VALID_TITLE_PREFIXES = ['SUCCESS:', 'MISTAKE:', 'LEARNING:', 'PREFERENCE:'];
15
+ const VALID_CATEGORIES = new Set(['error', 'learning', 'preference']);
16
+
17
+ /**
18
+ * Truncate text at a word boundary to avoid cutting mid-word.
19
+ * Falls back to hard truncation if no suitable space is found
20
+ * past the halfway point of maxLen.
21
+ */
22
+ function truncateAtWord(text, maxLen) {
23
+ if (!text || text.length <= maxLen) return text;
24
+ var truncated = text.slice(0, maxLen);
25
+ var lastSpace = truncated.lastIndexOf(' ');
26
+ return lastSpace > maxLen * 0.5 ? truncated.slice(0, lastSpace) + '...' : truncated + '...';
27
+ }
28
+ const {
29
+ assessFeedbackActionability,
30
+ normalizeFeedbackText,
31
+ } = require('./feedback-quality');
32
+
33
+ const INFERRED_TAG_RULES = [
34
+ { tag: 'thumbgate', keywords: ['thumbgate', 'feedback-loop', 'statusline', 'dashboard', 'mcp'] },
35
+ { tag: 'testing', keywords: ['test', 'testing', 'jest', 'coverage', 'verify', 'verification'] },
36
+ { tag: 'security', keywords: ['security', 'secret', 'credential', 'token', 'auth'] },
37
+ { tag: 'performance', keywords: ['perf', 'performance', 'latency', 'slow'] },
38
+ { tag: 'ui-components', keywords: ['ui', 'component', 'layout', 'style', 'figma'] },
39
+ { tag: 'api-integration', keywords: ['api', 'endpoint', 'request', 'response', 'server'] },
40
+ { tag: 'git-workflow', keywords: ['git', 'commit', 'branch', 'pr', 'pull request'] },
41
+ { tag: 'documentation', keywords: ['doc', 'docs', 'readme'] },
42
+ { tag: 'debugging', keywords: ['debug', 'debugging', 'error', 'bug', 'fix'] },
43
+ { tag: 'architecture', keywords: ['design', 'architecture', 'system'] },
44
+ { tag: 'data-modeling', keywords: ['schema', 'data', 'model', 'migration'] },
45
+ ];
46
+
47
+ function validateFeedbackMemory(memory) {
48
+ const issues = [];
49
+
50
+ if (!memory.title || typeof memory.title !== 'string') {
51
+ issues.push('title: required string');
52
+ } else {
53
+ const hasPrefix = VALID_TITLE_PREFIXES.some((p) => memory.title.startsWith(p));
54
+ if (!hasPrefix) {
55
+ issues.push(`title: must start with one of ${VALID_TITLE_PREFIXES.join(', ')}`);
56
+ }
57
+ const afterPrefix = memory.title.replace(/^(SUCCESS|MISTAKE|LEARNING|PREFERENCE):\s*/, '');
58
+ if (afterPrefix.length < 5) {
59
+ issues.push('title: description after prefix too short (min 5 chars)');
60
+ }
61
+ }
62
+
63
+ if (!memory.content || typeof memory.content !== 'string') {
64
+ issues.push('content: required string');
65
+ } else if (memory.content.length < MIN_CONTENT_LENGTH) {
66
+ issues.push(`content: too short (${memory.content.length} chars, min ${MIN_CONTENT_LENGTH})`);
67
+ }
68
+
69
+ if (!memory.category) {
70
+ issues.push('category: required');
71
+ } else if (!VALID_CATEGORIES.has(memory.category)) {
72
+ issues.push(`category: must be one of ${[...VALID_CATEGORIES].join(', ')} (got "${memory.category}")`);
73
+ }
74
+
75
+ if (!Array.isArray(memory.tags) || memory.tags.length === 0) {
76
+ issues.push('tags: at least 1 tag required');
77
+ } else {
78
+ const domainTags = memory.tags.filter((t) => !GENERIC_TAGS.has(t));
79
+ if (domainTags.length === 0) {
80
+ issues.push('tags: at least 1 non-generic tag required');
81
+ }
82
+ }
83
+
84
+ if (memory.title && memory.category) {
85
+ const titleIsError = memory.title.startsWith('MISTAKE:');
86
+ const titleIsSuccess = memory.title.startsWith('SUCCESS:') || memory.title.startsWith('LEARNING:');
87
+ if (titleIsError && memory.category !== 'error') {
88
+ issues.push('consistency: MISTAKE title should have category "error"');
89
+ }
90
+ if (titleIsSuccess && memory.category === 'error') {
91
+ issues.push('consistency: SUCCESS/LEARNING title should not have category "error"');
92
+ }
93
+ }
94
+
95
+ if (memory.rubricSummary != null) {
96
+ if (typeof memory.rubricSummary !== 'object') {
97
+ issues.push('rubricSummary: must be an object when provided');
98
+ } else {
99
+ const weightedScore = Number(memory.rubricSummary.weightedScore);
100
+ if (!Number.isFinite(weightedScore) || weightedScore < 0 || weightedScore > 1) {
101
+ issues.push('rubricSummary.weightedScore: must be a number between 0 and 1');
102
+ }
103
+ if (!Array.isArray(memory.rubricSummary.failingCriteria)) {
104
+ issues.push('rubricSummary.failingCriteria: must be an array');
105
+ }
106
+ if (!Array.isArray(memory.rubricSummary.failingGuardrails)) {
107
+ issues.push('rubricSummary.failingGuardrails: must be an array');
108
+ }
109
+ }
110
+ }
111
+
112
+ if (memory.bayesian != null) {
113
+ if (typeof memory.bayesian !== 'object') {
114
+ issues.push('bayesian: must be an object when provided');
115
+ } else {
116
+ const prior = Number(memory.bayesian.priorProbability);
117
+ if (!Number.isFinite(prior) || prior < 0 || prior > 1) {
118
+ issues.push('bayesian.priorProbability: must be a number between 0 and 1');
119
+ }
120
+ const uncertainty = Number(memory.bayesian.uncertainty);
121
+ if (!Number.isFinite(uncertainty) || uncertainty < 0 || uncertainty > 1) {
122
+ issues.push('bayesian.uncertainty: must be a number between 0 and 1');
123
+ }
124
+ if (typeof memory.bayesian.observations !== 'number') {
125
+ issues.push('bayesian.observations: must be a number');
126
+ }
127
+ }
128
+ }
129
+
130
+ return { valid: issues.length === 0, issues };
131
+ }
132
+
133
+ function inferFallbackDomainTag(params = {}) {
134
+ const explicitDomainTags = (params.tags || []).filter((tag) => !GENERIC_TAGS.has(tag));
135
+ if (explicitDomainTags.length > 0) return explicitDomainTags[0];
136
+
137
+ const text = normalizeFeedbackText([
138
+ params.context,
139
+ params.whatWorked,
140
+ params.whatWentWrong,
141
+ params.whatToChange,
142
+ ].filter(Boolean).join(' '));
143
+
144
+ if (!text) return 'general';
145
+
146
+ for (const rule of INFERRED_TAG_RULES) {
147
+ if (rule.keywords.some((keyword) => text.includes(keyword))) {
148
+ return rule.tag;
149
+ }
150
+ }
151
+
152
+ return 'general';
153
+ }
154
+
155
+ function resolveFeedbackAction(params) {
156
+ const {
157
+ signal,
158
+ context,
159
+ whatWentWrong,
160
+ whatToChange,
161
+ whatWorked,
162
+ reasoning,
163
+ visualEvidence,
164
+ tags,
165
+ rubricEvaluation,
166
+ } = params;
167
+
168
+ if (!context && !whatWentWrong && !whatWorked) {
169
+ return { type: 'no-action', reason: 'No context provided — cannot create actionable memory' };
170
+ }
171
+
172
+ const inferredDomainTag = inferFallbackDomainTag({
173
+ tags,
174
+ context,
175
+ whatWentWrong,
176
+ whatToChange,
177
+ whatWorked,
178
+ });
179
+ const domainTags = [...new Set([
180
+ ...(tags || []).filter((t) => !GENERIC_TAGS.has(t)),
181
+ inferredDomainTag,
182
+ ].filter(Boolean))];
183
+ const rubricSummary = rubricEvaluation
184
+ ? {
185
+ rubricId: rubricEvaluation.rubricId,
186
+ weightedScore: rubricEvaluation.weightedScore,
187
+ failingCriteria: rubricEvaluation.failingCriteria || [],
188
+ failingGuardrails: rubricEvaluation.failingGuardrails || [],
189
+ judgeDisagreements: rubricEvaluation.judgeDisagreements || [],
190
+ blockReasons: rubricEvaluation.blockReasons || [],
191
+ }
192
+ : null;
193
+ const rubricFailureTags = rubricSummary
194
+ ? (rubricSummary.failingCriteria || []).map((criterion) => `rubric-${criterion}`)
195
+ : [];
196
+
197
+ if (signal === 'negative') {
198
+ const actionability = assessFeedbackActionability({
199
+ signal: 'negative',
200
+ context,
201
+ whatWentWrong,
202
+ });
203
+ if (!actionability.promotable) {
204
+ const reason = actionability.issue === 'missing'
205
+ ? 'Negative feedback without context — cannot determine what went wrong'
206
+ : 'Negative feedback is too vague to promote — describe what failed in one sentence';
207
+ return { type: 'no-action', reason };
208
+ }
209
+
210
+ const content = [
211
+ whatWentWrong ? `What went wrong: ${whatWentWrong}` : `Context: ${context}`,
212
+ whatToChange ? `How to avoid: ${whatToChange}` : 'Action needed: investigate and prevent recurrence',
213
+ reasoning ? `Reasoning: ${reasoning}` : null,
214
+ visualEvidence ? `Visual Evidence: ${visualEvidence}` : null,
215
+ ].filter(Boolean).join('\n');
216
+ const rubricLines = [];
217
+ if (rubricSummary) {
218
+ rubricLines.push(`Rubric weighted score: ${rubricSummary.weightedScore}`);
219
+ if (rubricSummary.failingCriteria.length > 0) {
220
+ rubricLines.push(`Rubric failing criteria: ${rubricSummary.failingCriteria.join(', ')}`);
221
+ }
222
+ if (rubricSummary.failingGuardrails.length > 0) {
223
+ rubricLines.push(`Guardrails failed: ${rubricSummary.failingGuardrails.join(', ')}`);
224
+ }
225
+ if (rubricSummary.judgeDisagreements.length > 0) {
226
+ rubricLines.push('Judge disagreement detected; require manual review');
227
+ }
228
+ }
229
+
230
+ const description = truncateAtWord(whatWentWrong || context || '', MAX_TITLE_DESCRIPTION_LENGTH);
231
+
232
+ return {
233
+ type: 'store-mistake',
234
+ memory: {
235
+ title: `MISTAKE: ${description}`,
236
+ content: rubricLines.length > 0 ? `${content}\n${rubricLines.join('\n')}` : content,
237
+ category: 'error',
238
+ importance: 'high',
239
+ tags: ['feedback', 'negative', ...domainTags, ...rubricFailureTags],
240
+ rubricSummary,
241
+ bayesian: {
242
+ priorProbability: 0.5,
243
+ uncertainty: 0.5,
244
+ observations: 1,
245
+ lastUpdated: new Date().toISOString(),
246
+ },
247
+ },
248
+ };
249
+ }
250
+
251
+ if (signal === 'positive') {
252
+ if (rubricEvaluation && !rubricEvaluation.promotionEligible) {
253
+ const reasons = rubricEvaluation.blockReasons && rubricEvaluation.blockReasons.length > 0
254
+ ? rubricEvaluation.blockReasons.join('; ')
255
+ : 'rubric gate did not pass';
256
+ return { type: 'no-action', reason: `Rubric gate prevented promotion: ${reasons}` };
257
+ }
258
+
259
+ const actionability = assessFeedbackActionability({
260
+ signal: 'positive',
261
+ context,
262
+ whatWorked,
263
+ });
264
+ if (!actionability.promotable) {
265
+ const reason = actionability.issue === 'missing'
266
+ ? 'Positive feedback without context — cannot determine what worked'
267
+ : 'Positive feedback is too vague to promote — describe what worked in one sentence';
268
+ return { type: 'no-action', reason };
269
+ }
270
+
271
+ const content = [
272
+ whatWorked ? `What worked: ${whatWorked}` : `Approach: ${context}`,
273
+ reasoning ? `Reasoning: ${reasoning}` : null,
274
+ visualEvidence ? `Visual Evidence: ${visualEvidence}` : null,
275
+ ].filter(Boolean).join('\n');
276
+ const rubricLines = [];
277
+ if (rubricSummary) {
278
+ rubricLines.push(`Rubric weighted score: ${rubricSummary.weightedScore}`);
279
+ rubricLines.push(`Rubric criteria passed with no blocking guardrails.`);
280
+ }
281
+ const description = truncateAtWord(whatWorked || context || '', MAX_TITLE_DESCRIPTION_LENGTH);
282
+
283
+ return {
284
+ type: 'store-learning',
285
+ memory: {
286
+ title: `SUCCESS: ${description}`,
287
+ content: rubricLines.length > 0 ? `${content}\n${rubricLines.join('\n')}` : content,
288
+ category: 'learning',
289
+ importance: 'normal',
290
+ tags: ['feedback', 'positive', ...domainTags],
291
+ rubricSummary,
292
+ bayesian: {
293
+ priorProbability: 0.7,
294
+ uncertainty: 0.3,
295
+ observations: 1,
296
+ lastUpdated: new Date().toISOString(),
297
+ },
298
+ },
299
+ };
300
+ }
301
+
302
+ return { type: 'no-action', reason: `Unknown signal: ${signal}` };
303
+ }
304
+
305
+ function prepareForStorage(memory) {
306
+ const validation = validateFeedbackMemory(memory);
307
+ if (!validation.valid) {
308
+ return { ok: false, issues: validation.issues };
309
+ }
310
+ return { ok: true, memory };
311
+ }
312
+
313
+ /**
314
+ * parseTimestamp — Parse any ISO 8601 timestamp string into a Date object.
315
+ * Handles: Z-suffix ("2026-03-04T12:00:00.000Z"), no-suffix ("2026-03-04T12:00:00"),
316
+ * and UTC offset ("2026-03-04T12:00:00+05:00").
317
+ * Returns null (not NaN) for null, undefined, or unparseable input.
318
+ * NOTE: Do NOT change how timestamps are WRITTEN — new Date().toISOString() already
319
+ * produces correct ISO 8601+Z format. This helper is for READING only.
320
+ * Python's train_from_feedback.py strips Z with .replace("Z","") before fromisoformat().
321
+ * That pattern is safe because Node always writes Z-suffix. Do not alter write behavior.
322
+ * @param {string|null|undefined} ts - Timestamp string to parse
323
+ * @returns {Date|null}
324
+ */
325
+ function parseTimestamp(ts) {
326
+ if (ts == null) return null;
327
+ const d = new Date(String(ts).trim());
328
+ return isNaN(d.getTime()) ? null : d;
329
+ }
330
+
331
+ function runTests() {
332
+ let passed = 0;
333
+ let failed = 0;
334
+
335
+ function assert(condition, name) {
336
+ if (condition) {
337
+ passed++;
338
+ console.log(` PASS ${name}`);
339
+ } else {
340
+ failed++;
341
+ console.log(` FAIL ${name}`);
342
+ }
343
+ }
344
+
345
+ console.log('\nfeedback-schema.js tests\n');
346
+
347
+ const goodError = {
348
+ title: 'MISTAKE: Did not verify before claiming fixed',
349
+ content: 'Always run tests and show evidence before claiming the work is complete.',
350
+ category: 'error',
351
+ tags: ['feedback', 'negative', 'verification'],
352
+ };
353
+ assert(validateFeedbackMemory(goodError).valid, 'valid error memory passes');
354
+
355
+ const shortContent = {
356
+ title: 'MISTAKE: Bad fix regression',
357
+ content: 'thumbs down',
358
+ category: 'error',
359
+ tags: ['verification'],
360
+ };
361
+ assert(!validateFeedbackMemory(shortContent).valid, 'short content fails');
362
+
363
+ const bareThumbsDown = resolveFeedbackAction({ signal: 'negative' });
364
+ assert(bareThumbsDown.type === 'no-action', 'bare negative feedback becomes no-action');
365
+
366
+ const vagueThumbsUp = resolveFeedbackAction({
367
+ signal: 'positive',
368
+ context: 'thumbs up',
369
+ tags: ['verification'],
370
+ });
371
+ assert(vagueThumbsUp.type === 'no-action', 'generic positive context becomes no-action');
372
+
373
+ const fullNegative = resolveFeedbackAction({
374
+ signal: 'negative',
375
+ context: 'Pushed code with no tests',
376
+ whatWentWrong: 'Claimed fixed without test output',
377
+ whatToChange: 'Always run tests first',
378
+ tags: ['testing', 'verification'],
379
+ });
380
+ assert(fullNegative.type === 'store-mistake', 'negative feedback creates store-mistake action');
381
+
382
+ const prep = prepareForStorage(fullNegative.memory);
383
+ assert(prep.ok, 'store-mistake memory passes storage validation');
384
+
385
+ const fullPositive = resolveFeedbackAction({
386
+ signal: 'positive',
387
+ whatWorked: 'Ran tests and included output before final response',
388
+ tags: ['testing', 'verification'],
389
+ });
390
+ assert(fullPositive.type === 'store-learning', 'positive feedback creates store-learning action');
391
+
392
+ const blockedPositive = resolveFeedbackAction({
393
+ signal: 'positive',
394
+ whatWorked: 'Manual approval happened without evidence',
395
+ tags: ['testing'],
396
+ rubricEvaluation: {
397
+ promotionEligible: false,
398
+ blockReasons: ['failed_guardrails:testsPassed'],
399
+ failingCriteria: [],
400
+ failingGuardrails: ['testsPassed'],
401
+ weightedScore: 0.82,
402
+ rubricId: 'default-v1',
403
+ },
404
+ });
405
+ assert(blockedPositive.type === 'no-action', 'rubric gate blocks unsafe positive promotion');
406
+
407
+ console.log(`\nResults: ${passed} passed, ${failed} failed\n`);
408
+ process.exit(failed > 0 ? 1 : 0);
409
+ }
410
+
411
+ module.exports = {
412
+ validateFeedbackMemory,
413
+ resolveFeedbackAction,
414
+ prepareForStorage,
415
+ parseTimestamp,
416
+ truncateAtWord,
417
+ GENERIC_TAGS,
418
+ MIN_CONTENT_LENGTH,
419
+ MAX_TITLE_DESCRIPTION_LENGTH,
420
+ VALID_TITLE_PREFIXES,
421
+ VALID_CATEGORIES,
422
+ };
423
+
424
+ if (require.main === module && process.argv.includes('--test')) {
425
+ runTests();
426
+ }