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.
- package/README.md +12 -9
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +1 -1
- package/dist/core/auto-review.js +1 -1
- package/dist/core/commands/mad-db-command.js +146 -51
- package/dist/core/commands/mad-sks-command.js +15 -31
- package/dist/core/db-safety.js +35 -37
- package/dist/core/doctor/supabase-mcp-repair.js +2 -2
- package/dist/core/feature-registry.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +5 -4
- package/dist/core/mad-db/mad-db-capability.js +203 -74
- package/dist/core/mad-db/mad-db-coordinator.js +287 -0
- package/dist/core/mad-db/mad-db-executor.js +156 -0
- package/dist/core/mad-db/mad-db-ledger.js +1 -1
- package/dist/core/mad-db/mad-db-lock.js +40 -0
- package/dist/core/mad-db/mad-db-operation-store.js +140 -0
- package/dist/core/mad-db/mad-db-policy-resolver.js +42 -22
- package/dist/core/mad-db/mad-db-policy.js +195 -0
- package/dist/core/mad-db/mad-db-postconditions.js +30 -0
- package/dist/core/mad-db/mad-db-recovery.js +27 -0
- package/dist/core/mad-db/mad-db-result-lifecycle.js +31 -102
- package/dist/core/mad-db/mad-db-runtime-profile.js +121 -0
- package/dist/core/mad-db/mad-db-target.js +64 -0
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
- package/dist/core/pipeline-internals/runtime-core.js +40 -0
- package/dist/core/providers/glm/bench/glm-benchmark-types.js +1 -1
- package/dist/core/release/release-gate-dag.js +6 -5
- package/dist/core/routes.js +23 -8
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-column-anchor.js +5 -1
- package/dist/scripts/check-dist-runtime.js +3 -2
- package/dist/scripts/codex-0142-manifest-check.js +2 -1
- package/dist/scripts/mad-db-capability-check.js +13 -2
- package/dist/scripts/mad-db-command-check.js +7 -5
- package/dist/scripts/mad-db-hook-idempotency-check.js +21 -0
- package/dist/scripts/mad-db-ledger-check.js +2 -1
- package/dist/scripts/mad-db-lifecycle-hook-decision-check.js +5 -4
- package/dist/scripts/mad-db-mad-command-check.js +29 -16
- package/dist/scripts/mad-db-mcp-result-lifecycle-check.js +11 -10
- package/dist/scripts/mad-db-one-cycle-bounded-check.js +15 -18
- package/dist/scripts/mad-db-one-cycle-consumption-check.js +3 -3
- package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +9 -9
- package/dist/scripts/mad-db-operation-lifecycle-ledger-check.js +6 -6
- package/dist/scripts/mad-db-parallel-lifecycle-check.js +24 -0
- package/dist/scripts/mad-db-policy-v2-check.js +20 -0
- package/dist/scripts/mad-db-priority-resolver-check.js +5 -5
- package/dist/scripts/mad-db-real-supabase-e2e.js +166 -0
- package/dist/scripts/mad-db-route-identity-check.js +28 -0
- package/dist/scripts/mad-db-runtime-profile-lifecycle-check.js +24 -0
- package/dist/scripts/mad-db-safety-conflict-matrix-check.js +3 -3
- package/dist/scripts/mad-db-skill-policy-snapshot-check.js +15 -0
- package/dist/scripts/release-dag-full-coverage-check.js +6 -0
- package/dist/scripts/release-triwiki-first-runner-blackbox.js +5 -1
- package/package.json +13 -3
- package/schemas/mad-db/mad-db-capability.schema.json +92 -19
package/dist/core/init.js
CHANGED
|
@@ -13,6 +13,7 @@ import { CODEX_HOOK_EVENT_STATE_KEYS } from './codex-compat/codex-hook-events.js
|
|
|
13
13
|
import { codexCommandHookCurrentHash } from './codex-hooks/codex-hook-hash.js';
|
|
14
14
|
import { buildSksCoreSkillManifest, isCoreSkillName } from './codex-native/core-skill-manifest.js';
|
|
15
15
|
import { syncCoreSkillsIntegrity } from './codex-native/core-skill-integrity.js';
|
|
16
|
+
import { dbSafetyGuardSkillText, madDbSkillText } from './mad-db/mad-db-policy.js';
|
|
16
17
|
const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
|
|
17
18
|
const SKS_GENERATED_GIT_PATTERNS = [
|
|
18
19
|
'.sneakoscope/missions/',
|
|
@@ -351,7 +352,7 @@ export async function initProject(root, opts = {}) {
|
|
|
351
352
|
state: 'git-common-dir/sks-version-state.json'
|
|
352
353
|
}
|
|
353
354
|
},
|
|
354
|
-
database_safety: 'default_safe; $MAD-SKS scoped permission profile is
|
|
355
|
+
database_safety: 'default_safe; $MAD-SKS scoped permission profile keeps catastrophic safeguards active; first-class $MAD-DB is the explicit SQL-plane execution exception with mission-local write transport and read-back proof',
|
|
355
356
|
gx_renderer: 'deterministic_svg_html'
|
|
356
357
|
};
|
|
357
358
|
await writeJsonAtomic(manifestPath, manifest);
|
|
@@ -826,7 +827,7 @@ export async function initProject(root, opts = {}) {
|
|
|
826
827
|
// them relocated into the home config by the splitter, re-triggering the warning.
|
|
827
828
|
{
|
|
828
829
|
table: 'auto_review',
|
|
829
|
-
text: '[auto_review]\npolicy = "In MAD launches, allow
|
|
830
|
+
text: '[auto_review]\npolicy = "In MAD-SKS launches, allow only the scoped non-MadDB high-risk surfaces 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."'
|
|
830
831
|
}
|
|
831
832
|
];
|
|
832
833
|
}
|
|
@@ -1058,7 +1059,7 @@ export async function installSkills(root) {
|
|
|
1058
1059
|
'research': `---\nname: research\ndescription: Dollar-command route for $Research or $research frontier discovery workflows.\n---\n\nUse when the user invokes $Research/$research or asks for research, hypotheses, new mechanisms, falsification, or testable predictions. Prefer sks research prepare and sks research run. Research is not an implementation route: do not edit repository source, docs, package metadata, generated skills, or harness files; write only route-local mission artifacts under .sneakoscope/missions/<mission-id>/. Run the genius-lens agent council with named persona-inspired cognitive roles: Einstein Agent, Feynman Agent, Turing Agent, von Neumann Agent, and Skeptic Agent. These are lenses only; do not impersonate the historical people. Every Research agent ledger row must include display_name, persona, persona_boundary, effort=xhigh, reasoning_effort=xhigh, service_tier when available, one literal "Eureka!" idea, falsifiers, cheap_probes, and challenge_or_response before synthesis. This is not a fixed three-cycle route: repeat source gathering, Eureka ideas, evidence-bound debate, falsification, and synthesis pressure until every agent records final agreement, or until the explicit max-cycle safety cap pauses with an unpassed gate. Create research-source-skill.md as a route-local Skill Creator artifact, then maximize layered public web/source search across latest papers, official/government or leading-institution data, standards/primary docs, current news, public discourse, developer/practitioner sources, traditional background sources, and counterevidence before synthesis. Record research-source-skill.md, source-ledger.json, agent-ledger.json, debate-ledger.json, novelty-ledger.json, falsification-ledger.json, research-report.md, research-paper.md, genius-opinion-summary.md, and research-gate.json. debate-ledger.json must include consensus_iterations, unanimous_consensus, and per-agent agreements; research-gate.json cannot pass until unanimous_consensus=true with every agent agreement recorded. Context7 is optional and only needed when the research topic depends on external package/API/framework docs; do not use it as the default research evidence layer. Normal Research may take one or two hours when needed; favor real source collection, cross-layer comparison, falsification, and a concise paper manuscript over speed. Do not use --mock except for selftests or dry harness checks; if live source execution is unavailable, record a blocker and keep the gate unpassed. Do not use for ordinary code edits.\n`,
|
|
1059
1060
|
'autoresearch': `---\nname: autoresearch\ndescription: Dollar-command route for $AutoResearch or $autoresearch iterative experiment loops.\n---\n\nUse for $AutoResearch, iterative improvement, SEO/GEO, ranking, workflow, benchmark, or experiments. Define program, hypothesis, experiment, metric, keep/discard, falsification, next step, and Honest Mode. Load seo-geo-optimizer for README/npm/GitHub/schema/AI-search work.\n`,
|
|
1060
1061
|
'db': `---\nname: db\ndescription: Dollar-command route for $DB or $db database and Supabase safety checks.\n---\n\nUse when the user invokes $DB/$db or the task touches SQL, Supabase, Postgres, migrations, Prisma, Drizzle, Knex, MCP database tools, or production data. Run or follow sks db policy, sks db scan, sks db classify, and sks db check. Destructive database operations remain forbidden.\n`,
|
|
1061
|
-
'mad-db':
|
|
1062
|
+
'mad-db': `${madDbSkillText()}\n`,
|
|
1062
1063
|
'mad-sks': `---\nname: mad-sks\ndescription: Explicit high-risk authorization modifier for $MAD-SKS scoped permission widening across approved target-project surfaces.\n---\n\nUse only when the user explicitly invokes $MAD-SKS or top-level sks --mad. It can be combined with another route, such as $MAD-SKS $Team or $DB ... $MAD-SKS; in that case the other command remains the primary workflow and MAD-SKS is only the temporary permission grant. The widened permission applies only while the active mission gate is open, must be deactivated when the task ends, and can open approved scopes such as target-project file writes, shell commands, package installs, local service control, network operations, browser/Computer Use workflows, generated assets, file permissions, migrations, Supabase MCP database writes, column/schema cleanup, direct execute SQL, and normal targeted DB writes. Keep catastrophic safeguards active: whole database/schema/table removal, truncate, all-row delete/update, reset, dangerous project/branch management, credential exfiltration, persistent security weakening, destructive delete without explicit confirmation, and unrequested fallback implementation remain blocked. Do not carry MAD-SKS permission into later prompts or routes. The permission profile source is centralized in src/core/permission-gates.ts and emitted as dist/core/permission-gates.js so skill/hook/MCP-style gates share one decision function.\n`,
|
|
1063
1064
|
'gx': `---\nname: gx\ndescription: Dollar-command route for $GX or $gx deterministic GX visual context cartridges.\n---\n\nUse when the user invokes $GX/$gx or asks for architecture/context visualization through SKS. Prefer sks gx init, render, validate, drift, and snapshot. vgraph.json remains the source of truth.\n`,
|
|
1064
1065
|
'help': `---\nname: help\ndescription: Dollar-command route for $Help or $help explaining installed SKS commands and workflows.\n---\n\nUse when the user invokes $Help/$help or asks what commands exist. Prefer concise output from sks commands, sks usage <topic>, sks quickstart, sks aliases, and sks codex-app.\n`,
|
|
@@ -1073,7 +1074,7 @@ export async function installSkills(root) {
|
|
|
1073
1074
|
'autoresearch-loop': `---\nname: autoresearch-loop\ndescription: Iterative AutoResearch-style loop for open-ended improvement, discovery, prompt, ranking, SEO/GEO, and workflow-quality tasks.\n---\n\nUse for research, ranking, prompt/workflow improvement, benchmark gains, or repeated refinement. Loop: program, hypothesis, smallest falsifying experiment, metric, keep/discard, falsify, next step. Keep a ledger and do not claim improvement without evidence.\n`,
|
|
1074
1075
|
'hproof-claim-ledger': `---\nname: hproof-claim-ledger\ndescription: Extract atomic claims and classify support status.\n---\n\nEvery factual statement must become an atomic claim. Unsupported critical claims cannot be used for implementation or final answer. Database claims require DB safety evidence.\n`,
|
|
1075
1076
|
'hproof-evidence-bind': `---\nname: hproof-evidence-bind\ndescription: Bind claims to code, tests, decision contract, vgraph, beta, wiki, or GX render evidence.\n---\n\nEvidence priority: current code/tests, decision-contract.json, vgraph.json, beta.json, GX snapshot/render metadata, LLM Wiki coordinate index, user prompt. Database claims must respect .sneakoscope/db-safety.json. Wiki claims should carry id, hash, source path, and RGBA/trig coordinate anchors so they can be hydrated instead of treated as unsupported summaries.\n`,
|
|
1076
|
-
'db-safety-guard':
|
|
1077
|
+
'db-safety-guard': `${dbSafetyGuardSkillText()}\n`,
|
|
1077
1078
|
'gx-visual-generate': `---\nname: gx-visual-generate\ndescription: Render a deterministic SVG/HTML visual sheet from vgraph.json and beta.json.\n---\n\nUse sks gx render. vgraph.json is source of truth; renders embed source hash and RGBA wiki anchors.\n`,
|
|
1078
1079
|
'gx-visual-read': `---\nname: gx-visual-read\ndescription: Read a Sneakoscope Codex deterministic visual sheet and produce context notes.\n---\n\nExtract nodes, edges, invariants, tests, risks, uncertainties, and RGBA anchors from source/render/snapshot. Do not infer hidden nodes.\n`,
|
|
1079
1080
|
'gx-visual-validate': `---\nname: gx-visual-validate\ndescription: Validate render metadata against vgraph.json and beta.json.\n---\n\nRun sks gx validate and drift; fail stale or incomplete hashes, nodes, edges, invariants, or anchors.\n`,
|
|
@@ -1,48 +1,91 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { appendJsonlBounded, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
2
|
+
import { appendJsonlBounded, nowIso, readJson, sha256, writeJsonAtomic } from '../fsx.js';
|
|
3
3
|
import { findLatestMission, missionDir } from '../mission.js';
|
|
4
|
-
|
|
4
|
+
import { withMadDbLock } from './mad-db-lock.js';
|
|
5
|
+
import { MAD_DB_POLICY } from './mad-db-policy.js';
|
|
6
|
+
export const MAD_DB_CAPABILITY_SCHEMA_V1 = 'sks.mad-db-capability.v1';
|
|
7
|
+
export const MAD_DB_CAPABILITY_SCHEMA = 'sks.mad-db-capability.v2';
|
|
5
8
|
export const MAD_DB_CAPABILITY_FILE = 'mad-db-capability.json';
|
|
6
9
|
export const MAD_DB_ACK = 'I AUTHORIZE ONE-CYCLE DB BREAK-GLASS';
|
|
7
|
-
export const
|
|
10
|
+
export const MAD_DB_DEFAULT_TTL_MS = MAD_DB_POLICY.ttl.default_ms;
|
|
11
|
+
export const MAD_DB_MAX_TTL_MS = MAD_DB_POLICY.ttl.hard_max_ms;
|
|
8
12
|
export async function createMadDbCapability(root, input) {
|
|
9
13
|
if (input.ack !== MAD_DB_ACK)
|
|
10
14
|
throw new Error('mad_db_ack_phrase_mismatch');
|
|
11
15
|
const createdAt = nowIso();
|
|
12
|
-
const ttlMs = Math.min(MAD_DB_MAX_TTL_MS, Math.max(1, Math.floor(Number(input.ttlMs ||
|
|
16
|
+
const ttlMs = Math.min(MAD_DB_MAX_TTL_MS, Math.max(1, Math.floor(Number(input.ttlMs || MAD_DB_DEFAULT_TTL_MS))));
|
|
17
|
+
const projectRef = String(input.projectRef || process.env.SKS_MAD_DB_PROJECT_REF || process.env.SKS_MAD_DB_E2E_PROJECT_REF || 'fixture-project-ref').trim();
|
|
18
|
+
const profilePath = input.profilePath || '.sneakoscope/missions/<mission>/mad-db/runtime/codex-mad-db.config.toml';
|
|
19
|
+
const profileSha256 = input.profileSha256 || sha256(`${input.missionId}:${projectRef}:placeholder-profile`);
|
|
13
20
|
const capability = {
|
|
14
21
|
schema: MAD_DB_CAPABILITY_SCHEMA,
|
|
22
|
+
revision: 1,
|
|
15
23
|
mission_id: input.missionId,
|
|
16
24
|
cycle_id: input.cycleId || `mad-db-${Date.now().toString(36)}`,
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
project_root_hash: sha256(path.resolve(input.cwd || root)).slice(0, 24),
|
|
26
|
+
project_ref: projectRef,
|
|
27
|
+
target_environment: input.targetEnvironment || 'production',
|
|
28
|
+
allowed_schemas: input.allowedSchemas?.length ? input.allowedSchemas : ['public'],
|
|
29
|
+
codex_thread_id: input.codexThreadId ?? null,
|
|
30
|
+
runtime_session_id: input.runtimeSessionId || `mad-db-session-${Date.now().toString(36)}`,
|
|
31
|
+
operator_intent_hash: sha256(input.operatorIntent || input.ack || 'mad-db').slice(0, 32),
|
|
32
|
+
operator_ack_hash: sha256(input.ack).slice(0, 32),
|
|
33
|
+
scope: {
|
|
34
|
+
sql_plane: 'all_mutations',
|
|
35
|
+
control_plane: 'deny',
|
|
36
|
+
operations: input.operations?.length ? input.operations : [...MAD_DB_POLICY.sql_plane_allowed]
|
|
37
|
+
},
|
|
38
|
+
transport: {
|
|
39
|
+
profile_path: profilePath,
|
|
40
|
+
profile_sha256: profileSha256,
|
|
41
|
+
server_url_redacted: input.serverUrlRedacted || 'https://mcp.supabase.com/mcp?project_ref=<redacted>&features=database',
|
|
42
|
+
features: ['database'],
|
|
43
|
+
write_capable: true
|
|
44
|
+
},
|
|
45
|
+
issued_at: createdAt,
|
|
19
46
|
expires_at: new Date(Date.now() + ttlMs).toISOString(),
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
closed_at: null,
|
|
48
|
+
status: input.status || 'issued',
|
|
49
|
+
counters: {
|
|
50
|
+
attempts: 0,
|
|
51
|
+
reserved: 0,
|
|
52
|
+
succeeded: 0,
|
|
53
|
+
failed: 0
|
|
27
54
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
55
|
+
legacy_compat: {
|
|
56
|
+
one_cycle_only: true,
|
|
57
|
+
priority: 'highest',
|
|
58
|
+
scope: 'all_database_mutations'
|
|
59
|
+
}
|
|
33
60
|
};
|
|
34
61
|
const dir = missionDir(root, input.missionId);
|
|
35
62
|
await writeJsonAtomic(path.join(dir, MAD_DB_CAPABILITY_FILE), capability);
|
|
36
|
-
await appendJsonlBounded(path.join(dir, 'mad-db-ledger.jsonl'), {
|
|
63
|
+
await appendJsonlBounded(path.join(dir, 'mad-db-ledger.jsonl'), {
|
|
64
|
+
ts: nowIso(),
|
|
65
|
+
type: 'capability.created',
|
|
66
|
+
schema: capability.schema,
|
|
67
|
+
mission_id: capability.mission_id,
|
|
68
|
+
cycle_id: capability.cycle_id,
|
|
69
|
+
project_ref_hash: sha256(capability.project_ref).slice(0, 16),
|
|
70
|
+
runtime_session_id: capability.runtime_session_id,
|
|
71
|
+
expires_at: capability.expires_at,
|
|
72
|
+
status: capability.status
|
|
73
|
+
});
|
|
37
74
|
return capability;
|
|
38
75
|
}
|
|
39
76
|
export async function readMadDbCapability(root, missionId) {
|
|
40
|
-
const
|
|
41
|
-
|
|
77
|
+
const value = await readJson(path.join(missionDir(root, missionId), MAD_DB_CAPABILITY_FILE), null);
|
|
78
|
+
if (value?.schema === MAD_DB_CAPABILITY_SCHEMA)
|
|
79
|
+
return value;
|
|
80
|
+
if (value?.schema === MAD_DB_CAPABILITY_SCHEMA_V1)
|
|
81
|
+
return migrateV1Capability(value, missionId);
|
|
82
|
+
return null;
|
|
42
83
|
}
|
|
43
84
|
export async function resolveMadDbMissionId(root, state = {}, explicitMissionId = null) {
|
|
44
85
|
if (explicitMissionId && explicitMissionId !== 'latest')
|
|
45
86
|
return explicitMissionId;
|
|
87
|
+
if (state?.mad_db_capability_mission_id)
|
|
88
|
+
return String(state.mad_db_capability_mission_id);
|
|
46
89
|
if (state?.mission_id)
|
|
47
90
|
return String(state.mission_id);
|
|
48
91
|
return findLatestMission(root);
|
|
@@ -51,74 +94,160 @@ export function isMadDbCapabilityActive(capability, nowMs = Date.now()) {
|
|
|
51
94
|
if (!capability)
|
|
52
95
|
return false;
|
|
53
96
|
const expires = Date.parse(capability.expires_at || '');
|
|
54
|
-
return capability.
|
|
55
|
-
&& capability.
|
|
56
|
-
&& capability.
|
|
57
|
-
&&
|
|
97
|
+
return capability.schema === MAD_DB_CAPABILITY_SCHEMA
|
|
98
|
+
&& ['transport_ready', 'active'].includes(capability.status)
|
|
99
|
+
&& Boolean(capability.project_ref)
|
|
100
|
+
&& capability.transport?.write_capable === true
|
|
101
|
+
&& capability.transport?.features?.[0] === 'database'
|
|
58
102
|
&& Number.isFinite(expires)
|
|
59
103
|
&& expires > nowMs;
|
|
60
104
|
}
|
|
105
|
+
export async function activateMadDbCapability(root, missionId) {
|
|
106
|
+
return updateMadDbCapability(root, missionId, (capability) => ({
|
|
107
|
+
...capability,
|
|
108
|
+
status: capability.status === 'issued' ? 'active' : capability.status
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
export async function markMadDbTransportReady(root, missionId) {
|
|
112
|
+
return updateMadDbCapability(root, missionId, (capability) => ({
|
|
113
|
+
...capability,
|
|
114
|
+
status: capability.status === 'issued' ? 'transport_ready' : capability.status
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
export async function updateMadDbCapabilityCounters(root, missionId, delta) {
|
|
118
|
+
return updateMadDbCapability(root, missionId, (capability) => ({
|
|
119
|
+
...capability,
|
|
120
|
+
counters: {
|
|
121
|
+
attempts: capability.counters.attempts + Number(delta.attemptsDelta || 0),
|
|
122
|
+
reserved: capability.counters.reserved + Number(delta.reservedDelta || 0),
|
|
123
|
+
succeeded: capability.counters.succeeded + Number(delta.succeededDelta || 0),
|
|
124
|
+
failed: capability.counters.failed + Number(delta.failedDelta || 0)
|
|
125
|
+
}
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
export async function updateMadDbCapability(root, missionId, mutator) {
|
|
129
|
+
return withMadDbLock(root, missionId, 'capability', async () => {
|
|
130
|
+
const current = await readMadDbCapability(root, missionId);
|
|
131
|
+
if (!current)
|
|
132
|
+
return null;
|
|
133
|
+
const next = mutator(current);
|
|
134
|
+
const updated = {
|
|
135
|
+
...next,
|
|
136
|
+
revision: Number(current.revision || 0) + 1
|
|
137
|
+
};
|
|
138
|
+
await writeJsonAtomic(path.join(missionDir(root, missionId), MAD_DB_CAPABILITY_FILE), updated);
|
|
139
|
+
await appendJsonlBounded(path.join(missionDir(root, missionId), 'mad-db-ledger.jsonl'), {
|
|
140
|
+
ts: nowIso(),
|
|
141
|
+
type: 'capability.updated',
|
|
142
|
+
mission_id: missionId,
|
|
143
|
+
cycle_id: updated.cycle_id,
|
|
144
|
+
revision: updated.revision,
|
|
145
|
+
status: updated.status,
|
|
146
|
+
counters: updated.counters
|
|
147
|
+
});
|
|
148
|
+
return updated;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
61
151
|
export async function recordMadDbOperation(root, missionId, input = {}) {
|
|
62
152
|
const capability = await readMadDbCapability(root, missionId);
|
|
63
|
-
if (!
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
const maxOperations = Math.max(1, Number(capability.max_operations || 20));
|
|
67
|
-
const updated = {
|
|
68
|
-
...capability,
|
|
69
|
-
operation_count: operationCount,
|
|
70
|
-
max_operations: maxOperations
|
|
71
|
-
};
|
|
72
|
-
const dir = missionDir(root, missionId);
|
|
73
|
-
await writeJsonAtomic(path.join(dir, MAD_DB_CAPABILITY_FILE), updated);
|
|
74
|
-
await appendJsonlBounded(path.join(dir, 'mad-db-ledger.jsonl'), {
|
|
153
|
+
if (!capability)
|
|
154
|
+
return null;
|
|
155
|
+
await appendJsonlBounded(path.join(missionDir(root, missionId), 'mad-db-ledger.jsonl'), {
|
|
75
156
|
ts: nowIso(),
|
|
76
|
-
type: 'db_operation.
|
|
157
|
+
type: 'db_operation.legacy_recorded',
|
|
77
158
|
mission_id: missionId,
|
|
78
|
-
cycle_id:
|
|
159
|
+
cycle_id: capability.cycle_id,
|
|
79
160
|
operation_id: input.operationId || null,
|
|
80
161
|
tool_name: input.toolName || null,
|
|
81
|
-
sql_hash: input.sqlHash || null
|
|
82
|
-
operation_count: operationCount,
|
|
83
|
-
max_operations: maxOperations
|
|
162
|
+
sql_hash: input.sqlHash || null
|
|
84
163
|
});
|
|
85
|
-
|
|
86
|
-
return consumeMadDbCapability(root, missionId, { consumedBy: 'db-safety-checkDbOperation', reason: 'mad_db_max_operations_reached' });
|
|
87
|
-
}
|
|
88
|
-
return updated;
|
|
164
|
+
return capability;
|
|
89
165
|
}
|
|
90
166
|
export async function consumeMadDbCapability(root, missionId, input = {}) {
|
|
91
|
-
|
|
92
|
-
if (!capability || capability.consumed === true)
|
|
93
|
-
return capability;
|
|
94
|
-
const consumed = {
|
|
95
|
-
...capability,
|
|
96
|
-
consumed: true,
|
|
97
|
-
consumed_at: nowIso(),
|
|
98
|
-
consumed_by: input.consumedBy || input.reason || 'db-safety-policy-resolver'
|
|
99
|
-
};
|
|
100
|
-
const dir = missionDir(root, missionId);
|
|
101
|
-
await writeJsonAtomic(path.join(dir, MAD_DB_CAPABILITY_FILE), consumed);
|
|
102
|
-
await writeJsonAtomic(path.join(dir, 'mad-db-capability.consumed.json'), consumed);
|
|
103
|
-
await appendJsonlBounded(path.join(dir, 'mad-db-ledger.jsonl'), { ts: nowIso(), type: 'capability.consumed', mission_id: missionId, cycle_id: consumed.cycle_id, consumed_by: consumed.consumed_by });
|
|
104
|
-
return consumed;
|
|
167
|
+
return closeMadDbCycle(root, missionId, '', input.consumedBy || input.reason || 'mad_db_cycle_closed');
|
|
105
168
|
}
|
|
106
|
-
export async function closeMadDbCycle(root, missionId, cycleId) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
169
|
+
export async function closeMadDbCycle(root, missionId, cycleId = '', reason = 'mad_db_cycle_closed') {
|
|
170
|
+
const closed = await updateMadDbCapability(root, missionId, (capability) => {
|
|
171
|
+
if (cycleId && capability.cycle_id !== cycleId)
|
|
172
|
+
return capability;
|
|
173
|
+
return {
|
|
174
|
+
...capability,
|
|
175
|
+
status: capability.status === 'revoked' ? 'revoked' : 'closed',
|
|
176
|
+
closed_at: nowIso()
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
if (closed) {
|
|
180
|
+
await writeJsonAtomic(path.join(missionDir(root, missionId), 'mad-db-capability.closed.json'), {
|
|
181
|
+
schema: closed.schema,
|
|
182
|
+
mission_id: closed.mission_id,
|
|
183
|
+
cycle_id: closed.cycle_id,
|
|
184
|
+
closed_at: closed.closed_at,
|
|
185
|
+
close_reason: reason,
|
|
186
|
+
counters: closed.counters,
|
|
187
|
+
project_ref_hash: sha256(closed.project_ref).slice(0, 16)
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return closed;
|
|
113
191
|
}
|
|
114
192
|
export async function revokeMadDbCapability(root, missionId, reason = 'operator_revoked') {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
193
|
+
const revoked = await updateMadDbCapability(root, missionId, (capability) => ({
|
|
194
|
+
...capability,
|
|
195
|
+
status: 'revoked',
|
|
196
|
+
closed_at: nowIso()
|
|
197
|
+
}));
|
|
198
|
+
if (revoked) {
|
|
199
|
+
await appendJsonlBounded(path.join(missionDir(root, missionId), 'mad-db-ledger.jsonl'), {
|
|
200
|
+
ts: nowIso(),
|
|
201
|
+
type: 'capability.revoked',
|
|
202
|
+
mission_id: missionId,
|
|
203
|
+
cycle_id: revoked.cycle_id,
|
|
204
|
+
reason
|
|
205
|
+
});
|
|
206
|
+
}
|
|
122
207
|
return revoked;
|
|
123
208
|
}
|
|
209
|
+
function migrateV1Capability(value, missionId) {
|
|
210
|
+
const projectRef = process.env.SKS_MAD_DB_PROJECT_REF || process.env.SKS_MAD_DB_E2E_PROJECT_REF || 'legacy-v1-missing-project-ref';
|
|
211
|
+
return {
|
|
212
|
+
schema: MAD_DB_CAPABILITY_SCHEMA,
|
|
213
|
+
revision: 0,
|
|
214
|
+
mission_id: String(value.mission_id || missionId),
|
|
215
|
+
cycle_id: String(value.cycle_id || `mad-db-${Date.now().toString(36)}`),
|
|
216
|
+
project_root_hash: sha256(String(value.operator_ack?.cwd || process.cwd())).slice(0, 24),
|
|
217
|
+
project_ref: projectRef,
|
|
218
|
+
target_environment: 'production',
|
|
219
|
+
allowed_schemas: ['public'],
|
|
220
|
+
codex_thread_id: null,
|
|
221
|
+
runtime_session_id: `legacy-v1-${Date.now().toString(36)}`,
|
|
222
|
+
operator_intent_hash: sha256('legacy-v1-migrated').slice(0, 32),
|
|
223
|
+
operator_ack_hash: sha256(MAD_DB_ACK).slice(0, 32),
|
|
224
|
+
scope: {
|
|
225
|
+
sql_plane: 'all_mutations',
|
|
226
|
+
control_plane: 'deny',
|
|
227
|
+
operations: [...MAD_DB_POLICY.sql_plane_allowed]
|
|
228
|
+
},
|
|
229
|
+
transport: {
|
|
230
|
+
profile_path: '.sneakoscope/missions/<mission>/mad-db/runtime/codex-mad-db.config.toml',
|
|
231
|
+
profile_sha256: sha256('legacy-v1-missing-profile'),
|
|
232
|
+
server_url_redacted: 'https://mcp.supabase.com/mcp?project_ref=<legacy-redacted>&features=database',
|
|
233
|
+
features: ['database'],
|
|
234
|
+
write_capable: true
|
|
235
|
+
},
|
|
236
|
+
issued_at: String(value.created_at || nowIso()),
|
|
237
|
+
expires_at: String(value.expires_at || new Date(Date.now() - 1000).toISOString()),
|
|
238
|
+
closed_at: value.consumed ? String(value.consumed_at || nowIso()) : null,
|
|
239
|
+
status: value.enabled === false ? 'revoked' : value.consumed ? 'closed' : 'quarantined',
|
|
240
|
+
counters: {
|
|
241
|
+
attempts: Number(value.operation_count || 0),
|
|
242
|
+
reserved: Number(value.operation_count || 0),
|
|
243
|
+
succeeded: 0,
|
|
244
|
+
failed: 0
|
|
245
|
+
},
|
|
246
|
+
legacy_compat: {
|
|
247
|
+
one_cycle_only: true,
|
|
248
|
+
priority: 'highest',
|
|
249
|
+
scope: 'all_database_mutations'
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
124
253
|
//# sourceMappingURL=mad-db-capability.js.map
|