sneakoscope 5.1.2 → 5.5.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 +5 -1
- 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/commands/doctor.js +32 -3
- package/dist/config/skills-manifest.json +58 -58
- package/dist/core/agents/agent-lifecycle.js +5 -3
- package/dist/core/agents/agent-orchestrator.js +14 -2
- package/dist/core/agents/agent-proof-evidence.js +16 -0
- package/dist/core/agents/native-cli-worker.js +0 -1
- package/dist/core/bench.js +24 -2
- package/dist/core/codex-app/sks-menubar.js +526 -133
- package/dist/core/commands/agent-command.js +35 -1
- package/dist/core/commands/basic-cli.js +61 -4
- package/dist/core/commands/command-utils.js +14 -4
- package/dist/core/commands/goal-command.js +3 -0
- package/dist/core/commands/image-ux-review-command.js +4 -1
- package/dist/core/commands/mad-sks-command.js +10 -1
- package/dist/core/commands/menubar-command.js +2 -1
- package/dist/core/commands/naruto-command.js +12 -7
- package/dist/core/commands/ppt-command.js +40 -1
- package/dist/core/commands/qa-loop-command.js +4 -1
- package/dist/core/commands/release-command.js +3 -3
- package/dist/core/commands/research-command.js +4 -0
- package/dist/core/commands/route-success-helpers.js +2 -0
- package/dist/core/commands/run-command.js +22 -5
- package/dist/core/commands/seo-command.js +31 -4
- package/dist/core/db-safety.js +8 -1
- package/dist/core/doctor/doctor-dirty-planner.js +51 -7
- package/dist/core/feature-fixture-executor.js +262 -0
- package/dist/core/feature-fixtures.js +95 -52
- package/dist/core/fsx.js +1 -1
- package/dist/core/loops/loop-decomposer.js +4 -3
- package/dist/core/loops/loop-planner.js +7 -3
- package/dist/core/loops/loop-worker-runtime.js +6 -0
- package/dist/core/naruto/naruto-real-worker-child.js +1 -1
- package/dist/core/pipeline-internals/runtime-gates.js +39 -3
- package/dist/core/proof/selftest-proof-fixtures.js +8 -0
- package/dist/core/providers/glm/naruto/glm-naruto-requirement-coverage.js +18 -1
- package/dist/core/providers/glm/naruto/glm-naruto-requirement-ledger.js +28 -12
- package/dist/core/questions.js +12 -10
- package/dist/core/release/release-gate-node.js +2 -0
- package/dist/core/routes.js +11 -0
- package/dist/core/stop-gate/stop-gate-check.js +4 -0
- package/dist/core/stop-gate/stop-gate-writer.js +4 -0
- package/dist/core/trust-kernel/trust-report.js +10 -1
- package/dist/core/update/update-migration-state.js +23 -3
- package/dist/core/version.js +1 -1
- package/dist/core/work-order-ledger.js +60 -0
- package/dist/core/zellij/zellij-slot-telemetry.js +1 -1
- package/dist/scripts/doctor-context7-mcp-repair-blackbox.js +16 -0
- package/dist/scripts/doctor-fix-production-blackbox.js +26 -0
- package/dist/scripts/doctor-startup-config-repair-blackbox.js +13 -0
- package/dist/scripts/doctor-supabase-mcp-repair-blackbox.js +14 -0
- package/dist/scripts/gate-policy-audit-check.js +2 -2
- package/dist/scripts/naruto-real-active-pool-runtime-check.js +26 -1
- package/dist/scripts/packlist-performance-check.js +7 -1
- package/dist/scripts/project-skill-dedupe-blackbox.js +36 -0
- package/dist/scripts/release-dag-full-coverage-check.js +1 -1
- package/dist/scripts/release-gate-existence-audit.js +1 -1
- package/dist/scripts/release-gate-planner.js +1 -1
- package/dist/scripts/release-gate-script-parity-check.js +44 -16
- package/dist/scripts/release-metadata-1-19-check.js +2 -2
- package/dist/scripts/sks-menubar-install-check.js +57 -20
- package/dist/scripts/supabase-secret-preservation-blackbox.js +29 -0
- package/dist/scripts/update-preserves-supabase-keys-blackbox.js +27 -0
- package/dist/scripts/zellij-slot-telemetry-runtime-check.js +1 -0
- package/package.json +12 -65
- package/schemas/release/release-gate-node.schema.json +1 -0
- package/bench/tasks/t01-off-by-one/repo/package.json +0 -9
- package/bench/tasks/t01-off-by-one/repo/src/pagination.js +0 -5
- package/bench/tasks/t01-off-by-one/repo/test.js +0 -5
- package/bench/tasks/t01-off-by-one/task.json +0 -13
- package/bench/tasks/t02-signature-cochange/repo/package.json +0 -9
- package/bench/tasks/t02-signature-cochange/repo/src/admin.js +0 -5
- package/bench/tasks/t02-signature-cochange/repo/src/audit.js +0 -5
- package/bench/tasks/t02-signature-cochange/repo/src/labels.js +0 -3
- package/bench/tasks/t02-signature-cochange/repo/src/profile.js +0 -5
- package/bench/tasks/t02-signature-cochange/repo/test.js +0 -9
- package/bench/tasks/t02-signature-cochange/task.json +0 -12
- package/bench/tasks/t03-type-puzzle/repo/package.json +0 -9
- package/bench/tasks/t03-type-puzzle/repo/src/config.js +0 -4
- package/bench/tasks/t03-type-puzzle/repo/test.js +0 -6
- package/bench/tasks/t03-type-puzzle/task.json +0 -13
- package/bench/tasks/t04-refactor-preserve/repo/package.json +0 -9
- package/bench/tasks/t04-refactor-preserve/repo/src/cart.js +0 -8
- package/bench/tasks/t04-refactor-preserve/repo/test.js +0 -4
- package/bench/tasks/t04-refactor-preserve/task.json +0 -12
- package/bench/tasks/t05-performance/repo/package.json +0 -9
- package/bench/tasks/t05-performance/repo/src/pairs.js +0 -9
- package/bench/tasks/t05-performance/repo/test.js +0 -11
- package/bench/tasks/t05-performance/task.json +0 -12
- package/bench/tasks/t06-mistake-rule/repo/package.json +0 -9
- package/bench/tasks/t06-mistake-rule/repo/src/loader.js +0 -6
- package/bench/tasks/t06-mistake-rule/repo/test.js +0 -7
- package/bench/tasks/t06-mistake-rule/task.json +0 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { findLatestMission, loadMission } from '../mission.js';
|
|
2
|
+
import { findLatestMission, loadMission, missionDir } from '../mission.js';
|
|
3
3
|
import { readJson, readText, sksRoot, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
|
|
5
5
|
import { parseAgentCommandArgs } from '../agents/agent-command-surface.js';
|
|
@@ -23,6 +23,9 @@ export async function agentCommand(commandOrArgs = 'agent', maybeArgs = []) {
|
|
|
23
23
|
}
|
|
24
24
|
async function agentRun(parsed) {
|
|
25
25
|
const result = await runNativeAgentOrchestrator({ ...parsed, routeCommand: 'sks agent run', routeBlackboxKind: 'actual_agent_command' });
|
|
26
|
+
if (normalizeRouteName(parsed.route) === 'release-review' && result.mission_id) {
|
|
27
|
+
await writeReleaseReviewNativeAgentPlan(parsed, result);
|
|
28
|
+
}
|
|
26
29
|
return emit(parsed, result, () => {
|
|
27
30
|
console.log('Native agent mission: ' + result.mission_id);
|
|
28
31
|
console.log('Backend: ' + result.backend);
|
|
@@ -30,6 +33,37 @@ async function agentRun(parsed) {
|
|
|
30
33
|
console.log('Proof: ' + result.proof.status);
|
|
31
34
|
});
|
|
32
35
|
}
|
|
36
|
+
function normalizeRouteName(route = '') {
|
|
37
|
+
return String(route || '').replace(/^\$/, '').trim().toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* $Release-Review runs through the generic native agent orchestrator (sks agent run
|
|
41
|
+
* --route "$Release-Review"), which does not itself know it is a release-review run.
|
|
42
|
+
* Write the route-specific release-review-native-agent-plan.json summary artifact here
|
|
43
|
+
* so `route-release-review`'s fixture contract (plan file + agent proof evidence +
|
|
44
|
+
* agent effort policy) is genuinely satisfied by this command rather than only partially.
|
|
45
|
+
*/
|
|
46
|
+
async function writeReleaseReviewNativeAgentPlan(parsed, result) {
|
|
47
|
+
const root = await sksRoot();
|
|
48
|
+
const dir = missionDir(root, result.mission_id);
|
|
49
|
+
const plan = {
|
|
50
|
+
schema: 'sks.release-review-native-agent-plan.v1',
|
|
51
|
+
ok: Boolean(result.ok),
|
|
52
|
+
mission_id: result.mission_id,
|
|
53
|
+
route: '$Release-Review',
|
|
54
|
+
route_command: 'sks agent run',
|
|
55
|
+
backend: result.backend,
|
|
56
|
+
prompt: parsed.prompt,
|
|
57
|
+
roster: {
|
|
58
|
+
agent_count: result.roster?.agent_count ?? parsed.agents ?? null,
|
|
59
|
+
concurrency: result.roster?.concurrency ?? parsed.concurrency ?? null
|
|
60
|
+
},
|
|
61
|
+
proof_status: result.proof?.status || null,
|
|
62
|
+
agent_proof_evidence: 'agents/agent-proof-evidence.json',
|
|
63
|
+
agent_effort_policy: 'agents/agent-effort-policy.json'
|
|
64
|
+
};
|
|
65
|
+
await writeJsonAtomic(path.join(dir, 'release-review-native-agent-plan.json'), plan);
|
|
66
|
+
}
|
|
33
67
|
async function agentPlan(parsed) {
|
|
34
68
|
const root = await sksRoot();
|
|
35
69
|
const roster = buildAgentRoster({ agents: parsed.agents, concurrency: parsed.concurrency, prompt: parsed.prompt, readonly: parsed.readonly });
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import fsp from 'node:fs/promises';
|
|
4
2
|
import { spawnSync } from 'node:child_process';
|
|
5
3
|
import { COMMANDS, LEGACY_COMMAND_ALIASES } from '../../cli/command-registry.js';
|
|
6
4
|
import { flag } from '../../cli/args.js';
|
|
7
5
|
import { printJson, sksTextLogo } from '../../cli/output.js';
|
|
8
6
|
import { ui as cliUi } from '../../cli/cli-theme.js';
|
|
9
|
-
import { PACKAGE_VERSION, ensureDir, exists, nowIso, projectRoot, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
|
|
7
|
+
import { PACKAGE_VERSION, ensureDir, exists, nowIso, projectRoot, readJson, sksRoot, tmpdir, writeJsonAtomic } from '../fsx.js';
|
|
10
8
|
import { COMMAND_CATALOG, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS, USAGE_TOPICS, routePrompt, routeReasoning, reasoningInstruction } from '../routes.js';
|
|
11
9
|
import { initProject, normalizeInstallScope, sksCommandPrefix } from '../init.js';
|
|
12
10
|
import { buildFeatureRegistry, validateFeatureRegistry } from '../feature-registry.js';
|
|
11
|
+
import { runFeatureFixture } from '../feature-fixture-executor.js';
|
|
13
12
|
import { hooksExplainReport } from '../../cli/feature-commands.js';
|
|
14
13
|
import { writeSelftestRouteProof } from '../proof/selftest-proof-fixtures.js';
|
|
15
14
|
import { createMission } from '../mission.js';
|
|
@@ -281,9 +280,11 @@ export async function postinstallCommand(args = []) {
|
|
|
281
280
|
return postinstall({ bootstrap: bootstrapCommand, args });
|
|
282
281
|
}
|
|
283
282
|
export async function selftestCommand(args = []) {
|
|
283
|
+
if (flag(args, '--real'))
|
|
284
|
+
return selftestRealCommand(args);
|
|
284
285
|
process.env.CI = 'true';
|
|
285
286
|
const root = await projectRoot();
|
|
286
|
-
const tmp =
|
|
287
|
+
const tmp = tmpdir('sks-selftest-');
|
|
287
288
|
await ensureDir(tmp);
|
|
288
289
|
const registry = await buildFeatureRegistry({ root });
|
|
289
290
|
const coverage = validateFeatureRegistry(registry);
|
|
@@ -309,6 +310,62 @@ export async function selftestCommand(args = []) {
|
|
|
309
310
|
return printJson(result);
|
|
310
311
|
console.log('SKS selftest passed');
|
|
311
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* `sks selftest --real`: actually spawns every feature fixture whose kind is
|
|
315
|
+
* 'execute' or 'execute_and_validate_artifacts', derives real pass/fail status
|
|
316
|
+
* from the process exit code (and, for execute_and_validate_artifacts, from
|
|
317
|
+
* whether the declared expected_artifacts actually exist and match their
|
|
318
|
+
* declared schema), and writes a JSON report that explicitly lists fixtures
|
|
319
|
+
* skipped because they are mock/wiring_only kind rather than silently omitting
|
|
320
|
+
* them. This is additive: plain `sks selftest --mock` behavior is unchanged.
|
|
321
|
+
*/
|
|
322
|
+
export async function selftestRealCommand(args = []) {
|
|
323
|
+
process.env.CI = 'true';
|
|
324
|
+
const root = await projectRoot();
|
|
325
|
+
const registry = await buildFeatureRegistry({ root });
|
|
326
|
+
const executableKinds = new Set(['execute', 'execute_and_validate_artifacts']);
|
|
327
|
+
const results = [];
|
|
328
|
+
const skippedWiringOnly = [];
|
|
329
|
+
for (const feature of registry.features || []) {
|
|
330
|
+
const fx = feature.fixture || {};
|
|
331
|
+
if (executableKinds.has(fx.kind)) {
|
|
332
|
+
const run = await runFeatureFixture(feature, { root });
|
|
333
|
+
results.push(run);
|
|
334
|
+
}
|
|
335
|
+
else if (fx.kind === 'mock' || fx.kind === 'wiring_only' || fx.quality === 'wiring_only') {
|
|
336
|
+
skippedWiringOnly.push({ id: feature.id, kind: fx.kind, reason: fx.reason || 'mock_or_wiring_only_kind_not_executed' });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const failures = results.filter((row) => !row.ok);
|
|
340
|
+
const ok = failures.length === 0;
|
|
341
|
+
const report = {
|
|
342
|
+
schema: 'sks.selftest-real.v1',
|
|
343
|
+
ok,
|
|
344
|
+
version: PACKAGE_VERSION,
|
|
345
|
+
generated_at: nowIso(),
|
|
346
|
+
root,
|
|
347
|
+
checked: results.length,
|
|
348
|
+
passed: results.filter((row) => row.ok).length,
|
|
349
|
+
failed: failures.length,
|
|
350
|
+
results,
|
|
351
|
+
skipped_wiring_only: skippedWiringOnly,
|
|
352
|
+
skipped_wiring_only_count: skippedWiringOnly.length,
|
|
353
|
+
blockers: failures.flatMap((row) => row.blockers || [])
|
|
354
|
+
};
|
|
355
|
+
const reportPath = path.join(root, '.sneakoscope', 'reports', 'selftest-real-report.json');
|
|
356
|
+
await writeJsonAtomic(reportPath, report);
|
|
357
|
+
const output = { ...report, report_file: path.relative(root, reportPath) };
|
|
358
|
+
if (flag(args, '--json'))
|
|
359
|
+
return printJson(output);
|
|
360
|
+
console.log(`SKS selftest --real: ${ok ? 'passed' : 'blocked'} (checked=${results.length}, skipped_wiring_only=${skippedWiringOnly.length})`);
|
|
361
|
+
console.log(`Report: ${path.relative(root, reportPath)}`);
|
|
362
|
+
if (!ok) {
|
|
363
|
+
for (const blocker of report.blockers)
|
|
364
|
+
console.log(`- ${blocker}`);
|
|
365
|
+
process.exitCode = 1;
|
|
366
|
+
}
|
|
367
|
+
return output;
|
|
368
|
+
}
|
|
312
369
|
export async function reasoningCommand(args = []) {
|
|
313
370
|
const prompt = args.filter((arg) => !String(arg).startsWith('--')).join(' ').trim();
|
|
314
371
|
const route = routePrompt(prompt || '$SKS');
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { findLatestMission, listSessionStates } from '../mission.js';
|
|
2
2
|
import { DOLLAR_SKILL_NAMES, RECOMMENDED_SKILLS } from '../routes.js';
|
|
3
3
|
export const flag = (args = [], name) => args.includes(name);
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// Blindly dropping every "--"-prefixed argv token (the previous behavior of
|
|
5
|
+
// promptOf/positionalArgs) silently deletes any part of an unquoted work-order
|
|
6
|
+
// prompt that happens to contain a flag-lookalike phrase (e.g. "--files 로
|
|
7
|
+
// 확인해라"). Only strip tokens that are actually recognized boolean/global
|
|
8
|
+
// flags; anything else stays in the reconstructed prompt.
|
|
9
|
+
const KNOWN_BOOLEAN_FLAGS = new Set([
|
|
10
|
+
'--json', '--mock', '--execute', '--auto', '--visual', '--research', '--db',
|
|
11
|
+
'--legacy-goal-runtime', '--help', '-h'
|
|
12
|
+
]);
|
|
13
|
+
export function promptOf(args = [], knownFlags = KNOWN_BOOLEAN_FLAGS) {
|
|
14
|
+
return args.filter((x) => !knownFlags.has(String(x))).join(' ').trim();
|
|
6
15
|
}
|
|
7
16
|
export async function resolveMissionId(root, arg) {
|
|
8
17
|
if (arg && arg !== 'latest')
|
|
@@ -56,8 +65,9 @@ export function positionalArgs(args = []) {
|
|
|
56
65
|
i += 1;
|
|
57
66
|
continue;
|
|
58
67
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
68
|
+
if (KNOWN_BOOLEAN_FLAGS.has(arg))
|
|
69
|
+
continue;
|
|
70
|
+
out.push(arg);
|
|
61
71
|
}
|
|
62
72
|
return out;
|
|
63
73
|
}
|
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { exists, readJson, sksRoot } from '../fsx.js';
|
|
3
3
|
import { initProject } from '../init.js';
|
|
4
4
|
import { createMission, loadMission, setCurrent, stateFile } from '../mission.js';
|
|
5
|
+
import { createAndWriteWorkOrderLedgerForPrompt, closeWorkOrderLedgerForRouteResult } from '../work-order-ledger.js';
|
|
5
6
|
import { GOAL_BRIDGE_ARTIFACT, GOAL_WORKFLOW_ARTIFACT, updateGoalWorkflow, writeGoalWorkflow } from '../goal-workflow.js';
|
|
6
7
|
import { flag, promptOf, resolveMissionId } from './command-utils.js';
|
|
7
8
|
import { compileGoalToLoopPlan } from '../loops/goal-to-loop-compat.js';
|
|
@@ -37,11 +38,13 @@ async function goalCreate(args) {
|
|
|
37
38
|
if (flag(args, '--legacy-goal-runtime') || process.env.SKS_LEGACY_GOAL_RUNTIME === '1')
|
|
38
39
|
return legacyGoalCreate(root, prompt, args);
|
|
39
40
|
const { id, dir, mission } = await createMission(root, { mode: 'goal', prompt });
|
|
41
|
+
await createAndWriteWorkOrderLedgerForPrompt(dir, { missionId: id, route: 'Goal', prompt });
|
|
40
42
|
const workflow = await writeGoalWorkflow(dir, mission, { action: 'create', prompt });
|
|
41
43
|
const plan = await compileGoalToLoopPlan({ root, missionId: id, goalText: prompt, legacyGoalOptions: { native_goal: workflow.native_goal } });
|
|
42
44
|
const result = await runLoopPlan({ root, plan, parallelism: 'balanced' });
|
|
43
45
|
const gate = await evaluateLocalGate({ root, missionId: id, gateFile: 'loop-graph-proof.json' });
|
|
44
46
|
const ok = result.ok === true && gate.ok === true;
|
|
47
|
+
await closeWorkOrderLedgerForRouteResult(dir, { ok, blockers: gate.blockers || [] });
|
|
45
48
|
await setCurrent(root, { mission_id: id, mode: 'GOAL', route: 'Goal', route_command: '$Goal', phase: ok ? 'GOAL_LOOP_COMPLETED' : 'GOAL_LOOP_BLOCKED', questions_allowed: true, implementation_allowed: true, native_goal: workflow.native_goal, stop_gate: 'loop-graph-proof.json', stop_gate_blockers: gate.blockers }, { replace: true });
|
|
46
49
|
if (!ok)
|
|
47
50
|
process.exitCode = 1;
|
|
@@ -13,6 +13,7 @@ import { sha256File, imageDimensions } from '../wiki-image/image-hash.js';
|
|
|
13
13
|
import { writeRouteCollaborationArtifacts } from '../agents/route-collaboration-ledger.js';
|
|
14
14
|
import { codexChromeExtensionStatus } from '../codex-app.js';
|
|
15
15
|
import { requireCodexImagegen } from '../imagegen/require-imagegen.js';
|
|
16
|
+
import { evaluateGate } from '../stop-gate/gate-evaluator.js';
|
|
16
17
|
const ONE_BY_ONE_PNG_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMB/axX7V8AAAAASUVORK5CYII=';
|
|
17
18
|
const IMAGE_UX_REVIEW_ARTIFACT_PATHS = {
|
|
18
19
|
policy: IMAGE_UX_REVIEW_POLICY_ARTIFACT,
|
|
@@ -358,9 +359,11 @@ async function statusImageUxReview(root, args = []) {
|
|
|
358
359
|
const gate = await readJson(path.join(dir, 'image-ux-review-gate.json'), null);
|
|
359
360
|
const issueLedger = await readJson(path.join(dir, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT), null);
|
|
360
361
|
const generatedLedger = await readJson(path.join(dir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT), null);
|
|
361
|
-
const
|
|
362
|
+
const gateVerdict = await evaluateGate(root, missionId, 'image-ux-review-gate.json');
|
|
363
|
+
const result = { schema: 'sks.image-ux-review-status.v2', ok: true, mission_id: missionId, gate, gate_verdict: gateVerdict, issue_ledger: issueLedger, generated_review_ledger: generatedLedger };
|
|
362
364
|
if (flag(args, '--json'))
|
|
363
365
|
return printJson(result);
|
|
366
|
+
console.log(gateVerdict.verdict);
|
|
364
367
|
console.log(`Image UX Review mission: ${missionId}`);
|
|
365
368
|
console.log(`Gate: ${gate?.status || (gate?.passed ? 'passed' : gate ? 'present' : 'missing')}`);
|
|
366
369
|
if (gate?.verified_level)
|
|
@@ -26,6 +26,7 @@ import { repairZellijForSks } from '../zellij/zellij-self-heal.js';
|
|
|
26
26
|
import { buildMadGlmLaunchArtifact, buildMadGlmLaunchProfileNoWrite, resolveMadGlmLaunchKey, writeMadGlmCodexWrapper } from '../providers/glm/glm-mad-launch.js';
|
|
27
27
|
import { GLM_MAD_MODE } from '../providers/glm/glm-52-settings.js';
|
|
28
28
|
import { assertNonGlmMadRoute } from '../routes/model-mode-router.js';
|
|
29
|
+
import { evaluateGate } from '../stop-gate/gate-evaluator.js';
|
|
29
30
|
const MAD_SKS_DEFAULT_TTL_MS = 10 * 60 * 1000;
|
|
30
31
|
export async function madHighCommand(args = [], deps = {}) {
|
|
31
32
|
const subcommand = firstSubcommand(args);
|
|
@@ -1013,6 +1014,12 @@ async function madSksSubcommand(subcommand, args = []) {
|
|
|
1013
1014
|
if (subcommand === 'doctor' || subcommand === 'status') {
|
|
1014
1015
|
const protectedCore = resolveProtectedCore({ packageRoot: packageRoot(), targetRoot });
|
|
1015
1016
|
const before = await snapshotProtectedCore(packageRoot(), 'status');
|
|
1017
|
+
const statusMissionId = await findLatestMission(root);
|
|
1018
|
+
const gateVerdict = statusMissionId
|
|
1019
|
+
? await evaluateGate(root, statusMissionId, 'mad-sks-gate.json')
|
|
1020
|
+
: await evaluateGate(root, 'no-mission', 'mad-sks-gate.json');
|
|
1021
|
+
if (!json)
|
|
1022
|
+
console.log(gateVerdict.verdict);
|
|
1016
1023
|
return emit({
|
|
1017
1024
|
schema: subcommand === 'doctor' ? 'sks.mad-sks-doctor.v1' : 'sks.mad-sks-status.v1',
|
|
1018
1025
|
ok: true,
|
|
@@ -1022,7 +1029,9 @@ async function madSksSubcommand(subcommand, args = []) {
|
|
|
1022
1029
|
protected_core_snapshot: before,
|
|
1023
1030
|
protected_core_immutable: !protectedCore.engine_source_exception,
|
|
1024
1031
|
protected_core_write_allowed: protectedCore.engine_source_exception,
|
|
1025
|
-
permission_active: false
|
|
1032
|
+
permission_active: false,
|
|
1033
|
+
mission_id: statusMissionId,
|
|
1034
|
+
gate_verdict: gateVerdict
|
|
1026
1035
|
}, json);
|
|
1027
1036
|
}
|
|
1028
1037
|
if (subcommand === 'close' || subcommand === 'revoke') {
|
|
@@ -97,7 +97,8 @@ function printStatus(result) {
|
|
|
97
97
|
console.log(`Running: ${result.running ? 'yes' : 'no'}`);
|
|
98
98
|
console.log(`Launchd: ${result.launchd.ok ? result.launchd.state || 'present' : result.launchd.error || 'missing'}`);
|
|
99
99
|
console.log(`Version: ${result.build_stamp?.package_version || 'unknown'} (package ${result.package_version})`);
|
|
100
|
-
console.log(`Target: ${result.action_target.sks_entry || '
|
|
100
|
+
console.log(`Target: ${result.action_target.sks_entry || 'runtime'} ${result.action_target.ok ? '(smoke ok)' : '(smoke failed)'}`);
|
|
101
|
+
console.log(`Codex sync:${result.codex_sync.bundle_id || 'disabled'} ${result.codex_sync.codex_running === null ? '' : result.codex_sync.codex_running ? '(running)' : '(not running)'}`);
|
|
101
102
|
console.log(`Signature: ${result.signature.identifier || 'unknown'} ${result.signature.ok ? '(ok)' : '(check)'}`);
|
|
102
103
|
if (result.warnings.length) {
|
|
103
104
|
console.log('Warnings:');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { ui as cliUi } from '../../cli/cli-theme.js';
|
|
3
3
|
import { createMission, findLatestMission, loadMission, setCurrent } from '../mission.js';
|
|
4
|
+
import { createAndWriteWorkOrderLedgerForPrompt, closeWorkOrderLedgerForRouteResult } from '../work-order-ledger.js';
|
|
4
5
|
import { nowIso, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
|
|
5
6
|
import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
|
|
6
7
|
import { classifyOllamaWorkerSlice } from '../agents/agent-runner-ollama.js';
|
|
@@ -95,6 +96,7 @@ async function narutoRun(parsed) {
|
|
|
95
96
|
maxAgentCount: MAX_NARUTO_AGENT_COUNT
|
|
96
97
|
});
|
|
97
98
|
const mission = await createMission(root, { mode: 'naruto', prompt: parsed.prompt });
|
|
99
|
+
await createAndWriteWorkOrderLedgerForPrompt(mission.dir, { missionId: mission.id, route: 'Naruto', prompt: parsed.prompt });
|
|
98
100
|
await writeCodex0138CapabilityArtifacts(root, { missionId: mission.id }).catch(() => null);
|
|
99
101
|
await writeCodex0139CapabilityArtifacts(root, { missionId: mission.id }).catch(() => null);
|
|
100
102
|
const gitWorktreeCapability = writeCapable
|
|
@@ -280,13 +282,13 @@ async function narutoRun(parsed) {
|
|
|
280
282
|
rebalance_ready: rebalancePolicy.ok === true,
|
|
281
283
|
concurrency_governor_ready: true,
|
|
282
284
|
active_pool_simulated: activePool.ok === true,
|
|
283
|
-
verification_dag_ready:
|
|
284
|
-
gpt_final_pack_ready:
|
|
285
|
+
verification_dag_ready: Array.isArray(verificationDag?.tasks),
|
|
286
|
+
gpt_final_pack_ready: Boolean(gptFinalPack?.schema),
|
|
285
287
|
zellij_dashboard_ready: zellijDashboard.ok === true,
|
|
286
288
|
native_agent_proof: false,
|
|
287
289
|
final_arbiter_accepted: false,
|
|
288
290
|
session_cleanup: false,
|
|
289
|
-
blockers: [],
|
|
291
|
+
blockers: [...(workGraph.blockers || []), ...(allocationPolicy.blockers || [])],
|
|
290
292
|
updated_at: nowIso()
|
|
291
293
|
});
|
|
292
294
|
await setCurrent(root, {
|
|
@@ -417,9 +419,11 @@ async function narutoRun(parsed) {
|
|
|
417
419
|
const regressionProof = summarizeRegressionProof(workGraph, result);
|
|
418
420
|
await writeJsonAtomic(path.join(mission.dir, 'regression-proof-summary.json'), regressionProof);
|
|
419
421
|
const tddOk = !regressionProof.required || (regressionProof.regression_test_added && regressionProof.regression_test_failed_before_fix && regressionProof.regression_test_passed_after_fix);
|
|
422
|
+
const narutoGateFullPassed = result.ok === true && nativeProofOk && finalAccepted && parallelRuntimeOk && tddOk && workGraph.ok === true && allocationPolicy.ok === true;
|
|
423
|
+
const narutoGateFullBlockers = [...(result.proof?.blockers || []), ...(parallelRuntimeOk ? [] : ['naruto_parallel_runtime_proof_below_gate']), ...(tddOk ? [] : ['tdd_evidence_missing']), ...(workGraph.blockers || []), ...(allocationPolicy.blockers || [])];
|
|
420
424
|
await writeJsonAtomic(path.join(mission.dir, 'naruto-gate.json'), {
|
|
421
425
|
schema: 'sks.naruto-gate.v1',
|
|
422
|
-
passed:
|
|
426
|
+
passed: narutoGateFullPassed,
|
|
423
427
|
mission_id: mission.id,
|
|
424
428
|
clone_roster_built: true,
|
|
425
429
|
clone_count: roster.agent_count,
|
|
@@ -429,8 +433,8 @@ async function narutoRun(parsed) {
|
|
|
429
433
|
rebalance_ready: rebalancePolicy.ok === true,
|
|
430
434
|
concurrency_governor_ready: true,
|
|
431
435
|
active_pool_simulated: activePool.ok === true,
|
|
432
|
-
verification_dag_ready:
|
|
433
|
-
gpt_final_pack_ready:
|
|
436
|
+
verification_dag_ready: Array.isArray(verificationDag?.tasks),
|
|
437
|
+
gpt_final_pack_ready: Boolean(gptFinalPack?.schema),
|
|
434
438
|
zellij_dashboard_ready: zellijDashboard.ok === true,
|
|
435
439
|
native_agent_proof: nativeProofOk,
|
|
436
440
|
parallel_runtime_proof: parallelRuntimeOk,
|
|
@@ -440,9 +444,10 @@ async function narutoRun(parsed) {
|
|
|
440
444
|
regression_proof: 'regression-proof-summary.json',
|
|
441
445
|
final_arbiter_accepted: finalAccepted,
|
|
442
446
|
session_cleanup: result.proof?.all_sessions_closed === true || nativeProofOk,
|
|
443
|
-
blockers:
|
|
447
|
+
blockers: narutoGateFullBlockers,
|
|
444
448
|
updated_at: nowIso()
|
|
445
449
|
});
|
|
450
|
+
await closeWorkOrderLedgerForRouteResult(mission.dir, { ok: narutoGateFullPassed, blockers: narutoGateFullBlockers });
|
|
446
451
|
const clones = result.roster?.agent_count ?? roster.agent_count;
|
|
447
452
|
const localWorkerSummary = summarizeNarutoLocalWorkerResult(localWorker, result);
|
|
448
453
|
// Finalizer policy: when local LLM workers contributed patches, the GPT
|
|
@@ -337,6 +337,45 @@ function mockPptFixtureGate(gate = {}) {
|
|
|
337
337
|
blockers: ['ppt_fixture_mode_cannot_claim_real']
|
|
338
338
|
};
|
|
339
339
|
}
|
|
340
|
+
// The image-asset-ledger's `assets` array (see buildPptImageAssetLedger in ../ppt.ts) is the
|
|
341
|
+
// authoritative list of raster/bitmap image assets planned or generated for the deck; every
|
|
342
|
+
// entry in it is a raster PNG produced (or pending) via Codex App $imagegen/gpt-image-2. When the
|
|
343
|
+
// ledger omits `imagegen_evidence` outright, we must NOT assume the imagegen-required policy was
|
|
344
|
+
// satisfied. Instead derive a safe default from that asset list: any raster asset present forces
|
|
345
|
+
// the derived evidence to fail-closed (required + not passed) rather than silently pass.
|
|
346
|
+
function deriveImagegenEvidenceDefault(imageAssetLedger) {
|
|
347
|
+
if (imageAssetLedger?.imagegen_evidence)
|
|
348
|
+
return imageAssetLedger.imagegen_evidence;
|
|
349
|
+
const rasterAssets = Array.isArray(imageAssetLedger?.assets) ? imageAssetLedger.assets : [];
|
|
350
|
+
const rasterAssetCount = rasterAssets.length;
|
|
351
|
+
if (rasterAssetCount === 0) {
|
|
352
|
+
return {
|
|
353
|
+
schema: 'sks.ppt-imagegen-evidence.v1',
|
|
354
|
+
required: false,
|
|
355
|
+
passed: true,
|
|
356
|
+
blockers: [],
|
|
357
|
+
derived: true,
|
|
358
|
+
derivation_basis: { raster_asset_count: 0, source: 'derived_default_no_raster_assets' }
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
schema: 'sks.ppt-imagegen-evidence.v1',
|
|
363
|
+
required: true,
|
|
364
|
+
passed: false,
|
|
365
|
+
required_count: imageAssetLedger?.required_count || rasterAssetCount,
|
|
366
|
+
generated_count: imageAssetLedger?.generated_count || 0,
|
|
367
|
+
generated_image_evidence: false,
|
|
368
|
+
assets: [],
|
|
369
|
+
blockers: ['imagegen_evidence_missing'],
|
|
370
|
+
derived: true,
|
|
371
|
+
derivation_basis: {
|
|
372
|
+
raster_asset_count: rasterAssetCount,
|
|
373
|
+
source: 'derived_default_raster_assets_present',
|
|
374
|
+
note: `imagegen_evidence section was absent from the image-asset-ledger while ${rasterAssetCount} raster asset(s) were present; failing closed instead of silently passing.`
|
|
375
|
+
},
|
|
376
|
+
passed_note: 'imagegen_evidence_missing: ledger omitted imagegen_evidence despite raster assets requiring Codex App imagegen verification'
|
|
377
|
+
};
|
|
378
|
+
}
|
|
340
379
|
export async function evaluatePptGateArtifacts(dir, baseGate = {}) {
|
|
341
380
|
const factLedger = await readJson(path.join(dir, PPT_FACT_LEDGER_ARTIFACT), null);
|
|
342
381
|
const imageAssetLedger = await readJson(path.join(dir, PPT_IMAGE_ASSET_LEDGER_ARTIFACT), null);
|
|
@@ -370,7 +409,7 @@ export async function evaluatePptGateArtifacts(dir, baseGate = {}) {
|
|
|
370
409
|
const renderReportPassed = renderReport?.passed === true;
|
|
371
410
|
const factLedgerPassed = factLedger?.passed === true && Number(factLedger.unsupported_critical_claims_count || 0) === 0;
|
|
372
411
|
const imageAssetLedgerPassed = imageAssetLedger?.passed === true;
|
|
373
|
-
const imagegenEvidence = imageAssetLedger
|
|
412
|
+
const imagegenEvidence = deriveImagegenEvidenceDefault(imageAssetLedger);
|
|
374
413
|
const imagegenEvidencePassed = imagegenEvidence?.required === true ? imagegenEvidence?.passed === true : true;
|
|
375
414
|
const reviewLedgerPassed = reviewLedger?.passed === true;
|
|
376
415
|
const iterationReportPassed = iterationReport?.passed === true;
|
|
@@ -14,6 +14,7 @@ import { maybeFinalizeRoute } from '../proof/auto-finalize.js';
|
|
|
14
14
|
import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
|
|
15
15
|
import { flag, promptOf, readBoundedIntegerFlag, readFlagValue, readMaxCycles, resolveMissionId, safeReadTextFile } from './command-utils.js';
|
|
16
16
|
import { runCodexAppHandoff, qaLoopShouldRequestAppHandoff } from '../codex-app/codex-app-handoff.js';
|
|
17
|
+
import { evaluateGate } from '../stop-gate/gate-evaluator.js';
|
|
17
18
|
import { writeCodex0138CapabilityArtifacts } from '../codex-control/codex-0138-capability.js';
|
|
18
19
|
import { writeCodexAccountUsageArtifacts } from '../usage/codex-account-usage.js';
|
|
19
20
|
import { buildQaLoopBudgetPolicy, selectQaLoopEscalatedEffort } from '../qa-loop/qa-loop-budget-policy.js';
|
|
@@ -441,8 +442,10 @@ async function qaLoopStatus(args) {
|
|
|
441
442
|
const desktop = await readJson(path.join(dir, 'qa-loop', 'app-handoff.json'), null);
|
|
442
443
|
const desktopConfirmation = await readJson(path.join(dir, 'qa-loop', 'app-handoff-confirmation.json'), null);
|
|
443
444
|
const desktopReviewComplete = desktopConfirmation?.verdict === 'pass';
|
|
445
|
+
const gateVerdict = await evaluateGate(root, id, 'qa-gate.json');
|
|
444
446
|
if (flag(args, '--json'))
|
|
445
|
-
return console.log(JSON.stringify({ mission, state, qa: status, desktop_app_handoff: desktop, desktop_app_confirmation: desktopConfirmation, desktop_review_complete: desktopReviewComplete, native_agent_plan: nativeAgentPlan, agent_sessions: agentSessions?.sessions || null }, null, 2));
|
|
447
|
+
return console.log(JSON.stringify({ mission, state, qa: status, desktop_app_handoff: desktop, desktop_app_confirmation: desktopConfirmation, desktop_review_complete: desktopReviewComplete, native_agent_plan: nativeAgentPlan, agent_sessions: agentSessions?.sessions || null, gate_verdict: gateVerdict }, null, 2));
|
|
448
|
+
console.log(gateVerdict.verdict);
|
|
446
449
|
console.log('SKS QA-LOOP Status\n');
|
|
447
450
|
console.log(`Mission: ${id}`);
|
|
448
451
|
console.log(`Phase: ${state.phase || mission.phase}`);
|
|
@@ -27,7 +27,7 @@ export async function releaseCommand(args = []) {
|
|
|
27
27
|
await writeTextAtomic(stdoutPath, String(result.stdout || ''));
|
|
28
28
|
await writeTextAtomic(stderrPath, String(result.stderr || ''));
|
|
29
29
|
const readiness = await findReleaseReadinessReport(root);
|
|
30
|
-
const requiredSections = [
|
|
30
|
+
const requiredSections = [];
|
|
31
31
|
const missingSections = requiredSections.filter((section) => readiness.report?.[section] == null);
|
|
32
32
|
if (readiness.report)
|
|
33
33
|
await writeJsonAtomic(path.join(mission.dir, 'release-readiness-report.json'), readiness.report);
|
|
@@ -53,14 +53,14 @@ export async function releaseCommand(args = []) {
|
|
|
53
53
|
stdout_tail: tail(String(result.stdout || '')),
|
|
54
54
|
stderr_tail: tail(String(result.stderr || ''))
|
|
55
55
|
};
|
|
56
|
+
if (!report.ok)
|
|
57
|
+
process.exitCode = result.status || 1;
|
|
56
58
|
if (json)
|
|
57
59
|
return printJson(report);
|
|
58
60
|
if (result.stdout)
|
|
59
61
|
process.stdout.write(result.stdout);
|
|
60
62
|
if (result.stderr)
|
|
61
63
|
process.stderr.write(result.stderr);
|
|
62
|
-
if (!report.ok)
|
|
63
|
-
process.exitCode = result.status || 1;
|
|
64
64
|
return report;
|
|
65
65
|
}
|
|
66
66
|
async function findReleaseReadinessReport(root) {
|
|
@@ -23,6 +23,7 @@ import { readImplementationBlueprint, validateImplementationBlueprint } from '..
|
|
|
23
23
|
import { readExperimentPlan, validateExperimentPlan } from '../research/experiment-plan.js';
|
|
24
24
|
import { readReplicationPack, validateReplicationPack } from '../research/replication-pack.js';
|
|
25
25
|
import { readResearchFinalReview } from '../research/research-final-reviewer.js';
|
|
26
|
+
import { evaluateGate } from '../stop-gate/gate-evaluator.js';
|
|
26
27
|
const RESEARCH_DEFAULT_MAX_CYCLES = 12;
|
|
27
28
|
const RESEARCH_DEFAULT_CYCLE_TIMEOUT_MINUTES = 120;
|
|
28
29
|
const RESEARCH_MIN_CYCLE_TIMEOUT_MINUTES = 15;
|
|
@@ -360,6 +361,8 @@ async function researchStatus(args) {
|
|
|
360
361
|
const { dir, mission } = await loadMission(root, id);
|
|
361
362
|
const state = await readJson(stateFile(root), {});
|
|
362
363
|
const gate = await readJson(path.join(dir, 'research-gate.evaluated.json'), await readJson(path.join(dir, 'research-gate.json'), null));
|
|
364
|
+
const gateVerdict = await evaluateGate(root, id, 'research-gate.json');
|
|
365
|
+
console.log(gateVerdict.verdict);
|
|
363
366
|
const ledger = await readJson(path.join(dir, 'novelty-ledger.json'), null);
|
|
364
367
|
const sourceLedger = await readJson(path.join(dir, 'source-ledger.json'), null);
|
|
365
368
|
const agentLedger = await readJson(path.join(dir, 'agent-ledger.json'), null);
|
|
@@ -398,6 +401,7 @@ async function researchStatus(args) {
|
|
|
398
401
|
autoresearch_cycle_policy: plan?.autoresearch_cycle_policy || null,
|
|
399
402
|
legacy_alias_policy: plan?.native_agent_plan?.legacy_artifact_alias_policy || null,
|
|
400
403
|
gate,
|
|
404
|
+
gate_verdict: gateVerdict,
|
|
401
405
|
novelty_entries: ledger?.entries?.length ?? null,
|
|
402
406
|
source_entries: sourceLedger?.sources?.length ?? null,
|
|
403
407
|
source_layers_required: sourceLayerRows.length || gate?.metrics?.source_layers_required || gate?.source_layers_required || null,
|
|
@@ -36,6 +36,8 @@ export async function evaluateLocalGate(input) {
|
|
|
36
36
|
blockers.push('gate_not_passed');
|
|
37
37
|
if (gate.ok === false)
|
|
38
38
|
blockers.push('gate_ok_false');
|
|
39
|
+
if (gate.execution_class === 'mock_fixture')
|
|
40
|
+
blockers.push('gate_execution_class_mock_fixture');
|
|
39
41
|
if (Array.isArray(gate.blockers) && gate.blockers.length)
|
|
40
42
|
blockers.push(...gate.blockers.map(String));
|
|
41
43
|
if (Array.isArray(gate.missing_fields) && gate.missing_fields.length)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { exists, projectRoot, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
3
3
|
import { createMission, missionDir, setCurrent } from '../mission.js';
|
|
4
|
+
import { createAndWriteWorkOrderLedgerForPrompt, closeWorkOrderLedgerForRouteResult } from '../work-order-ledger.js';
|
|
4
5
|
import { maybeFinalizeRoute } from '../proof/auto-finalize.js';
|
|
5
6
|
import { routePrompt } from '../routes.js';
|
|
6
7
|
import { latestTrustReport } from '../trust-kernel/trust-report.js';
|
|
@@ -57,6 +58,7 @@ export async function runCommand(args = []) {
|
|
|
57
58
|
console.log(`Next: ${classification.next_action}`);
|
|
58
59
|
return result;
|
|
59
60
|
}
|
|
61
|
+
await createAndWriteWorkOrderLedgerForPrompt(dir, { missionId: id, route: route.command, prompt });
|
|
60
62
|
if (execute)
|
|
61
63
|
return executeRunRoute(root, { id, dir, route, prompt, args, classification, auto });
|
|
62
64
|
return finalizeMockRun(root, { id, route, prompt, args, classification, mode });
|
|
@@ -85,6 +87,7 @@ async function finalizeMockRun(root, { id, route, prompt, args, classification,
|
|
|
85
87
|
});
|
|
86
88
|
const trust = await loadTrustReport(root, id);
|
|
87
89
|
const completionOk = proof.ok && proof.proof?.status !== 'mock_only' && gate.passed === true;
|
|
90
|
+
await closeWorkOrderLedgerForRouteResult(missionDir(root, id), { ok: completionOk, blockers: gate.blockers });
|
|
88
91
|
await setCurrent(root, {
|
|
89
92
|
mission_id: id,
|
|
90
93
|
mode: 'RUN',
|
|
@@ -154,12 +157,14 @@ async function executeRunRoute(root, context) {
|
|
|
154
157
|
: await loadTrustReport(root, id);
|
|
155
158
|
const autoVerification = auto ? await runAutoVerification(root, id) : null;
|
|
156
159
|
const autoOk = autoVerification?.ok ?? true;
|
|
160
|
+
const executeOk = execution.ok && proof.ok && autoOk;
|
|
161
|
+
await closeWorkOrderLedgerForRouteResult(dir, { ok: executeOk, blockers: execution.blockers });
|
|
157
162
|
await setCurrent(root, {
|
|
158
163
|
mission_id: id,
|
|
159
164
|
mode: 'RUN',
|
|
160
165
|
route: route.id,
|
|
161
166
|
route_command: route.command,
|
|
162
|
-
phase:
|
|
167
|
+
phase: executeOk ? 'RUN_EXECUTE_DONE' : 'RUN_EXECUTE_BLOCKED',
|
|
163
168
|
implementation_allowed: execution.ok,
|
|
164
169
|
nested_mission_id: execution.nested_mission_id,
|
|
165
170
|
completion_proof: 'completion-proof.json',
|
|
@@ -167,7 +172,7 @@ async function executeRunRoute(root, context) {
|
|
|
167
172
|
});
|
|
168
173
|
const result = {
|
|
169
174
|
schema: 'sks.run.v2',
|
|
170
|
-
ok:
|
|
175
|
+
ok: executeOk,
|
|
171
176
|
mission_id: id,
|
|
172
177
|
route: route.command,
|
|
173
178
|
mode: auto ? 'auto' : 'execute',
|
|
@@ -242,11 +247,21 @@ async function executeRouteCommand(root, route, prompt, { auto = false } = {}) {
|
|
|
242
247
|
});
|
|
243
248
|
}
|
|
244
249
|
const commandArgs = safeRouteExecutionArgs(route, prompt, { auto });
|
|
250
|
+
// safeRouteExecutionArgs() falls back to `team <prompt> --mock` for any route it
|
|
251
|
+
// doesn't have a dedicated safe live command for, and several dedicated branches
|
|
252
|
+
// (DB/Wiki/Fast-Mode/with-local-llm-on/Commit/Commit-And-Push) run a fixed
|
|
253
|
+
// command that never references the prompt at all. Both must be labeled
|
|
254
|
+
// honestly rather than as a live-route completion: a --mock invocation cannot
|
|
255
|
+
// claim a real completion (14차 honest-mock principle), and a fixed command
|
|
256
|
+
// that never saw the prompt cannot claim to have addressed it.
|
|
257
|
+
const isMockFallback = commandArgs.includes('--mock');
|
|
258
|
+
const promptDelivered = Boolean(prompt) && commandArgs.includes(prompt);
|
|
245
259
|
const result = await runSks(root, commandArgs);
|
|
246
260
|
return routeExecutionResult(route, ['sks', ...commandArgs].join(' '), result, {
|
|
247
|
-
okStatus: 'completed',
|
|
248
|
-
trustStatus: 'verified_partial',
|
|
249
|
-
executionKind: 'live_route',
|
|
261
|
+
okStatus: isMockFallback ? 'verified_partial' : 'completed',
|
|
262
|
+
trustStatus: isMockFallback ? 'mock_only' : 'verified_partial',
|
|
263
|
+
executionKind: isMockFallback ? 'mock_safe' : 'live_route',
|
|
264
|
+
promptDelivered,
|
|
250
265
|
});
|
|
251
266
|
}
|
|
252
267
|
async function runAutoVerification(root, missionId) {
|
|
@@ -331,6 +346,8 @@ function routeExecutionResult(route, command, result, options = {}) {
|
|
|
331
346
|
};
|
|
332
347
|
if (options.steps)
|
|
333
348
|
execution.steps = options.steps;
|
|
349
|
+
if (options.promptDelivered !== undefined)
|
|
350
|
+
execution.prompt_delivered = options.promptDelivered;
|
|
334
351
|
return execution;
|
|
335
352
|
}
|
|
336
353
|
function classifyRunRoute(prompt, args) {
|
|
@@ -3,6 +3,7 @@ import { printJson } from '../../cli/output.js';
|
|
|
3
3
|
import { projectRoot } from '../fsx.js';
|
|
4
4
|
import { flag, readOption } from './command-utils.js';
|
|
5
5
|
import { runSearchVisibilityApply, runSearchVisibilityAudit, runSearchVisibilityDoctor, runSearchVisibilityFixture, runSearchVisibilityPlan, runSearchVisibilityRollback, runSearchVisibilityStatus, runSearchVisibilityVerify, resolveSearchVisibilityMission, } from '../search-visibility/index.js';
|
|
6
|
+
import { evaluateGate } from '../stop-gate/gate-evaluator.js';
|
|
6
7
|
import { evaluateLocalGate } from './route-success-helpers.js';
|
|
7
8
|
export async function seoCommand(args = []) {
|
|
8
9
|
return runSearchVisibilityCommand('seo', args, 'seo');
|
|
@@ -22,8 +23,12 @@ export async function runSearchVisibilityCommand(mode, args = [], displayCommand
|
|
|
22
23
|
if (!mission)
|
|
23
24
|
blockers.push('mission_required_for_apply');
|
|
24
25
|
else {
|
|
26
|
+
// explicit_apply_only precondition: a mutation plan must already exist on disk,
|
|
27
|
+
// proving a prior `plan` step was run and reviewed before this apply can mutate anything.
|
|
25
28
|
const planGate = await evaluateLocalGate({ root: mission.root, dir: mission.dir, gateFile: mode === 'seo' ? 'seo-gate.json' : 'geo-gate.json', requiredArtifacts: ['search-visibility/mutation-plan.json'] });
|
|
26
|
-
|
|
29
|
+
if (planGate.blockers.includes('missing_artifact:search-visibility/mutation-plan.json'))
|
|
30
|
+
blockers.push('seo_apply_missing_mutation_plan');
|
|
31
|
+
blockers.push(...planGate.blockers.filter((blocker) => blocker !== 'gate_not_passed' && blocker !== 'gate_ok_false' && blocker !== 'missing_artifact:search-visibility/mutation-plan.json'));
|
|
27
32
|
}
|
|
28
33
|
if (blockers.length) {
|
|
29
34
|
result = { schema: 'sks.search-visibility.apply-command.v1', ok: false, route: mode === 'seo' ? '$SEO-GEO-OPTIMIZER' : '$SEO-GEO-OPTIMIZER', status: 'blocked', blockers };
|
|
@@ -75,18 +80,38 @@ async function applySearchVisibilityGateResult(mode, action, result, options) {
|
|
|
75
80
|
: action === 'status'
|
|
76
81
|
? []
|
|
77
82
|
: ['search-visibility/verification-report.json'];
|
|
83
|
+
const gateFile = mode === 'seo' ? 'seo-gate.json' : 'geo-gate.json';
|
|
78
84
|
const evaluated = await evaluateLocalGate({
|
|
79
85
|
root: mission.root,
|
|
80
86
|
dir: mission.dir,
|
|
81
|
-
gateFile
|
|
87
|
+
gateFile,
|
|
82
88
|
requiredArtifacts
|
|
83
89
|
});
|
|
84
90
|
const blockers = [...new Set([...(result.blockers || []), ...evaluated.blockers])];
|
|
91
|
+
if (action === 'apply') {
|
|
92
|
+
// explicit_apply_only postcondition: apply must not report success unless both the mutation
|
|
93
|
+
// plan and the rollback manifest it produces are actually present on disk after execution.
|
|
94
|
+
if (blockers.includes('missing_artifact:search-visibility/mutation-plan.json') && !blockers.includes('seo_apply_missing_mutation_plan'))
|
|
95
|
+
blockers.push('seo_apply_missing_mutation_plan');
|
|
96
|
+
if (blockers.includes('missing_artifact:search-visibility/rollback-manifest.json') && !blockers.includes('seo_apply_missing_rollback_manifest'))
|
|
97
|
+
blockers.push('seo_apply_missing_rollback_manifest');
|
|
98
|
+
}
|
|
99
|
+
// Re-evaluate against the authoritative on-disk gate (catches mock/fixture-only gates,
|
|
100
|
+
// not just the presence checks `evaluateLocalGate` performs) before reporting final success.
|
|
101
|
+
const authoritative = await evaluateGate(mission.root, mission.id, gateFile);
|
|
102
|
+
if (!authoritative.pass) {
|
|
103
|
+
for (const reason of authoritative.reasons) {
|
|
104
|
+
if (!blockers.includes(reason))
|
|
105
|
+
blockers.push(reason);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const ok = result.ok === true && evaluated.ok === true && authoritative.pass === true && blockers.length === 0;
|
|
85
109
|
return {
|
|
86
110
|
...result,
|
|
87
|
-
ok
|
|
88
|
-
status:
|
|
111
|
+
ok,
|
|
112
|
+
status: ok ? result.status : 'blocked',
|
|
89
113
|
gate_evaluation: evaluated,
|
|
114
|
+
gate_verdict: authoritative.verdict,
|
|
90
115
|
blockers
|
|
91
116
|
};
|
|
92
117
|
}
|
|
@@ -167,6 +192,8 @@ function isBlocked(value) {
|
|
|
167
192
|
}
|
|
168
193
|
function printHuman(mode, action, value) {
|
|
169
194
|
const rec = value && typeof value === 'object' ? value : {};
|
|
195
|
+
if (action === 'status' && rec.gate_verdict)
|
|
196
|
+
console.log(String(rec.gate_verdict));
|
|
170
197
|
console.log(`SKS ${mode.toUpperCase()} ${action}: ${rec.status || (rec.ok === false ? 'blocked' : 'ok')}`);
|
|
171
198
|
if (rec.mission_id)
|
|
172
199
|
console.log(`Mission: ${rec.mission_id}`);
|