sneakoscope 4.6.1 → 4.6.3

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 (34) hide show
  1. package/README.md +23 -167
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/cli/command-registry.js +2 -1
  7. package/dist/cli/{ultra-search-command.js → insane-search-command.js} +17 -13
  8. package/dist/cli/xai-command.js +6 -6
  9. package/dist/commands/doctor.js +3 -1
  10. package/dist/core/commands/run-command.js +9 -5
  11. package/dist/core/db-safety.js +2 -2
  12. package/dist/core/doctor/global-sks-install-cleanup.js +73 -9
  13. package/dist/core/feature-fixtures.js +3 -0
  14. package/dist/core/fsx.js +1 -1
  15. package/dist/core/init.js +2 -1
  16. package/dist/core/mad-db/mad-db-coordinator.js +17 -8
  17. package/dist/core/mad-db/mad-db-executor.js +59 -7
  18. package/dist/core/mad-db/mad-db-policy-resolver.js +38 -1
  19. package/dist/core/mad-db/mad-db-policy.js +4 -3
  20. package/dist/core/mad-db/mad-db-runtime-profile.js +11 -2
  21. package/dist/core/mad-db/mad-db-target.js +31 -0
  22. package/dist/core/release-parallel-full-coverage.js +1 -1
  23. package/dist/core/routes.js +15 -14
  24. package/dist/core/update-check.js +11 -1
  25. package/dist/core/version.js +1 -1
  26. package/dist/scripts/mad-db-direct-apply-migration-hook-check.js +24 -1
  27. package/dist/scripts/mad-db-skill-policy-snapshot-check.js +4 -0
  28. package/dist/scripts/mad-db-supabase-transport-diagnostics-check.js +47 -0
  29. package/dist/scripts/release-metadata-1-19-check.js +1 -1
  30. package/dist/scripts/release-parallel-check.js +2 -2
  31. package/dist/scripts/release-parallel-full-coverage-check.js +1 -1
  32. package/dist/scripts/release-readiness-report.js +1 -1
  33. package/dist/scripts/ultra-search-provider-interface-check.js +1 -1
  34. package/package.json +5 -3
@@ -76,6 +76,7 @@ const FIXTURES = Object.freeze({
76
76
  'cli-commit': fixture('mock', 'sks commit --dry-run', [], 'pass'),
77
77
  'cli-commit-and-push': fixture('mock', 'sks commit-and-push --dry-run', [], 'pass'),
78
78
  'cli-context7': fixture('real_optional', 'sks context7 check --json', [], 'pass'),
79
+ 'cli-insane-search': fixture('execute', 'sks insane-search doctor --json', [], 'pass'),
79
80
  'cli-ultra-search': fixture('execute', 'sks ultra-search doctor --json', [], 'pass'),
80
81
  'cli-xai': fixture('real_optional', 'sks xai check --json', [], 'pass'),
81
82
  'cli-all-features': fixture('mock', 'sks all-features complete --json', [`.sneakoscope/reports/all-feature-completion-${PACKAGE_VERSION}.json`], 'pass'),
@@ -107,6 +108,8 @@ const FIXTURES = Object.freeze({
107
108
  'route-dfix': fixture('execute_and_validate_artifacts', 'sks dfix fixture --json', ['completion-proof.json', 'dfix-gate.json', 'dfix-verification.json'], 'pass'),
108
109
  'route-answer': fixture('mock', '$Answer answer-only route policy', [], 'pass'),
109
110
  'route-goal': fixture('mock', '$Goal bridge route', ['goal-workflow.json', 'completion-proof.json'], 'pass'),
111
+ 'route-insane-search': fixture('execute', 'sks run "$Insane-Search source intelligence fixture" --execute --json', [], 'pass'),
112
+ 'route-insanesearch': fixture('execute', 'sks run "$InsaneSearch source intelligence fixture" --execute --json', [], 'pass'),
110
113
  'route-ultra-search': fixture('execute', 'sks run "$Ultra-Search source intelligence fixture" --execute --json', [], 'pass'),
111
114
  'route-ultrasearch': fixture('execute', 'sks run "$UltraSearch source intelligence fixture" --execute --json', [], 'pass'),
112
115
  'route-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode geo --json', ['search-visibility/site-inventory.json', 'search-visibility/geo-findings.json', 'search-visibility/verification-report.json', 'geo-gate.json', 'completion-proof.json'], 'pass'),
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '4.6.1';
8
+ export const PACKAGE_VERSION = '4.6.3';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
package/dist/core/init.js CHANGED
@@ -1068,7 +1068,8 @@ export async function installSkills(root) {
1068
1068
  'reasoning-router': `---\nname: reasoning-router\ndescription: Temporary SKS reasoning-effort routing for every command and pipeline route.\n---\n\nmedium: simple copy/color/discovery/setup/mechanical edits. high: logic, safety, architecture, DB, orchestration, refactor, multi-file work. xhigh: research, AutoResearch, falsification, benchmarks, SEO/GEO, open-ended discovery, and From-Chat-IMG image work-order analysis. Routing is temporary; return to default after the gate. Inspect with sks reasoning and sks pipeline status.\n`,
1069
1069
  'pipeline-runner': `---\nname: pipeline-runner\ndescription: Execute SKS dollar-command routes as stateful pipelines with mission artifacts, route gates, Context7 evidence, temporary reasoning routing, reflection, and Honest Mode.\n---\n\nEvery $ command is a route. Use current.json, mission artifacts, and pipeline-plan.json as the execution plan: it records the lane, skipped stages, kept stages, verification, lean_decision, and no-unrequested-fallback invariant. Use temporary reasoning, TriWiki before stages, source hydration, Context7 when required, Team cleanup before reflection, reflection for full routes, and completion summary plus Honest Mode before final. Surface guard/scopes, record evidence, refresh/pack/validate TriWiki, and check sks pipeline status/resume/plan. ${leanEngineeringCompactText()} ${speedLanePolicyText()} ${skillDreamPolicyText()}\n`,
1070
1070
  'context7-docs': `---\nname: context7-docs\ndescription: Enforce Context7 MCP documentation evidence for SKS routes that depend on external libraries, frameworks, APIs, MCPs, package managers, DB SDKs, or generated docs.\n---\n\nWhen required, resolve-library-id, then query-docs for the resolved id. Legacy get-library-docs evidence is accepted. Prefer sks context7 tools/resolve/docs/evidence and finish only after both evidence stages exist. Check setup with sks context7 check.\n`,
1071
- 'ultra-search': `---\nname: ultra-search\ndescription: Dollar-command route for $Ultra-Search/$UltraSearch provider-independent source intelligence.\n---\n\nUse when the user invokes $Ultra-Search, $UltraSearch, or asks for UltraSearch source intelligence, source acquisition, X-search-style collection, URL acquisition, source normalization, claim ledgers, or citation proof. Prefer \`sks ultra-search doctor --json\` for readiness and \`sks ultra-search run "<query>" --mode balanced --json\` for provider-independent source proof; use \`sks ultra-search x "<query>" --json\` for X-search intent and \`sks ultra-search fetch "<url>" --json\` for URL acquisition. Context7 is required only when the query depends on current package/API/framework/MCP/generated documentation behavior. xAI/Grok credentials are optional and must not be required for route readiness. Evidence/artifacts live under \`.sneakoscope/missions/<ultra-* or route mission>/ultra-search/\`: intent.json, axes.json, query-variants.json, provider-plan.json, source-ledger.json, lead-ledger.json, claim-ledger.json, synthesis.md, ultra-search-proof.json, ultra-search-gate.json, and ultra-search-result.json. Do not turn weak discovery into supported claims; finish with an Honest Mode summary of verified sources, blockers, and unverified external coverage.\n`,
1071
+ 'insane-search': `---\nname: insane-search\ndescription: Dollar-command route for $Insane-Search/$InsaneSearch provider-independent source intelligence.\n---\n\nUse when the user invokes $Insane-Search, $InsaneSearch, legacy $Ultra-Search/$UltraSearch, or asks for InsaneSearch source intelligence, source acquisition, X-search-style collection, URL acquisition, source normalization, claim ledgers, or citation proof. Prefer \`sks insane-search doctor --json\` for readiness and \`sks insane-search run "<query>" --mode balanced --json\` for provider-independent source proof; use \`sks insane-search x "<query>" --json\` for X-search intent and \`sks insane-search fetch "<url>" --json\` for URL acquisition. Context7 is required only when the query depends on current package/API/framework/MCP/generated documentation behavior. xAI/Grok credentials are optional and must not be required for route readiness. Evidence/artifacts remain under \`.sneakoscope/missions/<ultra-* or route mission>/ultra-search/\`: intent.json, axes.json, query-variants.json, provider-plan.json, source-ledger.json, lead-ledger.json, claim-ledger.json, synthesis.md, ultra-search-proof.json, ultra-search-gate.json, and ultra-search-result.json. Do not turn weak discovery into supported claims; finish with an Honest Mode summary of verified sources, blockers, and unverified external coverage.\n`,
1072
+ 'ultra-search': `---\nname: ultra-search\ndescription: Compatibility alias for $Insane-Search/$InsaneSearch provider-independent source intelligence.\n---\n\nUse when the user invokes legacy $Ultra-Search/$UltraSearch or asks for InsaneSearch source intelligence, source acquisition, X-search-style collection, URL acquisition, source normalization, claim ledgers, or citation proof. Prefer \`sks ultra-search doctor --json\` for readiness and \`sks ultra-search run "<query>" --mode balanced --json\` for provider-independent source proof; use \`sks ultra-search x "<query>" --json\` for X-search intent and \`sks ultra-search fetch "<url>" --json\` for URL acquisition. Context7 is required only when the query depends on current package/API/framework/MCP/generated documentation behavior. xAI/Grok credentials are optional and must not be required for route readiness. Evidence/artifacts live under \`.sneakoscope/missions/<ultra-* or route mission>/ultra-search/\`: intent.json, axes.json, query-variants.json, provider-plan.json, source-ledger.json, lead-ledger.json, claim-ledger.json, synthesis.md, ultra-search-proof.json, ultra-search-gate.json, and ultra-search-result.json. Do not turn weak discovery into supported claims; finish with an Honest Mode summary of verified sources, blockers, and unverified external coverage.\n`,
1072
1073
  'search-visibility-core': `---\nname: search-visibility-core\ndescription: Shared kernel for seo-geo-optimizer audit, plan, explicit apply, rollback, verification, gates, and Completion Proof.\n---\n\nPurpose: keep Search Engine Optimization and Generative Engine Optimization on one typed search-visibility kernel instead of duplicate implementations. Use when $SEO-GEO-OPTIMIZER or \`sks seo-geo-optimizer\` is selected. Workflow: doctor detects package/static/Next evidence; audit writes source-backed inventory and findings; plan compiles safe mutation operations; apply requires explicit \`--apply\`; verify separates source, build, HTTP, browser, production, and measured outcome; rollback only reverses mission-owned operations. Safety: default read-only, never overwrite unmanaged robots.txt, sitemap, llms.txt, metadata, or structured data; do not hard-code customer routes; do not invent prices, reviews, availability, rankings, traffic, or AI citation outcomes. Evidence/artifacts: search-visibility/intake.json, adapter-detection.json, site-inventory.json, route-graph.json, robots-policy.json, structured-data-ledger.json, mutation-plan.json, mutation-journal.jsonl, rollback-manifest.json, verification-report.json, route gate, and completion-proof.json. Failure/recovery: unsupported frameworks stay audit/plan-only; missing production/browser/Search Console evidence remains unverified, not fabricated. CLI entrypoint: \`sks seo-geo-optimizer ... --mode seo|geo\`.\n`,
1073
1074
  'seo-geo-optimizer': `---\nname: seo-geo-optimizer\ndescription: Unified $SEO-GEO-OPTIMIZER route for Search Engine Optimization and Generative Engine Optimization.\n---\n\nPurpose: use one route name for SEO and GEO work while keeping the internal search-visibility mode explicit. Use when: the user asks for SEO audit/fix/verification, package/npm/GitHub search visibility, canonical, sitemap, robots.txt, hreflang, metadata, structured data, AI answer visibility, LLM citation readiness, answerability, entity/claim provenance, crawler policy, OAI-SearchBot/GPTBot/ChatGPT-User, Claude-SearchBot/ClaudeBot/Claude-User, or optional llms.txt planning. GEO means Generative Engine Optimization, not geolocation, GeoIP, maps, CDN geography, location permission, or regional redirect bugs. Workflow: run \`sks seo-geo-optimizer doctor --mode seo|geo\`, then audit, plan, explicit apply, verify, status, and rollback. Use \`--mode seo\` for technical/package search optimization and \`--mode geo\` for entity facts, claim evidence, answerability, crawler policy, and optional llms.txt. Safety: audit and plan must not mutate source; apply checks base hashes, ownership, scope, protected paths, rollback manifest, and post-verify. AI crawler policy must split search, training, user-directed retrieval, and ads/other; never use one allow_ai toggle and never auto-allow training crawlers. Evidence/artifacts: site-inventory.json, route-graph.json, seo-findings.json or geo-findings.json, entity-facts.json, claim-evidence-ledger.json, answerability-report.json, ai-crawler-policy.json, llms-txt-plan.json, mutation-plan.json, verification-report.json, seo-gate.json or geo-gate.json, completion-proof.json. Failure/recovery: unsupported frameworks stay plan-only; browser/production/Search Console/analytics outcomes are marked unverified when not actually run. Forbidden claims: no ranking, indexing, traffic lift, rich-result, answer inclusion, or AI citation guarantee; no keyword stuffing, doorway pages, fake reviews, fake prices, fake availability, fake shipping, fake awards, hidden AI-only text, or scaled spam. CLI entrypoint: \`sks seo-geo-optimizer doctor|audit|plan|apply|verify|status|rollback|fixture --mode seo|geo\`.\n`,
1074
1075
  'reflection': `---\nname: reflection\ndescription: Post-route self-review for full SKS routes that records real misses, gaps, and corrective lessons into TriWiki memory.\n---\n\nUse after full route work/tests and before final. DFix, Answer, Help, Wiki, SKS discovery are exempt. Do not invent faults. Write reflection.md; append real lessons to ${REFLECTION_MEMORY_PATH}; refresh/pack, validate context-pack.json, pass reflection-gate.json.\n\n${reflectionInstructionText()}\n`,
@@ -17,10 +17,10 @@ export async function prepareMadDbMission(input) {
17
17
  const blockers = [...target.blockers];
18
18
  let profile;
19
19
  if (!target.project_ref) {
20
- profile = await createMadDbRuntimeProfile({ root: input.root, missionId: id, cycleId, projectRef: 'missing-project-ref', runtimeSessionId });
20
+ profile = await createMadDbRuntimeProfile({ root: input.root, missionId: id, cycleId, projectRef: 'missing-project-ref', runtimeSessionId, mcpUrl: target.mcp_url });
21
21
  }
22
22
  else {
23
- profile = await createMadDbRuntimeProfile({ root: input.root, missionId: id, cycleId, projectRef: target.project_ref, runtimeSessionId });
23
+ profile = await createMadDbRuntimeProfile({ root: input.root, missionId: id, cycleId, projectRef: target.project_ref, runtimeSessionId, mcpUrl: target.mcp_url });
24
24
  }
25
25
  const capability = await createMadDbCapability(input.root, {
26
26
  missionId: id,
@@ -54,7 +54,7 @@ export async function prepareMadDbMission(input) {
54
54
  command: '$MAD-DB',
55
55
  mode: 'MADDB',
56
56
  task: input.task,
57
- target: { ...target, project_ref: target.project_ref ? `<hash:${target.project_ref_hash}>` : null },
57
+ target: redactTarget(target),
58
58
  capability_file: 'mad-db-capability.json',
59
59
  runtime_profile_manifest: 'mad-db/runtime/runtime-profile-manifest.json',
60
60
  tool_inventory: toolInventory ? 'mad-db/runtime/tool-inventory.json' : null
@@ -118,6 +118,8 @@ export async function runMadDbCycle(input) {
118
118
  await writeJsonAtomic(path.join(missionDir(input.root, prepared.mission_id), 'mad-db', 'runtime', 'tool-inventory.json'), inventory);
119
119
  if (!inventory.ok) {
120
120
  blockers.push('mad_db_execute_sql_or_apply_migration_unavailable');
121
+ if (inventory.error_kind)
122
+ blockers.push(inventory.error_kind);
121
123
  throw new Error('mad_db_tool_inventory_failed');
122
124
  }
123
125
  await activateMadDbCapability(input.root, prepared.mission_id);
@@ -154,6 +156,8 @@ export async function runMadDbCycle(input) {
154
156
  });
155
157
  if (!execution.ok)
156
158
  blockers.push('mad_db_tool_execution_failed');
159
+ if (!execution.ok && execution.error_kind)
160
+ blockers.push(execution.error_kind);
157
161
  if (execution.ok && input.verifySql) {
158
162
  const verifyStart = Date.now();
159
163
  readBack = await runReadBackChecks({
@@ -227,10 +231,7 @@ async function clearMadDbCurrentState(root, missionId, ok, restoration) {
227
231
  function redactCycleResult(result) {
228
232
  return {
229
233
  ...result,
230
- target: {
231
- ...result.target,
232
- project_ref: result.target.project_ref ? '<redacted>' : null
233
- }
234
+ target: redactTarget(result.target)
234
235
  };
235
236
  }
236
237
  async function resolveSqlInput(input) {
@@ -252,13 +253,21 @@ async function recreateProfileFromPrepared(root, prepared) {
252
253
  profile_path: prepared.runtime_profile.profile_path,
253
254
  profile_sha256: prepared.runtime_profile.profile_sha256,
254
255
  server_url_redacted: prepared.runtime_profile.server_url_redacted,
255
- server_url: `https://mcp.supabase.com/mcp?project_ref=${encodeURIComponent(projectRef)}&features=database`,
256
+ server_url: prepared.target.mcp_url || `https://mcp.supabase.com/mcp?project_ref=${encodeURIComponent(projectRef)}&features=database`,
257
+ server_url_source: prepared.target.mcp_url ? 'explicit_mcp_url' : 'generated_project_ref',
256
258
  features: ['database'],
257
259
  write_capable: true,
258
260
  normal_config_hash_before: prepared.runtime_profile.normal_config_hash_before,
259
261
  created_at: prepared.runtime_profile.created_at
260
262
  };
261
263
  }
264
+ function redactTarget(target) {
265
+ return {
266
+ ...target,
267
+ project_ref: target.project_ref ? `<hash:${target.project_ref_hash}>` : null,
268
+ mcp_url: target.mcp_url ? '<redacted>' : null
269
+ };
270
+ }
262
271
  export async function madDbRouteIdentityProof(root, missionId) {
263
272
  const capability = await readMadDbCapability(root, missionId);
264
273
  const state = await readJson(path.join(root, '.sneakoscope', 'state', 'current.json'), {});
@@ -33,10 +33,15 @@ export class MadDbMcpExecutor {
33
33
  execute_sql_available: hasTool(names, 'execute_sql'),
34
34
  apply_migration_available: hasTool(names, 'apply_migration'),
35
35
  duration_ms: Math.round(performance.now() - started),
36
- error_digest: null
36
+ error_digest: null,
37
+ error_summary: null,
38
+ error_kind: null,
39
+ retry_guidance: null
37
40
  };
38
41
  }
39
42
  catch (err) {
43
+ const summary = summarizeMadDbError(err);
44
+ const kind = classifyMadDbError(summary);
40
45
  return {
41
46
  schema: 'sks.mad-db-tool-inventory.v1',
42
47
  checked_at: nowIso(),
@@ -45,7 +50,10 @@ export class MadDbMcpExecutor {
45
50
  execute_sql_available: false,
46
51
  apply_migration_available: false,
47
52
  duration_ms: Math.round(performance.now() - started),
48
- error_digest: sha256(redactError(err)).slice(0, 32)
53
+ error_digest: sha256(summary).slice(0, 32),
54
+ error_summary: summary,
55
+ error_kind: kind,
56
+ retry_guidance: madDbRetryGuidance(kind)
49
57
  };
50
58
  }
51
59
  }
@@ -77,21 +85,29 @@ export class MadDbMcpExecutor {
77
85
  result_digest: sha256(JSON.stringify(redactToolResult(result))).slice(0, 32),
78
86
  row_count: extractRowCount(result),
79
87
  is_error: result.isError === true,
80
- duration_ms: Math.round(performance.now() - started)
88
+ duration_ms: Math.round(performance.now() - started),
89
+ error_summary: result.isError === true ? summarizeMadDbToolError(result) : null,
90
+ error_kind: result.isError === true ? classifyMadDbError(summarizeMadDbToolError(result)) : null,
91
+ retry_guidance: result.isError === true ? madDbRetryGuidance(classifyMadDbError(summarizeMadDbToolError(result))) : null
81
92
  };
82
93
  }
83
94
  catch (err) {
84
95
  lastError = err;
85
96
  }
86
97
  }
98
+ const summary = summarizeMadDbError(lastError);
99
+ const kind = classifyMadDbError(summary);
87
100
  return {
88
101
  schema: 'sks.mad-db-tool-result.v1',
89
102
  ok: false,
90
103
  tool_name: tool,
91
- result_digest: sha256(redactError(lastError)).slice(0, 32),
104
+ result_digest: sha256(summary).slice(0, 32),
92
105
  row_count: null,
93
106
  is_error: true,
94
- duration_ms: 0
107
+ duration_ms: 0,
108
+ error_summary: summary,
109
+ error_kind: kind,
110
+ retry_guidance: madDbRetryGuidance(kind)
95
111
  };
96
112
  }
97
113
  }
@@ -112,9 +128,45 @@ function authHeaders() {
112
128
  return undefined;
113
129
  return { Authorization: `Bearer ${token}` };
114
130
  }
115
- function redactError(err) {
131
+ export function summarizeMadDbError(err) {
116
132
  const text = err instanceof Error ? `${err.name}:${err.message}` : String(err);
117
- return text.replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, '$1<redacted>').replace(/(token|password|secret|apikey)=([^&\s]+)/gi, '$1=<redacted>');
133
+ return redactErrorText(text).slice(0, 1200);
134
+ }
135
+ function summarizeMadDbToolError(result) {
136
+ return redactErrorText(JSON.stringify(redactToolResult(result))).slice(0, 1200);
137
+ }
138
+ function redactErrorText(text) {
139
+ return String(text || '')
140
+ .replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, '$1<redacted>')
141
+ .replace(/(token|password|secret|apikey)=([^&\s]+)/gi, '$1=<redacted>')
142
+ .replace(/(postgres(?:ql)?:\/\/[^:\s/]+:)([^@\s]+)(@)/gi, '$1<redacted>$3');
143
+ }
144
+ export function classifyMadDbError(summary) {
145
+ const text = String(summary || '').toLowerCase();
146
+ if (/read[_ -]?only|read only|permission denied.+read/i.test(text))
147
+ return 'supabase_mcp_read_only_transport';
148
+ if (/timeout|timed out|etimedout|i\/o timeout|aborterror|deadline/i.test(text))
149
+ return 'supabase_sql_plane_timeout';
150
+ if (/connection terminated|econnreset|unexpected eof|socket hang up|server closed/i.test(text))
151
+ return 'supabase_sql_plane_connection_interrupted';
152
+ if (/sasl|password authentication failed|invalid login|authentication failed|sqlstate 28p01/i.test(text))
153
+ return 'supabase_sql_plane_auth_failed';
154
+ if (/fetch failed|enotfound|econnrefused|network|dns/i.test(text))
155
+ return 'supabase_mcp_transport_unreachable';
156
+ return 'supabase_mcp_tool_error';
157
+ }
158
+ export function madDbRetryGuidance(kind) {
159
+ if (kind === 'supabase_mcp_read_only_transport')
160
+ return 'The active MadDB cycle must use the mission-local write-capable MCP URL. Pass --mcp-url or SKS_MAD_DB_MCP_URL if a project-local read-only Supabase MCP config is shadowing the intended transport.';
161
+ if (kind === 'supabase_sql_plane_timeout')
162
+ return 'Retry with an explicit Supabase pooler/session/direct MCP transport if available; Supabase CLI docs also support --db-url for CLI migration paths when pooler/direct connectivity differs.';
163
+ if (kind === 'supabase_sql_plane_connection_interrupted')
164
+ return 'Treat this as a SQL-plane connectivity interruption, not a DB safety denial. Retry after checking Supabase project health, pooler/direct connection mode, and network reachability.';
165
+ if (kind === 'supabase_sql_plane_auth_failed')
166
+ return 'Check SUPABASE_ACCESS_TOKEN and the project/database credentials used by the Supabase MCP server; do not retry destructive SQL until auth is corrected.';
167
+ if (kind === 'supabase_mcp_transport_unreachable')
168
+ return 'Check access to https://mcp.supabase.com/mcp or pass a trusted local MCP URL with --mcp-url for the active MadDB cycle.';
169
+ return 'Inspect error_summary in mad-db-result.json; the failure occurred after MadDB capability gating, so do not report it as a safety-gate denial.';
118
170
  }
119
171
  function redactToolResult(value) {
120
172
  if (value == null)
@@ -1,7 +1,7 @@
1
1
  import { isMadDbCapabilityActive, readMadDbCapability } from './mad-db-capability.js';
2
2
  import { activeMadDbAllowsSqlPlane, isMadDbControlPlaneDeniedTool, madDbOperationClassesFromClassification } from './mad-db-policy.js';
3
3
  import { readJson, sha256 } from '../fsx.js';
4
- import { stateFile } from '../mission.js';
4
+ import { missionsDir, stateFile } from '../mission.js';
5
5
  export const MAD_DB_POLICY_DECISION_SCHEMA = 'sks.mad-db-policy-decision.v2';
6
6
  export async function resolveMadDbMutationPolicy(root, state = {}, classification = {}, explicitCapability) {
7
7
  const primary = await resolveMadDbMutationPolicyForState(root, state, classification, explicitCapability);
@@ -18,8 +18,45 @@ export async function resolveMadDbMutationPolicy(root, state = {}, classificatio
18
18
  };
19
19
  }
20
20
  }
21
+ const latestCapability = await findLatestActiveMadDbCapability(root);
22
+ if (latestCapability) {
23
+ const fallback = await resolveMadDbMutationPolicyForState(root, {
24
+ mad_db_active: true,
25
+ mad_db_capability_mission_id: latestCapability.mission_id
26
+ }, classification, latestCapability);
27
+ if (fallback.allowed === true) {
28
+ return {
29
+ ...fallback,
30
+ state_source: 'latest_active_mad_db_capability',
31
+ reasons: [...fallback.reasons, 'mad_db_latest_active_capability_used']
32
+ };
33
+ }
34
+ }
21
35
  return primary;
22
36
  }
37
+ async function findLatestActiveMadDbCapability(root) {
38
+ const fs = await import('node:fs/promises');
39
+ const entries = await fs.readdir(missionsDir(root), { withFileTypes: true }).catch(() => []);
40
+ const candidates = [];
41
+ for (const entry of entries) {
42
+ if (!entry.isDirectory() || !entry.name.startsWith('M-'))
43
+ continue;
44
+ const capability = await readMadDbCapability(root, entry.name).catch(() => null);
45
+ if (!capability || !isMadDbCapabilityActive(capability))
46
+ continue;
47
+ const issuedMs = Date.parse(capability.issued_at || '');
48
+ const expiresMs = Date.parse(capability.expires_at || '');
49
+ candidates.push({
50
+ capability,
51
+ issuedMs: Number.isFinite(issuedMs) ? issuedMs : 0,
52
+ expiresMs: Number.isFinite(expiresMs) ? expiresMs : 0
53
+ });
54
+ }
55
+ candidates.sort((a, b) => ((a.issuedMs - b.issuedMs)
56
+ || (a.expiresMs - b.expiresMs)
57
+ || a.capability.mission_id.localeCompare(b.capability.mission_id)));
58
+ return candidates.at(-1)?.capability || null;
59
+ }
23
60
  async function resolveMadDbMutationPolicyForState(root, state = {}, classification = {}, explicitCapability) {
24
61
  const missionId = explicitCapability?.mission_id || state?.mad_db_capability_mission_id || state?.mission_id;
25
62
  if (!missionId)
@@ -168,7 +168,7 @@ Use only when the operator explicitly invokes $MAD-DB/$mad-db or ${commandPrefix
168
168
 
169
169
  Keep normal Supabase MCP configuration read-only. MadDB must create a mission-local ephemeral write-capable Supabase MCP profile bound to capability v2, project_ref, root, mission, thread/session, intent, runtime profile hash, TTL, and SQL-plane operation classes. Verify execute_sql and apply_migration availability before claiming readiness. Require actual tool results plus independent read-back verification before claiming success. Close/revoke the capability and runtime profile in finally and prove read-only restoration.
170
170
 
171
- Still deny Supabase account/project/billing/credential control-plane actions, credential exfiltration, unrelated storage/edge-function/admin changes, persistent security weakening, and unrequested fallback implementation. Pair with db-safety-guard, Context7 evidence for MCP/API docs, route-local reflection, and Honest Mode.`;
171
+ Still deny Supabase account/project/billing/credential control-plane actions, credential exfiltration, unrelated non-database admin changes, and unrequested fallback implementation. Do not add prompt-only SQL deny lists inside active MadDB; capability binding, SQL-plane scope, operation ledgering, and read-back verification are the approval boundary. Pair with db-safety-guard, Context7 evidence for MCP/API docs, route-local reflection, and Honest Mode.`;
172
172
  }
173
173
  export function dbSafetyGuardSkillText() {
174
174
  return `---
@@ -177,10 +177,11 @@ description: Enforce Sneakoscope Codex database safety before using SQL, Supabas
177
177
  ---
178
178
 
179
179
  Rules:
180
- - Default mode is read-only: do not run DROP, TRUNCATE, mass DELETE/UPDATE, db reset, db push, project deletion, branch reset/merge/delete, or RLS-disabling operations.
180
+ - Default non-MadDB mode is read-only and routes writes/destructive SQL to the DB safety gate.
181
181
  - Supabase MCP must be read-only and project-scoped by default.
182
182
  - Live execute_sql writes are blocked unless a bound active MadDB capability v2 is present.
183
- - Active MadDB is the explicit exception: SQL-plane mutations requested by $MAD-DB or sks mad-db run|exec|apply-migration are allowed and must be executed with read-back verification.
183
+ - Active MadDB is the explicit exception: SQL-plane mutations requested by $MAD-DB or sks mad-db run|exec|apply-migration are allowed, including DROP, DELETE, TRUNCATE, RLS/policy changes, and execute_sql/apply_migration, and must be executed with read-back verification.
184
+ - Default read-only restrictions do not apply to SQL-plane work while the active MadDB capability v2 is bound.
184
185
  - Supabase project/account/billing/credential control-plane actions remain denied even in MadDB.
185
186
  - If no active bound MadDB cycle exists, fall back to read-only only.`;
186
187
  }
@@ -5,7 +5,7 @@ import { missionDir } from '../mission.js';
5
5
  export async function createMadDbRuntimeProfile(input) {
6
6
  const dir = path.join(missionDir(input.root, input.missionId), 'mad-db', 'runtime');
7
7
  await fs.mkdir(dir, { recursive: true });
8
- const url = madDbMcpUrl(input.projectRef);
8
+ const url = madDbMcpUrl(input.projectRef, input.mcpUrl);
9
9
  const text = [
10
10
  '[mcp_servers.supabase_mad_db]',
11
11
  `url = "${url}"`,
@@ -26,6 +26,7 @@ export async function createMadDbRuntimeProfile(input) {
26
26
  profile_sha256: profileHash,
27
27
  server_url_redacted: redactSupabaseUrl(url),
28
28
  server_url: url,
29
+ server_url_source: input.mcpUrl ? 'explicit_mcp_url' : 'generated_project_ref',
29
30
  features: ['database'],
30
31
  write_capable: true,
31
32
  normal_config_hash_before: normalHash,
@@ -74,7 +75,15 @@ export function redactedRuntimeProfile(profile) {
74
75
  const { server_url: _serverUrl, ...rest } = profile;
75
76
  return rest;
76
77
  }
77
- export function madDbMcpUrl(projectRef) {
78
+ export function madDbMcpUrl(projectRef, explicitUrl) {
79
+ if (explicitUrl) {
80
+ const parsed = new URL(explicitUrl);
81
+ if (projectRef && !parsed.searchParams.get('project_ref'))
82
+ parsed.searchParams.set('project_ref', projectRef);
83
+ parsed.searchParams.set('features', 'database');
84
+ parsed.searchParams.delete('read_only');
85
+ return parsed.toString();
86
+ }
78
87
  const params = new URLSearchParams();
79
88
  params.set('project_ref', projectRef);
80
89
  params.set('features', 'database');
@@ -1,10 +1,13 @@
1
1
  import path from 'node:path';
2
2
  import { exists, readText, sha256 } from '../fsx.js';
3
+ import { redactSupabaseUrl } from './mad-db-runtime-profile.js';
3
4
  export async function resolveMadDbTarget(root, input = {}) {
4
5
  const args = input.args || [];
5
6
  const explicit = input.projectRef || readOption(args, '--project-ref', '') || process.env.SKS_MAD_DB_PROJECT_REF || process.env.SKS_MAD_DB_E2E_PROJECT_REF || '';
6
7
  const candidates = explicit ? [explicit] : await projectRefCandidates(root);
7
8
  const projectRef = explicit || (candidates.length === 1 ? candidates[0] || '' : '');
9
+ const explicitMcpUrl = readOption(args, '--mcp-url', '') || process.env.SKS_MAD_DB_MCP_URL || process.env.SKS_MAD_DB_SUPABASE_MCP_URL || '';
10
+ const normalizedMcp = normalizeExplicitMcpUrl(explicitMcpUrl, projectRef || null);
8
11
  const target = normalizeTarget(input.target || readOption(args, '--target', '') || process.env.SKS_MAD_DB_TARGET || process.env.SKS_MAD_DB_E2E_TARGET || 'production');
9
12
  const allowedSchemas = input.allowedSchemas?.length
10
13
  ? input.allowedSchemas
@@ -12,10 +15,16 @@ export async function resolveMadDbTarget(root, input = {}) {
12
15
  const blockers = [];
13
16
  if (!projectRef)
14
17
  blockers.push(candidates.length > 1 ? 'mad_db_project_ref_ambiguous' : 'mad_db_project_ref_missing');
18
+ if (normalizedMcp.blocker)
19
+ blockers.push(normalizedMcp.blocker);
15
20
  return {
16
21
  schema: 'sks.mad-db-target.v1',
17
22
  project_ref: projectRef || null,
18
23
  project_ref_hash: projectRef ? sha256(projectRef).slice(0, 16) : null,
24
+ mcp_url: normalizedMcp.url,
25
+ mcp_url_hash: normalizedMcp.url ? sha256(normalizedMcp.url).slice(0, 16) : null,
26
+ mcp_url_redacted: normalizedMcp.url ? redactSupabaseUrl(normalizedMcp.url) : null,
27
+ mcp_url_source: normalizedMcp.url ? 'explicit_or_environment' : 'default_generated',
19
28
  target_environment: target,
20
29
  allowed_schemas: allowedSchemas.length ? allowedSchemas : ['public'],
21
30
  source: explicit ? 'explicit_or_environment' : candidates.length === 1 ? 'managed_config_single_candidate' : 'unresolved',
@@ -23,6 +32,28 @@ export async function resolveMadDbTarget(root, input = {}) {
23
32
  candidates: candidates.map((candidate) => `${sha256(candidate).slice(0, 8)}:${candidate.slice(0, 2)}...`)
24
33
  };
25
34
  }
35
+ function normalizeExplicitMcpUrl(value, projectRef) {
36
+ if (!value)
37
+ return { url: null, blocker: null };
38
+ let parsed;
39
+ try {
40
+ parsed = new URL(value);
41
+ }
42
+ catch {
43
+ return { url: null, blocker: 'mad_db_mcp_url_invalid' };
44
+ }
45
+ const isSupabaseRemote = parsed.protocol === 'https:' && parsed.hostname === 'mcp.supabase.com' && parsed.pathname === '/mcp';
46
+ const isLocalMcp = parsed.protocol === 'http:' && /^localhost$|^127\.0\.0\.1$/.test(parsed.hostname) && parsed.pathname.endsWith('/mcp');
47
+ if (!isSupabaseRemote && !isLocalMcp)
48
+ return { url: null, blocker: 'mad_db_mcp_url_untrusted_host' };
49
+ if (parsed.searchParams.get('read_only') === 'true')
50
+ return { url: null, blocker: 'mad_db_mcp_url_read_only_conflict' };
51
+ if (projectRef && !parsed.searchParams.get('project_ref'))
52
+ parsed.searchParams.set('project_ref', projectRef);
53
+ parsed.searchParams.set('features', 'database');
54
+ parsed.searchParams.delete('read_only');
55
+ return { url: parsed.toString(), blocker: null };
56
+ }
26
57
  export async function projectRootHash(root) {
27
58
  return sha256(path.resolve(root)).slice(0, 24);
28
59
  }
@@ -17,7 +17,7 @@ export const RELEASE_1_17_GATE_SNAPSHOT = Object.freeze([
17
17
  'release:readiness'
18
18
  ]);
19
19
  export const RELEASE_1_18_REQUIRED_GATES = Object.freeze([
20
- 'ultra-search:provider-interface',
20
+ 'insane-search:provider-interface',
21
21
  'source-intelligence:policy',
22
22
  'source-intelligence:all-modes',
23
23
  'codex-web:adapter',
@@ -32,7 +32,7 @@ export const FROM_CHAT_IMG_CHECKLIST_ARTIFACT = 'from-chat-img-checklist.md';
32
32
  export const FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT = 'from-chat-img-temp-triwiki.json';
33
33
  export const FROM_CHAT_IMG_QA_LOOP_ARTIFACT = 'from-chat-img-qa-loop.json';
34
34
  export const FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS = 5;
35
- export const USAGE_TOPICS = 'install|setup|bootstrap|root|deps|zellij|tmux|auto-review|team|qa-loop|ppt|image-ux-review|computer-use|goal|fast-mode|research|seo-geo-optimizer|db|git|codex|codex-app|codex-native|hooks|features|all-features|dfix|commit|commit-and-push|design|imagegen|dollar|context7|ultra-search|xai|pipeline|reasoning|guard|conflicts|versioning|eval|harness|hproof|gx|wiki|wrongness|code-structure|proof-field|skill-dream|rust';
35
+ export const USAGE_TOPICS = 'install|setup|bootstrap|root|deps|zellij|tmux|auto-review|team|qa-loop|ppt|image-ux-review|computer-use|goal|fast-mode|research|seo-geo-optimizer|db|git|codex|codex-app|codex-native|hooks|features|all-features|dfix|commit|commit-and-push|design|imagegen|dollar|context7|insane-search|ultra-search|xai|pipeline|reasoning|guard|conflicts|versioning|eval|harness|hproof|gx|wiki|wrongness|code-structure|proof-field|skill-dream|rust';
36
36
  export const CODEX_COMPUTER_USE_EVIDENCE_SOURCE = 'codex_computer_use';
37
37
  export const CODEX_IN_APP_BROWSER_EVIDENCE_SOURCE = 'codex_in_app_browser';
38
38
  export const CODEX_CHROME_EXTENSION_EVIDENCE_SOURCE = 'codex_chrome_extension';
@@ -541,19 +541,19 @@ export const ROUTES = [
541
541
  examples: ['$Research investigate this idea']
542
542
  },
543
543
  {
544
- id: 'UltraSearch',
545
- command: '$Ultra-Search',
544
+ id: 'InsaneSearch',
545
+ command: '$Insane-Search',
546
546
  mode: 'ULTRA_SEARCH',
547
547
  route: 'provider-independent source intelligence',
548
- description: 'Run UltraSearch source acquisition, source normalization, claim/proof ledgers, and provider-independent citation evidence without requiring xAI/Grok.',
549
- requiredSkills: ['ultra-search', 'pipeline-runner', 'context7-docs', 'honest-mode'],
550
- dollarAliases: ['$UltraSearch'],
548
+ description: 'Run InsaneSearch source acquisition, source normalization, claim/proof ledgers, and provider-independent citation evidence without requiring xAI/Grok.',
549
+ requiredSkills: ['insane-search', 'pipeline-runner', 'context7-docs', 'honest-mode'],
550
+ dollarAliases: ['$InsaneSearch', '$Ultra-Search', '$UltraSearch'],
551
551
  lifecycle: ['source_intent', 'query_variants', 'provider_plan', 'source_ledgers', 'claim_ledgers', 'ultra_search_gate', 'honest_mode'],
552
552
  context7Policy: 'if_external_docs',
553
553
  reasoningPolicy: 'high',
554
554
  stopGate: 'ultra-search/ultra-search-gate.json',
555
- cliEntrypoint: 'sks ultra-search doctor|run|x|fetch|status|inspect|sources|claims|cache|bench|migrate-xai',
556
- examples: ['$Ultra-Search run "current package release notes"', '$UltraSearch x "site:x.com product launch"']
555
+ cliEntrypoint: 'sks insane-search doctor|run|x|fetch|status|inspect|sources|claims|cache|bench|migrate-xai',
556
+ examples: ['$Insane-Search run "current package release notes"', '$InsaneSearch x "site:x.com product launch"']
557
557
  },
558
558
  {
559
559
  id: 'SEOGEOOptimizer',
@@ -721,8 +721,9 @@ export const COMMAND_CATALOG = [
721
721
  { name: 'image-ux-review', usage: 'sks ux-review run --image <path> --fix --json | sks image-ux-review status <mission-id|latest> [--json]', description: 'Run or inspect $Image-UX-Review gpt-image-2/imagegen annotated UI/UX review artifacts, issue ledgers, safe fix loops, recapture, and proof gates.' },
722
722
  { name: 'computer-use', usage: 'sks computer-use import|status|smoke|require ... [--json]', description: 'Record native Mac/non-web Computer Use visual evidence while keeping web verification on the Chrome Extension path.' },
723
723
  { name: 'context7', usage: 'sks context7 check|setup|tools|resolve|docs|evidence ...', description: 'Check, configure, and call the local Context7 MCP requirement.' },
724
- { name: 'ultra-search', usage: 'sks ultra-search doctor|run|x|fetch|status|inspect|sources|claims|cache|bench|migrate-xai', description: 'Run provider-independent UltraSearch source intelligence.' },
725
- { name: 'xai', usage: 'sks xai check|status|docs', description: 'Deprecated compatibility notice; use sks ultra-search.' },
724
+ { name: 'insane-search', usage: 'sks insane-search doctor|run|x|fetch|status|inspect|sources|claims|cache|bench|migrate-xai', description: 'Run provider-independent InsaneSearch source intelligence.' },
725
+ { name: 'ultra-search', usage: 'compatibility alias for sks insane-search', description: 'Deprecated compatibility alias; use sks insane-search.' },
726
+ { name: 'xai', usage: 'sks xai check|status|docs', description: 'Deprecated compatibility notice; use sks insane-search.' },
726
727
  { name: 'recallpulse', usage: 'sks recallpulse run|status|eval|governance|checklist <mission-id|latest>', description: 'Run report-only RecallPulse active recall, durable status, proof capsule, evidence envelope, and governance checks.' },
727
728
  { name: 'pipeline', usage: 'sks pipeline status|resume|plan|answer ...', description: 'Inspect the active skill-first route, materialized execution plan, ambiguity gates, and completion gates.' },
728
729
  { name: 'guard', usage: 'sks guard check [--json]', description: 'Check SKS harness self-protection lock, fingerprints, and source-repo exception state.' },
@@ -899,7 +900,7 @@ export function looksLikeGenerativeEngineOptimizationRequest(prompt = '') {
899
900
  }
900
901
  export function looksLikeUltraSearchRequest(prompt = '') {
901
902
  const text = String(prompt || '');
902
- return /\b(?:UltraSearch|Ultra-Search|ultra\s*search|source\s+intelligence|provider-independent\s+source|source\s+acquisition|citation\s+proof|x-search)\b|울트라\s*서치|소스\s*인텔리전스/i.test(text);
903
+ return /\b(?:InsaneSearch|Insane-Search|insane\s*search|UltraSearch|Ultra-Search|ultra\s*search|source\s+intelligence|provider-independent\s+source|source\s+acquisition|citation\s+proof|x-search)\b|인세인\s*서치|울트라\s*서치|소스\s*인텔리전스/i.test(text);
903
904
  }
904
905
  export function routePrompt(prompt) {
905
906
  const text = stripVisibleDecisionAnswerBlocks(prompt);
@@ -949,7 +950,7 @@ export function routePrompt(prompt) {
949
950
  if (/\b(qa[-\s]?loop|qaloop|e2e\s+qa|qa\s+e2e)\b/i.test(text))
950
951
  return routeById('QALoop');
951
952
  if (looksLikeUltraSearchRequest(text) && !looksLikeCodeChangingWork(text) && !looksLikeAnswerOnlyRequest(text))
952
- return routeById('UltraSearch');
953
+ return routeById('InsaneSearch');
953
954
  if (looksLikeGenerativeEngineOptimizationRequest(text))
954
955
  return routeById('SEOGEOOptimizer');
955
956
  if (looksLikeSeoRequest(text))
@@ -1051,7 +1052,7 @@ export function routeRequiresSubagents(route, prompt = '') {
1051
1052
  return false;
1052
1053
  if (route.id === 'ImageUXReview')
1053
1054
  return false;
1054
- if (route.id === 'UltraSearch')
1055
+ if (route.id === 'InsaneSearch')
1055
1056
  return false;
1056
1057
  if (route.id === 'SEOGEOOptimizer')
1057
1058
  return false;
@@ -1137,7 +1138,7 @@ export function routeReasoning(route, prompt = '') {
1137
1138
  return teamRouteReasoning(text);
1138
1139
  if (route?.id === 'Research' || route?.id === 'AutoResearch')
1139
1140
  return reasoning('xhigh', 'research_or_experiment_route');
1140
- if (route?.id === 'UltraSearch')
1141
+ if (route?.id === 'InsaneSearch')
1141
1142
  return reasoning('high', 'source_intelligence_route');
1142
1143
  if (route?.id === 'SEOGEOOptimizer')
1143
1144
  return reasoning('high', 'search_visibility_route');
@@ -389,7 +389,7 @@ export async function detectEffectiveSksVersion(options = {}) {
389
389
  const pathCandidate = candidates.find((candidate) => candidate.source.startsWith('PATH:'))?.version || null;
390
390
  const npmGlobalCandidate = candidates.find((candidate) => candidate.source.startsWith('npm-global:'))?.version || null;
391
391
  const packageRootCandidate = candidates.find((candidate) => candidate.source === 'packageRoot:package.json')?.version || null;
392
- const current = highestPackageVersion(candidates.map((candidate) => candidate.version));
392
+ const current = effectiveInstalledVersion(candidates);
393
393
  return {
394
394
  current,
395
395
  runtime_current: PACKAGE_VERSION,
@@ -533,6 +533,16 @@ function updateNowError(install, newBinary, newVersionDoctor, migrationCurrent)
533
533
  return 'project update migration receipt was not current';
534
534
  return 'update failed';
535
535
  }
536
+ function effectiveInstalledVersion(candidates) {
537
+ const firstBySource = (source) => candidates.find((candidate) => candidate.source === source)?.version || null;
538
+ const firstByPrefix = (prefix) => candidates.find((candidate) => candidate.source.startsWith(prefix))?.version || null;
539
+ return firstBySource('env:SKS_INSTALLED_SKS_VERSION')
540
+ || firstByPrefix('npm-global:')
541
+ || firstByPrefix('PATH:')
542
+ || firstBySource('runtime')
543
+ || firstBySource('packageRoot:package.json')
544
+ || highestPackageVersion(candidates.map((candidate) => candidate.version));
545
+ }
536
546
  function highestPackageVersion(versions) {
537
547
  return versions
538
548
  .filter((version) => typeof version === 'string' && version.length > 0)
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '4.6.1';
1
+ export const PACKAGE_VERSION = '4.6.3';
2
2
  //# sourceMappingURL=version.js.map
@@ -46,5 +46,28 @@ assertGate(decision.mad_db.operation_classes.includes('migration_apply'), 'direc
46
46
  assertGate(decision.mad_db.state_source === 'persisted_sks_state', 'drifted hook payload state must fall back to persisted SKS MadDB state', decision);
47
47
  assertGate(updated?.counters.reserved === 1, 'direct apply_migration reservation must land on the real MadDB mission capability', updated || {});
48
48
  assertGate(wrongMissionDirExists === false, 'direct apply_migration must not create or write under the drifted payload mission id');
49
- emitGate('mad-db:direct-apply-migration-hook', { mission_id: mission.id, operation_id: decision.mad_db.operation_id, counters: updated?.counters });
49
+ const unrelatedMission = await createMission(root, { mode: 'team', prompt: 'unrelated current-state drift fixture' });
50
+ const unrelatedStateFromCodex = { mission_id: unrelatedMission.id, mode: 'TEAM', phase: 'TOOL_CALL' };
51
+ const executeDecision = await checkDbOperation(root, unrelatedStateFromCodex, {
52
+ tool_name: 'mcp__supabase__execute_sql',
53
+ tool_call_id: 'direct-execute-sql-drop-delete-call',
54
+ tool_input: {
55
+ query: 'drop table if exists public.fixture_old; delete from public.fixture;'
56
+ }
57
+ });
58
+ const afterExecute = await readMadDbCapability(root, mission.id);
59
+ const unrelatedOperationsDir = path.join(missionDir(root, unrelatedMission.id), 'mad-db', 'runtime', 'operations');
60
+ assertGate(executeDecision.allowed === true && executeDecision.mad_db?.active === true, 'active MadDB capability must allow direct execute_sql after current state drifts away from MadDB', executeDecision);
61
+ assertGate(executeDecision.mad_db.state_source === 'latest_active_mad_db_capability', 'direct execute_sql must fall back to the latest active MadDB capability when persisted state is unrelated', executeDecision);
62
+ for (const operation of ['direct_execute_sql', 'drop', 'all_row_delete']) {
63
+ assertGate(executeDecision.mad_db.operation_classes.includes(operation), `direct execute_sql destructive SQL must reserve ${operation}`, executeDecision);
64
+ }
65
+ assertGate(afterExecute?.counters.reserved === 2, 'direct execute_sql reservation must land on the real MadDB mission capability', afterExecute || {});
66
+ assertGate(fs.existsSync(unrelatedOperationsDir) === false, 'direct execute_sql must not write operation lifecycle files under the unrelated current mission');
67
+ emitGate('mad-db:direct-apply-migration-hook', {
68
+ mission_id: mission.id,
69
+ apply_operation_id: decision.mad_db.operation_id,
70
+ execute_operation_id: executeDecision.mad_db.operation_id,
71
+ counters: afterExecute?.counters
72
+ });
50
73
  //# sourceMappingURL=mad-db-direct-apply-migration-hook-check.js.map
@@ -9,6 +9,10 @@ for (const token of ['table/schema DROP', 'all-row mutations', 'TRUNCATE', 'exec
9
9
  }
10
10
  assertGate(dbSafetySkill.includes('Active MadDB is the explicit exception'), 'db safety skill must name active MadDB exception', { dbSafetySkill });
11
11
  assertGate(!madDbSkill.includes('Keep catastrophic safeguards active: whole database/schema/table removal'), 'MadDB skill must not carry old destructive-operation denial text', { madDbSkill });
12
+ assertGate(!madDbSkill.includes('persistent security weakening'), 'MadDB skill must not carry prompt-only SQL-plane denial text', { madDbSkill });
13
+ assertGate(madDbSkill.includes('Do not add prompt-only SQL deny lists inside active MadDB'), 'MadDB skill must prevent SQL-plane prompt veto lists', { madDbSkill });
14
+ assertGate(!dbSafetySkill.includes('do not run DROP'), 'db safety skill must not conflict with active MadDB SQL-plane allowance', { dbSafetySkill });
15
+ assertGate(dbSafetySkill.includes('Default read-only restrictions do not apply to SQL-plane work while the active MadDB capability v2 is bound'), 'db safety skill must explicitly remove default restrictions during active MadDB', { dbSafetySkill });
12
16
  assertGate(initText.includes('madDbSkillText()') && initText.includes('dbSafetyGuardSkillText()'), 'init must generate skills from typed MadDB policy SSOT', {});
13
17
  assertGate(MAD_DB_POLICY.active_mode.sql_plane === 'allow_all_mutations' && MAD_DB_POLICY.normal_supabase_mcp.read_only_required === true, 'typed policy must encode active SQL-plane and normal read-only modes', MAD_DB_POLICY);
14
18
  emitGate('mad-db:skill-policy', { schema: MAD_DB_POLICY.schema, operation_classes: MAD_DB_POLICY.sql_plane_allowed.length });