sneakoscope 4.1.1 → 4.2.0

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 (59) hide show
  1. package/README.md +12 -9
  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 +1 -1
  7. package/dist/core/auto-review.js +1 -1
  8. package/dist/core/commands/mad-db-command.js +146 -51
  9. package/dist/core/commands/mad-sks-command.js +15 -31
  10. package/dist/core/db-safety.js +35 -37
  11. package/dist/core/doctor/supabase-mcp-repair.js +2 -2
  12. package/dist/core/feature-registry.js +1 -1
  13. package/dist/core/fsx.js +1 -1
  14. package/dist/core/init.js +5 -4
  15. package/dist/core/mad-db/mad-db-capability.js +203 -74
  16. package/dist/core/mad-db/mad-db-coordinator.js +287 -0
  17. package/dist/core/mad-db/mad-db-executor.js +156 -0
  18. package/dist/core/mad-db/mad-db-ledger.js +1 -1
  19. package/dist/core/mad-db/mad-db-lock.js +40 -0
  20. package/dist/core/mad-db/mad-db-operation-store.js +140 -0
  21. package/dist/core/mad-db/mad-db-policy-resolver.js +42 -22
  22. package/dist/core/mad-db/mad-db-policy.js +195 -0
  23. package/dist/core/mad-db/mad-db-postconditions.js +30 -0
  24. package/dist/core/mad-db/mad-db-recovery.js +27 -0
  25. package/dist/core/mad-db/mad-db-result-lifecycle.js +31 -102
  26. package/dist/core/mad-db/mad-db-runtime-profile.js +121 -0
  27. package/dist/core/mad-db/mad-db-target.js +64 -0
  28. package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
  29. package/dist/core/pipeline-internals/runtime-core.js +40 -0
  30. package/dist/core/providers/glm/bench/glm-benchmark-types.js +1 -1
  31. package/dist/core/release/release-gate-dag.js +6 -5
  32. package/dist/core/routes.js +23 -8
  33. package/dist/core/version.js +1 -1
  34. package/dist/core/zellij/zellij-slot-column-anchor.js +5 -1
  35. package/dist/scripts/check-dist-runtime.js +3 -2
  36. package/dist/scripts/codex-0142-manifest-check.js +2 -1
  37. package/dist/scripts/mad-db-capability-check.js +13 -2
  38. package/dist/scripts/mad-db-command-check.js +7 -5
  39. package/dist/scripts/mad-db-hook-idempotency-check.js +21 -0
  40. package/dist/scripts/mad-db-ledger-check.js +2 -1
  41. package/dist/scripts/mad-db-lifecycle-hook-decision-check.js +5 -4
  42. package/dist/scripts/mad-db-mad-command-check.js +29 -16
  43. package/dist/scripts/mad-db-mcp-result-lifecycle-check.js +11 -10
  44. package/dist/scripts/mad-db-one-cycle-bounded-check.js +15 -18
  45. package/dist/scripts/mad-db-one-cycle-consumption-check.js +3 -3
  46. package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +9 -9
  47. package/dist/scripts/mad-db-operation-lifecycle-ledger-check.js +6 -6
  48. package/dist/scripts/mad-db-parallel-lifecycle-check.js +24 -0
  49. package/dist/scripts/mad-db-policy-v2-check.js +20 -0
  50. package/dist/scripts/mad-db-priority-resolver-check.js +5 -5
  51. package/dist/scripts/mad-db-real-supabase-e2e.js +166 -0
  52. package/dist/scripts/mad-db-route-identity-check.js +28 -0
  53. package/dist/scripts/mad-db-runtime-profile-lifecycle-check.js +24 -0
  54. package/dist/scripts/mad-db-safety-conflict-matrix-check.js +3 -3
  55. package/dist/scripts/mad-db-skill-policy-snapshot-check.js +15 -0
  56. package/dist/scripts/release-dag-full-coverage-check.js +6 -0
  57. package/dist/scripts/release-triwiki-first-runner-blackbox.js +5 -1
  58. package/package.json +13 -3
  59. package/schemas/mad-db/mad-db-capability.schema.json +92 -19
package/README.md CHANGED
@@ -35,15 +35,18 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **4.1.1** tightens the 4.1 readiness path for publishing and first-command migration. Doctor now keeps optional native surfaces route-gated, writes v2 project migration receipts tied to an installation epoch, and keeps the migration profile fast enough for update gates.
38
+ SKS **4.2.0** stabilizes MadDB SQL-plane execution so explicit `$MAD-DB` and `sks mad-db run|exec|apply-migration` invocations use a first-class, mission-bound break-glass route instead of inheriting `$MAD-SKS` state.
39
39
 
40
- What changed in 4.1.1:
40
+ What changed in 4.2.0:
41
+
42
+ - **First-class MadDB route.** `$MAD-DB` no longer aliases `$MAD-SKS`; it creates one authoritative mission, capability, runtime profile, inventory check, execution, read-back, and closeout cycle.
43
+ - **Capability v2 binding.** MadDB capabilities bind project root, project ref hash, mission/cycle/session identity, runtime profile hash, TTL, operator intent, and SQL-plane operation classes.
44
+ - **Ephemeral Supabase write profile.** Persistent Supabase MCP config stays read-only; write-capable MCP settings exist only inside the active MadDB mission and are removed in `finally`.
45
+ - **Exact lifecycle correlation.** Hook/result handling is keyed by canonical `tool_call_id`, uses idempotent operation state, and avoids unsafe tool-name result matching under parallel calls.
46
+ - **Policy/docs/test SSOT.** MadDB route metadata, generated skill guidance, DB safety wording, Doctor guidance, release gates, docs, scanner coverage, and local regression tests share the typed MadDB policy surface.
47
+ - **Release metadata truth.** Package, CLI version constants, Rust crate metadata, README, changelog, and release checks all point at 4.2.0.
41
48
 
42
- - **Core vs route readiness.** Computer Use and Chrome/web review are manual-required route gates, not core blockers for ordinary Doctor/update readiness.
43
- - **Migration receipt v2.** First-command migration writes project receipts with installation epoch, project hash, required blockers, and optional warnings so stale locks or optional capabilities do not block normal commands.
44
- - **Fast migration Doctor.** `sks doctor --fix --profile migration --machine-only` skips optional Codex App, Zellij, provider, native, and deep diagnostic work while preserving core readiness checks.
45
- - **MAD bootstrap latency.** MAD defers update prompts, provider setup, UI snapshots, pane proof, and native swarm proof until the route actually needs them.
46
- - **Release metadata truth.** Package, CLI version constants, Rust crate metadata, README, changelog, and release checks all point at 4.1.1.
49
+ What changed in 4.1.1:
47
50
 
48
51
  What changed in 4.1.0:
49
52
 
@@ -504,7 +507,7 @@ sks --mad
504
507
  sks --mad --allow-package-install --allow-service-control --allow-network --yes
505
508
  ```
506
509
 
507
- This syncs existing codex-lb provider auth, creates/uses the `sks-mad-high` xhigh maintenance profile, opens the MAD-SKS permission gate for that Zellij run, starts a same-mission read-only native agent swarm, and launches a Codex CLI layout whose right-side lanes read that MAD ledger. Bare `sks --mad` grants target-project file and shell scope only; add explicit `--allow-*` flags for packages, services, network, Computer Use, browser use, generated assets, file permissions, DB writes, or other high-risk scopes. MAD-SKS is not a DB-only unlock: it is explicit user authorization to widen approved target-project scopes. Catastrophic database wipe/all-row/project-management safeguards remain active, and the pipeline contract still forbids unrequested fallback implementation code.
510
+ This syncs existing codex-lb provider auth, creates/uses the `sks-mad-high` xhigh maintenance profile, opens the MAD-SKS permission gate for that Zellij run, starts a same-mission read-only native agent swarm, and launches a Codex CLI layout whose right-side lanes read that MAD ledger. Bare `sks --mad` grants target-project file and shell scope only; add explicit `--allow-*` flags for packages, services, network, Computer Use, browser use, generated assets, file permissions, DB writes, or other high-risk scopes. MAD-SKS is not a DB-only unlock and does not create a MadDB capability. Catastrophic database wipe/all-row/project-management safeguards remain active outside the first-class MadDB route, and the pipeline contract still forbids unrequested fallback implementation code.
508
511
 
509
512
  Before launching, SKS checks npm for a newer `sneakoscope` and prints a non-blocking update notice when one is available; use `sks update now` or `sks doctor --fix` when you want SKS to update itself. Use `--yes` to approve missing dependency installs automatically. Tune MAD swarm startup with `--mad-agents <n>`, `--mad-swarm-work-items <n>`, and `--mad-swarm-backend <backend>`; `--no-mad-swarm` keeps only the cockpit UI if you need a temporary fallback.
510
513
 
@@ -717,7 +720,7 @@ Use these inside Codex App or another agent prompt. They are prompt commands, no
717
720
 
718
721
  Common prompts: `$Team`, `$From-Chat-IMG`, `$with-local-llm-on`, `$with-local-llm-off`, `$DFix`, `$Answer`, `$SKS`, `$QA-LOOP`, `$PPT`, `$Computer-Use`/`$CU`, `$Goal`, `$Research`, `$AutoResearch`, `$DB`, `$MAD-SKS`, `$MAD-DB`, `$GX`, `$Wiki`, and `$Help`.
719
722
 
720
- `$MAD-DB` is the prompt-visible Mad-DB alias for one-cycle DB break-glass work. It maps to the same guarded MAD-SKS permission route, while the terminal lifecycle remains `sks mad-db status|enable|revoke`; it is not a permanent DB unlock and catastrophic DB safeguards remain active.
723
+ `$MAD-DB` is the first-class MadDB SQL-plane execution route. `sks mad-db run|exec|apply-migration` creates the bound mission/capability/runtime profile, verifies Supabase `execute_sql` and `apply_migration`, executes the requested SQL-plane mutation, reads back postconditions, and then closes the write profile while proving normal read-only restoration. Supabase project/account/billing/credential control-plane actions remain denied. See `docs/mad-db.md`.
721
724
 
722
725
  ## 🔁 Common Workflows
723
726
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "4.1.1"
79
+ version = "4.2.0"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "4.1.1"
3
+ version = "4.2.0"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 4.1.1"),
7
+ Some("--version") => println!("sks-rs 4.2.0"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '4.1.1';
2
+ const FAST_PACKAGE_VERSION = '4.2.0';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -112,7 +112,7 @@ export const COMMANDS = {
112
112
  zellij: entry('beta', 'Inspect Zellij runtime status and explain repair (no auto-install)', 'dist/commands/zellij.js', directCommand(() => import('../commands/zellij.js'), 'dist/commands/zellij.js')),
113
113
  'mad-sks': entry('beta', 'MAD-SKS scoped permission modifier', 'dist/commands/mad-sks.js', directCommand(() => import('../commands/mad-sks.js'), 'dist/commands/mad-sks.js')),
114
114
  glm: entry('beta', 'Run GLM 5.2 MAD mode through OpenRouter', 'dist/core/commands/glm-command.js', argsCommand(() => import('../core/commands/glm-command.js'), 'glmCommand', 'dist/core/commands/glm-command.js')),
115
- 'mad-db': entry('beta', 'Create or inspect one-cycle Mad-DB break-glass capability tokens', 'dist/commands/mad-db.js', directCommand(() => import('../commands/mad-db.js'), 'dist/commands/mad-db.js')),
115
+ 'mad-db': entry('beta', 'Run first-class MadDB SQL-plane execution cycles with mission-local Supabase write transport', 'dist/commands/mad-db.js', directCommand(() => import('../commands/mad-db.js'), 'dist/commands/mad-db.js')),
116
116
  'auto-review': entry('beta', 'Manage auto-review profile', 'dist/commands/auto-review.js', directCommand(() => import('../commands/auto-review.js'), 'dist/commands/auto-review.js')),
117
117
  'dollar-commands': entry('stable', 'List Codex App dollar commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
118
118
  'fast-mode': entry('stable', 'Toggle SKS Fast mode default for dollar-command routes', 'dist/core/commands/fast-mode-command.js', argsCommand(() => import('../core/commands/fast-mode-command.js'), 'fastModeCommand', 'dist/core/commands/fast-mode-command.js')),
@@ -314,7 +314,7 @@ function removeLegacyProfileConfig(text, profile) {
314
314
  function upsertAutoReviewPolicy(text) {
315
315
  const policy = [
316
316
  '[auto_review]',
317
- 'policy = "In MAD launches, allow live-server work, normal DB writes, Supabase MCP DB writes, direct execute SQL, schema cleanup, and migration application for the active invocation. Deny only catastrophic database wipes, all-row value deletion/update, dangerous project or branch management, credential exfiltration, persistent security weakening, broad unrelated file deletion, and unrequested fallback implementation code."'
317
+ 'policy = "In MAD-SKS launches, allow only scoped non-MadDB high-risk work approved for the active invocation and keep catastrophic DB wipe/all-row safeguards active. In first-class MAD-DB cycles, the explicit $MAD-DB or sks mad-db run|exec|apply-migration invocation is the SQL-plane approval boundary: execute requested execute_sql/apply_migration mutations with mission-local write transport, read-back proof, and final read-only restoration. Supabase project/account/billing/credential control-plane actions remain denied."'
318
318
  ].join('\n');
319
319
  const existing = readTableString(text, 'auto_review', 'policy');
320
320
  if (existing && /unrequested fallback implementation code/i.test(existing))
@@ -1,98 +1,178 @@
1
- import { initProject } from '../init.js';
2
- import { createMission, findLatestMission, setCurrent } from '../mission.js';
3
- import { exists, sksRoot } from '../fsx.js';
4
1
  import path from 'node:path';
5
- import { createMadDbCapability, isMadDbCapabilityActive, MAD_DB_ACK, readMadDbCapability, resolveMadDbMissionId, revokeMadDbCapability } from '../mad-db/mad-db-capability.js';
2
+ import { initProject } from '../init.js';
3
+ import { findLatestMission, setCurrent } from '../mission.js';
4
+ import { exists, readText, sksRoot } from '../fsx.js';
5
+ import { closeMadDbCycle, isMadDbCapabilityActive, MAD_DB_ACK, readMadDbCapability, resolveMadDbMissionId, revokeMadDbCapability } from '../mad-db/mad-db-capability.js';
6
+ import { closeMadDbRuntimeProfile, verifyReadOnlyRestored } from '../mad-db/mad-db-runtime-profile.js';
7
+ import { runMadDbCycle } from '../mad-db/mad-db-coordinator.js';
8
+ import { resolveMadDbTarget } from '../mad-db/mad-db-target.js';
9
+ import { quarantineStaleMadDbRuntimeProfiles } from '../mad-db/mad-db-recovery.js';
10
+ import { sha256 } from '../fsx.js';
6
11
  export async function madDbCommand(args = []) {
7
12
  const action = String(args[0] && !String(args[0]).startsWith('--') ? args[0] : 'status');
8
13
  const rest = action === args[0] ? args.slice(1) : args;
9
14
  const root = await sksRoot();
10
15
  if (!(await exists(path.join(root, '.sneakoscope'))))
11
16
  await initProject(root, {});
17
+ if (action === 'run')
18
+ return runMadDb(root, rest);
19
+ if (action === 'exec')
20
+ return execMadDb(root, rest);
21
+ if (action === 'apply-migration')
22
+ return applyMigrationMadDb(root, rest);
23
+ if (action === 'doctor')
24
+ return doctorMadDb(root, rest);
25
+ if (action === 'close')
26
+ return closeMadDb(root, rest);
12
27
  if (action === 'enable')
13
28
  return enableMadDb(root, rest);
14
29
  if (action === 'revoke')
15
30
  return revokeMadDb(root, rest);
16
31
  if (action === 'status')
17
32
  return statusMadDb(root, rest);
18
- console.error('Usage: sks mad-db enable --ack "I AUTHORIZE ONE-CYCLE DB BREAK-GLASS" [--mission latest|new|M-...] | status | revoke');
33
+ console.error('Usage: sks mad-db run "<task-or-sql>" | exec --sql "<SQL>" | apply-migration --name <name> --file <sql-file> | doctor|status|close|revoke [--json]');
19
34
  process.exitCode = 1;
20
35
  }
36
+ async function runMadDb(root, args) {
37
+ const task = positionalText(args) || readOption(args, '--task', '');
38
+ const sql = readOption(args, '--sql', '');
39
+ const result = await runMadDbCycle({
40
+ root,
41
+ action: 'run',
42
+ task,
43
+ sql: sql || null,
44
+ verifySql: readOption(args, '--verify-sql', '') || null,
45
+ args
46
+ });
47
+ return printResult(result, args);
48
+ }
49
+ async function execMadDb(root, args) {
50
+ const sql = readOption(args, '--sql', '') || positionalText(args);
51
+ const result = await runMadDbCycle({
52
+ root,
53
+ action: 'exec',
54
+ task: sql || 'sks mad-db exec',
55
+ sql: sql || null,
56
+ verifySql: readOption(args, '--verify-sql', '') || null,
57
+ args
58
+ });
59
+ return printResult(result, args);
60
+ }
61
+ async function applyMigrationMadDb(root, args) {
62
+ const file = readOption(args, '--file', '');
63
+ const sql = readOption(args, '--sql', '') || (file ? await readText(path.resolve(file), '') : '');
64
+ const result = await runMadDbCycle({
65
+ root,
66
+ action: 'apply-migration',
67
+ task: `apply migration ${readOption(args, '--name', 'mad_db_migration')}`,
68
+ sql: sql || null,
69
+ migrationName: readOption(args, '--name', `mad_db_${Date.now()}`),
70
+ migrationFile: file || null,
71
+ verifySql: readOption(args, '--verify-sql', '') || null,
72
+ args
73
+ });
74
+ return printResult(result, args);
75
+ }
76
+ async function doctorMadDb(root, args) {
77
+ const target = await resolveMadDbTarget(root, { args });
78
+ const recovery = await quarantineStaleMadDbRuntimeProfiles(root);
79
+ const restoration = await verifyReadOnlyRestored(root, null);
80
+ const result = {
81
+ schema: 'sks.mad-db-doctor.v1',
82
+ ok: target.blockers.length === 0 && restoration.persistent_supabase_read_only,
83
+ target: { ...target, project_ref: target.project_ref ? `<hash:${target.project_ref_hash}>` : null },
84
+ stale_recovery: recovery,
85
+ read_only_restoration: restoration,
86
+ execute_sql_apply_migration_inventory_checked: false,
87
+ note: 'doctor does not open write transport; run/exec/apply-migration verifies tool inventory inside a bound cycle'
88
+ };
89
+ return printJsonOrText(result, args, result.ok ? 'MadDB doctor passed local checks.' : `MadDB doctor found blockers: ${[...target.blockers, ...restoration.blockers].join(', ')}`);
90
+ }
21
91
  async function enableMadDb(root, args) {
22
92
  const json = hasFlag(args, '--json');
23
93
  const ack = readOption(args, '--ack', '');
24
94
  if (ack !== MAD_DB_ACK) {
25
- const result = { schema: 'sks.mad-db-command.v1', ok: false, action: 'enable', reason: 'ack_phrase_required', required_ack: MAD_DB_ACK };
95
+ const result = { schema: 'sks.mad-db-command.v2', ok: false, action: 'enable', reason: 'deprecated_enable_no_capability', required_ack: MAD_DB_ACK, token_only: true };
26
96
  if (json)
27
97
  return console.log(JSON.stringify(result, null, 2));
28
- console.error(`Mad-DB enable blocked. Required --ack ${JSON.stringify(MAD_DB_ACK)}`);
98
+ console.error(`MadDB enable is deprecated and does not create a capability. Use sks mad-db run|exec|apply-migration for an executable cycle. Legacy ack was ${JSON.stringify(MAD_DB_ACK)}.`);
29
99
  process.exitCode = 2;
30
100
  return result;
31
101
  }
32
- const requestedMission = readOption(args, '--mission', 'latest');
33
- let missionId = requestedMission === 'new' ? null : await resolveMadDbMissionId(root, {}, requestedMission);
34
- if (!missionId) {
35
- const created = await createMission(root, { mode: 'mad-db', prompt: 'sks mad-db enable one-cycle DB break-glass' });
36
- missionId = created.id;
37
- }
38
- const capability = await createMadDbCapability(root, {
39
- missionId,
40
- ack,
41
- cwd: process.cwd(),
42
- ttlMs: Number(readOption(args, '--ttl-ms', String(2 * 60 * 60 * 1000)))
43
- });
44
- await setCurrent(root, {
45
- mission_id: missionId,
46
- route: 'MadDB',
47
- route_command: '$MAD-DB',
48
- mode: 'MADDB',
49
- phase: 'MADDB_ONE_CYCLE_CAPABILITY_ACTIVE',
50
- mad_db_active: true,
51
- mad_db_cycle_id: capability.cycle_id,
52
- mad_db_capability_file: 'mad-db-capability.json',
53
- mad_db_ack_phrase: 'accepted',
54
- stop_gate: 'mad-db-capability.json'
55
- });
56
- const result = { schema: 'sks.mad-db-command.v1', ok: true, action: 'enable', mission_id: missionId, capability };
102
+ const result = {
103
+ schema: 'sks.mad-db-command.v2',
104
+ ok: false,
105
+ action: 'enable',
106
+ reason: 'deprecated_enable_no_capability',
107
+ token_only: true,
108
+ executable_commands: ['sks mad-db run', 'sks mad-db exec', 'sks mad-db apply-migration']
109
+ };
57
110
  if (json)
58
111
  return console.log(JSON.stringify(result, null, 2));
59
- console.log(`Mad-DB one-cycle capability active for ${missionId}; expires ${capability.expires_at}.`);
112
+ console.error('MadDB enable no longer creates a capability. Use sks mad-db run|exec|apply-migration to create the bound mission/profile/capability and execute SQL.');
113
+ process.exitCode = 2;
60
114
  return result;
61
115
  }
62
116
  async function statusMadDb(root, args) {
63
- const json = hasFlag(args, '--json');
64
117
  const missionId = await resolveMadDbMissionId(root, {}, readOption(args, '--mission', 'latest'));
65
118
  const capability = missionId ? await readMadDbCapability(root, missionId) : null;
66
119
  const result = {
67
- schema: 'sks.mad-db-command.v1',
120
+ schema: 'sks.mad-db-status.v2',
68
121
  ok: true,
69
122
  action: 'status',
70
123
  mission_id: missionId,
71
124
  active: isMadDbCapabilityActive(capability),
72
- capability
125
+ capability: capability ? redactCapability(capability) : null
73
126
  };
74
- if (json)
75
- return console.log(JSON.stringify(result, null, 2));
76
- if (!missionId || !capability)
77
- console.log('Mad-DB: no capability found.');
78
- else
79
- console.log(`Mad-DB: ${result.active ? 'active' : 'inactive'} for ${missionId}; consumed=${capability.consumed}; expires=${capability.expires_at}.`);
80
- return result;
127
+ return printJsonOrText(result, args, !missionId || !capability ? 'MadDB: no capability found.' : `MadDB: ${result.active ? 'active' : 'inactive'} for ${missionId}; status=${capability.status}; expires=${capability.expires_at}.`);
128
+ }
129
+ async function closeMadDb(root, args) {
130
+ const missionId = await resolveMadDbMissionId(root, {}, readOption(args, '--mission', 'latest')) || await findLatestMission(root);
131
+ const capability = missionId ? await readMadDbCapability(root, missionId) : null;
132
+ const restoration = missionId ? await closeMadDbRuntimeProfile({ root, missionId, reason: 'operator_close' }) : null;
133
+ const closed = missionId && capability ? await closeMadDbCycle(root, missionId, capability.cycle_id, 'operator_close') : null;
134
+ if (missionId)
135
+ await setCurrent(root, { mad_db_active: false, phase: 'MADDB_CLOSED' });
136
+ const result = { schema: 'sks.mad-db-close.v2', ok: Boolean(closed), action: 'close', mission_id: missionId, capability: closed ? redactCapability(closed) : null, read_only_restoration: restoration };
137
+ return printJsonOrText(result, args, closed ? `MadDB cycle closed for ${missionId}.` : 'MadDB: no capability to close.');
81
138
  }
82
139
  async function revokeMadDb(root, args) {
83
- const json = hasFlag(args, '--json');
84
140
  const missionId = await resolveMadDbMissionId(root, {}, readOption(args, '--mission', 'latest')) || await findLatestMission(root);
85
141
  const revoked = missionId ? await revokeMadDbCapability(root, missionId, readOption(args, '--reason', 'operator_revoked')) : null;
142
+ const restoration = missionId ? await closeMadDbRuntimeProfile({ root, missionId, reason: 'operator_revoke' }) : null;
86
143
  await setCurrent(root, { mad_db_active: false, phase: 'MADDB_REVOKED' });
87
- const result = { schema: 'sks.mad-db-command.v1', ok: Boolean(revoked), action: 'revoke', mission_id: missionId, capability: revoked };
88
- if (json)
89
- return console.log(JSON.stringify(result, null, 2));
90
- if (!revoked)
91
- console.log('Mad-DB: no capability to revoke.');
144
+ const result = { schema: 'sks.mad-db-command.v2', ok: Boolean(revoked), action: 'revoke', mission_id: missionId, capability: revoked ? redactCapability(revoked) : null, read_only_restoration: restoration };
145
+ return printJsonOrText(result, args, revoked ? `MadDB capability revoked for ${missionId}.` : 'MadDB: no capability to revoke.');
146
+ }
147
+ function printResult(result, args) {
148
+ if (hasFlag(args, '--json')) {
149
+ console.log(JSON.stringify(result, null, 2));
150
+ }
151
+ else if (result.ok) {
152
+ console.log(`MadDB complete: mission=${result.mission_id} cycle=${result.cycle_id} execution=${result.execution?.ok ? 'succeeded' : 'unknown'} verification=${result.read_back?.ok === true ? 'passed' : 'not-requested'} read-only-restored=${result.read_only_restoration?.ok === true}`);
153
+ }
154
+ else {
155
+ console.error(`MadDB failed: mission=${result.mission_id} blockers=${(result.blockers || []).join(', ') || 'unknown'} read-only-restored=${result.read_only_restoration?.ok === true}`);
156
+ process.exitCode = 1;
157
+ }
158
+ return result;
159
+ }
160
+ function printJsonOrText(result, args, text) {
161
+ if (hasFlag(args, '--json'))
162
+ console.log(JSON.stringify(result, null, 2));
92
163
  else
93
- console.log(`Mad-DB capability revoked for ${missionId}.`);
164
+ console.log(text);
165
+ if (result.ok === false)
166
+ process.exitCode = process.exitCode || 1;
94
167
  return result;
95
168
  }
169
+ function redactCapability(capability) {
170
+ return {
171
+ ...capability,
172
+ project_ref: capability.project_ref ? `<hash:${sha256(capability.project_ref).slice(0, 16)}>` : null,
173
+ transport: capability.transport ? { ...capability.transport, server_url_redacted: capability.transport.server_url_redacted || '<redacted>' } : null
174
+ };
175
+ }
96
176
  function hasFlag(args, flag) {
97
177
  return args.includes(flag);
98
178
  }
@@ -100,7 +180,22 @@ function readOption(args, name, fallback) {
100
180
  const index = args.indexOf(name);
101
181
  if (index >= 0 && args[index + 1] && !String(args[index + 1]).startsWith('--'))
102
182
  return String(args[index + 1]);
103
- const prefixed = args.find((arg) => String(arg).startsWith(name + '='));
183
+ const prefixed = args.find((arg) => String(arg).startsWith(`${name}=`));
104
184
  return prefixed ? prefixed.slice(name.length + 1) : fallback;
105
185
  }
186
+ function positionalText(args) {
187
+ const out = [];
188
+ for (let index = 0; index < args.length; index += 1) {
189
+ const arg = args[index];
190
+ if (!arg)
191
+ continue;
192
+ if (arg.startsWith('--')) {
193
+ if (!arg.includes('=') && args[index + 1] && !String(args[index + 1]).startsWith('--'))
194
+ index += 1;
195
+ continue;
196
+ }
197
+ out.push(arg);
198
+ }
199
+ return out.join(' ').trim();
200
+ }
106
201
  //# sourceMappingURL=mad-db-command.js.map
@@ -19,7 +19,6 @@ import { repairCodexConfigEperm } from '../codex/codex-config-eperm-repair.js';
19
19
  import { runCodexLaunchPreflight } from '../preflight/parallel-preflight-engine.js';
20
20
  import { diffCodexAppUiSnapshots, writeCodexAppUiSnapshot } from '../codex-app/codex-app-ui-state-snapshot.js';
21
21
  import { checkSksUpdateNotice } from '../update/update-notice.js';
22
- import { createMadDbCapability, MAD_DB_ACK } from '../mad-db/mad-db-capability.js';
23
22
  import { writeCodex0138CapabilityArtifacts } from '../codex-control/codex-0138-capability.js';
24
23
  import { writeCodex0139CapabilityArtifacts } from '../codex-control/codex-0139-capability.js';
25
24
  import { resolveCodexNativeInvocationPlan } from '../codex-native/codex-native-invocation-router.js';
@@ -203,38 +202,22 @@ export async function madHighCommand(args = [], deps = {}) {
203
202
  process.exitCode = 1;
204
203
  return glmRuntime;
205
204
  }
206
- const madDbCapability = madDbGrant.enabled
207
- ? await createMadDbCapability(madLaunch.root, { missionId: madLaunch.mission_id, ack: madDbGrant.ack, cwd: process.cwd() })
208
- : null;
209
- if (madDbCapability) {
205
+ if (madDbGrant.requested) {
210
206
  const grantReport = {
211
- schema: 'sks.mad-sks-launch-grants.v1',
207
+ schema: 'sks.mad-sks-launch-grants.v2',
212
208
  generated_at: nowIso(),
213
209
  mission_id: madLaunch.mission_id,
214
210
  mad_sks_active: true,
215
- mad_db_active: true,
216
- mad_db_default_grant: madDbGrant.source === 'sks_mad_default',
211
+ mad_db_active: false,
212
+ mad_db_default_grant: false,
217
213
  mad_db_grant_source: madDbGrant.source,
218
- mad_db_one_cycle_only: true,
219
- mad_db_capability_file: 'mad-db-capability.json',
220
- mad_db_cycle_id: madDbCapability.cycle_id,
221
- mad_db_expires_at: madDbCapability.expires_at,
214
+ mad_db_requires_first_class_route: true,
215
+ mad_db_cli: 'sks mad-db run|exec|apply-migration',
216
+ mad_db_capability_file: null,
222
217
  standalone_mad_db_enable_still_requires_ack: true
223
218
  };
224
219
  await writeJsonAtomic(path.join(madLaunch.dir, 'mad-sks-launch-grants.json'), grantReport);
225
- await setCurrent(madLaunch.root, {
226
- mission_id: madLaunch.mission_id,
227
- mad_db_active: true,
228
- mad_db_cycle_id: madDbCapability.cycle_id,
229
- mad_db_capability_file: 'mad-db-capability.json',
230
- mad_db_break_glass: true,
231
- mad_db_default_grant: madDbGrant.source === 'sks_mad_default',
232
- mad_db_grant_source: madDbGrant.source,
233
- mad_db_one_cycle_only: true,
234
- mad_db_priority_override_active: true,
235
- mad_sks_launch_grants_file: 'mad-sks-launch-grants.json'
236
- });
237
- await appendJsonlBounded(path.join(madLaunch.dir, 'events.jsonl'), { ts: nowIso(), type: 'mad_db.capability_created', grant_source: madDbGrant.source, cycle_id: madDbCapability.cycle_id, expires_at: madDbCapability.expires_at });
220
+ await appendJsonlBounded(path.join(madLaunch.dir, 'events.jsonl'), { ts: nowIso(), type: 'mad_db.first_class_route_required', grant_source: madDbGrant.source, cli: grantReport.mad_db_cli });
238
221
  }
239
222
  const updateNotice = {
240
223
  schema: 'sks.update-notice.v1',
@@ -256,11 +239,11 @@ export async function madHighCommand(args = [], deps = {}) {
256
239
  console.log(`SKS MAD ready: ${glmRuntime?.profile?.profile_name || madHighProfileName()} | gate ${madLaunch.mission_id}`);
257
240
  if (glmRuntime?.profile)
258
241
  console.log(`GLM MAD launch active: ${glmRuntime.profile.model} via OpenRouter; GPT fallback blocked.`);
259
- if (madDbCapability)
260
- console.log(`MAD-DB one-cycle capability active (${madDbGrant.source}); expires ${madDbCapability.expires_at}.`);
242
+ if (madDbGrant.requested)
243
+ console.log('MAD-DB flag observed; use the first-class route: sks mad-db run|exec|apply-migration.');
261
244
  if (updateNotice.update_available === true)
262
245
  console.log(`SKS update notice: ${updateNotice.latest_version} available (non-blocking).`);
263
- console.log('Scoped high-power maintenance authority active; add explicit --allow-* flags for packages, services, network, browser/Computer Use, generated assets, file permissions, or system/admin scopes. MAD-DB one-cycle DB break-glass is already active for this launch; protected-core, audit, and one-cycle bounds remain.');
246
+ console.log('Scoped high-power maintenance authority active; add explicit --allow-* flags for packages, services, network, browser/Computer Use, generated assets, file permissions, or system/admin scopes. MAD-DB SQL-plane execution is not active in MAD-SKS; use the first-class MadDB route for DROP/TRUNCATE/all-row SQL-plane work.');
264
247
  const launchLb = lb.status === 'present' ? { ...lb, status: 'configured' } : lb;
265
248
  const madSksEnv = {
266
249
  SKS_PROTECTED_CORE_POLICY: madLaunch.gate.protected_core_policy,
@@ -411,10 +394,11 @@ function buildGlmMadLaunchOpts(cleanArgs = [], opts = {}) {
411
394
  }
412
395
  export function resolveMadLaunchMadDbGrant(args = []) {
413
396
  const list = (args || []).map((arg) => String(arg));
397
+ const requested = list.includes('--mad-db');
414
398
  return {
415
- enabled: true,
416
- source: list.includes('--mad-db') ? 'sks_mad_explicit_redundant_flag' : 'sks_mad_default',
417
- ack: MAD_DB_ACK,
399
+ enabled: false,
400
+ requested,
401
+ source: requested ? 'mad_db_first_class_route_required' : 'not_requested',
418
402
  one_cycle_only: true
419
403
  };
420
404
  }
@@ -3,9 +3,10 @@ import { exists, readJson, writeJsonAtomic, readText, nowIso, appendJsonlBounded
3
3
  import { missionDir, setCurrent } from './mission.js';
4
4
  import { evaluateMadSksPermissionGate, isMadSksRouteState } from './permission-gates.js';
5
5
  import { resolveMadDbMutationPolicy } from './mad-db/mad-db-policy-resolver.js';
6
- import { recordMadDbOperation } from './mad-db/mad-db-capability.js';
7
- import { appendMadDbLedgerEvent, appendMadDbOperationLifecycle } from './mad-db/mad-db-ledger.js';
8
- import { lifecycleHookFromUnknown, recordPendingMadDbLifecycleHook } from './mad-db/mad-db-result-lifecycle.js';
6
+ import { appendMadDbLedgerEvent } from './mad-db/mad-db-ledger.js';
7
+ import { lifecycleHookFromUnknown } from './mad-db/mad-db-result-lifecycle.js';
8
+ import { extractCanonicalToolCallId, reserveMadDbOperation, transitionMadDbOperation } from './mad-db/mad-db-operation-store.js';
9
+ import { madDbOperationClassesFromClassification } from './mad-db/mad-db-policy.js';
9
10
  export const DEFAULT_DB_SAFETY_POLICY = Object.freeze({
10
11
  schema_version: 1,
11
12
  mode: 'read_only_default',
@@ -511,19 +512,29 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
511
512
  const madDb = await resolveMadDbMutationPolicy(root, state, classification);
512
513
  if (madDb.allowed === true && state?.mission_id) {
513
514
  const madDbDecision = madDb;
514
- const operationId = `mad-db-op-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
515
- const sqlHash = classification.sql?.statements?.length ? sha256(String(classification.sql.statements.join('\n'))) : null;
516
- await appendMadDbOperationLifecycle(root, state.mission_id, {
517
- type: 'db_operation.started',
518
- operationId,
519
- cycleId: madDbDecision.cycle_id,
520
- toolName: classification.toolName || null,
521
- sqlHash,
522
- destructive: classification.level === 'destructive'
515
+ const sqlText = classification.sql?.statements?.length ? String(classification.sql.statements.join('\n')) : null;
516
+ const sqlHash = sqlText ? sha256(sqlText) : null;
517
+ const toolCallId = extractCanonicalToolCallId(payload) || `payload-${sha256(JSON.stringify({ tool: classification.toolName || '', sqlHash, level: classification.level })).slice(0, 16)}`;
518
+ const operationClasses = madDbDecision.operation_classes?.length ? madDbDecision.operation_classes : madDbOperationClassesFromClassification(classification);
519
+ const reservation = await reserveMadDbOperation({
520
+ root,
521
+ missionId: String(state.mission_id),
522
+ capability: madDbDecision.capability,
523
+ toolCallId,
524
+ toolName: classification.toolName || 'unknown_database_tool',
525
+ sql: sqlText,
526
+ operationClasses
527
+ });
528
+ await transitionMadDbOperation({
529
+ root,
530
+ missionId: String(state.mission_id),
531
+ toolCallId,
532
+ state: 'started'
523
533
  });
524
534
  const lifecycleHook = {
525
535
  mission_id: String(state.mission_id),
526
- operation_id: operationId,
536
+ operation_id: reservation.operation.operation_id,
537
+ tool_call_id: toolCallId,
527
538
  cycle_id: madDbDecision.cycle_id || null,
528
539
  tool_name: classification.toolName || null,
529
540
  sql_hash: sqlHash,
@@ -538,35 +549,22 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
538
549
  mad_db: {
539
550
  active: true,
540
551
  priority: 'highest',
552
+ capability_schema: 'sks.mad-db-capability.v2',
541
553
  one_cycle_only: true,
542
554
  cycle_id: madDbDecision.cycle_id,
543
555
  capability_file: 'mad-db-capability.json',
544
- consumed: false,
545
- operation_id: operationId,
556
+ status: reservation.capability.status,
557
+ operation_id: reservation.operation.operation_id,
558
+ tool_call_id: toolCallId,
559
+ tool_call_id_missing: extractCanonicalToolCallId(payload) === null,
546
560
  lifecycle_result_pending: true,
547
561
  ledger_result_hook: lifecycleHook,
548
- operation_count: Number(madDbDecision.operation_count || 0) + 1,
549
- max_operations: madDbDecision.max_operations || 20
562
+ operation_classes: operationClasses,
563
+ counters: reservation.capability.counters,
564
+ idempotent_reservation_reused: reservation.reused
550
565
  }
551
566
  };
552
- await appendMadDbLedgerEvent(root, state.mission_id, { type: 'db_mutation.allowed', cycle_id: madDbDecision.cycle_id, mode: madDbDecision.mode, classification, operation_id: operationId });
553
- await appendMadDbOperationLifecycle(root, state.mission_id, {
554
- type: 'db_operation.allowed',
555
- operationId,
556
- cycleId: madDbDecision.cycle_id,
557
- toolName: classification.toolName || null,
558
- sqlHash,
559
- destructive: classification.level === 'destructive',
560
- resultStatus: 'unknown_pending_tool_result'
561
- });
562
- const updatedCapability = await recordMadDbOperation(root, state.mission_id, {
563
- operationId,
564
- ...(classification.toolName ? { toolName: classification.toolName } : {}),
565
- ...(sqlHash ? { sqlHash } : {})
566
- });
567
- decision.mad_db.consumed = updatedCapability?.consumed === true;
568
- decision.mad_db.operation_count = updatedCapability?.operation_count ?? decision.mad_db.operation_count;
569
- await recordPendingMadDbLifecycleHook(root, state.mission_id, lifecycleHook);
567
+ await appendMadDbLedgerEvent(root, state.mission_id, { type: 'db_mutation.allowed', cycle_id: madDbDecision.cycle_id, mode: madDbDecision.mode, classification, operation_id: reservation.operation.operation_id, tool_call_id: toolCallId });
570
568
  await appendJsonlBounded(path.join(missionDir(root, state.mission_id), 'db-safety.jsonl'), { ts: nowIso(), decision });
571
569
  return decision;
572
570
  }
@@ -605,8 +603,8 @@ export function dbBlockReason(decision) {
605
603
  return [
606
604
  'Sneakoscope Codex Database Safety Gate blocked this operation.',
607
605
  `Reasons: ${(decision.reasons || []).join(', ') || 'unknown'}.`,
608
- 'Destructive database operations are never allowed. Production writes are forbidden. Supabase/Postgres MCP write tools must not be used for live destructive changes.',
609
- 'Use read-only/project-scoped Supabase MCP URLs, create migration files, and apply them only to local or preview/branch environments when explicitly allowed by the sealed contract.'
606
+ 'Default DB mode is read-only. Destructive SQL-plane operations require an explicit active MadDB v2 capability opened by $MAD-DB or sks mad-db run|exec|apply-migration.',
607
+ 'Use read-only/project-scoped Supabase MCP URLs outside MadDB. Supabase project/account/billing/credential control-plane operations remain denied even in MadDB.'
610
608
  ].join(' ');
611
609
  }
612
610
  export async function scanDbSafety(root, opts = {}) {
@@ -40,8 +40,8 @@ export async function repairSupabaseMcp(input) {
40
40
  manual_required: manualRequired,
41
41
  next_action: manualRequired
42
42
  ? tokenEnvPresent
43
- ? 'Set Supabase MCP to read-only or explicitly approve write-scoped MCP use.'
44
- : 'Set SUPABASE_ACCESS_TOKEN only if Supabase write MCP features are required; otherwise keep Supabase MCP disabled/read-only.'
43
+ ? 'Set persistent Supabase MCP to read-only. Write-scoped Supabase MCP is allowed only through a mission-local MadDB runtime profile.'
44
+ : 'Set SUPABASE_ACCESS_TOKEN only when an explicit MadDB run needs Supabase MCP auth; otherwise keep persistent Supabase MCP disabled/read-only.'
45
45
  : null,
46
46
  blockers: readyBlocking ? ['supabase_mcp_write_access_not_safe_by_default'] : [],
47
47
  warnings: [
@@ -871,7 +871,7 @@ function routeKnownGaps(command) {
871
871
  if (command === '$MAD-SKS')
872
872
  return ['permission closed by owning gate'];
873
873
  if (command === '$MAD-DB')
874
- return ['one-cycle capability must be explicitly enabled, consumed, or revoked'];
874
+ return ['active cycle must execute requested SQL-plane work, read back postconditions, and close the mission-local write profile'];
875
875
  return [];
876
876
  }
877
877
  function checkRow(id, ok, blockers = []) {
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.1.1';
8
+ export const PACKAGE_VERSION = '4.2.0';
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() {