thumbgate 1.34.3 → 1.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
  2. package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
  3. package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
  4. package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
  5. package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
  6. package/.claude-plugin/plugin.json +1 -1
  7. package/.well-known/llms.txt +1 -0
  8. package/.well-known/mcp/server-card.json +1 -1
  9. package/CONTRIBUTING.md +95 -0
  10. package/README.md +195 -632
  11. package/THIRD_PARTY_NOTICES.md +89 -0
  12. package/adapters/claude/.mcp.json +2 -2
  13. package/adapters/forge/forge.yaml +3 -3
  14. package/adapters/future-agi/.mcp.json +8 -0
  15. package/adapters/future-agi/FUTURE_AGI.md +23 -0
  16. package/adapters/future-agi/config.toml +3 -0
  17. package/adapters/future-agi/future-agi-bridge.js +9 -0
  18. package/adapters/future-agi/opencode.json +8 -0
  19. package/adapters/herdr/herdr-plugin.toml +18 -0
  20. package/adapters/mcp/server-stdio.js +238 -25
  21. package/adapters/opencode/opencode.json +1 -1
  22. package/adapters/workos/WORKOS.md +52 -0
  23. package/bin/cli.js +373 -5
  24. package/bin/futureagi-bridge +9 -0
  25. package/config/gate-templates.json +653 -4
  26. package/config/gates/actor-critic-audit.json +34 -0
  27. package/config/gates/default.json +21 -2
  28. package/config/gates/five-walls-governance.json +34 -0
  29. package/config/gates/future-agi-guardrails.json +34 -0
  30. package/config/gates/radware-threat-defense-2026.json +61 -0
  31. package/config/gates/simatree-data-governance.json +33 -0
  32. package/config/mcp-allowlists.json +4 -0
  33. package/config/merge-quality-checks.json +10 -1
  34. package/config/model-candidates.json +382 -24
  35. package/config/model-tiers.json +18 -0
  36. package/config/post-deploy-marketing-pages.json +10 -0
  37. package/config/progressive/01-wire-only.json +11 -0
  38. package/config/progressive/02-dashboard-empty-ok.json +10 -0
  39. package/config/progressive/03-one-lesson.json +10 -0
  40. package/config/progressive/04-warn-fires.json +11 -0
  41. package/config/progressive/05-strict-optional.json +11 -0
  42. package/config/progressive/README.md +15 -0
  43. package/config/schemas/broker-execution-receipt.schema.json +139 -0
  44. package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
  45. package/conformance/provider-attestation/vectors.json +320 -0
  46. package/docs/specs/provider-execution-attestation-v1.md +69 -0
  47. package/openapi/openapi.yaml +15 -0
  48. package/package.json +401 -147
  49. package/public/about.html +2 -2
  50. package/public/ai-malpractice-prevention.html +7 -7
  51. package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
  52. package/public/blog/a-receipt-is-not-world-state.html +388 -0
  53. package/public/blog/git-at-agent-scale.html +374 -0
  54. package/public/blog/no-llm-in-the-gate.html +133 -0
  55. package/public/blog.html +80 -0
  56. package/public/case-studies.html +16 -1
  57. package/public/compare.html +28 -0
  58. package/public/diagnostic.html +216 -7
  59. package/public/docs/connectors.html +39 -0
  60. package/public/federal.html +2 -2
  61. package/public/founders.html +639 -0
  62. package/public/index.html +87 -9
  63. package/public/install.html +8 -8
  64. package/public/learn.html +39 -0
  65. package/public/numbers.html +2 -2
  66. package/public/peter.html +310 -0
  67. package/public/platform-partners.html +119 -0
  68. package/public/pricing.html +24 -3
  69. package/public/privacy.html +117 -0
  70. package/public/pro.html +17 -0
  71. package/public/support.html +62 -0
  72. package/public/terms.html +130 -0
  73. package/public/third-party-notices.html +95 -0
  74. package/public/yt.html +351 -0
  75. package/scripts/action-receipts.js +133 -3
  76. package/scripts/adaptive-governance-arena.js +349 -0
  77. package/scripts/admin-override.js +205 -0
  78. package/scripts/agent-action-inventory.js +869 -0
  79. package/scripts/agent-audit-trace.js +42 -2
  80. package/scripts/agent-egress-policy.js +1117 -0
  81. package/scripts/agent-memory-lifecycle.js +141 -2
  82. package/scripts/agent-operations-planner.js +441 -1
  83. package/scripts/agent-readiness.js +68 -0
  84. package/scripts/agent-security-central.js +647 -0
  85. package/scripts/allowlist-bridge-honesty.js +417 -0
  86. package/scripts/async-job-runner.js +102 -11
  87. package/scripts/audit-trail.js +212 -0
  88. package/scripts/auto-promote-gates.js +178 -27
  89. package/scripts/billing.js +1 -1
  90. package/scripts/broker-execution-receipts.js +719 -0
  91. package/scripts/budget-aware-gates-proof.js +423 -0
  92. package/scripts/claude-feedback-sync.js +29 -3
  93. package/scripts/claw-harness-production.js +237 -0
  94. package/scripts/cli-progress.js +111 -0
  95. package/scripts/cli-schema.js +163 -1
  96. package/scripts/codex-runbook-flywheel.js +318 -0
  97. package/scripts/context-footprint.js +186 -0
  98. package/scripts/contextfs.js +143 -61
  99. package/scripts/dashboard-limits.js +27 -0
  100. package/scripts/dashboard.js +279 -9
  101. package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
  102. package/scripts/docker-sandbox-planner.js +18 -0
  103. package/scripts/double-blind-eval-protocol.js +252 -0
  104. package/scripts/edotenv-rl-gateway.js +259 -0
  105. package/scripts/ensure-production-search-corpus.js +162 -0
  106. package/scripts/eval-holdout.js +311 -0
  107. package/scripts/feedback-aggregate.js +21 -2
  108. package/scripts/feedback-loop.js +87 -5
  109. package/scripts/feedback-quality.js +9 -0
  110. package/scripts/file-ledger-lock.js +4 -1
  111. package/scripts/financial-control-plane.js +41 -1
  112. package/scripts/find-dormant-requires.js +118 -0
  113. package/scripts/fs-utils.js +84 -8
  114. package/scripts/gate-stats.js +2 -2
  115. package/scripts/gates-engine.js +859 -58
  116. package/scripts/generate-case-study-outreach.js +24 -15
  117. package/scripts/git-at-scale.js +628 -0
  118. package/scripts/governance-conflict-audit.js +1650 -0
  119. package/scripts/governance-difficulty-curriculum.js +328 -0
  120. package/scripts/graphrag-retrieval.js +275 -0
  121. package/scripts/gurobi-optimizer.js +324 -0
  122. package/scripts/gurobi_optimizer.py +485 -0
  123. package/scripts/harness-selector.js +82 -1
  124. package/scripts/hidden-entry-points.js +284 -0
  125. package/scripts/human-escalation.js +199 -1
  126. package/scripts/hybrid-feedback-context.js +152 -19
  127. package/scripts/intent-governed-execution.js +602 -0
  128. package/scripts/intervention-policy.js +123 -20
  129. package/scripts/jit-harness-compose.js +628 -0
  130. package/scripts/jsonl-watcher.js +10 -0
  131. package/scripts/lesson-embedding-index.js +95 -12
  132. package/scripts/lesson-retrieval.js +105 -19
  133. package/scripts/local-model-profile.js +19 -2
  134. package/scripts/mailer/resend-mailer.js +1 -1
  135. package/scripts/matryoshka-embedding.js +235 -0
  136. package/scripts/mcp-oauth.js +42 -4
  137. package/scripts/mcp-session-handles.js +1016 -0
  138. package/scripts/mcp-wiring-doctor.js +314 -0
  139. package/scripts/memory-firewall.js +115 -2
  140. package/scripts/memory-scope-readiness.js +299 -0
  141. package/scripts/memory-vs-rag-route.js +161 -0
  142. package/scripts/model-tier-router.js +148 -21
  143. package/scripts/nvidia-specdecode-al-doctor.js +536 -0
  144. package/scripts/openui-catalog-compose-honesty.js +593 -0
  145. package/scripts/operational-integrity.js +19 -1
  146. package/scripts/override-audit.js +213 -0
  147. package/scripts/package-manager-honesty-doctor.js +458 -0
  148. package/scripts/pr-manager.js +63 -1
  149. package/scripts/prove-herdr-adapter.js +52 -0
  150. package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
  151. package/scripts/prove-workos.js +73 -0
  152. package/scripts/provider-attestation-conformance.js +192 -0
  153. package/scripts/provider-receipt-contract.js +136 -0
  154. package/scripts/qwen38-max-cost-optimizer.js +401 -0
  155. package/scripts/radware-threat-defense.js +280 -0
  156. package/scripts/rag-embedding-identity.js +221 -0
  157. package/scripts/rag-precision-guardrails.js +112 -2
  158. package/scripts/remote-feedback-capture.js +159 -0
  159. package/scripts/research-agent-harness.js +256 -0
  160. package/scripts/rsi-safety-hillclimb.js +200 -0
  161. package/scripts/rule-sprawl.js +188 -0
  162. package/scripts/schedule-manager.js +147 -0
  163. package/scripts/self-heal.js +8 -0
  164. package/scripts/session-lease.js +415 -0
  165. package/scripts/simatree-data-governance.js +347 -0
  166. package/scripts/slo-alert-engine.js +172 -7
  167. package/scripts/solver-parity.js +539 -0
  168. package/scripts/stealth-memory-injection-gate.js +333 -0
  169. package/scripts/switchyard-router.js +366 -0
  170. package/scripts/telemetry-analytics.js +84 -27
  171. package/scripts/temporal-decay-weighting.js +138 -0
  172. package/scripts/test-all.js +165 -0
  173. package/scripts/token-savings.js +42 -0
  174. package/scripts/tool-kpi-tracker.js +108 -5
  175. package/scripts/tool-registry.js +193 -5
  176. package/scripts/universal-claim-evaluator.js +14 -2
  177. package/scripts/vector-store.js +279 -9
  178. package/scripts/workflow-notebook.js +391 -0
  179. package/scripts/workflow-sentinel.js +111 -12
  180. package/scripts/workos-production-guard.js +260 -0
  181. package/scripts/workspace-search-route.js +515 -0
  182. package/server.json +2 -2
  183. package/src/agent-identity-boundary.js +76 -0
  184. package/src/agent-retrieval-cache.js +155 -0
  185. package/src/alert-noise-ledger.js +502 -0
  186. package/src/api/server.js +802 -185
  187. package/src/git-fast-cache.js +220 -0
  188. package/src/git-wal-sync.js +156 -0
  189. package/src/hash-anchored-edit.js +82 -0
  190. package/src/hermes-platform-protocol.js +475 -0
  191. package/src/hermes-sync-plane.js +241 -0
  192. package/src/index.js +30 -1
  193. package/src/iso42001-compliance-guard.js +97 -0
  194. package/src/latency-budget.js +244 -0
  195. package/src/mcp-writeguard.js +316 -0
  196. package/src/miminions-adapter.js +106 -0
  197. package/src/pipeline-compass.js +104 -0
  198. package/src/ppl-alert-pipeline.js +284 -0
  199. package/src/rendezvous-router.js +90 -0
  200. package/src/security-questionnaire.js +195 -0
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ThumbGate — Simatree Enterprise Data Lifecycle & BI Governance Engine
5
+ *
6
+ * Inspired by Wesley Flores (Managing Partner at Simatree, Top 25 Information Manager,
7
+ * Computerworld Best Practices BI Leader).
8
+ *
9
+ * Core Principles:
10
+ * 1. "Why Before How" — Data operations require explicit business intent and rollback receipts.
11
+ * 2. Full Lifecycle Data Governance — Interdicts unverified schema/table mutations, lakehouse drops,
12
+ * and ungrounded batch modifications across BigQuery, Snowflake, Databricks, and Postgres.
13
+ * 3. Bayesian Uncertainty Estimation — Computes statistical posterior confidence bounds before
14
+ * AI data agents dispatch queries or mutations against enterprise analytics tables.
15
+ * 4. PMO Transformation Gates — Validates multi-stage IT modernization milestones with verifiable receipts.
16
+ */
17
+
18
+ 'use strict';
19
+
20
+ const fs = require('node:fs');
21
+ const path = require('node:path');
22
+
23
+ /**
24
+ * One part of a SQL object name, in every form the advertised warehouses emit:
25
+ * a bare identifier (`customers`), a double-quoted one (Postgres/Snowflake),
26
+ * a backtick-quoted one (BigQuery/Databricks — note it may contain dots and
27
+ * hyphens, as in `prod-project.analytics.customers`), or a bracketed one
28
+ * (SQL Server, `[dbo]`).
29
+ */
30
+ const SQL_IDENT_PART = '(?:[\\w$]+|"[^"]+"|`[^`]+`|\\[[^\\]]+\\])';
31
+
32
+ /**
33
+ * A possibly schema- or project-qualified table reference: one or more parts
34
+ * joined by dots.
35
+ *
36
+ * WHY THIS IS NOT `\w+`: `\w+` matches only the single-part form. With it,
37
+ * `DELETE FROM analytics.customers;` failed the destructive test entirely and
38
+ * received an authorized receipt with no rollback plan, no risk tier and no
39
+ * declared targetEntities — the exact full-table deletion this gate exists to
40
+ * stop. Every qualified and quoted form was equally invisible.
41
+ */
42
+ const QUALIFIED_TABLE = `${SQL_IDENT_PART}(?:\\s*\\.\\s*${SQL_IDENT_PART})*`;
43
+
44
+ const DESTRUCTIVE_SQL_PATTERNS = [
45
+ /\bDROP\s+(TABLE|DATABASE|SCHEMA|VIEW|INDEX)\b/i,
46
+ /\bTRUNCATE\s+(TABLE)?\b/i,
47
+ /\bALTER\s+TABLE\s+.*\b(DROP\s+COLUMN|RENAME\s+TO)\b/i,
48
+ new RegExp(`\\bDELETE\\s+FROM\\s+${QUALIFIED_TABLE}(\\s+WHERE\\s+(1=1|TRUE))?\\s*;?$`, 'i'),
49
+ // An UPDATE with no WHERE clause rewrites every row. The previous form put the
50
+ // negative lookahead after `.*`, where backtracking always satisfies it, so the
51
+ // clause was inert and the pattern reduced to "any UPDATE ... SET".
52
+ new RegExp(`\\bUPDATE\\s+${QUALIFIED_TABLE}\\s+SET\\b(?![\\s\\S]*\\bWHERE\\b)`, 'i'),
53
+ ];
54
+
55
+ const LOW_SIGNAL_INTENT_PATTERNS = [
56
+ /^(test|testing|fix|update|clean|temp|misc|stuff|run|do it|asap)$/i,
57
+ /^(as per request|needed|required|ticket|todo)$/i,
58
+ ];
59
+
60
+ /**
61
+ * Evaluates whether a data mutation complies with the "Why Before How" intent mandate.
62
+ *
63
+ * @param {Object} payload
64
+ * @param {string} payload.sql - SQL statement or data operation command
65
+ * @param {string} [payload.businessIntent] - Explicit rationale for the operation
66
+ * @param {string} [payload.riskTier] - 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'
67
+ * @param {Array<string>} [payload.targetEntities] - Target tables/views
68
+ * @param {Object} [payload.rollbackPlan] - Defined rollback procedure or snapshot ID
69
+ * @returns {Object} Evaluation verdict and telemetry
70
+ */
71
+ function evaluateDataLifecycleIntent(payload = {}) {
72
+ const sql = String(payload.sql || '').trim();
73
+ const businessIntent = String(payload.businessIntent || '').trim();
74
+ const riskTier = String(payload.riskTier || 'MEDIUM').toUpperCase();
75
+ const targetEntities = Array.isArray(payload.targetEntities) ? payload.targetEntities : [];
76
+ const rollbackPlan = payload.rollbackPlan && typeof payload.rollbackPlan === 'object' ? payload.rollbackPlan : null;
77
+
78
+ const violations = [];
79
+ let isDestructive = false;
80
+
81
+ for (const pattern of DESTRUCTIVE_SQL_PATTERNS) {
82
+ if (pattern.test(sql)) {
83
+ isDestructive = true;
84
+ break;
85
+ }
86
+ }
87
+
88
+ // 1. Intent check
89
+ if (!businessIntent) {
90
+ violations.push('MISSING_BUSINESS_INTENT: Data operations require an explicit "why" before execution.');
91
+ } else if (businessIntent.length < 15 || LOW_SIGNAL_INTENT_PATTERNS.some((p) => p.test(businessIntent))) {
92
+ violations.push(`INSUFFICIENT_BUSINESS_INTENT: Intent "${businessIntent}" lacks context-grounded rationale.`);
93
+ }
94
+
95
+ // 2. Destructive SQL checks
96
+ if (isDestructive) {
97
+ if (!rollbackPlan || !rollbackPlan.snapshotId) {
98
+ violations.push('DESTRUCTIVE_MUTATION_WITHOUT_ROLLBACK: Destructive SQL requires a verified snapshotId or restore receipt.');
99
+ }
100
+ if (riskTier !== 'HIGH' && riskTier !== 'CRITICAL') {
101
+ violations.push('INVALID_RISK_TIER: Destructive operations must be classified as HIGH or CRITICAL risk.');
102
+ }
103
+ if (targetEntities.length === 0) {
104
+ violations.push('UNSPECIFIED_TARGET_ENTITIES: Destructive operations must declare explicit targetEntities.');
105
+ }
106
+ }
107
+
108
+ const allowed = violations.length === 0;
109
+ const score = Math.max(0, 100 - (violations.length * 35));
110
+
111
+ return {
112
+ allowed,
113
+ score,
114
+ isDestructive,
115
+ intentValidated: Boolean(businessIntent && businessIntent.length >= 15),
116
+ riskTier,
117
+ targetEntities,
118
+ violations,
119
+ receipt: allowed ? {
120
+ gate: 'simatree-data-governance',
121
+ timestamp: new Date().toISOString(),
122
+ intent: businessIntent,
123
+ entities: targetEntities,
124
+ authorized: true,
125
+ } : null,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Estimates Bayesian uncertainty and posterior confidence bounds for AI data queries.
131
+ *
132
+ * @param {Object} metrics
133
+ * @param {number} [metrics.sampleSize=100] - Number of historical benchmark runs
134
+ * @param {number} [metrics.priorSuccessRate=0.95] - Prior probability of successful execution (0-1)
135
+ * @param {number} [metrics.observedFailures=0] - Observed anomalies or schema drift incidents
136
+ * @param {number} [metrics.schemaDriftScore=0.0] - Drift score between 0.0 (pristine) and 1.0 (corrupted)
137
+ * @param {number} [metrics.uncertaintyThreshold=0.20] - Max tolerable posterior uncertainty (0-1)
138
+ * @returns {Object} Bayesian posterior metrics and safety gate verdict
139
+ */
140
+ function estimateBayesianUncertainty(metrics = {}) {
141
+ const sampleSize = Math.max(1, Number(metrics.sampleSize) || 100);
142
+ const priorSuccess = Math.min(1, Math.max(0.1, Number(metrics.priorSuccessRate) || 0.95));
143
+ const observedFailures = Math.max(0, Number(metrics.observedFailures) || 0);
144
+ const schemaDriftScore = Math.min(1, Math.max(0, Number(metrics.schemaDriftScore) || 0.0));
145
+ const threshold = Number(metrics.uncertaintyThreshold) || 0.20;
146
+
147
+ // Beta distribution update (Beta(alpha, beta))
148
+ // Prior pseudo-counts: alpha_0 = priorSuccess * 10, beta_0 = (1 - priorSuccess) * 10
149
+ const alpha0 = priorSuccess * 10;
150
+ const beta0 = (1 - priorSuccess) * 10;
151
+
152
+ const observedSuccesses = Math.max(0, sampleSize - observedFailures);
153
+ const alphaPost = alpha0 + observedSuccesses;
154
+ const betaPost = beta0 + observedFailures + (schemaDriftScore * 10);
155
+
156
+ const posteriorMean = alphaPost / (alphaPost + betaPost);
157
+ const posteriorVariance = (alphaPost * betaPost) / (Math.pow(alphaPost + betaPost, 2) * (alphaPost + betaPost + 1));
158
+ const uncertainty = Math.min(1, Math.sqrt(posteriorVariance) * 3 + schemaDriftScore * 0.4);
159
+
160
+ const safe = uncertainty <= threshold && posteriorMean >= 0.80;
161
+
162
+ return {
163
+ safe,
164
+ posteriorMean: Number(posteriorMean.toFixed(4)),
165
+ uncertainty: Number(uncertainty.toFixed(4)),
166
+ threshold,
167
+ schemaDriftScore,
168
+ verdict: safe ? 'CONFIDENT_EXECUTION' : 'HIGH_UNCERTAINTY_INTERDICTED',
169
+ recommendation: safe
170
+ ? 'Execute data pipeline stage autonomously.'
171
+ : 'Require operator escalation or rollback snapshot pre-execution.',
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Validates PMO transformation stages for enterprise data architectures.
177
+ *
178
+ * @param {Object} plan - Multi-stage transformation plan
179
+ * @param {Array<Object>} plan.stages - Sequence of milestone stages
180
+ * @returns {Object} PMO compliance audit result
181
+ */
182
+ function auditPMOTransformationGate(plan = {}) {
183
+ const stages = Array.isArray(plan.stages) ? plan.stages : [];
184
+ const findings = [];
185
+
186
+ if (stages.length === 0) {
187
+ return {
188
+ compliant: false,
189
+ score: 0,
190
+ findings: ['PLAN_EMPTY: Transformation plan contains no actionable milestone stages.'],
191
+ };
192
+ }
193
+
194
+ stages.forEach((stage, idx) => {
195
+ const stageId = stage.id || `stage_${idx + 1}`;
196
+ if (!stage.name) {
197
+ findings.push(`${stageId}: Missing stage name.`);
198
+ }
199
+ if (!stage.outcomeMetric) {
200
+ findings.push(`${stageId}: Missing measurable outcomeMetric.`);
201
+ }
202
+ if (!stage.rollbackReceipt && stage.isMutating) {
203
+ findings.push(`${stageId}: Mutating stage lacks verifiable rollbackReceipt.`);
204
+ }
205
+ if (!stage.owner) {
206
+ findings.push(`${stageId}: Missing assigned stakeholder / owner.`);
207
+ }
208
+ });
209
+
210
+ const compliant = findings.length === 0;
211
+ const score = Math.max(0, 100 - (findings.length * 20));
212
+
213
+ return {
214
+ compliant,
215
+ score,
216
+ totalStages: stages.length,
217
+ findings,
218
+ };
219
+ }
220
+
221
+ /**
222
+ * Doctor check verifying runtime integrity.
223
+ */
224
+ function checkDoctor() {
225
+ const sampleEvaluation = evaluateDataLifecycleIntent({
226
+ sql: 'SELECT customer_id, revenue FROM analytics.sales_monthly WHERE year = 2026',
227
+ businessIntent: 'Monthly recurring revenue cohort analysis for Q3 executive review.',
228
+ riskTier: 'LOW',
229
+ targetEntities: ['analytics.sales_monthly'],
230
+ });
231
+
232
+ const sampleBayesian = estimateBayesianUncertainty({
233
+ sampleSize: 200,
234
+ priorSuccessRate: 0.98,
235
+ observedFailures: 1,
236
+ schemaDriftScore: 0.02,
237
+ });
238
+
239
+ const samplePMO = auditPMOTransformationGate({
240
+ stages: [
241
+ {
242
+ id: 'STAGE_1',
243
+ name: 'Lakehouse Ingestion Validation',
244
+ outcomeMetric: 'zero_schema_mismatch',
245
+ owner: 'data-engineering',
246
+ isMutating: false,
247
+ },
248
+ ],
249
+ });
250
+
251
+ const ok = sampleEvaluation.allowed && sampleBayesian.safe && samplePMO.compliant;
252
+
253
+ return {
254
+ ok,
255
+ name: 'simatree-data-governance',
256
+ version: '1.35.0',
257
+ evaluator: sampleEvaluation.allowed ? 'ONLINE' : 'ERROR',
258
+ bayesianModel: sampleBayesian.safe ? 'ONLINE' : 'ERROR',
259
+ pmoGate: samplePMO.compliant ? 'ONLINE' : 'ERROR',
260
+ timestamp: new Date().toISOString(),
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Path-based main check. The `require.main === module` form trips SonarCloud
266
+ * rule javascript:S3403 (always-false strict equality under strict type
267
+ * inference), so we compare paths instead.
268
+ *
269
+ * WHY realpath AND NOT A BARE `path.resolve` COMPARE: when this CLI is launched
270
+ * through a symlink — an npm `bin` shim, a global install, `npx` — Node leaves
271
+ * `process.argv[1]` as the symlink path while `__filename` is already the
272
+ * realpath of the target. A plain resolve-and-compare is then false, and the
273
+ * process would exit 0 having printed nothing: no `--doctor` report, no
274
+ * `--eval` result, no `--sql` verdict. `require.main === module` did not have
275
+ * that hole because Node's module resolution canonicalises first. Canonicalising
276
+ * both sides restores the original semantics.
277
+ *
278
+ * realpathSync throws on a path that no longer exists, so each side falls back
279
+ * to its resolved form; that keeps the check total rather than crashing on a
280
+ * deleted or exotic entry point.
281
+ */
282
+ function canonicalPath(candidate) {
283
+ const resolved = path.resolve(candidate);
284
+ try {
285
+ return fs.realpathSync(resolved);
286
+ } catch {
287
+ return resolved;
288
+ }
289
+ }
290
+
291
+ function isDirectInvocation() {
292
+ const entryPoint = process.argv[1];
293
+ if (!entryPoint) return false;
294
+ return canonicalPath(entryPoint) === canonicalPath(__filename);
295
+ }
296
+
297
+ // CLI Interface
298
+ if (isDirectInvocation()) {
299
+ const args = process.argv.slice(2);
300
+
301
+ if (args.includes('--doctor')) {
302
+ const report = checkDoctor();
303
+ console.log(JSON.stringify(report, null, 2));
304
+ process.exit(report.ok ? 0 : 1);
305
+ }
306
+
307
+ if (args.includes('--eval')) {
308
+ const evalIdx = args.indexOf('--eval');
309
+ const input = args[evalIdx + 1] || '{}';
310
+ try {
311
+ const payload = JSON.parse(input);
312
+ const res = evaluateDataLifecycleIntent(payload);
313
+ console.log(JSON.stringify(res, null, 2));
314
+ process.exit(res.allowed ? 0 : 1);
315
+ } catch (err) {
316
+ console.error(`Invalid JSON input: ${err.message}`);
317
+ process.exit(1);
318
+ }
319
+ }
320
+
321
+ if (args.includes('--sql')) {
322
+ const sqlIdx = args.indexOf('--sql');
323
+ const sql = args[sqlIdx + 1] || '';
324
+ const whyIdx = args.indexOf('--why');
325
+ const why = whyIdx !== -1 ? args[whyIdx + 1] : '';
326
+
327
+ const res = evaluateDataLifecycleIntent({
328
+ sql,
329
+ businessIntent: why,
330
+ targetEntities: ['unspecified'],
331
+ });
332
+
333
+ console.log(JSON.stringify(res, null, 2));
334
+ process.exit(res.allowed ? 0 : 1);
335
+ }
336
+
337
+ console.log('Usage: node scripts/simatree-data-governance.js [--doctor | --eval <json> | --sql <query> --why <intent>]');
338
+ process.exit(0);
339
+ }
340
+
341
+ module.exports = {
342
+ evaluateDataLifecycleIntent,
343
+ estimateBayesianUncertainty,
344
+ auditPMOTransformationGate,
345
+ checkDoctor,
346
+ DESTRUCTIVE_SQL_PATTERNS,
347
+ };
@@ -1,14 +1,179 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
+
3
4
  const fs = require('fs');
4
5
  const path = require('path');
5
6
  const { computeToolKpis, getAtRiskTools } = require('./tool-kpi-tracker');
6
7
  const { deliver } = require('./webhook-delivery');
7
8
  const { resolveFeedbackDir } = require('./feedback-paths');
8
- const DEFAULT_SLOS = { successRate: 90, p95LatencyMs: 500, minCallsForAlert: 3 };
9
- function getAlertLogPath() { return path.join(resolveFeedbackDir(), 'slo-alerts.jsonl'); }
10
- function checkSloViolations({ slos, periodHours = 24 } = {}) { const t = { ...DEFAULT_SLOS, ...slos }; const atRisk = getAtRiskTools({ successRateThreshold: t.successRate, p95Threshold: t.p95LatencyMs, periodHours }); const violations = atRisk.map((tool) => { const reasons = []; if (tool.successRate < t.successRate) reasons.push(`success rate ${tool.successRate}% < ${t.successRate}% SLO`); if (tool.p95 > t.p95LatencyMs) reasons.push(`P95 ${tool.p95}ms > ${t.p95LatencyMs}ms SLO`); return { toolName: tool.toolName, ...tool, reasons, severity: tool.successRate < 70 ? 'critical' : 'warning' }; }); return { thresholds: t, periodHours, violations, violationCount: violations.length, checkedAt: new Date().toISOString() }; }
11
- function logAlert(alert) { const lp = getAlertLogPath(); const dir = path.dirname(lp); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.appendFileSync(lp, JSON.stringify({ ...alert, loggedAt: new Date().toISOString() }) + '\n'); }
12
- async function runSloCheck({ slos, periodHours = 24, platform, webhookUrl } = {}) { const result = checkSloViolations({ slos, periodHours }); if (result.violationCount > 0) { logAlert(result); if (platform && webhookUrl) { const title = `ThumbGate SLO Alert — ${result.violationCount} violation(s)`; const lines = result.violations.map((v) => `- ${v.toolName}: ${v.reasons.join(', ')} [${v.severity}]`); await deliver(platform, webhookUrl, title, lines.join('\n')); } } return result; }
13
- function formatSloSection(r) { if (!r || r.violationCount === 0) return ''; const lines = ['', 'SLO Violations:']; for (const v of r.violations) lines.push(` - [${v.severity.toUpperCase()}] ${v.toolName}: ${v.reasons.join('; ')} (${v.requestCount} calls)`); return lines.join('\n'); }
14
- module.exports = { DEFAULT_SLOS, checkSloViolations, runSloCheck, formatSloSection, logAlert, getAlertLogPath };
9
+
10
+ // Agentic tool-hop latency wall (Akamai State of AI Inference 2026): most critical
11
+ // use cases need ≤500ms end-to-end; many need ≤250ms. More GPUs do not fix
12
+ // CPU/tool hops budget each hop and fail closed when p95 exceeds the tier.
13
+ const AGENTIC_HOP_LATENCY_BUDGETS_MS = Object.freeze({
14
+ 'read-only': 250,
15
+ 'minimal-impact': 250,
16
+ 'contained-write': 500,
17
+ critical: 1000,
18
+ default: 500,
19
+ });
20
+
21
+ const DEFAULT_SLOS = {
22
+ successRate: 90,
23
+ // Matches Akamai's published agentic 500ms wall for generic tool hops.
24
+ p95LatencyMs: AGENTIC_HOP_LATENCY_BUDGETS_MS.default,
25
+ minCallsForAlert: 3,
26
+ };
27
+
28
+ function getAlertLogPath() {
29
+ return path.join(resolveFeedbackDir(), 'slo-alerts.jsonl');
30
+ }
31
+
32
+ function hopLatencyBudgetMs(writeRiskTier) {
33
+ if (writeRiskTier && Object.prototype.hasOwnProperty.call(AGENTIC_HOP_LATENCY_BUDGETS_MS, writeRiskTier)) {
34
+ return AGENTIC_HOP_LATENCY_BUDGETS_MS[writeRiskTier];
35
+ }
36
+ return AGENTIC_HOP_LATENCY_BUDGETS_MS.default;
37
+ }
38
+
39
+ /**
40
+ * Evaluate per-tool p95 against WriteGuard-tier hop budgets.
41
+ * Measures the hop chain ThumbGate actually sees (tool KPI), not tokens/sec.
42
+ */
43
+ function checkHopLatencyBudgets({
44
+ periodHours = 24,
45
+ feedbackDir,
46
+ minCalls = DEFAULT_SLOS.minCallsForAlert,
47
+ budgets = AGENTIC_HOP_LATENCY_BUDGETS_MS,
48
+ } = {}) {
49
+ const { tools, totalCalls, evidenceStatus } = computeToolKpis({ periodHours, feedbackDir });
50
+ const evaluated = tools
51
+ .filter((tool) => tool.requestCount >= minCalls)
52
+ .map((tool) => {
53
+ const budgetMs = hopLatencyBudgetMs(tool.writeRiskTier);
54
+ const withinBudget = tool.p95 <= budgetMs;
55
+ return {
56
+ toolName: tool.toolName,
57
+ writeRiskTier: tool.writeRiskTier || null,
58
+ requestCount: tool.requestCount,
59
+ p95: tool.p95,
60
+ budgetMs,
61
+ withinBudget,
62
+ overBudgetMs: withinBudget ? 0 : tool.p95 - budgetMs,
63
+ };
64
+ });
65
+ const violations = evaluated.filter((tool) => !tool.withinBudget);
66
+ return {
67
+ wallMs: budgets.default || AGENTIC_HOP_LATENCY_BUDGETS_MS.default,
68
+ budgets: { ...AGENTIC_HOP_LATENCY_BUDGETS_MS, ...budgets },
69
+ periodHours,
70
+ totalCalls,
71
+ evidenceStatus,
72
+ tools: evaluated,
73
+ violations,
74
+ violationCount: violations.length,
75
+ checkedAt: new Date().toISOString(),
76
+ };
77
+ }
78
+
79
+ function checkSloViolations({ slos, periodHours = 24, feedbackDir } = {}) {
80
+ const t = { ...DEFAULT_SLOS, ...slos };
81
+ const atRisk = getAtRiskTools({
82
+ successRateThreshold: t.successRate,
83
+ p95Threshold: t.p95LatencyMs,
84
+ periodHours,
85
+ feedbackDir,
86
+ });
87
+ const hopBudgets = checkHopLatencyBudgets({
88
+ periodHours,
89
+ feedbackDir,
90
+ minCalls: t.minCallsForAlert,
91
+ });
92
+ const byName = new Map(atRisk.map((tool) => [tool.toolName, { ...tool, reasons: [] }]));
93
+
94
+ for (const tool of atRisk) {
95
+ const entry = byName.get(tool.toolName);
96
+ if (tool.successRate < t.successRate) {
97
+ entry.reasons.push(`success rate ${tool.successRate}% < ${t.successRate}% SLO`);
98
+ }
99
+ if (tool.p95 > t.p95LatencyMs) {
100
+ entry.reasons.push(`P95 ${tool.p95}ms > ${t.p95LatencyMs}ms SLO`);
101
+ }
102
+ }
103
+
104
+ for (const hop of hopBudgets.violations) {
105
+ if (!byName.has(hop.toolName)) {
106
+ byName.set(hop.toolName, {
107
+ toolName: hop.toolName,
108
+ requestCount: hop.requestCount,
109
+ successRate: null,
110
+ p95: hop.p95,
111
+ writeRiskTier: hop.writeRiskTier,
112
+ reasons: [],
113
+ });
114
+ }
115
+ const entry = byName.get(hop.toolName);
116
+ entry.writeRiskTier = hop.writeRiskTier;
117
+ entry.budgetMs = hop.budgetMs;
118
+ entry.reasons.push(
119
+ `hop P95 ${hop.p95}ms > ${hop.budgetMs}ms ${hop.writeRiskTier || 'default'} budget`,
120
+ );
121
+ }
122
+
123
+ const violations = [...byName.values()]
124
+ .filter((tool) => tool.reasons.length > 0)
125
+ .map((tool) => ({
126
+ ...tool,
127
+ severity: (tool.successRate != null && tool.successRate < 70) ? 'critical' : 'warning',
128
+ }));
129
+
130
+ return {
131
+ thresholds: t,
132
+ periodHours,
133
+ hopBudgets,
134
+ violations,
135
+ violationCount: violations.length,
136
+ checkedAt: new Date().toISOString(),
137
+ };
138
+ }
139
+
140
+ function logAlert(alert) {
141
+ const lp = getAlertLogPath();
142
+ const dir = path.dirname(lp);
143
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
144
+ fs.appendFileSync(lp, `${JSON.stringify({ ...alert, loggedAt: new Date().toISOString() })}\n`);
145
+ }
146
+
147
+ async function runSloCheck({ slos, periodHours = 24, platform, webhookUrl } = {}) {
148
+ const result = checkSloViolations({ slos, periodHours });
149
+ if (result.violationCount > 0) {
150
+ logAlert(result);
151
+ if (platform && webhookUrl) {
152
+ const title = `ThumbGate SLO Alert — ${result.violationCount} violation(s)`;
153
+ const lines = result.violations.map((v) => `- ${v.toolName}: ${v.reasons.join(', ')} [${v.severity}]`);
154
+ await deliver(platform, webhookUrl, title, lines.join('\n'));
155
+ }
156
+ }
157
+ return result;
158
+ }
159
+
160
+ function formatSloSection(r) {
161
+ if (!r || r.violationCount === 0) return '';
162
+ const lines = ['', 'SLO Violations:'];
163
+ for (const v of r.violations) {
164
+ lines.push(` - [${v.severity.toUpperCase()}] ${v.toolName}: ${v.reasons.join('; ')} (${v.requestCount} calls)`);
165
+ }
166
+ return lines.join('\n');
167
+ }
168
+
169
+ module.exports = {
170
+ AGENTIC_HOP_LATENCY_BUDGETS_MS,
171
+ DEFAULT_SLOS,
172
+ checkHopLatencyBudgets,
173
+ checkSloViolations,
174
+ formatSloSection,
175
+ getAlertLogPath,
176
+ hopLatencyBudgetMs,
177
+ logAlert,
178
+ runSloCheck,
179
+ };