sneakoscope 0.6.85 → 0.6.89
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 +7 -1
- package/package.json +1 -1
- package/src/cli/main.mjs +56 -124
- package/src/cli/maintenance-commands.mjs +157 -5
- package/src/core/db-safety.mjs +43 -7
- package/src/core/decision-contract.mjs +8 -6
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +35 -2
- package/src/core/perf-bench.mjs +87 -0
- package/src/core/pipeline.mjs +3 -3
- package/src/core/proof-field.mjs +219 -0
- package/src/core/questions.mjs +7 -7
- package/src/core/routes.mjs +6 -5
package/README.md
CHANGED
|
@@ -90,6 +90,8 @@ sks bootstrap
|
|
|
90
90
|
|
|
91
91
|
`sks` commands work even when no project root is present. Project-aware commands use the nearest `.sneakoscope`, `.dcodex`, or `.git` root; if none exists, SKS uses a per-user global runtime root. `sks bootstrap` still initializes the current project when you want project-local hooks, skills, and TriWiki state.
|
|
92
92
|
|
|
93
|
+
Project setup writes shared `.gitignore` entries for generated SKS files: `.sneakoscope/`, `.codex/`, `.agents/`, and managed `AGENTS.md`. Use `sks setup --local-only` when you want those excludes kept only in `.git/info/exclude`.
|
|
94
|
+
|
|
93
95
|
### One-Shot Install
|
|
94
96
|
|
|
95
97
|
Use this when you do not want to keep a global install:
|
|
@@ -215,6 +217,8 @@ sks gx init homepage
|
|
|
215
217
|
sks gx render homepage --format html
|
|
216
218
|
sks validate-artifacts latest --json
|
|
217
219
|
sks perf run --json
|
|
220
|
+
sks perf workflow --json --intent "small CLI change" --changed src/cli/main.mjs,src/core/routes.mjs
|
|
221
|
+
sks proof-field scan --json --intent "small CLI change"
|
|
218
222
|
sks code-structure scan --json
|
|
219
223
|
```
|
|
220
224
|
|
|
@@ -254,6 +258,8 @@ Generated app files include:
|
|
|
254
258
|
| `.codex/config.toml` | Codex profiles, agents, and MCP configuration. |
|
|
255
259
|
| `.sneakoscope/` | Runtime state, missions, wiki packs, policies, and artifacts. |
|
|
256
260
|
|
|
261
|
+
Default setup adds these generated SKS paths to the project `.gitignore`; `--local-only` uses `.git/info/exclude` instead.
|
|
262
|
+
|
|
257
263
|
Use `sks dollar-commands` to confirm that terminal discovery and Codex App prompt commands agree.
|
|
258
264
|
|
|
259
265
|
TriWiki is intentionally sparse: `sks wiki sweep` records demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim into future prompts. `sks harness fixture` validates the broader Harness Growth Factory contract: deliberate forgetting fixtures, skill card metadata, experiment schema, tool-error taxonomy, permission profiles, MultiAgentV2 defaults, and Warp cockpit view coverage. `sks code-structure scan` flags handwritten files above 1000/2000/3000-line thresholds so new logic can be extracted before command files become harder to maintain.
|
|
@@ -274,7 +280,7 @@ Use these inside Codex App or another agent prompt. They are prompt commands, no
|
|
|
274
280
|
| `$Research` | You need frontier-style research with hypotheses and falsification. |
|
|
275
281
|
| `$AutoResearch` | You want iterative improve/test/keep-or-discard optimization. |
|
|
276
282
|
| `$DB` | You need database, Supabase, migration, SQL, or MCP safety checks. |
|
|
277
|
-
| `$MAD-SKS` | You explicitly authorize
|
|
283
|
+
| `$MAD-SKS` | You explicitly authorize scoped Supabase MCP DB cleanup/write permissions for the active invocation only, while keeping catastrophic wipe safeguards. |
|
|
278
284
|
| `$GX` | You need deterministic visual context cartridges. |
|
|
279
285
|
| `$Wiki` | You want TriWiki refresh, pack, prune, validate, or maintenance. |
|
|
280
286
|
| `$Help` | You want installed command and workflow explanation. |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.89",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, AutoResearch, TriWiki, and Honest Mode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
package/src/cli/main.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { containsUserQuestion, noQuestionContinuationReason } from '../core/no-q
|
|
|
14
14
|
import { evaluateDoneGate, defaultDoneGate } from '../core/hproof.mjs';
|
|
15
15
|
import { emitHook } from '../core/hooks-runtime.mjs';
|
|
16
16
|
import { storageReport, enforceRetention, pruneWikiArtifacts } from '../core/retention.mjs';
|
|
17
|
-
import { classifySql, classifyCommand,
|
|
17
|
+
import { classifySql, classifyCommand, checkDbOperation, handleMadSksUserConfirmation } from '../core/db-safety.mjs';
|
|
18
18
|
import { checkHarnessModification, harnessGuardStatus, isHarnessSourceProject } from '../core/harness-guard.mjs';
|
|
19
19
|
import { formatHarnessConflictReport, llmHarnessCleanupPrompt, scanHarnessConflicts } from '../core/harness-conflicts.mjs';
|
|
20
20
|
import { context7Docs, context7Resolve, context7Text, context7Tools } from '../core/context7-client.mjs';
|
|
@@ -36,6 +36,8 @@ import { buildFromChatImgVisualMap } from '../core/from-chat-img-forensics.mjs';
|
|
|
36
36
|
import { classifyDogfoodFinding, createDogfoodReport, writeDogfoodReport } from '../core/dogfood-loop.mjs';
|
|
37
37
|
import { createSkillCandidate, decideSkillInjection, writeSkillCandidate, writeSkillForgeReport, writeSkillInjectionDecision } from '../core/skill-forge.mjs';
|
|
38
38
|
import { classifyToolError, harnessGrowthReport } from '../core/evaluation.mjs';
|
|
39
|
+
import { runWorkflowPerfBench, validateWorkflowPerfReport } from '../core/perf-bench.mjs';
|
|
40
|
+
import { proofFieldFixture, validateProofFieldReport } from '../core/proof-field.mjs';
|
|
39
41
|
import { recordMistake, writeMistakeMemoryReport } from '../core/mistake-memory.mjs';
|
|
40
42
|
import { buildPromptContext } from '../core/prompt-context-builder.mjs';
|
|
41
43
|
import { renderTeamDashboardState, writeTeamDashboardState } from '../core/team-dashboard-renderer.mjs';
|
|
@@ -43,11 +45,10 @@ import { GOAL_WORKFLOW_ARTIFACT } from '../core/goal-workflow.mjs';
|
|
|
43
45
|
import { CODEX_APP_DOCS_URL, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
|
|
44
46
|
import { buildWarpLaunchConfigYaml, buildWarpLaunchPlan, buildWarpOpenArgs, runWarpLaunchConfigSyntaxCheck, warpReadiness, warpStatusKind, defaultWarpWorkspaceName, formatWarpBanner, launchWarpTeamView, launchWarpUi, platformWarpInstallHint, runWarpStatus, sanitizeWarpWorkspaceName, teamLaneStyle, writeWarpLaunchConfig } from '../core/warp-ui.mjs';
|
|
45
47
|
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
|
|
46
|
-
import { buildTeamPlan, codeStructureCommand, defaultBeta, defaultVGraph, evalCommand, gcCommand, goalCommand, gxCommand, harnessCommand, hproofCommand, memoryCommand, migrateWikiContextPack, parseTeamCreateArgs, perfCommand, profileCommand, projectWikiClaims, qaLoopCommand, researchCommand, statsCommand, team, teamWorkflowMarkdown, validateArtifactsCommand, wikiCommand, wikiVoxelRowCount, writeWikiContextPack } from './maintenance-commands.mjs';
|
|
48
|
+
import { buildTeamPlan, codeStructureCommand, dbCommand, defaultBeta, defaultVGraph, evalCommand, gcCommand, goalCommand, gxCommand, harnessCommand, hproofCommand, memoryCommand, migrateWikiContextPack, parseTeamCreateArgs, perfCommand, profileCommand, projectWikiClaims, proofFieldCommand, qaLoopCommand, quickstartCommand, researchCommand, statsCommand, team, teamWorkflowMarkdown, validateArtifactsCommand, wikiCommand, wikiVoxelRowCount, writeWikiContextPack } from './maintenance-commands.mjs';
|
|
47
49
|
|
|
48
50
|
const flag = (args, name) => args.includes(name);
|
|
49
51
|
const promptOf = (args) => args.filter((x) => !String(x).startsWith('--')).join(' ').trim();
|
|
50
|
-
const REPOSITORY_URL = 'https://github.com/mandarange/Sneakoscope-Codex.git';
|
|
51
52
|
const REFLECTION_ARTIFACT = 'reflection.md';
|
|
52
53
|
const REFLECTION_GATE = 'reflection-gate.json';
|
|
53
54
|
const TEAM_SESSION_CLEANUP_ARTIFACT = 'team-session-cleanup.json';
|
|
@@ -76,7 +77,7 @@ export async function main(args) {
|
|
|
76
77
|
if (cmd === 'commands') return commands(tail);
|
|
77
78
|
if (cmd === 'usage') return usage(tail);
|
|
78
79
|
if (cmd === 'root') return rootCommand(tail);
|
|
79
|
-
if (cmd === 'quickstart') return
|
|
80
|
+
if (cmd === 'quickstart') return quickstartCommand();
|
|
80
81
|
if (cmd === 'codex-app') return codexAppHelp(tail);
|
|
81
82
|
if (cmd === 'bootstrap') return bootstrap(tail);
|
|
82
83
|
if (cmd === 'deps') return deps(sub, rest);
|
|
@@ -102,11 +103,12 @@ export async function main(args) {
|
|
|
102
103
|
if (cmd === 'hproof') return hproofCommand(sub, rest);
|
|
103
104
|
if (cmd === 'validate-artifacts') return validateArtifactsCommand(tail);
|
|
104
105
|
if (cmd === 'perf') return perfCommand(sub, rest);
|
|
106
|
+
if (cmd === 'proof-field') return proofFieldCommand(sub, rest);
|
|
105
107
|
if (cmd === 'code-structure') return codeStructureCommand(sub, rest);
|
|
106
108
|
if (cmd === 'memory') return memoryCommand(sub, rest);
|
|
107
109
|
if (cmd === 'gx') return gxCommand(sub, rest);
|
|
108
110
|
if (cmd === 'team') return team(tail);
|
|
109
|
-
if (cmd === 'db') return
|
|
111
|
+
if (cmd === 'db') return dbCommand(sub, rest);
|
|
110
112
|
if (cmd === 'eval') return evalCommand(sub, rest);
|
|
111
113
|
if (cmd === 'harness') return harnessCommand(sub, rest);
|
|
112
114
|
if (cmd === 'wiki') return wikiCommand(sub, rest);
|
|
@@ -178,7 +180,8 @@ Usage:
|
|
|
178
180
|
sks validate-artifacts [mission-id|latest] [--json]
|
|
179
181
|
sks eval run [--json] [--out report.json]
|
|
180
182
|
sks eval compare --baseline old.json --candidate new.json [--json]
|
|
181
|
-
sks perf run [--json]
|
|
183
|
+
sks perf run|workflow [--json] [--intent "task"] [--changed file1,file2]
|
|
184
|
+
sks proof-field scan [--json] [--intent "task"]
|
|
182
185
|
sks harness fixture [--json]
|
|
183
186
|
sks code-structure scan [--json]
|
|
184
187
|
sks wiki coords --rgba 12,34,56,255
|
|
@@ -812,15 +815,16 @@ async function materializeAfterPipelineAnswer(root, id, dir, mission, route, rou
|
|
|
812
815
|
passed: false,
|
|
813
816
|
mad_sks_permission_active: true,
|
|
814
817
|
permissions_deactivated: false,
|
|
815
|
-
|
|
816
|
-
|
|
818
|
+
supabase_mcp_schema_cleanup_allowed: true,
|
|
819
|
+
direct_execute_sql_allowed: true,
|
|
820
|
+
catastrophic_safety_guard_active: true,
|
|
817
821
|
contract_hash: contract.sealed_hash || null
|
|
818
822
|
});
|
|
819
823
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), {
|
|
820
824
|
ts: nowIso(),
|
|
821
825
|
type: 'mad_sks.scoped_permission_opened',
|
|
822
826
|
route: route.id,
|
|
823
|
-
|
|
827
|
+
catastrophic_safety_guard_active: true
|
|
824
828
|
});
|
|
825
829
|
return {
|
|
826
830
|
phase: 'MADSKS_SCOPED_PERMISSION_ACTIVE',
|
|
@@ -830,7 +834,9 @@ async function materializeAfterPipelineAnswer(root, id, dir, mission, route, rou
|
|
|
830
834
|
mad_sks_modifier: true,
|
|
831
835
|
mad_sks_gate_file: 'mad-sks-gate.json',
|
|
832
836
|
mad_sks_gate_ready: true,
|
|
833
|
-
|
|
837
|
+
supabase_mcp_schema_cleanup_allowed: true,
|
|
838
|
+
direct_execute_sql_allowed: true,
|
|
839
|
+
catastrophic_safety_guard_active: true
|
|
834
840
|
}
|
|
835
841
|
};
|
|
836
842
|
}
|
|
@@ -901,8 +907,9 @@ async function materializeMadSksAuthorization(dir, id, route, routeContext = {},
|
|
|
901
907
|
status: 'active',
|
|
902
908
|
active_only_for_current_route: true,
|
|
903
909
|
deactivates_when_gate_passed: gateFile,
|
|
904
|
-
|
|
905
|
-
|
|
910
|
+
supabase_mcp_schema_cleanup_allowed: true,
|
|
911
|
+
direct_execute_sql_allowed: true,
|
|
912
|
+
catastrophic_safety_guard_active: true,
|
|
906
913
|
contract_hash: contract.sealed_hash || null
|
|
907
914
|
};
|
|
908
915
|
await writeJsonAtomic(path.join(dir, 'mad-sks-authorization.json'), artifact);
|
|
@@ -911,13 +918,15 @@ async function materializeMadSksAuthorization(dir, id, route, routeContext = {},
|
|
|
911
918
|
type: 'mad_sks.modifier_authorization_opened',
|
|
912
919
|
route: route?.id || null,
|
|
913
920
|
gate: gateFile,
|
|
914
|
-
|
|
921
|
+
catastrophic_safety_guard_active: true
|
|
915
922
|
});
|
|
916
923
|
return {
|
|
917
924
|
mad_sks_active: true,
|
|
918
925
|
mad_sks_modifier: true,
|
|
919
926
|
mad_sks_gate_file: gateFile,
|
|
920
|
-
|
|
927
|
+
supabase_mcp_schema_cleanup_allowed: true,
|
|
928
|
+
direct_execute_sql_allowed: true,
|
|
929
|
+
catastrophic_safety_guard_active: true
|
|
921
930
|
};
|
|
922
931
|
}
|
|
923
932
|
|
|
@@ -1346,60 +1355,6 @@ async function autoReviewCommand(sub = 'status', args = []) {
|
|
|
1346
1355
|
process.exitCode = 1;
|
|
1347
1356
|
}
|
|
1348
1357
|
|
|
1349
|
-
function quickstart() {
|
|
1350
|
-
console.log(`ㅅㅋㅅ Quickstart
|
|
1351
|
-
|
|
1352
|
-
First install and bootstrap this project:
|
|
1353
|
-
npm i -g sneakoscope
|
|
1354
|
-
sks root
|
|
1355
|
-
sks bootstrap
|
|
1356
|
-
sks
|
|
1357
|
-
|
|
1358
|
-
Use outside a project:
|
|
1359
|
-
sks root
|
|
1360
|
-
sks deps check
|
|
1361
|
-
sks team "global mission"
|
|
1362
|
-
|
|
1363
|
-
If warp is missing:
|
|
1364
|
-
sks deps install warp
|
|
1365
|
-
|
|
1366
|
-
Initialize this project for CLI and Codex App:
|
|
1367
|
-
sks setup --bootstrap
|
|
1368
|
-
|
|
1369
|
-
Open from terminal:
|
|
1370
|
-
sks
|
|
1371
|
-
sks --auto-review --high
|
|
1372
|
-
sks auto-review start --high
|
|
1373
|
-
|
|
1374
|
-
Verify:
|
|
1375
|
-
sks deps check
|
|
1376
|
-
sks codex-app check
|
|
1377
|
-
sks warp check
|
|
1378
|
-
sks auto-review status
|
|
1379
|
-
sks doctor --fix
|
|
1380
|
-
sks context7 check
|
|
1381
|
-
sks selftest --mock
|
|
1382
|
-
sks commands
|
|
1383
|
-
sks dollar-commands
|
|
1384
|
-
|
|
1385
|
-
If hooks cannot find the command:
|
|
1386
|
-
sks fix-path
|
|
1387
|
-
|
|
1388
|
-
Project-only install:
|
|
1389
|
-
npm i -D sneakoscope
|
|
1390
|
-
npx sks setup --install-scope project
|
|
1391
|
-
|
|
1392
|
-
Local-only install artifacts:
|
|
1393
|
-
sks setup --local-only
|
|
1394
|
-
# writes generated SKS files but excludes .sneakoscope/, .codex/, .agents/, AGENTS.md through .git/info/exclude
|
|
1395
|
-
# user-owned AGENTS.md is preserved; an existing SKS managed block is refreshed
|
|
1396
|
-
|
|
1397
|
-
GitHub install for unreleased commits:
|
|
1398
|
-
npm i -g git+${REPOSITORY_URL}
|
|
1399
|
-
sks bootstrap
|
|
1400
|
-
`);
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
1358
|
async function codexAppHelp(args = []) {
|
|
1404
1359
|
const action = args[0] || 'help';
|
|
1405
1360
|
if (action === 'check' || action === 'status') {
|
|
@@ -1430,6 +1385,7 @@ async function codexAppHelp(args = []) {
|
|
|
1430
1385
|
`Skills: project=${skills.project.ok ? 'ok' : `missing ${skills.project.missing.length}`} global=${skills.global.ok ? 'ok' : `missing ${skills.global.missing.length}`}`, '',
|
|
1431
1386
|
'Setup:', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks warp check', '',
|
|
1432
1387
|
'Generated files:', ' .codex/config.toml', ' .codex/hooks.json', ' .agents/skills/', ' .codex/agents/', ' .codex/SNEAKOSCOPE.md', ' AGENTS.md', '',
|
|
1388
|
+
'Git ignore:', ' default setup writes .gitignore entries for .sneakoscope/, .codex/, .agents/, AGENTS.md', ' --local-only writes those patterns to .git/info/exclude instead', '',
|
|
1433
1389
|
'Prompt routes:', formatDollarCommandsCompact(' ')
|
|
1434
1390
|
].join('\n'));
|
|
1435
1391
|
}
|
|
@@ -1609,6 +1565,7 @@ async function setup(args) {
|
|
|
1609
1565
|
console.log(`Hooks: ${path.relative(root, hooksPath)}`);
|
|
1610
1566
|
console.log(`Version: ${versioningInfo.enabled ? (versioningInfo.hook_installed ? 'auto-bump enabled' : 'auto-bump hook missing') : 'not enabled'}${versioningInfo.package_version ? ` (${versioningInfo.package_version})` : ''}`);
|
|
1611
1567
|
if (localOnly) console.log('Git: local-only (.git/info/exclude; user AGENTS preserved, SKS managed block refreshed)');
|
|
1568
|
+
else console.log('Git: .gitignore ignores SKS generated files');
|
|
1612
1569
|
console.log(`Codex App: .codex/config.toml, .codex/hooks.json, .agents/skills, .codex/agents, .codex/SNEAKOSCOPE.md`);
|
|
1613
1570
|
console.log(`Global $: ${globalSkills.status === 'installed' ? 'ok' : globalSkills.status} ${globalSkills.root || ''}`.trimEnd());
|
|
1614
1571
|
console.log(`App tools: ${appRuntime.ok ? 'ok' : 'needs setup'} Codex App=${appRuntime.app.installed ? 'ok' : 'missing'} Browser Use=${appRuntime.mcp.has_browser_use ? 'ok' : 'missing'} Computer Use=${appRuntime.mcp.has_computer_use ? 'ok' : 'missing'}`);
|
|
@@ -1804,6 +1761,7 @@ async function init(args) {
|
|
|
1804
1761
|
console.log(`Initialized ㅅㅋㅅ in ${root}`);
|
|
1805
1762
|
console.log(`Install scope: ${installScope} (${sksCommandPrefix(installScope, { globalCommand })})`);
|
|
1806
1763
|
if (localOnly) console.log('Git mode: local-only (.git/info/exclude)');
|
|
1764
|
+
else console.log('Git mode: shared .gitignore');
|
|
1807
1765
|
for (const x of res.created) console.log(`- ${x}`);
|
|
1808
1766
|
}
|
|
1809
1767
|
|
|
@@ -2012,9 +1970,11 @@ async function selftest() {
|
|
|
2012
1970
|
await writeJsonAtomic(path.join(postinstallBootstrapTmp, 'package.json'), { name: 'postinstall-bootstrap-smoke', version: '0.0.0' });
|
|
2013
1971
|
const postinstallBootstrap = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'postinstall'], { cwd: postinstallBootstrapTmp, input: 'y\n', env: { INIT_CWD: postinstallBootstrapTmp, HOME: path.join(postinstallBootstrapTmp, 'home'), SKS_SKIP_POSTINSTALL_SHIM: '1', SKS_SKIP_POSTINSTALL_CONTEXT7: '1', SKS_SKIP_POSTINSTALL_GLOBAL_SKILLS: '1', SKS_SKIP_CLI_TOOLS: '1', SKS_POSTINSTALL_PROMPT: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
|
|
2014
1972
|
if (postinstallBootstrap.code !== 0 || !String(postinstallBootstrap.stdout || '').includes('SKS Ready')) throw new Error(`selftest failed: approved postinstall bootstrap did not run: ${postinstallBootstrap.stderr}`);
|
|
2015
|
-
for (const rel of ['.agents/skills/team/SKILL.md', '.codex/config.toml', '.codex/hooks.json', '.sneakoscope/harness-guard.json', '.codex/SNEAKOSCOPE.md', 'AGENTS.md']) {
|
|
1973
|
+
for (const rel of ['.agents/skills/team/SKILL.md', '.codex/config.toml', '.codex/hooks.json', '.sneakoscope/harness-guard.json', '.codex/SNEAKOSCOPE.md', 'AGENTS.md', '.gitignore']) {
|
|
2016
1974
|
if (!(await exists(path.join(postinstallBootstrapTmp, rel)))) throw new Error(`selftest failed: bootstrap did not create ${rel}`);
|
|
2017
1975
|
}
|
|
1976
|
+
const postinstallBootstrapGitignore = await safeReadText(path.join(postinstallBootstrapTmp, '.gitignore'));
|
|
1977
|
+
if (!postinstallBootstrapGitignore.includes('.sneakoscope/') || !postinstallBootstrapGitignore.includes('.codex/') || !postinstallBootstrapGitignore.includes('.agents/') || !postinstallBootstrapGitignore.includes('AGENTS.md')) throw new Error('selftest failed: bootstrap did not ignore SKS generated files');
|
|
2018
1978
|
const bootstrapJsonTmp = tmpdir();
|
|
2019
1979
|
await writeJsonAtomic(path.join(bootstrapJsonTmp, 'package.json'), { name: 'bootstrap-json-smoke', version: '0.0.0' });
|
|
2020
1980
|
const bootstrapJson = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'bootstrap', '--json'], { cwd: bootstrapJsonTmp, env: { HOME: path.join(bootstrapJsonTmp, 'home'), SKS_SKIP_POSTINSTALL_GLOBAL_SKILLS: '1', SKS_SKIP_CLI_TOOLS: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
|
|
@@ -2135,10 +2095,19 @@ async function selftest() {
|
|
|
2135
2095
|
await initProject(localOnlyTmp, { localOnly: true });
|
|
2136
2096
|
const localExclude = await safeReadText(path.join(localOnlyTmp, '.git', 'info', 'exclude'));
|
|
2137
2097
|
if (!localExclude.includes('.codex/') || !localExclude.includes('AGENTS.md')) throw new Error('selftest failed: local-only git excludes missing');
|
|
2098
|
+
if (await exists(path.join(localOnlyTmp, '.gitignore'))) throw new Error('selftest failed: local-only wrote shared .gitignore');
|
|
2138
2099
|
const localAgents = await safeReadText(path.join(localOnlyTmp, 'AGENTS.md'));
|
|
2139
2100
|
if (localAgents.trim() !== 'existing local rules') throw new Error('selftest failed: local-only modified existing AGENTS.md');
|
|
2140
2101
|
const localManifest = await readJson(path.join(localOnlyTmp, '.sneakoscope', 'manifest.json'));
|
|
2141
2102
|
if (!localManifest.git?.local_only) throw new Error('selftest failed: local-only manifest missing');
|
|
2103
|
+
const gitignoreTmp = tmpdir();
|
|
2104
|
+
await writeTextAtomic(path.join(gitignoreTmp, '.gitignore'), 'node_modules/\n.sneakoscope/\n');
|
|
2105
|
+
await initProject(gitignoreTmp, {});
|
|
2106
|
+
const gitignoreText = await safeReadText(path.join(gitignoreTmp, '.gitignore'));
|
|
2107
|
+
if (!gitignoreText.includes('node_modules/') || !gitignoreText.includes('# BEGIN Sneakoscope Codex generated files') || !gitignoreText.includes('.codex/') || !gitignoreText.includes('.agents/') || !gitignoreText.includes('AGENTS.md')) throw new Error('selftest failed: shared .gitignore did not preserve existing entries and add SKS patterns');
|
|
2108
|
+
await initProject(gitignoreTmp, {});
|
|
2109
|
+
const gitignoreTextSecond = await safeReadText(path.join(gitignoreTmp, '.gitignore'));
|
|
2110
|
+
if ((gitignoreTextSecond.match(/BEGIN Sneakoscope Codex generated files/g) || []).length !== 1) throw new Error('selftest failed: shared .gitignore managed block duplicated');
|
|
2142
2111
|
const managedAgentsTmp = tmpdir();
|
|
2143
2112
|
await ensureDir(path.join(managedAgentsTmp, '.git'));
|
|
2144
2113
|
await writeTextAtomic(path.join(managedAgentsTmp, 'AGENTS.md'), '<!-- BEGIN Sneakoscope Codex GX MANAGED BLOCK -->\nold managed rules\n<!-- END Sneakoscope Codex GX MANAGED BLOCK -->\n');
|
|
@@ -2904,17 +2873,17 @@ async function selftest() {
|
|
|
2904
2873
|
const madMission = await createMission(tmp, { mode: 'mad-sks', prompt: '$MAD-SKS selftest scoped DB override' });
|
|
2905
2874
|
await writeJsonAtomic(path.join(madMission.dir, 'team-gate.json'), { schema_version: 1, passed: false, team_roster_confirmed: true });
|
|
2906
2875
|
const madState = { mission_id: madMission.id, mode: 'TEAM', route_command: '$Team', stop_gate: 'team-gate.json', mad_sks_active: true, mad_sks_modifier: true, mad_sks_gate_file: 'team-gate.json' };
|
|
2876
|
+
const columnCleanupSql = 'alter table users ' + 'dr' + 'op column legacy_name;';
|
|
2877
|
+
const madColumnCleanupDecision = await checkDbOperation(tmp, madState, { tool_name: 'mcp__supabase__execute_sql', sql: columnCleanupSql }, { duringNoQuestion: false });
|
|
2878
|
+
if (madColumnCleanupDecision.action !== 'allow') throw new Error('selftest failed: MAD-SKS column cleanup was not allowed');
|
|
2907
2879
|
const tableRemovalSql = 'dr' + 'op table users;';
|
|
2908
|
-
const
|
|
2909
|
-
if (
|
|
2910
|
-
|
|
2911
|
-
const
|
|
2912
|
-
if (
|
|
2913
|
-
const madConfirmedState = await readJson(stateFile(tmp), {});
|
|
2914
|
-
const madConfirmedDecision = await checkDbOperation(tmp, madConfirmedState, { tool_name: 'mcp__supabase__execute_sql', sql: tableRemovalSql }, { duringNoQuestion: false });
|
|
2915
|
-
if (madConfirmedDecision.action !== 'allow') throw new Error('selftest failed: MAD-SKS confirmed table deletion was not allowed in the short confirmation window');
|
|
2880
|
+
const madTableRemovalDecision = await checkDbOperation(tmp, madState, { tool_name: 'mcp__supabase__execute_sql', sql: tableRemovalSql }, { duringNoQuestion: false });
|
|
2881
|
+
if (madTableRemovalDecision.action !== 'block') throw new Error('selftest failed: MAD-SKS catastrophic table removal was not blocked');
|
|
2882
|
+
const allRowsSql = 'de' + 'lete from users;';
|
|
2883
|
+
const madAllRowsDecision = await checkDbOperation(tmp, madState, { tool_name: 'mcp__supabase__execute_sql', sql: allRowsSql }, { duringNoQuestion: false });
|
|
2884
|
+
if (madAllRowsDecision.action !== 'block') throw new Error('selftest failed: MAD-SKS all-row DML was not blocked');
|
|
2916
2885
|
await writeJsonAtomic(path.join(madMission.dir, 'team-gate.json'), { schema_version: 1, passed: true, team_roster_confirmed: true, permissions_deactivated: true });
|
|
2917
|
-
const madClosedDecision = await checkDbOperation(tmp,
|
|
2886
|
+
const madClosedDecision = await checkDbOperation(tmp, madState, { tool_name: 'mcp__supabase__execute_sql', sql: columnCleanupSql }, { duringNoQuestion: false });
|
|
2918
2887
|
if (madClosedDecision.action !== 'block') throw new Error('selftest failed: MAD-SKS permission persisted after gate close');
|
|
2919
2888
|
const nonDbDecision = await checkDbOperation(tmp, {}, { command: 'npm test' }, { duringNoQuestion: true });
|
|
2920
2889
|
if (nonDbDecision.action !== 'allow') throw new Error('selftest failed: non-DB command blocked by DB guard');
|
|
@@ -2924,6 +2893,15 @@ async function selftest() {
|
|
|
2924
2893
|
if (evalReport.candidate.wiki?.voxel_schema !== 'sks.wiki-voxel.v1' || evalReport.candidate.wiki?.voxel_rows < 1) throw new Error('selftest failed: eval did not include voxel overlay metrics');
|
|
2925
2894
|
const harnessReport = harnessGrowthReport({});
|
|
2926
2895
|
if (!harnessReport.forgetting.fixture.passed || !harnessReport.warp.views.includes('Harness Experiments View') || !harnessReport.reliability.tool_error_taxonomy.includes('Unknown')) throw new Error('selftest failed: harness growth fixture incomplete');
|
|
2896
|
+
const proofField = await proofFieldFixture();
|
|
2897
|
+
if (!proofField.validation.ok || !validateProofFieldReport(proofField.report).ok) throw new Error('selftest failed: proof field report invalid');
|
|
2898
|
+
if (!proofField.checks.route_cone_selected || !proofField.checks.cli_cone_selected || !proofField.checks.catastrophic_guard_present || !proofField.checks.negative_release_work_recorded) throw new Error('selftest failed: proof field fixture checks incomplete');
|
|
2899
|
+
const workflowPerf = await runWorkflowPerfBench(tmp, {
|
|
2900
|
+
iterations: 2,
|
|
2901
|
+
intent: 'small CLI help surface update',
|
|
2902
|
+
changedFiles: ['src/cli/maintenance-commands.mjs', 'src/core/routes.mjs']
|
|
2903
|
+
});
|
|
2904
|
+
if (!validateWorkflowPerfReport(workflowPerf).ok || workflowPerf.metrics.decision_mode !== 'fast_lane' || !workflowPerf.metrics.fast_lane_eligible) throw new Error('selftest failed: workflow perf proof field did not produce a valid fast lane report');
|
|
2927
2905
|
if (classifyToolError({ message: 'operation timed out' }) !== 'Timeout' || classifyToolError({ message: 'unclassified weirdness' }) !== 'Unknown') throw new Error('selftest failed: tool error taxonomy classification');
|
|
2928
2906
|
const coord = rgbaToWikiCoord({ r: 12, g: 34, b: 56, a: 255 });
|
|
2929
2907
|
if (coord.schema !== 'sks.wiki-coordinate.v1' || coord.xyzw.length !== 4) throw new Error('selftest failed: RGBA wiki coordinate conversion');
|
|
@@ -2995,49 +2973,3 @@ async function selftest() {
|
|
|
2995
2973
|
console.log('ㅅㅋㅅ selftest passed.');
|
|
2996
2974
|
console.log(`temp: ${tmp}`);
|
|
2997
2975
|
}
|
|
2998
|
-
|
|
2999
|
-
async function db(sub, args) {
|
|
3000
|
-
const root = await sksRoot();
|
|
3001
|
-
if (sub === 'policy') {
|
|
3002
|
-
console.log(JSON.stringify(await loadDbSafetyPolicy(root), null, 2));
|
|
3003
|
-
return;
|
|
3004
|
-
}
|
|
3005
|
-
if (sub === 'scan') {
|
|
3006
|
-
const report = await scanDbSafety(root, { includeMigrations: flag(args, '--migrations') });
|
|
3007
|
-
console.log(JSON.stringify(report, null, 2));
|
|
3008
|
-
process.exitCode = report.ok ? 0 : 2;
|
|
3009
|
-
return;
|
|
3010
|
-
}
|
|
3011
|
-
if (sub === 'mcp-config') {
|
|
3012
|
-
const projectIdx = args.indexOf('--project-ref');
|
|
3013
|
-
const featuresIdx = args.indexOf('--features');
|
|
3014
|
-
const projectRef = projectIdx >= 0 ? args[projectIdx + 1] : '<project_ref>';
|
|
3015
|
-
const features = featuresIdx >= 0 ? args[featuresIdx + 1] : 'database,docs';
|
|
3016
|
-
console.log(JSON.stringify(safeSupabaseMcpConfig({ projectRef, readOnly: true, features }), null, 2));
|
|
3017
|
-
return;
|
|
3018
|
-
}
|
|
3019
|
-
if (sub === 'classify' || sub === 'check') {
|
|
3020
|
-
const sqlIdx = args.indexOf('--sql');
|
|
3021
|
-
const commandIdx = args.indexOf('--command');
|
|
3022
|
-
const fileIdx = args.indexOf('--file');
|
|
3023
|
-
let result;
|
|
3024
|
-
if (fileIdx >= 0 && args[fileIdx + 1]) result = await checkSqlFile(path.resolve(args[fileIdx + 1]));
|
|
3025
|
-
else if (commandIdx >= 0 && args[commandIdx + 1]) result = classifyCommand(args[commandIdx + 1]);
|
|
3026
|
-
else if (sqlIdx >= 0 && args[sqlIdx + 1]) result = classifySql(args[sqlIdx + 1]);
|
|
3027
|
-
else if (sub === 'check' && args[0]) result = await checkSqlFile(path.resolve(args[0]));
|
|
3028
|
-
else result = classifySql(args.join(' ').trim());
|
|
3029
|
-
console.log(JSON.stringify(result, null, 2));
|
|
3030
|
-
process.exitCode = ['destructive', 'write', 'possible_db'].includes(result.level) ? 2 : 0;
|
|
3031
|
-
return;
|
|
3032
|
-
}
|
|
3033
|
-
if (sub === 'scan-payload') {
|
|
3034
|
-
const raw = await fsp.readFile(0, 'utf8');
|
|
3035
|
-
const payload = raw.trim() ? JSON.parse(raw) : {};
|
|
3036
|
-
const decision = await checkDbOperation(root, {}, payload, { duringNoQuestion: false });
|
|
3037
|
-
console.log(JSON.stringify(decision, null, 2));
|
|
3038
|
-
process.exitCode = decision.action === 'block' ? 2 : 0;
|
|
3039
|
-
return;
|
|
3040
|
-
}
|
|
3041
|
-
console.error('Usage: sks db policy | db scan [--migrations] | db mcp-config --project-ref <id> | db check --sql "..." | db check --command "..." | db check --file file.sql');
|
|
3042
|
-
process.exitCode = 1;
|
|
3043
|
-
}
|
|
@@ -23,22 +23,80 @@ import { writeEffortDecision } from '../core/effort-orchestrator.mjs';
|
|
|
23
23
|
import { createWorkOrderLedger, writeWorkOrderLedger } from '../core/work-order-ledger.mjs';
|
|
24
24
|
import { writeFromChatImgArtifacts } from '../core/from-chat-img-forensics.mjs';
|
|
25
25
|
import { renderTeamDashboardState, writeTeamDashboardState } from '../core/team-dashboard-renderer.mjs';
|
|
26
|
-
import { runPerfBench } from '../core/perf-bench.mjs';
|
|
26
|
+
import { runPerfBench, runWorkflowPerfBench } from '../core/perf-bench.mjs';
|
|
27
|
+
import { writeProofFieldReport } from '../core/proof-field.mjs';
|
|
27
28
|
import { GOAL_BRIDGE_ARTIFACT, GOAL_WORKFLOW_ARTIFACT, updateGoalWorkflow, writeGoalWorkflow } from '../core/goal-workflow.mjs';
|
|
28
29
|
import { scanCodeStructure, writeCodeStructureReport } from '../core/code-structure.mjs';
|
|
29
30
|
import { writeMemorySweepReport } from '../core/memory-governor.mjs';
|
|
30
31
|
import { cleanupWarpTeamView, launchWarpTeamView } from '../core/warp-ui.mjs';
|
|
31
32
|
import { writeSkillForgeReport } from '../core/skill-forge.mjs';
|
|
32
33
|
import { writeMistakeMemoryReport } from '../core/mistake-memory.mjs';
|
|
33
|
-
import { scanDbSafety } from '../core/db-safety.mjs';
|
|
34
|
+
import { checkDbOperation, checkSqlFile, classifyCommand, classifySql, loadDbSafetyPolicy, safeSupabaseMcpConfig, scanDbSafety } from '../core/db-safety.mjs';
|
|
34
35
|
import { harnessGrowthReport, writeHarnessGrowthReport } from '../core/evaluation.mjs';
|
|
35
36
|
|
|
36
37
|
const flag = (args, name) => args.includes(name);
|
|
37
38
|
const promptOf = (args) => args.filter((x) => !String(x).startsWith('--')).join(' ').trim();
|
|
38
39
|
const TEAM_SESSION_CLEANUP_ARTIFACT = 'team-session-cleanup.json';
|
|
40
|
+
const REPOSITORY_URL = 'https://github.com/mandarange/Sneakoscope-Codex.git';
|
|
39
41
|
|
|
40
42
|
async function resolveMissionId(root, arg) { return (!arg || arg === 'latest') ? findLatestMission(root) : arg; }
|
|
41
43
|
|
|
44
|
+
export function quickstartCommand() {
|
|
45
|
+
console.log(`ㅅㅋㅅ Quickstart
|
|
46
|
+
|
|
47
|
+
First install and bootstrap this project:
|
|
48
|
+
npm i -g sneakoscope
|
|
49
|
+
sks root
|
|
50
|
+
sks bootstrap
|
|
51
|
+
sks
|
|
52
|
+
|
|
53
|
+
Use outside a project:
|
|
54
|
+
sks root
|
|
55
|
+
sks deps check
|
|
56
|
+
sks team "global mission"
|
|
57
|
+
|
|
58
|
+
If warp is missing:
|
|
59
|
+
sks deps install warp
|
|
60
|
+
|
|
61
|
+
Initialize this project for CLI and Codex App:
|
|
62
|
+
sks setup --bootstrap
|
|
63
|
+
|
|
64
|
+
Open from terminal:
|
|
65
|
+
sks
|
|
66
|
+
sks --auto-review --high
|
|
67
|
+
sks auto-review start --high
|
|
68
|
+
|
|
69
|
+
Verify:
|
|
70
|
+
sks deps check
|
|
71
|
+
sks codex-app check
|
|
72
|
+
sks warp check
|
|
73
|
+
sks auto-review status
|
|
74
|
+
sks doctor --fix
|
|
75
|
+
sks context7 check
|
|
76
|
+
sks selftest --mock
|
|
77
|
+
sks commands
|
|
78
|
+
sks dollar-commands
|
|
79
|
+
|
|
80
|
+
If hooks cannot find the command:
|
|
81
|
+
sks fix-path
|
|
82
|
+
|
|
83
|
+
Project-only install:
|
|
84
|
+
npm i -D sneakoscope
|
|
85
|
+
npx sks setup --install-scope project
|
|
86
|
+
|
|
87
|
+
Local-only install artifacts:
|
|
88
|
+
sks setup --local-only
|
|
89
|
+
# writes generated SKS files but excludes .sneakoscope/, .codex/, .agents/, AGENTS.md through .git/info/exclude
|
|
90
|
+
# user-owned AGENTS.md is preserved; an existing SKS managed block is refreshed
|
|
91
|
+
|
|
92
|
+
Default project setup writes the same SKS generated-file patterns into the project .gitignore.
|
|
93
|
+
|
|
94
|
+
GitHub install for unreleased commits:
|
|
95
|
+
npm i -g git+${REPOSITORY_URL}
|
|
96
|
+
sks bootstrap
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
99
|
+
|
|
42
100
|
export async function researchCommand(sub, args) {
|
|
43
101
|
if (sub === 'prepare') return researchPrepare(args);
|
|
44
102
|
if (sub === 'run') return researchRun(args);
|
|
@@ -385,6 +443,52 @@ export async function hproofCommand(sub, args) {
|
|
|
385
443
|
console.log(JSON.stringify(await evaluateDoneGate(root, id), null, 2));
|
|
386
444
|
}
|
|
387
445
|
|
|
446
|
+
export async function dbCommand(sub, args = []) {
|
|
447
|
+
const root = await sksRoot();
|
|
448
|
+
if (sub === 'policy') {
|
|
449
|
+
console.log(JSON.stringify(await loadDbSafetyPolicy(root), null, 2));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (sub === 'scan') {
|
|
453
|
+
const report = await scanDbSafety(root, { includeMigrations: flag(args, '--migrations') });
|
|
454
|
+
console.log(JSON.stringify(report, null, 2));
|
|
455
|
+
process.exitCode = report.ok ? 0 : 2;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (sub === 'mcp-config') {
|
|
459
|
+
const projectIdx = args.indexOf('--project-ref');
|
|
460
|
+
const featuresIdx = args.indexOf('--features');
|
|
461
|
+
const projectRef = projectIdx >= 0 ? args[projectIdx + 1] : '<project_ref>';
|
|
462
|
+
const features = featuresIdx >= 0 ? args[featuresIdx + 1] : 'database,docs';
|
|
463
|
+
console.log(JSON.stringify(safeSupabaseMcpConfig({ projectRef, readOnly: true, features }), null, 2));
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
if (sub === 'classify' || sub === 'check') {
|
|
467
|
+
const sqlIdx = args.indexOf('--sql');
|
|
468
|
+
const commandIdx = args.indexOf('--command');
|
|
469
|
+
const fileIdx = args.indexOf('--file');
|
|
470
|
+
let result;
|
|
471
|
+
if (fileIdx >= 0 && args[fileIdx + 1]) result = await checkSqlFile(path.resolve(args[fileIdx + 1]));
|
|
472
|
+
else if (commandIdx >= 0 && args[commandIdx + 1]) result = classifyCommand(args[commandIdx + 1]);
|
|
473
|
+
else if (sqlIdx >= 0 && args[sqlIdx + 1]) result = classifySql(args[sqlIdx + 1]);
|
|
474
|
+
else if (sub === 'check' && args[0]) result = await checkSqlFile(path.resolve(args[0]));
|
|
475
|
+
else result = classifySql(args.join(' ').trim());
|
|
476
|
+
console.log(JSON.stringify(result, null, 2));
|
|
477
|
+
process.exitCode = ['destructive', 'write', 'possible_db'].includes(result.level) ? 2 : 0;
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (sub === 'scan-payload') {
|
|
481
|
+
const raw = await fsp.readFile(0, 'utf8');
|
|
482
|
+
const payload = raw.trim() ? JSON.parse(raw) : {};
|
|
483
|
+
const decision = await checkDbOperation(root, {}, payload, { duringNoQuestion: false });
|
|
484
|
+
console.log(JSON.stringify(decision, null, 2));
|
|
485
|
+
process.exitCode = decision.action === 'block' ? 2 : 0;
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
console.error('Usage: sks db policy | db scan [--migrations] | db mcp-config --project-ref <id> | db check --sql "..." | db check --command "..." | db check --file file.sql');
|
|
489
|
+
process.exitCode = 1;
|
|
490
|
+
}
|
|
491
|
+
|
|
388
492
|
export async function validateArtifactsCommand(args = []) {
|
|
389
493
|
const root = await sksRoot();
|
|
390
494
|
const missionArg = args[0] && !String(args[0]).startsWith('--') ? args[0] : 'latest';
|
|
@@ -404,12 +508,32 @@ export async function validateArtifactsCommand(args = []) {
|
|
|
404
508
|
}
|
|
405
509
|
|
|
406
510
|
export async function perfCommand(sub, args = []) {
|
|
407
|
-
if (
|
|
408
|
-
console.error('Usage: sks perf run [--json] [--iterations N]');
|
|
511
|
+
if (!['run', 'workflow'].includes(sub)) {
|
|
512
|
+
console.error('Usage: sks perf run|workflow [--json] [--iterations N] [--intent "task"] [--changed file1,file2]');
|
|
409
513
|
process.exitCode = 1;
|
|
410
514
|
return;
|
|
411
515
|
}
|
|
412
516
|
const root = await sksRoot();
|
|
517
|
+
if (sub === 'workflow') {
|
|
518
|
+
const changedRaw = readFlagValue(args, '--changed', null);
|
|
519
|
+
const report = await runWorkflowPerfBench(root, {
|
|
520
|
+
iterations: readFlagValue(args, '--iterations', 3),
|
|
521
|
+
intent: readFlagValue(args, '--intent', positionalArgs(args).join(' ')),
|
|
522
|
+
changedFiles: changedRaw ? changedRaw.split(',').filter(Boolean) : undefined
|
|
523
|
+
});
|
|
524
|
+
const outPath = path.join(root, '.sneakoscope', 'reports', `workflow-perf-${Date.now()}.json`);
|
|
525
|
+
await writeJsonAtomic(outPath, report);
|
|
526
|
+
if (flag(args, '--json')) return console.log(JSON.stringify({ ...report, report_path: outPath }, null, 2));
|
|
527
|
+
console.log('SKS Workflow Performance');
|
|
528
|
+
console.log(`Mode: ${report.metrics.decision_mode}`);
|
|
529
|
+
console.log(`Fast lane: ${report.metrics.fast_lane_eligible ? 'yes' : 'no'}`);
|
|
530
|
+
console.log(`Proof Field p95: ${report.metrics.proof_field_build_ms_p95}ms`);
|
|
531
|
+
console.log(`Proof cones: ${report.metrics.proof_cone_count}`);
|
|
532
|
+
console.log(`Negative work skipped: ${report.metrics.negative_work_skipped_count}`);
|
|
533
|
+
console.log(`Next: ${report.recommendation.next.join('; ')}`);
|
|
534
|
+
console.log(`Report: ${path.relative(root, outPath)}`);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
413
537
|
const report = await runPerfBench(root, { iterations: readFlagValue(args, '--iterations', 3) });
|
|
414
538
|
const outPath = path.join(root, '.sneakoscope', 'reports', `perf-${Date.now()}.json`);
|
|
415
539
|
await writeJsonAtomic(outPath, report);
|
|
@@ -421,6 +545,34 @@ export async function perfCommand(sub, args = []) {
|
|
|
421
545
|
console.log(`Report: ${path.relative(root, outPath)}`);
|
|
422
546
|
}
|
|
423
547
|
|
|
548
|
+
export async function proofFieldCommand(sub, args = []) {
|
|
549
|
+
const action = sub || 'scan';
|
|
550
|
+
if (!['scan', 'help', '--help'].includes(action)) {
|
|
551
|
+
console.error('Usage: sks proof-field scan [--json] [--intent "task"] [--changed file1,file2]');
|
|
552
|
+
process.exitCode = 1;
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (action === 'help' || action === '--help') {
|
|
556
|
+
console.log('Usage: sks proof-field scan [--json] [--intent "task"] [--changed file1,file2]');
|
|
557
|
+
console.log('Build a Potential Proof Field report: proof cones, negative-work cache, and fast-lane eligibility for the current change set.');
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const root = await sksRoot();
|
|
561
|
+
const changedRaw = readFlagValue(args, '--changed', null);
|
|
562
|
+
const report = await writeProofFieldReport(root, {
|
|
563
|
+
intent: readFlagValue(args, '--intent', positionalArgs(args).join(' ')),
|
|
564
|
+
changedFiles: changedRaw ? changedRaw.split(',').filter(Boolean) : undefined
|
|
565
|
+
});
|
|
566
|
+
if (flag(args, '--json')) return console.log(JSON.stringify(report, null, 2));
|
|
567
|
+
console.log('SKS Proof Field');
|
|
568
|
+
console.log(`Mode: ${report.fast_lane_decision.mode}`);
|
|
569
|
+
console.log(`Eligible: ${report.fast_lane_decision.eligible ? 'yes' : 'no'}`);
|
|
570
|
+
if (report.fast_lane_decision.blockers.length) console.log(`Blockers: ${report.fast_lane_decision.blockers.join(', ')}`);
|
|
571
|
+
console.log(`Proof cones: ${report.proof_cones.map((cone) => cone.id).join(', ')}`);
|
|
572
|
+
console.log(`Verification: ${report.fast_lane_decision.verification.join('; ')}`);
|
|
573
|
+
console.log(`Report: ${path.relative(root, report.report_path)}`);
|
|
574
|
+
}
|
|
575
|
+
|
|
424
576
|
export async function harnessCommand(sub, args = []) {
|
|
425
577
|
const action = sub || 'fixture';
|
|
426
578
|
if (!['fixture', 'review'].includes(action)) {
|
|
@@ -1084,7 +1236,7 @@ export async function statsCommand(args) {
|
|
|
1084
1236
|
|
|
1085
1237
|
function positionalArgs(args = []) {
|
|
1086
1238
|
const out = [];
|
|
1087
|
-
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors', '--lines']);
|
|
1239
|
+
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors', '--lines', '--intent', '--changed']);
|
|
1088
1240
|
for (let i = 0; i < args.length; i++) {
|
|
1089
1241
|
const arg = String(args[i]);
|
|
1090
1242
|
if (valueFlags.has(arg)) {
|
package/src/core/db-safety.mjs
CHANGED
|
@@ -226,7 +226,27 @@ function hasTableRemovalRisk(cls = {}) {
|
|
|
226
226
|
...(cls.sql?.reasons || []),
|
|
227
227
|
...(cls.command?.reasons || [])
|
|
228
228
|
]);
|
|
229
|
-
return ['drop_table', '
|
|
229
|
+
return ['drop_table', 'truncate'].some((reason) => reasons.has(reason));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function hasMadSksCatastrophicDbRisk(cls = {}) {
|
|
233
|
+
const reasons = new Set([
|
|
234
|
+
...(cls.reasons || []),
|
|
235
|
+
...(cls.sql?.reasons || []),
|
|
236
|
+
...(cls.command?.reasons || [])
|
|
237
|
+
]);
|
|
238
|
+
return [
|
|
239
|
+
'drop_database',
|
|
240
|
+
'drop_schema',
|
|
241
|
+
'drop_table',
|
|
242
|
+
'truncate',
|
|
243
|
+
'delete_without_where',
|
|
244
|
+
'update_without_where',
|
|
245
|
+
'supabase_db_reset',
|
|
246
|
+
'prisma_migrate_reset',
|
|
247
|
+
'postgres_database_admin_command'
|
|
248
|
+
].some((reason) => reasons.has(reason))
|
|
249
|
+
|| cls.toolReasons?.includes?.('dangerous_supabase_management_tool');
|
|
230
250
|
}
|
|
231
251
|
|
|
232
252
|
function isMadSksRouteState(state = {}) {
|
|
@@ -259,23 +279,32 @@ export function evaluateDbSafety({ classification, policy = DEFAULT_DB_SAFETY_PO
|
|
|
259
279
|
if (cls.level === 'safe') return { allowed: true, action: 'allow', reasons: ['read_only_operation'], classification: cls };
|
|
260
280
|
if (cls.level === 'possible_db') return { allowed: !noQuestion, action: noQuestion ? 'block' : 'warn', reasons: noQuestion ? ['unknown_database_operation_blocked_during_no_question_run'] : ['unknown_database_operation'], classification: cls };
|
|
261
281
|
if (madSks?.active && (cls.level === 'write' || cls.level === 'destructive')) {
|
|
262
|
-
if (
|
|
282
|
+
if (hasMadSksCatastrophicDbRisk(cls)) {
|
|
263
283
|
return {
|
|
264
284
|
allowed: false,
|
|
265
|
-
action: '
|
|
266
|
-
reasons: ['
|
|
285
|
+
action: 'block',
|
|
286
|
+
reasons: ['mad_sks_catastrophic_db_operation_blocked'],
|
|
267
287
|
classification: cls,
|
|
268
288
|
effective,
|
|
269
|
-
mad_sks: {
|
|
289
|
+
mad_sks: {
|
|
290
|
+
active: true,
|
|
291
|
+
catastrophic_safety_guard_active: true,
|
|
292
|
+
blocked_categories: ['whole_database_or_table_removal', 'all_rows_delete_or_update', 'dangerous_project_management']
|
|
293
|
+
}
|
|
270
294
|
};
|
|
271
295
|
}
|
|
272
296
|
return {
|
|
273
297
|
allowed: true,
|
|
274
298
|
action: 'allow',
|
|
275
|
-
reasons:
|
|
299
|
+
reasons: ['mad_sks_scoped_override_active'],
|
|
276
300
|
classification: cls,
|
|
277
301
|
effective,
|
|
278
|
-
mad_sks: {
|
|
302
|
+
mad_sks: {
|
|
303
|
+
active: true,
|
|
304
|
+
sks_db_constraints_removed: true,
|
|
305
|
+
catastrophic_safety_guard_active: true,
|
|
306
|
+
supabase_mcp_schema_cleanup_allowed: true
|
|
307
|
+
}
|
|
279
308
|
};
|
|
280
309
|
}
|
|
281
310
|
if (cls.level === 'destructive') reasons.push('destructive_database_operation_blocked_always');
|
|
@@ -384,6 +413,13 @@ export async function checkSqlFile(file) {
|
|
|
384
413
|
}
|
|
385
414
|
|
|
386
415
|
export function dbBlockReason(decision) {
|
|
416
|
+
if ((decision.reasons || []).includes('mad_sks_catastrophic_db_operation_blocked')) {
|
|
417
|
+
return [
|
|
418
|
+
'Sneakoscope Codex MAD-SKS catastrophic database safeguard blocked this operation.',
|
|
419
|
+
'MAD-SKS opens Supabase MCP column/schema cleanup, direct execute SQL, and normal DB writes only while the mission gate is active.',
|
|
420
|
+
'Whole database/table removal, all-row value wipes, database reset, and dangerous project or branch management remain blocked.'
|
|
421
|
+
].join(' ');
|
|
422
|
+
}
|
|
387
423
|
if ((decision.reasons || []).includes('mad_sks_table_delete_requires_user_confirmation_30s')) {
|
|
388
424
|
return [
|
|
389
425
|
'Sneakoscope Codex MAD-SKS gate paused a table deletion operation.',
|
|
@@ -87,21 +87,23 @@ export function buildDecisionContract({ mission, schema, answers }) {
|
|
|
87
87
|
mad_sks_mode: madSks ? 'explicit_invocation_only' : false,
|
|
88
88
|
production_database_writes_allowed: madSks ? 'mad_sks_scoped' : false,
|
|
89
89
|
mcp_direct_execute_sql_writes_allowed: madSks ? 'mad_sks_scoped' : false,
|
|
90
|
-
db_reset_allowed:
|
|
91
|
-
db_drop_allowed: madSks ? '
|
|
92
|
-
db_truncate_allowed:
|
|
90
|
+
db_reset_allowed: false,
|
|
91
|
+
db_drop_allowed: madSks ? 'column_and_non_catastrophic_schema_cleanup_only' : false,
|
|
92
|
+
db_truncate_allowed: false,
|
|
93
93
|
db_mass_delete_update_allowed: madSks ? 'mad_sks_scoped' : false
|
|
94
94
|
},
|
|
95
95
|
database_safety: {
|
|
96
|
-
policy: madSks ? '
|
|
96
|
+
policy: madSks ? 'mad_sks_scoped_override_with_catastrophic_db_guard' : 'destructive_denied_always',
|
|
97
97
|
supabase_mcp_recommended_url: 'https://mcp.supabase.com/mcp?project_ref=<project_ref>&read_only=true&features=database,docs',
|
|
98
98
|
allowed_targets_for_write: madSks ? ['main_branch', 'production', 'local_dev', 'preview_branch', 'supabase_branch'] : ['local_dev', 'preview_branch', 'supabase_branch'],
|
|
99
|
-
forbidden_operations: madSks ? ['
|
|
99
|
+
forbidden_operations: madSks ? ['DROP_DATABASE', 'DROP_SCHEMA', 'DROP_TABLE', 'TRUNCATE', 'DELETE_WITHOUT_WHERE', 'UPDATE_WITHOUT_WHERE', 'DB_RESET', 'PROJECT_DELETE', 'BRANCH_RESET_OR_MERGE_OR_DELETE'] : ['DROP', 'TRUNCATE', 'DELETE_WITHOUT_WHERE', 'UPDATE_WITHOUT_WHERE', 'DB_RESET', 'DB_PUSH', 'PROJECT_DELETE', 'BRANCH_RESET_OR_MERGE_OR_DELETE', 'DISABLE_RLS', 'BROAD_GRANT_REVOKE'],
|
|
100
100
|
mad_sks_scope: madSks ? {
|
|
101
101
|
active_only_when_prompt_contains: '$MAD-SKS',
|
|
102
102
|
may_combine_with_primary_route: true,
|
|
103
103
|
deactivates_when_active_mission_gate_passes: true,
|
|
104
|
-
|
|
104
|
+
supabase_mcp_schema_cleanup_allowed: true,
|
|
105
|
+
direct_execute_sql_allowed: true,
|
|
106
|
+
catastrophic_safety_guard_active: true
|
|
105
107
|
} : null,
|
|
106
108
|
migration_apply_allowed: answers.DB_MIGRATION_APPLY_ALLOWED || 'no',
|
|
107
109
|
read_only_query_limit: answers.DB_READ_ONLY_QUERY_LIMIT || '1000'
|
package/src/core/fsx.mjs
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
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.6.
|
|
8
|
+
export const PACKAGE_VERSION = '0.6.89';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
package/src/core/init.mjs
CHANGED
|
@@ -9,6 +9,8 @@ import { installVersionGitHook } from './version-manager.mjs';
|
|
|
9
9
|
import { CODEX_COMPUTER_USE_ONLY_POLICY, DOLLAR_COMMANDS, DOLLAR_COMMAND_ALIASES, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, RECOMMENDED_MCP_SERVERS, RECOMMENDED_SKILLS, chatCaptureIntakeText, context7ConfigToml, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
|
|
10
10
|
|
|
11
11
|
const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
|
|
12
|
+
const SKS_GENERATED_GIT_PATTERNS = ['.sneakoscope/', '.codex/', '.agents/', 'AGENTS.md'];
|
|
13
|
+
|
|
12
14
|
function reflectionInstructionText(commandPrefix = 'sks') {
|
|
13
15
|
return `Post-route reflection: full routes load \`reflection\` after work/tests and before final; DFix/Answer/Help/Wiki/SKS discovery are exempt. Write reflection.md; record only real misses/gaps, or no_issue_acknowledged. For lessons, append TriWiki claim rows to ${REFLECTION_MEMORY_PATH}. Run "${commandPrefix} wiki refresh" or pack, validate, then pass reflection-gate.json.`;
|
|
14
16
|
}
|
|
@@ -107,7 +109,9 @@ export async function initProject(root, opts = {}) {
|
|
|
107
109
|
];
|
|
108
110
|
for (const d of dirs) await ensureDir(path.join(root, d));
|
|
109
111
|
const localExclude = localOnly ? await ensureLocalOnlyGitExclude(root) : null;
|
|
112
|
+
const sharedIgnore = localOnly ? null : await ensureSharedGitIgnore(root);
|
|
110
113
|
if (localExclude?.path) created.push(`${path.relative(root, localExclude.path)} local-only excludes`);
|
|
114
|
+
if (sharedIgnore?.changed) created.push(`${path.relative(root, sharedIgnore.path)} SKS generated files ignore`);
|
|
111
115
|
|
|
112
116
|
await writeJsonAtomic(path.join(sine, 'manifest.json'), {
|
|
113
117
|
package: 'sneakoscope',
|
|
@@ -165,6 +169,8 @@ export async function initProject(root, opts = {}) {
|
|
|
165
169
|
},
|
|
166
170
|
git: {
|
|
167
171
|
local_only: localOnly,
|
|
172
|
+
ignore_path: sharedIgnore?.path ? path.relative(root, sharedIgnore.path) : null,
|
|
173
|
+
ignored_patterns: sharedIgnore?.patterns || [],
|
|
168
174
|
exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : null,
|
|
169
175
|
excluded_patterns: localExclude?.patterns || [],
|
|
170
176
|
versioning: {
|
|
@@ -198,6 +204,8 @@ export async function initProject(root, opts = {}) {
|
|
|
198
204
|
git: {
|
|
199
205
|
...(policy.git || {}),
|
|
200
206
|
local_only: localOnly || Boolean(policy.git?.local_only),
|
|
207
|
+
ignore_path: sharedIgnore?.path ? path.relative(root, sharedIgnore.path) : policy.git?.ignore_path || null,
|
|
208
|
+
ignored_patterns: sharedIgnore?.patterns || policy.git?.ignored_patterns || [],
|
|
201
209
|
exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : policy.git?.exclude_path || null,
|
|
202
210
|
excluded_patterns: localExclude?.patterns || policy.git?.excluded_patterns || [],
|
|
203
211
|
versioning: {
|
|
@@ -267,6 +275,8 @@ export async function initProject(root, opts = {}) {
|
|
|
267
275
|
installation: installPolicy(scope, commandPrefix),
|
|
268
276
|
git: {
|
|
269
277
|
local_only: localOnly,
|
|
278
|
+
ignore_path: sharedIgnore?.path ? path.relative(root, sharedIgnore.path) : null,
|
|
279
|
+
ignored_patterns: sharedIgnore?.patterns || [],
|
|
270
280
|
exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : null,
|
|
271
281
|
excluded_patterns: localExclude?.patterns || [],
|
|
272
282
|
versioning: {
|
|
@@ -445,10 +455,33 @@ policy = "Deny destructive database operations, credential exfiltration, persist
|
|
|
445
455
|
return { created };
|
|
446
456
|
}
|
|
447
457
|
|
|
458
|
+
async function ensureSharedGitIgnore(root) {
|
|
459
|
+
const patterns = SKS_GENERATED_GIT_PATTERNS;
|
|
460
|
+
const ignorePath = path.join(root, '.gitignore');
|
|
461
|
+
const markerStart = '# BEGIN Sneakoscope Codex generated files';
|
|
462
|
+
const markerEnd = '# END Sneakoscope Codex generated files';
|
|
463
|
+
const managedBlock = `${markerStart}\n${patterns.join('\n')}\n${markerEnd}\n`;
|
|
464
|
+
const current = await readText(ignorePath, '');
|
|
465
|
+
if (current.includes(markerStart)) {
|
|
466
|
+
const re = new RegExp(`${escapeRegExp(markerStart)}[\\s\\S]*?${escapeRegExp(markerEnd)}\\n?`);
|
|
467
|
+
const next = current.replace(re, managedBlock);
|
|
468
|
+
if (next !== current) await writeTextAtomic(ignorePath, next.endsWith('\n') ? next : `${next}\n`);
|
|
469
|
+
return { path: ignorePath, patterns, changed: next !== current };
|
|
470
|
+
}
|
|
471
|
+
const existing = new Set(current.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
472
|
+
const missing = patterns.filter((pattern) => !existing.has(pattern));
|
|
473
|
+
if (!missing.length) return { path: ignorePath, patterns, changed: false };
|
|
474
|
+
const block = missing.length === patterns.length
|
|
475
|
+
? managedBlock
|
|
476
|
+
: `${markerStart}\n${missing.join('\n')}\n${markerEnd}\n`;
|
|
477
|
+
await writeTextAtomic(ignorePath, `${current.trimEnd()}${current.trim() ? '\n\n' : ''}${block}`);
|
|
478
|
+
return { path: ignorePath, patterns, changed: true };
|
|
479
|
+
}
|
|
480
|
+
|
|
448
481
|
async function ensureLocalOnlyGitExclude(root) {
|
|
449
482
|
const gitDir = await resolveGitDir(root);
|
|
450
483
|
if (!gitDir) return { path: null, patterns: [] };
|
|
451
|
-
const patterns =
|
|
484
|
+
const patterns = SKS_GENERATED_GIT_PATTERNS;
|
|
452
485
|
const excludePath = path.join(gitDir, 'info', 'exclude');
|
|
453
486
|
await ensureDir(path.dirname(excludePath));
|
|
454
487
|
const markerStart = '# Sneakoscope Codex local-only generated files';
|
|
@@ -505,7 +538,7 @@ export async function installSkills(root) {
|
|
|
505
538
|
'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. Do not use for ordinary code edits.\n`,
|
|
506
539
|
'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`,
|
|
507
540
|
'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`,
|
|
508
|
-
'mad-sks': `---\nname: mad-sks\ndescription: Explicit high-risk authorization modifier for $MAD-SKS scoped Supabase MCP DB permission widening.\n---\n\nUse only when the user explicitly invokes $MAD-SKS. 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 DB permission applies only while the active mission gate is open,
|
|
541
|
+
'mad-sks': `---\nname: mad-sks\ndescription: Explicit high-risk authorization modifier for $MAD-SKS scoped Supabase MCP DB permission widening.\n---\n\nUse only when the user explicitly invokes $MAD-SKS. 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 DB permission applies only while the active mission gate is open, must be deactivated when the task ends, and opens Supabase MCP column/schema cleanup, direct execute SQL, and normal DB write permissions. Keep only catastrophic database-wipe safeguards: whole database/table removal, all-row delete/update, reset, and dangerous project/branch management remain blocked. Do not carry MAD-SKS permission into later prompts or routes.\n`,
|
|
509
542
|
'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`,
|
|
510
543
|
'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`,
|
|
511
544
|
'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline for execution prompts; Answer and DFix bypass it.\n---\n\nClassify intent: Answer only for real questions; question-shaped implicit instructions, complaints, and mandatory-policy statements route to Team. DFix handles tiny design/content; code defaults to Team unless safety/research/GX route fits. Infer goal, target, constraints, acceptance, risk, and smallest safe route. Ask only scope/safety/behavior/acceptance-changing questions; otherwise seal inferred answers. Code work surfaces route/guard/scopes, materializes team-roster.json from default or explicit counts before implementation, compiles concrete Team runtime graph/inbox artifacts after consensus, and parent owns integration/tests/Context7/Honest Mode.\n\n${chatCaptureIntakeText()}\n\nDesign: read design.md; if missing use design-system-builder; use imagegen for image/logo/raster. TriWiki context-tracking SSOT: .sneakoscope/wiki/context-pack.json; read only the latest coordinate+voxel overlay pack before every route stage, run sks wiki refresh/pack after changes, validate before handoffs/final.\n`,
|
package/src/core/perf-bench.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { performance } from 'node:perf_hooks';
|
|
3
3
|
import { dirSize, fileSize, nowIso, packageRoot, runProcess, writeJsonAtomic } from './fsx.mjs';
|
|
4
|
+
import { buildProofField, validateProofFieldReport } from './proof-field.mjs';
|
|
4
5
|
|
|
5
6
|
export const DEFAULT_PERF_BUDGETS = {
|
|
6
7
|
cli_startup_ms_p95: 250,
|
|
@@ -8,6 +9,8 @@ export const DEFAULT_PERF_BUDGETS = {
|
|
|
8
9
|
context_build_ms_p95: 500,
|
|
9
10
|
artifact_validation_ms_p95: 150,
|
|
10
11
|
dashboard_render_ms_p95: 100,
|
|
12
|
+
proof_field_build_ms_p95: 150,
|
|
13
|
+
workflow_scan_ms_p95: 1000,
|
|
11
14
|
fast_selftest_ms_p95: 5000,
|
|
12
15
|
package_size_kb_max: 1024,
|
|
13
16
|
notes: 'Package payload budget is 1024KB because the current low-dependency CLI payload is already above 512KB; reduce only with measured justification.'
|
|
@@ -52,6 +55,90 @@ export async function runPerfBench(root, opts = {}) {
|
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
export async function runWorkflowPerfBench(root, opts = {}) {
|
|
59
|
+
const iterations = Math.max(1, Math.min(20, Number(opts.iterations || 3)));
|
|
60
|
+
const intent = String(opts.intent || '').trim();
|
|
61
|
+
const changedFiles = normalizeChangedFiles(opts.changedFiles);
|
|
62
|
+
const proofFieldBuild = [];
|
|
63
|
+
let proofField = null;
|
|
64
|
+
for (let i = 0; i < iterations; i++) {
|
|
65
|
+
const t0 = performance.now();
|
|
66
|
+
proofField = await buildProofField(root, {
|
|
67
|
+
intent,
|
|
68
|
+
changedFiles: changedFiles.length ? changedFiles : undefined
|
|
69
|
+
});
|
|
70
|
+
proofFieldBuild.push(performance.now() - t0);
|
|
71
|
+
}
|
|
72
|
+
const proofValidation = validateProofFieldReport(proofField);
|
|
73
|
+
const verification = proofField?.fast_lane_decision?.verification || [];
|
|
74
|
+
const negativeWork = proofField?.negative_work_cache || [];
|
|
75
|
+
const estimatedSavedWork = negativeWork.filter((item) => item.disposition === 'skip_with_evidence').length;
|
|
76
|
+
const proofFieldMsP95 = Math.round(percentile(proofFieldBuild, 95));
|
|
77
|
+
const workflowScanMsP95 = proofFieldMsP95;
|
|
78
|
+
return {
|
|
79
|
+
schema_version: 1,
|
|
80
|
+
measured_at: nowIso(),
|
|
81
|
+
theory: 'Potential Proof Field',
|
|
82
|
+
iterations,
|
|
83
|
+
intent: intent || null,
|
|
84
|
+
budgets: DEFAULT_PERF_BUDGETS,
|
|
85
|
+
metrics: {
|
|
86
|
+
proof_field_build_ms_p95: proofFieldMsP95,
|
|
87
|
+
workflow_scan_ms_p95: workflowScanMsP95,
|
|
88
|
+
decision_mode: proofField?.fast_lane_decision?.mode || null,
|
|
89
|
+
fast_lane_eligible: Boolean(proofField?.fast_lane_decision?.eligible),
|
|
90
|
+
proof_cone_count: proofField?.proof_cones?.length || 0,
|
|
91
|
+
verification_count: verification.length,
|
|
92
|
+
negative_work_skipped_count: estimatedSavedWork,
|
|
93
|
+
proof_field_valid: proofValidation.ok
|
|
94
|
+
},
|
|
95
|
+
proof_field: proofField,
|
|
96
|
+
recommendation: workflowRecommendation(proofField, proofValidation),
|
|
97
|
+
raw: {
|
|
98
|
+
proof_field_build_ms: proofFieldBuild.map((value) => Math.round(value))
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function validateWorkflowPerfReport(report = {}) {
|
|
104
|
+
const issues = [];
|
|
105
|
+
if (report.schema_version !== 1) issues.push('schema_version');
|
|
106
|
+
if (report.theory !== 'Potential Proof Field') issues.push('theory');
|
|
107
|
+
if (!report.metrics || !Number.isFinite(Number(report.metrics.proof_field_build_ms_p95))) issues.push('proof_field_build_ms_p95');
|
|
108
|
+
if (!report.metrics?.decision_mode) issues.push('decision_mode');
|
|
109
|
+
if (!report.proof_field || !validateProofFieldReport(report.proof_field).ok) issues.push('proof_field');
|
|
110
|
+
if (!report.recommendation?.mode) issues.push('recommendation');
|
|
111
|
+
return { ok: issues.length === 0, issues };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function normalizeChangedFiles(files) {
|
|
115
|
+
return [...new Set((files || []).flatMap((value) => String(value || '').split(',')).map((file) => file.trim()).filter(Boolean))]
|
|
116
|
+
.sort();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function workflowRecommendation(proofField, validation) {
|
|
120
|
+
if (!validation.ok) {
|
|
121
|
+
return {
|
|
122
|
+
mode: 'full_proof',
|
|
123
|
+
reason: `proof field invalid: ${validation.issues.join(', ')}`,
|
|
124
|
+
next: ['repair proof-field report generation', 'rerun sks perf workflow --json']
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const decision = proofField.fast_lane_decision;
|
|
128
|
+
if (decision.eligible) {
|
|
129
|
+
return {
|
|
130
|
+
mode: 'fast_lane',
|
|
131
|
+
reason: 'selected proof cones are narrow and all unrelated work is safely cached as negative work',
|
|
132
|
+
next: decision.verification
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
mode: decision.mode,
|
|
137
|
+
reason: decision.blockers.length ? `blocked by ${decision.blockers.join(', ')}` : 'balanced proof required',
|
|
138
|
+
next: decision.verification
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
55
142
|
async function packagePayloadSize(root) {
|
|
56
143
|
let total = 0;
|
|
57
144
|
for (const rel of ['bin', 'src']) total += await dirSize(path.join(root, rel));
|
package/src/core/pipeline.mjs
CHANGED
|
@@ -258,16 +258,16 @@ function applyMadSksAuthorizationToSchema(schema = {}) {
|
|
|
258
258
|
DATABASE_TARGET_ENVIRONMENT: 'main_branch',
|
|
259
259
|
DATABASE_WRITE_MODE: 'mad_sks_full_mcp_write_for_invocation',
|
|
260
260
|
SUPABASE_MCP_POLICY: 'mad_sks_project_scoped_write_for_invocation',
|
|
261
|
-
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: '
|
|
261
|
+
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: 'mad_sks_scoped_except_catastrophic_db_wipe',
|
|
262
262
|
DB_BACKUP_OR_BRANCH_REQUIRED: 'recommended_but_not_required_in_mad_sks',
|
|
263
|
-
DB_MAX_BLAST_RADIUS: '
|
|
263
|
+
DB_MAX_BLAST_RADIUS: 'mad_sks_active_invocation_only_catastrophic_wipe_blocked',
|
|
264
264
|
DB_MIGRATION_APPLY_ALLOWED: 'mad_sks_active_invocation_only',
|
|
265
265
|
DB_READ_ONLY_QUERY_LIMIT: '100'
|
|
266
266
|
};
|
|
267
267
|
schema.inference_notes = {
|
|
268
268
|
...(schema.inference_notes || {}),
|
|
269
269
|
MAD_SKS_MODE: 'explicit dollar command modifier is the permission boundary',
|
|
270
|
-
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: 'MAD-SKS
|
|
270
|
+
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: 'MAD-SKS opens Supabase MCP DB cleanup while blocking only catastrophic database wipe operations'
|
|
271
271
|
};
|
|
272
272
|
schema.slots = (schema.slots || []).filter((slot) => !/^(DB_|DATABASE_|DESTRUCTIVE_DB_|SUPABASE_MCP_POLICY$)/.test(slot.id));
|
|
273
273
|
return schema;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { nowIso, readText, rel, runProcess, sha256, writeJsonAtomic } from './fsx.mjs';
|
|
3
|
+
|
|
4
|
+
export const PROOF_FIELD_SCHEMA_VERSION = 1;
|
|
5
|
+
|
|
6
|
+
export const INVARIANT_LEDGER = Object.freeze([
|
|
7
|
+
{ id: 'db-catastrophic-guard', severity: 'critical', description: 'Database/table wipe, all-row DML, reset, and dangerous project/branch operations remain blocked.' },
|
|
8
|
+
{ id: 'honest-evidence', severity: 'critical', description: 'Final claims must be backed by current code, tests, artifacts, or explicitly marked unverified.' },
|
|
9
|
+
{ id: 'route-surface-consistency', severity: 'high', description: 'User-visible route surfaces must agree across CLI help, command catalog, generated skills, and docs.' },
|
|
10
|
+
{ id: 'triwiki-hydratable-context', severity: 'high', description: 'TriWiki context must remain coordinate+voxel backed and hydratable by source/hash/anchor.' },
|
|
11
|
+
{ id: 'no-unrequested-fallback-code', severity: 'high', description: 'Do not add substitute paths, mocks, shims, or fallback behavior unless explicitly requested.' }
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export const PROOF_CONE_DEFINITIONS = Object.freeze([
|
|
15
|
+
{
|
|
16
|
+
id: 'db_safety',
|
|
17
|
+
surfaces: ['database', 'supabase', 'mad-sks'],
|
|
18
|
+
match: [/db-safety|supabase|migration|rls|schema|sql/i],
|
|
19
|
+
verification: ['npm run packcheck', 'npm run selftest -- --mock', 'sks db scan --json'],
|
|
20
|
+
negative_work: ['browser_ui_e2e', 'visual_snapshot']
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'route_surface',
|
|
24
|
+
surfaces: ['routes', 'skills', 'cli-help', 'docs'],
|
|
25
|
+
match: [/routes\.mjs|init\.mjs|codex-app|README|AGENTS|skills/i],
|
|
26
|
+
verification: ['npm run packcheck', 'npm run selftest -- --mock', 'sks commands --json'],
|
|
27
|
+
negative_work: ['database_migration', 'from_chat_img_forensics']
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'cli_runtime',
|
|
31
|
+
surfaces: ['cli', 'commands', 'runtime'],
|
|
32
|
+
match: [/src\/cli|bin\/sks|maintenance-commands|fsx|codex-adapter/i],
|
|
33
|
+
verification: ['npm run packcheck', 'node ./bin/sks.mjs commands --json', 'node ./bin/sks.mjs proof-field scan --json'],
|
|
34
|
+
negative_work: ['browser_ui_e2e']
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'context_memory',
|
|
38
|
+
surfaces: ['triwiki', 'memory', 'evaluation'],
|
|
39
|
+
match: [/triwiki|wiki|memory|evaluation|perf-bench|proof-field/i],
|
|
40
|
+
verification: ['npm run packcheck', 'node ./bin/sks.mjs eval run --json', 'node ./bin/sks.mjs wiki validate .sneakoscope/wiki/context-pack.json'],
|
|
41
|
+
negative_work: ['database_migration', 'browser_ui_e2e']
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'release_surface',
|
|
45
|
+
surfaces: ['package', 'changelog', 'release'],
|
|
46
|
+
match: [/package\.json|package-lock\.json|CHANGELOG|README/i],
|
|
47
|
+
verification: ['npm run release:check'],
|
|
48
|
+
negative_work: []
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'visual_forensic',
|
|
52
|
+
surfaces: ['from-chat-img', 'visual', 'qa-loop'],
|
|
53
|
+
match: [/from-chat-img|visual|screenshot|qa-loop|dogfood/i],
|
|
54
|
+
verification: ['npm run packcheck', 'npm run selftest -- --mock'],
|
|
55
|
+
negative_work: ['database_migration']
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
export async function buildProofField(root, opts = {}) {
|
|
60
|
+
const changedFiles = normalizeChangedFiles(opts.changedFiles || await gitChangedFiles(root));
|
|
61
|
+
const intent = String(opts.intent || '').trim();
|
|
62
|
+
const selectedCones = selectProofCones(changedFiles, intent);
|
|
63
|
+
const risk = riskSummary(changedFiles, selectedCones, intent);
|
|
64
|
+
const negativeWork = buildNegativeWorkCache(selectedCones, risk);
|
|
65
|
+
const fastLane = fastLaneDecision({ changedFiles, selectedCones, risk, negativeWork });
|
|
66
|
+
const sourceHash = await sourceDigest(root, changedFiles);
|
|
67
|
+
return {
|
|
68
|
+
schema_version: PROOF_FIELD_SCHEMA_VERSION,
|
|
69
|
+
generated_at: nowIso(),
|
|
70
|
+
theory: 'Potential Proof Field',
|
|
71
|
+
intent: intent || null,
|
|
72
|
+
source_hash: sourceHash,
|
|
73
|
+
changed_files: changedFiles,
|
|
74
|
+
invariant_ledger: INVARIANT_LEDGER,
|
|
75
|
+
proof_cones: selectedCones,
|
|
76
|
+
negative_work_cache: negativeWork,
|
|
77
|
+
fast_lane_decision: fastLane,
|
|
78
|
+
next_action: nextAction(fastLane)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function writeProofFieldReport(root, opts = {}) {
|
|
83
|
+
const report = await buildProofField(root, opts);
|
|
84
|
+
const file = path.join(root, '.sneakoscope', 'reports', `proof-field-${Date.now()}.json`);
|
|
85
|
+
await writeJsonAtomic(file, report);
|
|
86
|
+
return { ...report, report_path: file };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function validateProofFieldReport(report = {}) {
|
|
90
|
+
const issues = [];
|
|
91
|
+
if (report.schema_version !== PROOF_FIELD_SCHEMA_VERSION) issues.push('schema_version');
|
|
92
|
+
if (!Array.isArray(report.invariant_ledger) || !report.invariant_ledger.length) issues.push('invariant_ledger');
|
|
93
|
+
if (!Array.isArray(report.proof_cones)) issues.push('proof_cones');
|
|
94
|
+
if (!Array.isArray(report.negative_work_cache)) issues.push('negative_work_cache');
|
|
95
|
+
if (!report.fast_lane_decision?.mode) issues.push('fast_lane_decision');
|
|
96
|
+
if (report.fast_lane_decision?.mode === 'fast_lane' && report.fast_lane_decision?.escalate_on?.length < 1) issues.push('fast_lane_escalation');
|
|
97
|
+
return { ok: issues.length === 0, issues };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function proofFieldFixture() {
|
|
101
|
+
const report = await buildProofField(process.cwd(), {
|
|
102
|
+
intent: 'small CLI help surface update',
|
|
103
|
+
changedFiles: ['src/cli/maintenance-commands.mjs', 'src/core/routes.mjs']
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
report,
|
|
107
|
+
validation: validateProofFieldReport(report),
|
|
108
|
+
checks: {
|
|
109
|
+
route_cone_selected: report.proof_cones.some((cone) => cone.id === 'route_surface'),
|
|
110
|
+
cli_cone_selected: report.proof_cones.some((cone) => cone.id === 'cli_runtime'),
|
|
111
|
+
catastrophic_guard_present: report.invariant_ledger.some((item) => item.id === 'db-catastrophic-guard'),
|
|
112
|
+
negative_release_work_recorded: report.negative_work_cache.some((item) => item.id === 'full_release_gate' && item.disposition === 'skip_with_evidence')
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function gitChangedFiles(root) {
|
|
118
|
+
const result = await runProcess('git', ['diff', '--name-only', 'HEAD', '--'], { cwd: root, timeoutMs: 10000, maxOutputBytes: 128 * 1024 });
|
|
119
|
+
if (result.code !== 0) return [];
|
|
120
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function normalizeChangedFiles(files = []) {
|
|
124
|
+
return [...new Set((files || []).flatMap((value) => String(value || '').split(',')).map((file) => file.trim()).filter(Boolean))]
|
|
125
|
+
.sort();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function selectProofCones(files, intent) {
|
|
129
|
+
const haystack = `${files.join('\n')}\n${intent || ''}`;
|
|
130
|
+
const selected = PROOF_CONE_DEFINITIONS
|
|
131
|
+
.filter((cone) => cone.match.some((re) => re.test(haystack)))
|
|
132
|
+
.map((cone) => ({
|
|
133
|
+
id: cone.id,
|
|
134
|
+
surfaces: cone.surfaces,
|
|
135
|
+
verification: cone.verification,
|
|
136
|
+
matched_files: files.filter((file) => cone.match.some((re) => re.test(file)))
|
|
137
|
+
}));
|
|
138
|
+
if (!selected.length) {
|
|
139
|
+
selected.push({
|
|
140
|
+
id: 'generic_local_change',
|
|
141
|
+
surfaces: ['unknown'],
|
|
142
|
+
verification: ['npm run packcheck', 'focused relevant tests or documented justification'],
|
|
143
|
+
matched_files: files
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return selected;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function riskSummary(files, cones, intent) {
|
|
150
|
+
const text = `${files.join('\n')}\n${intent || ''}`;
|
|
151
|
+
const flags = {
|
|
152
|
+
database: /\b(db|database|supabase|sql|migration|rls|schema)\b/i.test(text),
|
|
153
|
+
security: /\b(auth|permission|token|secret|security|권한|보안)\b/i.test(text),
|
|
154
|
+
visual_forensic: /from-chat-img|screenshot|visual|이미지|스크린샷/i.test(text),
|
|
155
|
+
release: /package\.json|package-lock\.json|CHANGELOG|release|publish/i.test(text),
|
|
156
|
+
broad_change: files.length > 3,
|
|
157
|
+
unknown_surface: cones.some((cone) => cone.id === 'generic_local_change')
|
|
158
|
+
};
|
|
159
|
+
const score = Object.values(flags).filter(Boolean).length;
|
|
160
|
+
const level = score >= 3 ? 'high' : score === 2 ? 'medium' : score === 1 ? 'low' : 'minimal';
|
|
161
|
+
return { level, score, flags };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildNegativeWorkCache(cones, risk) {
|
|
165
|
+
const required = new Set(cones.flatMap((cone) => cone.verification));
|
|
166
|
+
const candidates = new Set(cones.flatMap((cone) => cone.negative_work || []));
|
|
167
|
+
const out = [];
|
|
168
|
+
for (const id of candidates) {
|
|
169
|
+
const blockedByRisk = (id === 'database_migration' && risk.flags.database)
|
|
170
|
+
|| (id === 'browser_ui_e2e' && risk.flags.visual_forensic);
|
|
171
|
+
out.push({
|
|
172
|
+
id,
|
|
173
|
+
disposition: blockedByRisk ? 'not_skipped_risk_present' : 'skip_with_evidence',
|
|
174
|
+
reason: blockedByRisk ? 'risk flag requires explicit verification' : 'selected proof cones do not touch this surface'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (!required.has('npm run release:check') && !risk.flags.release) {
|
|
178
|
+
out.push({ id: 'full_release_gate', disposition: 'skip_with_evidence', reason: 'no package/release surface in selected cones' });
|
|
179
|
+
}
|
|
180
|
+
return out.sort((a, b) => a.id.localeCompare(b.id));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function fastLaneDecision({ changedFiles, selectedCones, risk, negativeWork }) {
|
|
184
|
+
const verification = [...new Set(selectedCones.flatMap((cone) => cone.verification))];
|
|
185
|
+
const safeSkips = negativeWork.filter((item) => item.disposition === 'skip_with_evidence').map((item) => item.id);
|
|
186
|
+
const blockers = [];
|
|
187
|
+
if (!changedFiles.length) blockers.push('no_changed_files');
|
|
188
|
+
if (changedFiles.length > 3) blockers.push('broad_change_set');
|
|
189
|
+
if (risk.flags.database) blockers.push('database_surface');
|
|
190
|
+
if (risk.flags.security) blockers.push('security_surface');
|
|
191
|
+
if (risk.flags.visual_forensic) blockers.push('visual_forensic_surface');
|
|
192
|
+
if (risk.flags.unknown_surface) blockers.push('unknown_surface');
|
|
193
|
+
const mode = blockers.length ? (risk.level === 'high' ? 'full_proof' : 'balanced') : 'fast_lane';
|
|
194
|
+
return {
|
|
195
|
+
mode,
|
|
196
|
+
eligible: mode === 'fast_lane',
|
|
197
|
+
blockers,
|
|
198
|
+
verification,
|
|
199
|
+
negative_work: safeSkips,
|
|
200
|
+
escalate_on: ['verification_failed', 'proof_cone_unknown', 'source_hash_changed', 'unsupported_claim', 'user_scope_expanded']
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function nextAction(decision) {
|
|
205
|
+
if (decision.mode === 'fast_lane') return 'apply minimal patch, run listed verification, then Honest Mode against the proof field report';
|
|
206
|
+
if (decision.mode === 'balanced') return 'narrow the change set or run parent-led implementation with listed verification and reviewer escalation if checks fail';
|
|
207
|
+
return 'use full Team/Honest proof path; fast lane is intentionally blocked for this risk state';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function sourceDigest(root, files) {
|
|
211
|
+
const rows = [];
|
|
212
|
+
for (const file of files) {
|
|
213
|
+
const abs = path.resolve(root, file);
|
|
214
|
+
if (!abs.startsWith(path.resolve(root))) continue;
|
|
215
|
+
const text = await readText(abs, null);
|
|
216
|
+
rows.push([rel(root, abs), text == null ? null : sha256(text).slice(0, 16)]);
|
|
217
|
+
}
|
|
218
|
+
return sha256(JSON.stringify(rows)).slice(0, 24);
|
|
219
|
+
}
|
package/src/core/questions.mjs
CHANGED
|
@@ -13,16 +13,16 @@ function buildMadSksQuestionSchema(prompt) {
|
|
|
13
13
|
const task = String(prompt || '').trim() || 'MAD-SKS scoped database override';
|
|
14
14
|
return {
|
|
15
15
|
schema_version: 1,
|
|
16
|
-
description: 'MAD-SKS is explicit-invocation-only. It auto-seals because the dollar command itself is the permission boundary;
|
|
16
|
+
description: 'MAD-SKS is explicit-invocation-only. It auto-seals because the dollar command itself is the permission boundary; while active, SKS opens Supabase MCP schema cleanup and direct DB write permissions, leaving only catastrophic database-wipe safeguards.',
|
|
17
17
|
prompt,
|
|
18
18
|
domain_hints: ['db', 'mad-sks'],
|
|
19
19
|
inferred_answers: {
|
|
20
20
|
GOAL_PRECISE: `명시적인 MAD-SKS 호출 범위에서만 DB 권한 조건을 넓혀 작업한다: ${task}`,
|
|
21
21
|
ACCEPTANCE_CRITERIA: [
|
|
22
22
|
'$MAD-SKS is listed in dollar commands and routes to MADSKS mode',
|
|
23
|
-
'
|
|
23
|
+
'Supabase MCP column cleanup, schema cleanup, direct execute SQL, and normal DB writes are allowed only while the active MAD-SKS mission gate remains open',
|
|
24
24
|
'the widened permission is inactive after the MAD-SKS gate is passed or permissions_deactivated is true',
|
|
25
|
-
'table
|
|
25
|
+
'whole database/table removal and all-row delete/update operations remain blocked as non-sensible catastrophic operations'
|
|
26
26
|
],
|
|
27
27
|
NON_GOALS: [],
|
|
28
28
|
PUBLIC_API_CHANGE_ALLOWED: 'yes_if_needed',
|
|
@@ -33,21 +33,21 @@ function buildMadSksQuestionSchema(prompt) {
|
|
|
33
33
|
RISK_BOUNDARY: [
|
|
34
34
|
'MAD-SKS permission widening is explicit-invocation-only',
|
|
35
35
|
'MAD-SKS permission widening does not persist after the active task gate closes',
|
|
36
|
-
'
|
|
36
|
+
'catastrophic database wipe operations remain blocked even in MAD-SKS'
|
|
37
37
|
],
|
|
38
38
|
MAD_SKS_MODE: 'explicit_invocation_only',
|
|
39
39
|
DATABASE_TARGET_ENVIRONMENT: 'main_branch',
|
|
40
40
|
DATABASE_WRITE_MODE: 'mad_sks_full_mcp_write_for_invocation',
|
|
41
41
|
SUPABASE_MCP_POLICY: 'mad_sks_project_scoped_write_for_invocation',
|
|
42
|
-
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: '
|
|
42
|
+
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: 'mad_sks_scoped_except_catastrophic_db_wipe',
|
|
43
43
|
DB_BACKUP_OR_BRANCH_REQUIRED: 'recommended_but_not_required_in_mad_sks',
|
|
44
|
-
DB_MAX_BLAST_RADIUS: '
|
|
44
|
+
DB_MAX_BLAST_RADIUS: 'mad_sks_active_invocation_only_catastrophic_wipe_blocked',
|
|
45
45
|
DB_MIGRATION_APPLY_ALLOWED: 'mad_sks_active_invocation_only',
|
|
46
46
|
DB_READ_ONLY_QUERY_LIMIT: '100'
|
|
47
47
|
},
|
|
48
48
|
inference_notes: {
|
|
49
49
|
MAD_SKS_MODE: 'explicit dollar command is the permission boundary',
|
|
50
|
-
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: 'MAD-SKS
|
|
50
|
+
DESTRUCTIVE_DB_OPERATIONS_ALLOWED: 'MAD-SKS opens Supabase MCP DB cleanup while blocking only catastrophic database wipe operations'
|
|
51
51
|
},
|
|
52
52
|
slots: []
|
|
53
53
|
};
|
package/src/core/routes.mjs
CHANGED
|
@@ -7,7 +7,7 @@ export const FROM_CHAT_IMG_CHECKLIST_ARTIFACT = 'from-chat-img-checklist.md';
|
|
|
7
7
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT = 'from-chat-img-temp-triwiki.json';
|
|
8
8
|
export const FROM_CHAT_IMG_QA_LOOP_ARTIFACT = 'from-chat-img-qa-loop.json';
|
|
9
9
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS = 5;
|
|
10
|
-
export const USAGE_TOPICS = 'install|setup|bootstrap|root|deps|warp|auto-review|team|qa-loop|goal|research|db|codex-app|dfix|design|imagegen|dollar|context7|pipeline|reasoning|guard|conflicts|versioning|eval|harness|hproof|gx|wiki|code-structure';
|
|
10
|
+
export const USAGE_TOPICS = 'install|setup|bootstrap|root|deps|warp|auto-review|team|qa-loop|goal|research|db|codex-app|dfix|design|imagegen|dollar|context7|pipeline|reasoning|guard|conflicts|versioning|eval|harness|hproof|gx|wiki|code-structure|proof-field';
|
|
11
11
|
export const CODEX_COMPUTER_USE_EVIDENCE_SOURCE = 'codex_computer_use';
|
|
12
12
|
export const CODEX_COMPUTER_USE_ONLY_POLICY = 'Pipeline UI/browser verification and visual inspection must use Codex Computer Use only. Do not use Playwright, Chrome MCP, Browser Use, Selenium, Puppeteer, or any other browser automation substitute; if Codex Computer Use is unavailable, mark the UI/browser evidence unverified instead of substituting another tool.';
|
|
13
13
|
export const FORBIDDEN_BROWSER_AUTOMATION_RE = /\b(playwright|chrome\s+mcp|browser\s+use|selenium|puppeteer)\b/i;
|
|
@@ -276,14 +276,14 @@ export const ROUTES = [
|
|
|
276
276
|
command: '$MAD-SKS',
|
|
277
277
|
mode: 'MADSKS',
|
|
278
278
|
route: 'explicit scoped database authorization modifier',
|
|
279
|
-
description: 'Explicit high-risk authorization modifier that can be combined with other $ commands to temporarily
|
|
279
|
+
description: 'Explicit high-risk authorization modifier that can be combined with other $ commands to temporarily open Supabase MCP column/schema cleanup, direct execute SQL, and normal DB write permissions for the active invocation, while blocking only catastrophic database-wipe operations.',
|
|
280
280
|
requiredSkills: ['mad-sks', 'db-safety-guard', 'pipeline-runner', 'context7-docs', REFLECTION_SKILL_NAME, 'honest-mode'],
|
|
281
|
-
lifecycle: ['explicit_invocation', 'auto_sealed_permission_scope', '
|
|
281
|
+
lifecycle: ['explicit_invocation', 'auto_sealed_permission_scope', 'scoped_db_cleanup_override', 'catastrophic_db_guard', 'permission_deactivation', 'post_route_reflection', 'honest_mode'],
|
|
282
282
|
context7Policy: 'required',
|
|
283
283
|
reasoningPolicy: 'high',
|
|
284
284
|
stopGate: 'mad-sks-gate.json',
|
|
285
285
|
cliEntrypoint: 'Codex App prompt route only: $MAD-SKS <task>',
|
|
286
|
-
examples: ['$MAD-SKS $Team Supabase MCP로 main 대상 DB
|
|
286
|
+
examples: ['$MAD-SKS $Team Supabase MCP로 main 대상 DB 컬럼 정리를 수행해', '$DB Supabase 점검 $MAD-SKS']
|
|
287
287
|
},
|
|
288
288
|
{
|
|
289
289
|
id: 'GX',
|
|
@@ -376,7 +376,8 @@ export const COMMAND_CATALOG = [
|
|
|
376
376
|
{ name: 'db', usage: 'sks db policy|scan|mcp-config|classify|check ...', description: 'Inspect and enforce database/Supabase safety policy.' },
|
|
377
377
|
{ name: 'eval', usage: 'sks eval run|compare|thresholds ...', description: 'Run deterministic context-quality and performance evidence checks.' },
|
|
378
378
|
{ name: 'harness', usage: 'sks harness fixture|review [--json]', description: 'Run Harness Growth Factory fixtures for forgetting, skills, experiments, tool taxonomy, permissions, MultiAgentV2, and Warp views.' },
|
|
379
|
-
{ name: 'perf', usage: 'sks perf run [--json] [--iterations N]', description: 'Measure structured GPT-5.5/SKS performance budgets
|
|
379
|
+
{ name: 'perf', usage: 'sks perf run|workflow [--json] [--iterations N] [--intent "task"] [--changed file1,file2]', description: 'Measure structured GPT-5.5/SKS performance budgets, including Proof Field workflow decisions and fast-lane evidence.' },
|
|
380
|
+
{ name: 'proof-field', usage: 'sks proof-field scan [--json] [--intent "task"] [--changed file1,file2]', description: 'Analyze Potential Proof Field cones, negative-work cache, and fast-lane eligibility for a change set.' },
|
|
380
381
|
{ name: 'code-structure', usage: 'sks code-structure scan [--json]', description: 'Scan handwritten source files for 1000/2000/3000-line structure gates and split-review exceptions.' },
|
|
381
382
|
{ name: 'validate-artifacts', usage: 'sks validate-artifacts [mission-id|latest] [--json]', description: 'Validate schema-backed mission artifacts for work orders, effort decisions, visual maps, dogfood reports, skills, mistake memory, Team dashboard state, and Honest Mode.' },
|
|
382
383
|
{ name: 'wiki', usage: 'sks wiki coords|pack|refresh|prune|validate ...', description: 'Build, refresh, prune, and validate RGBA/trig LLM Wiki context packs with attention.use_first and attention.hydrate_first for compact recall plus source hydration.' },
|