thumbgate 0.9.9
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.
- package/.claude-plugin/README.md +134 -0
- package/.claude-plugin/bundle/icon.png +0 -0
- package/.claude-plugin/bundle/icon.svg +18 -0
- package/.claude-plugin/bundle/server/index.js +24 -0
- package/.claude-plugin/marketplace.json +36 -0
- package/.claude-plugin/plugin.json +21 -0
- package/.well-known/mcp/server-card.json +231 -0
- package/LICENSE +21 -0
- package/README.md +375 -0
- package/adapters/README.md +9 -0
- package/adapters/amp/skills/rlhf-feedback/SKILL.md +22 -0
- package/adapters/chatgpt/INSTALL.md +83 -0
- package/adapters/chatgpt/openapi.yaml +1281 -0
- package/adapters/claude/.mcp.json +14 -0
- package/adapters/codex/config.toml +9 -0
- package/adapters/gemini/function-declarations.json +224 -0
- package/adapters/mcp/server-stdio.js +788 -0
- package/adapters/opencode/opencode.json +15 -0
- package/bin/cli.js +1483 -0
- package/bin/memory.sh +64 -0
- package/bin/obsidian-sync.sh +20 -0
- package/bin/postinstall.js +37 -0
- package/config/build-metadata.json +4 -0
- package/config/e2e-critical-flows.json +45 -0
- package/config/gate-templates.json +77 -0
- package/config/gates/claim-verification.json +29 -0
- package/config/gates/computer-use.json +39 -0
- package/config/gates/default.json +117 -0
- package/config/github-about.json +25 -0
- package/config/mcp-allowlists.json +135 -0
- package/config/model-tiers.json +33 -0
- package/config/partner-routing.json +132 -0
- package/config/policy-bundles/constrained-v1.json +64 -0
- package/config/policy-bundles/default-v1.json +91 -0
- package/config/rubrics/default-v1.json +52 -0
- package/config/skill-packs/react-testing.json +23 -0
- package/config/skill-packs/stripe-integration/references/api-spec.json +1 -0
- package/config/skill-packs/stripe-integration/references/webhook-guide.md +3 -0
- package/config/skill-specs/pr-reviewer.json +9 -0
- package/config/skill-specs/release-status.json +9 -0
- package/config/skill-specs/ticket-triage.json +9 -0
- package/config/subagent-profiles.json +32 -0
- package/config/tessl-tiles.json +29 -0
- package/config/thumbgate-settings.managed.json +12 -0
- package/openapi/openapi.yaml +1281 -0
- package/package.json +286 -0
- package/plugins/amp-skill/INSTALL.md +52 -0
- package/plugins/amp-skill/SKILL.md +64 -0
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +22 -0
- package/plugins/claude-codex-bridge/.mcp.json +12 -0
- package/plugins/claude-codex-bridge/INSTALL.md +43 -0
- package/plugins/claude-codex-bridge/README.md +46 -0
- package/plugins/claude-codex-bridge/scripts/codex-bridge.js +288 -0
- package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +24 -0
- package/plugins/claude-codex-bridge/skills/result/SKILL.md +22 -0
- package/plugins/claude-codex-bridge/skills/review/SKILL.md +28 -0
- package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +27 -0
- package/plugins/claude-codex-bridge/skills/setup/SKILL.md +21 -0
- package/plugins/claude-codex-bridge/skills/status/SKILL.md +19 -0
- package/plugins/claude-skill/INSTALL.md +55 -0
- package/plugins/claude-skill/SKILL.md +46 -0
- package/plugins/codex-profile/.codex-plugin/plugin.json +43 -0
- package/plugins/codex-profile/.mcp.json +12 -0
- package/plugins/codex-profile/AGENTS.md +20 -0
- package/plugins/codex-profile/INSTALL.md +66 -0
- package/plugins/codex-profile/README.md +37 -0
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +23 -0
- package/plugins/cursor-marketplace/CHANGELOG.md +30 -0
- package/plugins/cursor-marketplace/LICENSE +21 -0
- package/plugins/cursor-marketplace/README.md +124 -0
- package/plugins/cursor-marketplace/agents/reliability-reviewer.md +31 -0
- package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
- package/plugins/cursor-marketplace/commands/capture-feedback.md +33 -0
- package/plugins/cursor-marketplace/commands/check-gates.md +25 -0
- package/plugins/cursor-marketplace/commands/show-lessons.md +27 -0
- package/plugins/cursor-marketplace/hooks/hooks.json +10 -0
- package/plugins/cursor-marketplace/mcp.json +12 -0
- package/plugins/cursor-marketplace/rules/feedback-capture.mdc +34 -0
- package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +30 -0
- package/plugins/cursor-marketplace/rules/session-continuity.mdc +28 -0
- package/plugins/cursor-marketplace/scripts/gate-check.sh +11 -0
- package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +47 -0
- package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +31 -0
- package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +30 -0
- package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +33 -0
- package/plugins/gemini-extension/INSTALL.md +92 -0
- package/plugins/gemini-extension/gemini_prompt.txt +14 -0
- package/plugins/gemini-extension/tool_contract.json +45 -0
- package/plugins/opencode-profile/INSTALL.md +57 -0
- package/public/assets/instagram-card.png +0 -0
- package/public/assets/tiktok-agent-memory.mp4 +0 -0
- package/public/blog.html +400 -0
- package/public/dashboard.html +1093 -0
- package/public/guide.html +317 -0
- package/public/index.html +1195 -0
- package/public/learn/agent-harness-pattern.html +180 -0
- package/public/learn/ai-agent-persistent-memory.html +202 -0
- package/public/learn/learn.css +45 -0
- package/public/learn/mcp-pre-action-gates-explained.html +172 -0
- package/public/learn/stop-ai-agent-force-push.html +134 -0
- package/public/learn/vibe-coding-safety-net.html +142 -0
- package/public/learn.html +213 -0
- package/public/lessons.html +650 -0
- package/public/vercel.json +8 -0
- package/scripts/__pycache__/train_from_feedback.cpython-314.pyc +0 -0
- package/scripts/a2ui-engine.js +73 -0
- package/scripts/access-anomaly-detector.js +12 -0
- package/scripts/adk-consolidator.js +266 -0
- package/scripts/agent-readiness.js +220 -0
- package/scripts/agent-security-hardening.js +227 -0
- package/scripts/agentic-data-pipeline.js +847 -0
- package/scripts/analytics-report.js +328 -0
- package/scripts/analytics-window.js +158 -0
- package/scripts/async-job-runner.js +1001 -0
- package/scripts/audit-trail.js +398 -0
- package/scripts/auto-promote-gates.js +293 -0
- package/scripts/auto-wire-hooks.js +316 -0
- package/scripts/autonomous-sales-agent.js +39 -0
- package/scripts/autoresearch-runner.js +216 -0
- package/scripts/background-agent-governance.js +237 -0
- package/scripts/behavioral-extraction.js +93 -0
- package/scripts/belief-update.js +84 -0
- package/scripts/billing.js +2438 -0
- package/scripts/bot-detector.js +50 -0
- package/scripts/budget-guard.js +173 -0
- package/scripts/build-claude-mcpb.js +189 -0
- package/scripts/build-metadata.js +97 -0
- package/scripts/check-congruence.js +322 -0
- package/scripts/cli-feedback.js +135 -0
- package/scripts/cli-telemetry.js +87 -0
- package/scripts/cloudflare-dynamic-sandbox.js +315 -0
- package/scripts/code-reasoning.js +350 -0
- package/scripts/codegraph-context.js +466 -0
- package/scripts/commercial-offer.js +56 -0
- package/scripts/computer-use-firewall.js +250 -0
- package/scripts/context-engine.js +694 -0
- package/scripts/contextfs.js +1287 -0
- package/scripts/conversation-context.js +119 -0
- package/scripts/creator-campaigns.js +239 -0
- package/scripts/daemon-manager.js +108 -0
- package/scripts/daily-digest.js +11 -0
- package/scripts/dashboard-render-spec.js +395 -0
- package/scripts/dashboard.js +1058 -0
- package/scripts/data-governance.js +173 -0
- package/scripts/delegation-runtime.js +900 -0
- package/scripts/deploy-gcp.sh +44 -0
- package/scripts/deploy-policy.js +231 -0
- package/scripts/disagreement-mining.js +315 -0
- package/scripts/dispatch-brief.js +159 -0
- package/scripts/distribution-surfaces.js +44 -0
- package/scripts/dpo-optimizer.js +206 -0
- package/scripts/ensure-repo-bootstrap.js +129 -0
- package/scripts/ephemeral-agent-store.js +219 -0
- package/scripts/eval-harness.js +56 -0
- package/scripts/evolution-state.js +241 -0
- package/scripts/experiment-tracker.js +267 -0
- package/scripts/export-databricks-bundle.js +242 -0
- package/scripts/export-dpo-pairs.js +344 -0
- package/scripts/export-kto-pairs.js +309 -0
- package/scripts/export-training.js +450 -0
- package/scripts/failure-diagnostics.js +558 -0
- package/scripts/feedback-attribution.js +313 -0
- package/scripts/feedback-fallback.js +110 -0
- package/scripts/feedback-history-distiller.js +391 -0
- package/scripts/feedback-inbox-read.js +162 -0
- package/scripts/feedback-loop.js +1887 -0
- package/scripts/feedback-paths.js +145 -0
- package/scripts/feedback-quality.js +139 -0
- package/scripts/feedback-root-consolidator.js +238 -0
- package/scripts/feedback-schema.js +426 -0
- package/scripts/feedback-session.js +286 -0
- package/scripts/feedback-to-memory.js +185 -0
- package/scripts/feedback-to-rules.js +164 -0
- package/scripts/filesystem-search.js +405 -0
- package/scripts/funnel-analytics.js +35 -0
- package/scripts/gate-satisfy.js +42 -0
- package/scripts/gate-stats.js +116 -0
- package/scripts/gate-templates.js +70 -0
- package/scripts/gates-engine.js +816 -0
- package/scripts/generate-paperbanana-diagrams.sh +99 -0
- package/scripts/generate-pretool-hook.sh +40 -0
- package/scripts/github-about.js +350 -0
- package/scripts/github-outreach.js +65 -0
- package/scripts/gtm-revenue-loop.js +520 -0
- package/scripts/hallucination-detector.js +226 -0
- package/scripts/hf-papers.js +317 -0
- package/scripts/history-distiller.js +200 -0
- package/scripts/hook-auto-capture.sh +100 -0
- package/scripts/hook-stop-pr-thread-check.sh +68 -0
- package/scripts/hook-stop-self-score.sh +51 -0
- package/scripts/hook-stop-verify-deploy.sh +31 -0
- package/scripts/hook-thumbgate-cache-updater.js +48 -0
- package/scripts/hook-verify-before-done.sh +20 -0
- package/scripts/hosted-config.js +156 -0
- package/scripts/hybrid-feedback-context.js +675 -0
- package/scripts/install-mcp.js +159 -0
- package/scripts/intent-router.js +392 -0
- package/scripts/internal-agent-bootstrap.js +490 -0
- package/scripts/jsonl-watcher.js +155 -0
- package/scripts/lesson-db.js +613 -0
- package/scripts/lesson-inference.js +310 -0
- package/scripts/lesson-retrieval.js +95 -0
- package/scripts/lesson-rotation.js +137 -0
- package/scripts/lesson-search.js +644 -0
- package/scripts/lesson-synthesis.js +196 -0
- package/scripts/license.js +50 -0
- package/scripts/local-model-profile.js +384 -0
- package/scripts/markdown-escape.js +12 -0
- package/scripts/marketing-experiment.js +671 -0
- package/scripts/mcp-config.js +149 -0
- package/scripts/mcp-policy.js +99 -0
- package/scripts/memalign-recall.js +111 -0
- package/scripts/memory-firewall.js +222 -0
- package/scripts/memory-migration.js +296 -0
- package/scripts/meta-policy.js +190 -0
- package/scripts/metered-billing.js +16 -0
- package/scripts/model-tier-router.js +301 -0
- package/scripts/money-watcher.js +71 -0
- package/scripts/multi-hop-recall.js +240 -0
- package/scripts/natural-language-harness.js +330 -0
- package/scripts/obsidian-export.js +713 -0
- package/scripts/operational-dashboard.js +103 -0
- package/scripts/operational-summary.js +93 -0
- package/scripts/optimize-context.js +17 -0
- package/scripts/org-dashboard.js +201 -0
- package/scripts/partner-orchestration.js +146 -0
- package/scripts/per-step-scoring.js +165 -0
- package/scripts/perplexity-marketing.js +466 -0
- package/scripts/pii-scanner.js +153 -0
- package/scripts/plan-gate.js +154 -0
- package/scripts/post-everywhere.js +308 -0
- package/scripts/post-to-x-retry.sh +22 -0
- package/scripts/post-to-x.js +369 -0
- package/scripts/pr-manager.js +236 -0
- package/scripts/predictive-insights.js +356 -0
- package/scripts/principle-extractor.js +162 -0
- package/scripts/pro-features.js +40 -0
- package/scripts/pro-local-dashboard.js +174 -0
- package/scripts/problem-detail.js +53 -0
- package/scripts/product-feedback.js +134 -0
- package/scripts/profile-router.js +245 -0
- package/scripts/prompt-dlp.js +221 -0
- package/scripts/prompt-guard.js +83 -0
- package/scripts/prove-adapters.js +863 -0
- package/scripts/prove-attribution.js +365 -0
- package/scripts/prove-automation.js +653 -0
- package/scripts/prove-autoresearch.js +304 -0
- package/scripts/prove-claim-verification.js +277 -0
- package/scripts/prove-cloudflare-sandbox.js +163 -0
- package/scripts/prove-data-pipeline.js +410 -0
- package/scripts/prove-data-quality.js +227 -0
- package/scripts/prove-evolution.js +352 -0
- package/scripts/prove-harnesses.js +287 -0
- package/scripts/prove-intelligence.js +259 -0
- package/scripts/prove-lancedb.js +371 -0
- package/scripts/prove-local-intelligence.js +342 -0
- package/scripts/prove-loop-closure.js +263 -0
- package/scripts/prove-predictive-insights.js +357 -0
- package/scripts/prove-runtime.js +350 -0
- package/scripts/prove-seo-gsd.js +234 -0
- package/scripts/prove-settings.js +279 -0
- package/scripts/prove-subway-upgrades.js +277 -0
- package/scripts/prove-tessl.js +229 -0
- package/scripts/prove-training-export.js +327 -0
- package/scripts/prove-workflow-contract.js +116 -0
- package/scripts/prove-xmemory.js +332 -0
- package/scripts/publish-decision.js +133 -0
- package/scripts/pulse.js +80 -0
- package/scripts/rate-limiter.js +125 -0
- package/scripts/reddit-dm-outreach.js +182 -0
- package/scripts/reddit-monitor-cron.sh +26 -0
- package/scripts/reflector-agent.js +221 -0
- package/scripts/reminder-engine.js +132 -0
- package/scripts/revenue-status.js +472 -0
- package/scripts/risk-scorer.js +459 -0
- package/scripts/rlaif-self-audit.js +129 -0
- package/scripts/rlhf_session_start.sh +32 -0
- package/scripts/rubric-engine.js +230 -0
- package/scripts/schedule-manager.js +251 -0
- package/scripts/secret-scanner.js +414 -0
- package/scripts/self-heal.js +147 -0
- package/scripts/self-healing-check.js +188 -0
- package/scripts/semantic-layer.js +98 -0
- package/scripts/seo-gsd.js +1153 -0
- package/scripts/settings-hierarchy.js +214 -0
- package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
- package/scripts/skill-exporter.js +262 -0
- package/scripts/skill-generator.js +446 -0
- package/scripts/skill-materializer.js +134 -0
- package/scripts/skill-packs.js +136 -0
- package/scripts/skill-proposer.js +99 -0
- package/scripts/skill-quality-tracker.js +282 -0
- package/scripts/slo-alert-engine.js +14 -0
- package/scripts/slow-loop.js +72 -0
- package/scripts/social-analytics/db/schema.sql +32 -0
- package/scripts/social-analytics/db/social-analytics.db +0 -0
- package/scripts/social-analytics/digest.js +256 -0
- package/scripts/social-analytics/generate-instagram-card.js +97 -0
- package/scripts/social-analytics/instagram-thumbgate-post.js +107 -0
- package/scripts/social-analytics/load-env.js +46 -0
- package/scripts/social-analytics/mcp-server.js +289 -0
- package/scripts/social-analytics/normalizer.js +580 -0
- package/scripts/social-analytics/notify.js +162 -0
- package/scripts/social-analytics/poll-all.js +92 -0
- package/scripts/social-analytics/pollers/github.js +195 -0
- package/scripts/social-analytics/pollers/instagram.js +253 -0
- package/scripts/social-analytics/pollers/linkedin.js +330 -0
- package/scripts/social-analytics/pollers/plausible.js +247 -0
- package/scripts/social-analytics/pollers/reddit.js +306 -0
- package/scripts/social-analytics/pollers/threads.js +233 -0
- package/scripts/social-analytics/pollers/tiktok.js +203 -0
- package/scripts/social-analytics/pollers/x.js +227 -0
- package/scripts/social-analytics/pollers/youtube.js +304 -0
- package/scripts/social-analytics/pollers/zernio.js +183 -0
- package/scripts/social-analytics/publish-instagram-thumbgate.js +98 -0
- package/scripts/social-analytics/publish-thumbgate-launch.js +316 -0
- package/scripts/social-analytics/publishers/devto.js +122 -0
- package/scripts/social-analytics/publishers/instagram.js +317 -0
- package/scripts/social-analytics/publishers/linkedin.js +294 -0
- package/scripts/social-analytics/publishers/reddit.js +390 -0
- package/scripts/social-analytics/publishers/threads.js +275 -0
- package/scripts/social-analytics/publishers/tiktok.js +217 -0
- package/scripts/social-analytics/publishers/x.js +259 -0
- package/scripts/social-analytics/publishers/youtube.js +223 -0
- package/scripts/social-analytics/publishers/zernio.js +378 -0
- package/scripts/social-analytics/run-digest.js +34 -0
- package/scripts/social-analytics/store.js +257 -0
- package/scripts/social-analytics/utm.js +143 -0
- package/scripts/social-pipeline.js +2628 -0
- package/scripts/social-quality-gate.js +18 -0
- package/scripts/social-reply-monitor.js +445 -0
- package/scripts/status-dashboard.js +155 -0
- package/scripts/statusline-lesson.js +16 -0
- package/scripts/statusline-tower.js +8 -0
- package/scripts/statusline.sh +116 -0
- package/scripts/stripe-live-status.js +115 -0
- package/scripts/subagent-profiles.js +79 -0
- package/scripts/sync-gh-secrets-from-env.sh +70 -0
- package/scripts/sync-github-about.js +52 -0
- package/scripts/sync-version.js +447 -0
- package/scripts/synthetic-dpo.js +234 -0
- package/scripts/telemetry-analytics.js +821 -0
- package/scripts/tessl-export.js +371 -0
- package/scripts/test-coverage.js +120 -0
- package/scripts/thompson-sampling.js +417 -0
- package/scripts/thumbgate-search.js +189 -0
- package/scripts/tool-kpi-tracker.js +12 -0
- package/scripts/tool-registry.js +811 -0
- package/scripts/train_from_feedback.py +933 -0
- package/scripts/user-profile.js +78 -0
- package/scripts/validate-feedback.js +581 -0
- package/scripts/validate-workflow-contract.js +287 -0
- package/scripts/vector-store.js +197 -0
- package/scripts/verification-loop.js +291 -0
- package/scripts/verify-obsidian-setup.sh +269 -0
- package/scripts/verify-run.js +269 -0
- package/scripts/webhook-delivery.js +62 -0
- package/scripts/weekly-auto-post.js +124 -0
- package/scripts/workflow-runs.js +154 -0
- package/scripts/workflow-sprint-intake.js +475 -0
- package/scripts/workspace-evolver.js +374 -0
- package/scripts/x-autonomous-marketing.js +139 -0
- package/scripts/xmemory-lite.js +405 -0
- package/skills/agent-memory/SKILL.md +97 -0
- package/skills/rlhf-feedback/SKILL.md +49 -0
- package/skills/solve-architecture-autonomy/SKILL.md +17 -0
- package/skills/solve-architecture-autonomy/tool.js +33 -0
- package/skills/thumbgate/SKILL.md +114 -0
- package/src/api/server.js +4206 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# GSD: Deploy ThumbGate Control Plane to Google Cloud Run
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
PROJECT_ID=$(gcloud config get-value project)
|
|
7
|
+
SERVICE_NAME="rlhf-control-plane"
|
|
8
|
+
REGION="us-central1"
|
|
9
|
+
|
|
10
|
+
: "${THUMBGATE_API_KEY:?THUMBGATE_API_KEY is required}"
|
|
11
|
+
: "${THUMBGATE_API_KEY_ROTATED_AT:?THUMBGATE_API_KEY_ROTATED_AT is required}"
|
|
12
|
+
: "${STRIPE_SECRET_KEY:?STRIPE_SECRET_KEY is required}"
|
|
13
|
+
: "${STRIPE_SECRET_KEY_ROTATED_AT:?STRIPE_SECRET_KEY_ROTATED_AT is required}"
|
|
14
|
+
: "${STRIPE_WEBHOOK_SECRET:?STRIPE_WEBHOOK_SECRET is required}"
|
|
15
|
+
: "${STRIPE_WEBHOOK_SECRET_ROTATED_AT:?STRIPE_WEBHOOK_SECRET_ROTATED_AT is required}"
|
|
16
|
+
: "${THUMBGATE_PUBLIC_APP_ORIGIN:?THUMBGATE_PUBLIC_APP_ORIGIN is required}"
|
|
17
|
+
: "${THUMBGATE_BILLING_API_BASE_URL:?THUMBGATE_BILLING_API_BASE_URL is required}"
|
|
18
|
+
|
|
19
|
+
THUMBGATE_FEEDBACK_DIR="${THUMBGATE_FEEDBACK_DIR:-/data/feedback}"
|
|
20
|
+
THUMBGATE_GA_MEASUREMENT_ID="${THUMBGATE_GA_MEASUREMENT_ID:-}"
|
|
21
|
+
THUMBGATE_GOOGLE_SITE_VERIFICATION="${THUMBGATE_GOOGLE_SITE_VERIFICATION:-}"
|
|
22
|
+
|
|
23
|
+
node scripts/deploy-policy.js --profiles=runtime,billing
|
|
24
|
+
|
|
25
|
+
echo "🚀 Deploying Agentic Control Plane to $REGION..."
|
|
26
|
+
|
|
27
|
+
gcloud builds submit --tag gcr.io/$PROJECT_ID/$SERVICE_NAME
|
|
28
|
+
gcloud run deploy $SERVICE_NAME \
|
|
29
|
+
--image gcr.io/$PROJECT_ID/$SERVICE_NAME \
|
|
30
|
+
--platform managed \
|
|
31
|
+
--region $REGION \
|
|
32
|
+
--allow-unauthenticated \
|
|
33
|
+
--set-env-vars \
|
|
34
|
+
THUMBGATE_API_KEY="$THUMBGATE_API_KEY",\
|
|
35
|
+
STRIPE_SECRET_KEY="$STRIPE_SECRET_KEY",\
|
|
36
|
+
STRIPE_WEBHOOK_SECRET="$STRIPE_WEBHOOK_SECRET",\
|
|
37
|
+
THUMBGATE_PUBLIC_APP_ORIGIN="$THUMBGATE_PUBLIC_APP_ORIGIN",\
|
|
38
|
+
THUMBGATE_BILLING_API_BASE_URL="$THUMBGATE_BILLING_API_BASE_URL",\
|
|
39
|
+
THUMBGATE_FEEDBACK_DIR="$THUMBGATE_FEEDBACK_DIR",\
|
|
40
|
+
THUMBGATE_GA_MEASUREMENT_ID="$THUMBGATE_GA_MEASUREMENT_ID",\
|
|
41
|
+
THUMBGATE_GOOGLE_SITE_VERIFICATION="$THUMBGATE_GOOGLE_SITE_VERIFICATION"
|
|
42
|
+
|
|
43
|
+
echo "✅ Success! Your Control Plane is live."
|
|
44
|
+
gcloud run services describe $SERVICE_NAME --region $REGION --format='value(status.url)'
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { normalizeOrigin } = require('./hosted-config');
|
|
5
|
+
|
|
6
|
+
const SECRET_POLICY = {
|
|
7
|
+
THUMBGATE_API_KEY: { rotatedAtEnv: 'THUMBGATE_API_KEY_ROTATED_AT', maxAgeDays: 30 },
|
|
8
|
+
STRIPE_SECRET_KEY: { rotatedAtEnv: 'STRIPE_SECRET_KEY_ROTATED_AT', maxAgeDays: 30 },
|
|
9
|
+
STRIPE_WEBHOOK_SECRET: { rotatedAtEnv: 'STRIPE_WEBHOOK_SECRET_ROTATED_AT', maxAgeDays: 30 },
|
|
10
|
+
RAILWAY_TOKEN: { rotatedAtEnv: 'RAILWAY_TOKEN_ROTATED_AT', maxAgeDays: 90 },
|
|
11
|
+
GITHUB_MARKETPLACE_WEBHOOK_SECRET: {
|
|
12
|
+
rotatedAtEnv: 'GITHUB_MARKETPLACE_WEBHOOK_SECRET_ROTATED_AT',
|
|
13
|
+
maxAgeDays: 90,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const PROFILE_DEFS = {
|
|
18
|
+
runtime: {
|
|
19
|
+
requiredSecrets: ['THUMBGATE_API_KEY'],
|
|
20
|
+
requiredVars: [],
|
|
21
|
+
},
|
|
22
|
+
billing: {
|
|
23
|
+
requiredSecrets: ['STRIPE_SECRET_KEY', 'STRIPE_WEBHOOK_SECRET'],
|
|
24
|
+
requiredVars: ['THUMBGATE_PUBLIC_APP_ORIGIN', 'THUMBGATE_BILLING_API_BASE_URL'],
|
|
25
|
+
},
|
|
26
|
+
deploy: {
|
|
27
|
+
requiredSecrets: ['RAILWAY_TOKEN'],
|
|
28
|
+
requiredVars: [
|
|
29
|
+
'RAILWAY_PROJECT_ID',
|
|
30
|
+
'RAILWAY_ENVIRONMENT_ID',
|
|
31
|
+
'RAILWAY_HEALTHCHECK_URL',
|
|
32
|
+
'THUMBGATE_PUBLIC_APP_ORIGIN',
|
|
33
|
+
'THUMBGATE_BILLING_API_BASE_URL',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
github_marketplace: {
|
|
37
|
+
requiredSecrets: ['GITHUB_MARKETPLACE_WEBHOOK_SECRET'],
|
|
38
|
+
requiredVars: [],
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function parseTimestamp(value) {
|
|
43
|
+
if (!value || typeof value !== 'string') {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const parsed = new Date(value);
|
|
47
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getAgeDays(timestamp, now = new Date()) {
|
|
54
|
+
return Math.floor((now.getTime() - timestamp.getTime()) / 86_400_000);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isAbsoluteHttpUrl(value) {
|
|
58
|
+
if (!value || typeof value !== 'string') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const parsed = new URL(value);
|
|
63
|
+
return /^https?:$/.test(parsed.protocol);
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizeProfiles(profiles) {
|
|
70
|
+
const unique = new Set();
|
|
71
|
+
for (const profile of profiles || []) {
|
|
72
|
+
const trimmed = String(profile || '').trim();
|
|
73
|
+
if (!trimmed) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!PROFILE_DEFS[trimmed]) {
|
|
77
|
+
throw new Error(`Unknown deploy policy profile: ${trimmed}`);
|
|
78
|
+
}
|
|
79
|
+
unique.add(trimmed);
|
|
80
|
+
}
|
|
81
|
+
return Array.from(unique);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function collectRequiredItems(profiles, key) {
|
|
85
|
+
const items = new Set();
|
|
86
|
+
for (const profile of profiles) {
|
|
87
|
+
for (const value of PROFILE_DEFS[profile][key]) {
|
|
88
|
+
items.add(value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return Array.from(items);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function evaluateDeployPolicy(env = process.env, { profiles = ['runtime'], now = new Date() } = {}) {
|
|
95
|
+
const selectedProfiles = normalizeProfiles(profiles);
|
|
96
|
+
const requiredSecrets = collectRequiredItems(selectedProfiles, 'requiredSecrets');
|
|
97
|
+
const requiredVars = collectRequiredItems(selectedProfiles, 'requiredVars');
|
|
98
|
+
const errors = [];
|
|
99
|
+
|
|
100
|
+
for (const name of requiredVars) {
|
|
101
|
+
const value = String(env[name] || '').trim();
|
|
102
|
+
if (!value) {
|
|
103
|
+
errors.push({ type: 'missing_variable', name, message: `${name} is required` });
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if ((name.endsWith('_ORIGIN') || name.endsWith('_BASE_URL')) && !normalizeOrigin(value)) {
|
|
108
|
+
errors.push({ type: 'invalid_origin', name, message: `${name} must be an absolute http(s) origin` });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (name === 'RAILWAY_HEALTHCHECK_URL' && !isAbsoluteHttpUrl(value)) {
|
|
112
|
+
errors.push({ type: 'invalid_url', name, message: `${name} must be an absolute http(s) URL` });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const name of requiredSecrets) {
|
|
117
|
+
const secretValue = String(env[name] || '');
|
|
118
|
+
if (!secretValue.trim()) {
|
|
119
|
+
errors.push({ type: 'missing_secret', name, message: `${name} is required` });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const policy = SECRET_POLICY[name];
|
|
124
|
+
if (!policy) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const rotatedAtRaw = String(env[policy.rotatedAtEnv] || '').trim();
|
|
129
|
+
if (!rotatedAtRaw) {
|
|
130
|
+
errors.push({
|
|
131
|
+
type: 'missing_rotation_timestamp',
|
|
132
|
+
name: policy.rotatedAtEnv,
|
|
133
|
+
message: `${policy.rotatedAtEnv} is required for ${name}`,
|
|
134
|
+
});
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const rotatedAt = parseTimestamp(rotatedAtRaw);
|
|
139
|
+
if (!rotatedAt) {
|
|
140
|
+
errors.push({
|
|
141
|
+
type: 'invalid_rotation_timestamp',
|
|
142
|
+
name: policy.rotatedAtEnv,
|
|
143
|
+
message: `${policy.rotatedAtEnv} must be a valid ISO timestamp`,
|
|
144
|
+
});
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const ageDays = getAgeDays(rotatedAt, now);
|
|
149
|
+
if (ageDays < 0) {
|
|
150
|
+
errors.push({
|
|
151
|
+
type: 'future_rotation_timestamp',
|
|
152
|
+
name: policy.rotatedAtEnv,
|
|
153
|
+
message: `${policy.rotatedAtEnv} cannot be in the future`,
|
|
154
|
+
});
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (ageDays > policy.maxAgeDays) {
|
|
159
|
+
errors.push({
|
|
160
|
+
type: 'stale_secret',
|
|
161
|
+
name,
|
|
162
|
+
message: `${name} is stale (${ageDays}d old, max ${policy.maxAgeDays}d)`,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
ok: errors.length === 0,
|
|
169
|
+
checkedAt: now.toISOString(),
|
|
170
|
+
profiles: selectedProfiles,
|
|
171
|
+
requiredSecrets,
|
|
172
|
+
requiredVars,
|
|
173
|
+
errors,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function formatReport(report) {
|
|
178
|
+
const lines = [];
|
|
179
|
+
lines.push(`Deploy Policy Check @ ${report.checkedAt}`);
|
|
180
|
+
lines.push(`Profiles: ${report.profiles.join(', ') || 'none'}`);
|
|
181
|
+
lines.push(`Result: ${report.ok ? 'PASS' : 'FAIL'}`);
|
|
182
|
+
lines.push(`Secrets checked: ${report.requiredSecrets.length}`);
|
|
183
|
+
lines.push(`Variables checked: ${report.requiredVars.length}`);
|
|
184
|
+
if (report.errors.length) {
|
|
185
|
+
lines.push('');
|
|
186
|
+
for (const error of report.errors) {
|
|
187
|
+
lines.push(`- ${error.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return `${lines.join('\n')}\n`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function parseArgs(argv) {
|
|
194
|
+
const args = new Set(argv);
|
|
195
|
+
const profileArg = argv.find((value) => value.startsWith('--profiles='));
|
|
196
|
+
const profiles = profileArg
|
|
197
|
+
? profileArg.slice('--profiles='.length).split(',').map((value) => value.trim()).filter(Boolean)
|
|
198
|
+
: ['runtime', 'billing', 'deploy'];
|
|
199
|
+
return {
|
|
200
|
+
json: args.has('--json'),
|
|
201
|
+
profiles,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function runCli(argv = process.argv.slice(2)) {
|
|
206
|
+
const options = parseArgs(argv);
|
|
207
|
+
const report = evaluateDeployPolicy(process.env, { profiles: options.profiles });
|
|
208
|
+
|
|
209
|
+
if (options.json) {
|
|
210
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
211
|
+
} else {
|
|
212
|
+
process.stdout.write(formatReport(report));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!report.ok) {
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = {
|
|
221
|
+
SECRET_POLICY,
|
|
222
|
+
PROFILE_DEFS,
|
|
223
|
+
parseTimestamp,
|
|
224
|
+
getAgeDays,
|
|
225
|
+
evaluateDeployPolicy,
|
|
226
|
+
formatReport,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
if (require.main === module) {
|
|
230
|
+
runCli();
|
|
231
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Disagreement Mining
|
|
4
|
+
*
|
|
5
|
+
* Inspired by Dropbox's "human-calibrated LLM labeling" approach.
|
|
6
|
+
* The strongest learning signal comes from cases where the Thompson
|
|
7
|
+
* Sampling model DISAGREES with actual user feedback signals. These
|
|
8
|
+
* disagreements are prioritized for review and DPO pair generation.
|
|
9
|
+
*
|
|
10
|
+
* Zero external npm dependencies.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { loadModel, getReliability } = require('./thompson-sampling');
|
|
17
|
+
const { readJSONL, getFeedbackPaths, inferDomain } = require('./feedback-loop');
|
|
18
|
+
const { extractDomainKeys } = require('./export-dpo-pairs');
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Domain → Thompson category mapping
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Map an inferDomain() result to the closest Thompson Sampling category.
|
|
26
|
+
* Thompson uses: code_edit, git, testing, pr_review, search, architecture,
|
|
27
|
+
* security, debugging, uncategorized.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} domain - Domain from inferDomain()
|
|
30
|
+
* @returns {string} Thompson category key
|
|
31
|
+
*/
|
|
32
|
+
function domainToCategory(domain) {
|
|
33
|
+
const mapping = {
|
|
34
|
+
'testing': 'testing',
|
|
35
|
+
'security': 'security',
|
|
36
|
+
'debugging': 'debugging',
|
|
37
|
+
'architecture': 'architecture',
|
|
38
|
+
'git-workflow': 'git',
|
|
39
|
+
'ui-components': 'code_edit',
|
|
40
|
+
'api-integration': 'code_edit',
|
|
41
|
+
'documentation': 'uncategorized',
|
|
42
|
+
'data-modeling': 'architecture',
|
|
43
|
+
'performance': 'debugging',
|
|
44
|
+
'general': 'uncategorized',
|
|
45
|
+
};
|
|
46
|
+
return mapping[domain] || 'uncategorized';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Core: Mine Disagreements
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Find disagreements between Thompson model predictions and actual user signals.
|
|
55
|
+
*
|
|
56
|
+
* A "disagreement" is when:
|
|
57
|
+
* - Thompson reliability > highThreshold (e.g. 0.6) but user gave negative signal
|
|
58
|
+
* - Thompson reliability < lowThreshold (e.g. 0.4) but user gave positive signal
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} [opts]
|
|
61
|
+
* @param {string} [opts.feedbackDir] - Override feedback dir
|
|
62
|
+
* @param {string} [opts.modelPath] - Override model path
|
|
63
|
+
* @param {number} [opts.highThreshold=0.6] - Reliability above which positive is expected
|
|
64
|
+
* @param {number} [opts.lowThreshold=0.4] - Reliability below which negative is expected
|
|
65
|
+
* @returns {{ disagreements: Array, stats: Object }}
|
|
66
|
+
*/
|
|
67
|
+
function mineDisagreements(opts) {
|
|
68
|
+
const options = opts || {};
|
|
69
|
+
const highThreshold = options.highThreshold != null ? options.highThreshold : 0.6;
|
|
70
|
+
const lowThreshold = options.lowThreshold != null ? options.lowThreshold : 0.4;
|
|
71
|
+
|
|
72
|
+
const paths = getFeedbackPaths();
|
|
73
|
+
const feedbackDir = options.feedbackDir || paths.FEEDBACK_DIR;
|
|
74
|
+
const modelPath = options.modelPath || path.join(feedbackDir, 'feedback_model.json');
|
|
75
|
+
const feedbackLogPath = path.join(feedbackDir, 'feedback-log.jsonl');
|
|
76
|
+
|
|
77
|
+
const model = loadModel(modelPath);
|
|
78
|
+
const reliability = getReliability(model);
|
|
79
|
+
const events = readJSONL(feedbackLogPath);
|
|
80
|
+
|
|
81
|
+
const disagreements = [];
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < events.length; i++) {
|
|
84
|
+
const event = events[i];
|
|
85
|
+
if (!event.signal) continue;
|
|
86
|
+
|
|
87
|
+
const isPositive = event.signal === 'positive';
|
|
88
|
+
const domain = inferDomain(event.tags, event.context);
|
|
89
|
+
const category = domainToCategory(domain);
|
|
90
|
+
const catReliability = reliability[category]
|
|
91
|
+
? reliability[category].reliability
|
|
92
|
+
: 0.5;
|
|
93
|
+
|
|
94
|
+
let disagreementType = null;
|
|
95
|
+
let strength = 0;
|
|
96
|
+
|
|
97
|
+
if (catReliability > highThreshold && !isPositive) {
|
|
98
|
+
// Model thinks this category is reliable, but user disagrees
|
|
99
|
+
disagreementType = 'model_overconfident';
|
|
100
|
+
strength = catReliability - highThreshold;
|
|
101
|
+
} else if (catReliability < lowThreshold && isPositive) {
|
|
102
|
+
// Model thinks this category is unreliable, but user approves
|
|
103
|
+
disagreementType = 'model_underconfident';
|
|
104
|
+
strength = lowThreshold - catReliability;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (disagreementType) {
|
|
108
|
+
disagreements.push({
|
|
109
|
+
feedbackIndex: i,
|
|
110
|
+
feedbackId: event.id || `event-${i}`,
|
|
111
|
+
signal: event.signal,
|
|
112
|
+
domain,
|
|
113
|
+
category,
|
|
114
|
+
categoryReliability: catReliability,
|
|
115
|
+
disagreementType,
|
|
116
|
+
disagreementStrength: Math.round(strength * 1000) / 1000,
|
|
117
|
+
context: event.context || '',
|
|
118
|
+
tags: event.tags || [],
|
|
119
|
+
timestamp: event.timestamp || null,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Sort by disagreement strength descending (strongest signal first)
|
|
125
|
+
disagreements.sort((a, b) => b.disagreementStrength - a.disagreementStrength);
|
|
126
|
+
|
|
127
|
+
const rate = events.length > 0
|
|
128
|
+
? Math.round((disagreements.length / events.length) * 1000) / 1000
|
|
129
|
+
: 0;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
disagreements,
|
|
133
|
+
stats: {
|
|
134
|
+
totalEvents: events.length,
|
|
135
|
+
disagreementCount: disagreements.length,
|
|
136
|
+
disagreementRate: rate,
|
|
137
|
+
overconfident: disagreements.filter((d) => d.disagreementType === 'model_overconfident').length,
|
|
138
|
+
underconfident: disagreements.filter((d) => d.disagreementType === 'model_underconfident').length,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Amplify: Generate DPO pairs from disagreements
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generate amplified DPO pairs from disagreements.
|
|
149
|
+
* For each disagreement, synthesize a preference pair where:
|
|
150
|
+
* - chosen = what the user signal implies (corrective direction)
|
|
151
|
+
* - rejected = what the model predicted
|
|
152
|
+
*
|
|
153
|
+
* @param {Array} disagreements - From mineDisagreements()
|
|
154
|
+
* @returns {Array} DPO preference pairs with amplification metadata
|
|
155
|
+
*/
|
|
156
|
+
function amplifyFromDisagreements(disagreements) {
|
|
157
|
+
if (!Array.isArray(disagreements)) return [];
|
|
158
|
+
|
|
159
|
+
return disagreements.map((d) => {
|
|
160
|
+
const domainKeys = extractDomainKeys({
|
|
161
|
+
tags: d.tags,
|
|
162
|
+
title: d.context,
|
|
163
|
+
});
|
|
164
|
+
const domainLabel = domainKeys.length > 0 ? domainKeys.join(', ') : d.domain;
|
|
165
|
+
|
|
166
|
+
const prompt = `Domain: ${domainLabel}. ` +
|
|
167
|
+
`The agent performed a ${d.domain} task. ` +
|
|
168
|
+
(d.context ? `Context: ${d.context}` : 'How should the agent handle this scenario?');
|
|
169
|
+
|
|
170
|
+
let chosen;
|
|
171
|
+
let rejected;
|
|
172
|
+
|
|
173
|
+
if (d.disagreementType === 'model_overconfident') {
|
|
174
|
+
// Model thought it was good, user said no — user's correction is "chosen"
|
|
175
|
+
chosen = `The agent should NOT rely on its current ${d.domain} approach. ` +
|
|
176
|
+
`User feedback indicates failure despite model confidence of ${d.categoryReliability.toFixed(2)}. ` +
|
|
177
|
+
'Corrective action needed.';
|
|
178
|
+
rejected = `The agent's ${d.domain} approach is reliable ` +
|
|
179
|
+
`(model confidence: ${d.categoryReliability.toFixed(2)}). Continue current behavior.`;
|
|
180
|
+
} else {
|
|
181
|
+
// Model thought it was bad, user said it was fine — model is too cautious
|
|
182
|
+
chosen = `The agent's ${d.domain} approach succeeded. ` +
|
|
183
|
+
`User approved despite model skepticism (confidence: ${d.categoryReliability.toFixed(2)}). ` +
|
|
184
|
+
'This approach should be trusted more.';
|
|
185
|
+
rejected = `The agent's ${d.domain} approach is unreliable ` +
|
|
186
|
+
`(model confidence: ${d.categoryReliability.toFixed(2)}). Avoid this approach.`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
prompt,
|
|
191
|
+
chosen,
|
|
192
|
+
rejected,
|
|
193
|
+
metadata: {
|
|
194
|
+
disagreementStrength: d.disagreementStrength,
|
|
195
|
+
disagreementType: d.disagreementType,
|
|
196
|
+
domain: d.domain,
|
|
197
|
+
category: d.category,
|
|
198
|
+
originalFeedbackId: d.feedbackId,
|
|
199
|
+
amplified: true,
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Calibrate: Cross-reference prevention rules with Thompson model
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Calibrate prevention rules by cross-referencing disagreements.
|
|
211
|
+
* Only promote rules where BOTH Thompson model AND user signals agree
|
|
212
|
+
* on the failure pattern (concordance check).
|
|
213
|
+
*
|
|
214
|
+
* @param {string} [feedbackDir] - Override feedback dir
|
|
215
|
+
* @returns {{ calibratedRules: Array, droppedRules: Array, concordanceRate: number }}
|
|
216
|
+
*/
|
|
217
|
+
function calibratePreventionRules(feedbackDir) {
|
|
218
|
+
const paths = getFeedbackPaths();
|
|
219
|
+
const dir = feedbackDir || paths.FEEDBACK_DIR;
|
|
220
|
+
const modelPath = path.join(dir, 'feedback_model.json');
|
|
221
|
+
const memoryLogPath = path.join(dir, 'memory-log.jsonl');
|
|
222
|
+
|
|
223
|
+
const model = loadModel(modelPath);
|
|
224
|
+
const reliability = getReliability(model);
|
|
225
|
+
const memories = readJSONL(memoryLogPath);
|
|
226
|
+
|
|
227
|
+
// Filter to error memories only
|
|
228
|
+
const errorMemories = memories.filter((m) => m.category === 'error');
|
|
229
|
+
|
|
230
|
+
const calibratedRules = [];
|
|
231
|
+
const droppedRules = [];
|
|
232
|
+
|
|
233
|
+
for (const mem of errorMemories) {
|
|
234
|
+
const domain = inferDomain(mem.tags, mem.content || mem.title || '');
|
|
235
|
+
const category = domainToCategory(domain);
|
|
236
|
+
const catReliability = reliability[category]
|
|
237
|
+
? reliability[category].reliability
|
|
238
|
+
: 0.5;
|
|
239
|
+
|
|
240
|
+
const rule = {
|
|
241
|
+
domain,
|
|
242
|
+
category,
|
|
243
|
+
title: mem.title || '',
|
|
244
|
+
categoryReliability: catReliability,
|
|
245
|
+
memoryId: mem.id || null,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Concordance: both user (error memory) and Thompson (low reliability) agree
|
|
249
|
+
if (catReliability < 0.5) {
|
|
250
|
+
calibratedRules.push(rule);
|
|
251
|
+
} else {
|
|
252
|
+
droppedRules.push(rule);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const total = calibratedRules.length + droppedRules.length;
|
|
257
|
+
const concordanceRate = total > 0
|
|
258
|
+
? Math.round((calibratedRules.length / total) * 1000) / 1000
|
|
259
|
+
: 1;
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
calibratedRules,
|
|
263
|
+
droppedRules,
|
|
264
|
+
concordanceRate,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// CLI
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
|
|
272
|
+
function parseArgs(argv) {
|
|
273
|
+
const args = {};
|
|
274
|
+
argv.forEach((arg) => {
|
|
275
|
+
if (!arg.startsWith('--')) return;
|
|
276
|
+
const [key, ...rest] = arg.slice(2).split('=');
|
|
277
|
+
args[key] = rest.length ? rest.join('=') : true;
|
|
278
|
+
});
|
|
279
|
+
return args;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (require.main === module) {
|
|
283
|
+
const args = parseArgs(process.argv.slice(2));
|
|
284
|
+
|
|
285
|
+
if (args.calibrate) {
|
|
286
|
+
const result = calibratePreventionRules(args['feedback-dir']);
|
|
287
|
+
console.log(JSON.stringify(result, null, 2));
|
|
288
|
+
} else {
|
|
289
|
+
// --mine or --amplify (amplify implies mine)
|
|
290
|
+
const result = mineDisagreements({
|
|
291
|
+
feedbackDir: args['feedback-dir'],
|
|
292
|
+
modelPath: args['model-path'],
|
|
293
|
+
highThreshold: args['high-threshold'] ? Number(args['high-threshold']) : undefined,
|
|
294
|
+
lowThreshold: args['low-threshold'] ? Number(args['low-threshold']) : undefined,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (args.amplify) {
|
|
298
|
+
const pairs = amplifyFromDisagreements(result.disagreements);
|
|
299
|
+
console.log(JSON.stringify({ pairs, stats: result.stats }, null, 2));
|
|
300
|
+
} else {
|
|
301
|
+
console.log(JSON.stringify(result, null, 2));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
// Exports
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
|
|
310
|
+
module.exports = {
|
|
311
|
+
mineDisagreements,
|
|
312
|
+
amplifyFromDisagreements,
|
|
313
|
+
calibratePreventionRules,
|
|
314
|
+
domainToCategory,
|
|
315
|
+
};
|