sneakoscope 2.0.8 → 2.0.10
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 +8 -4
- 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/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +33 -8
- package/dist/cli/command-registry.js +1 -0
- package/dist/commands/doctor.js +18 -1
- package/dist/commands/zellij-slot-pane.js +26 -0
- package/dist/commands/zellij.js +144 -1
- package/dist/core/agents/agent-orchestrator.js +202 -9
- package/dist/core/agents/agent-role-config.js +92 -0
- package/dist/core/agents/native-cli-session-swarm.js +230 -48
- package/dist/core/commands/mad-sks-command.js +17 -26
- package/dist/core/commands/naruto-command.js +155 -37
- package/dist/core/doctor/doctor-readiness-matrix.js +6 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +4 -0
- package/dist/core/init.js +1 -0
- package/dist/core/naruto/naruto-active-pool.js +141 -0
- package/dist/core/naruto/naruto-concurrency-governor.js +17 -2
- package/dist/core/naruto/naruto-real-worker-child.js +35 -0
- package/dist/core/naruto/naruto-real-worker-runtime.js +121 -0
- package/dist/core/naruto/naruto-work-graph.js +2 -1
- package/dist/core/release/release-gate-cache-v2.js +58 -4
- package/dist/core/release/release-gate-dag.js +36 -25
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-dashboard-renderer.js +22 -6
- package/dist/core/zellij/zellij-launcher.js +3 -3
- package/dist/core/zellij/zellij-layout-builder.js +1 -1
- package/dist/core/zellij/zellij-right-column-layout-proof.js +42 -0
- package/dist/core/zellij/zellij-right-column-manager.js +304 -0
- package/dist/core/zellij/zellij-slot-pane-renderer.js +82 -0
- package/dist/core/zellij/zellij-ui-mode.js +16 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +152 -17
- package/dist/scripts/agent-role-config-repair-check.js +33 -0
- package/dist/scripts/codex-sdk-release-review-pipeline-check.js +5 -5
- package/dist/scripts/doctor-fix-proves-codex-read-check.js +26 -5
- package/dist/scripts/git-worktree-integration-primary-check.js +4 -2
- package/dist/scripts/git-worktree-integration-primary-runtime-check.js +20 -0
- package/dist/scripts/lib/codex-sdk-gate-lib.js +4 -0
- package/dist/scripts/mad-sks-zellij-default-pane-worker-check.js +2 -2
- package/dist/scripts/mutation-callsite-coverage-check.js +2 -1
- package/dist/scripts/naruto-concurrency-governor-check.js +2 -1
- package/dist/scripts/naruto-extreme-parallelism-check.js +22 -0
- package/dist/scripts/naruto-extreme-parallelism-real-check.js +42 -0
- package/dist/scripts/naruto-real-active-pool-check.js +39 -0
- package/dist/scripts/naruto-real-active-pool-runtime-check.js +53 -0
- package/dist/scripts/naruto-work-graph-check.js +1 -1
- package/dist/scripts/naruto-zellij-dynamic-right-column-check.js +48 -0
- package/dist/scripts/product-design-auto-install-check.js +3 -3
- package/dist/scripts/product-design-plugin-routing-check.js +3 -3
- package/dist/scripts/readme-architecture-imagegen-official-check.js +4 -3
- package/dist/scripts/release-cache-glob-hashing-check.js +42 -0
- package/dist/scripts/release-check-dynamic-execute.js +27 -1
- package/dist/scripts/release-check-dynamic.js +38 -11
- package/dist/scripts/release-check-stamp.js +7 -2
- package/dist/scripts/release-dag-full-coverage-check.js +35 -0
- package/dist/scripts/release-dynamic-performance-check.js +31 -1
- package/dist/scripts/release-gate-existence-audit.js +29 -33
- package/dist/scripts/release-parallel-speed-budget-check.js +67 -13
- package/dist/scripts/release-readiness-report.js +14 -3
- package/dist/scripts/zellij-dashboard-pane-check.js +6 -4
- package/dist/scripts/zellij-developer-controls-check.js +20 -0
- package/dist/scripts/zellij-dynamic-pane-lifecycle-check.js +21 -0
- package/dist/scripts/zellij-initial-main-only-blackbox.js +28 -0
- package/dist/scripts/zellij-right-column-geometry-proof.js +162 -0
- package/dist/scripts/zellij-right-column-headless-overflow-check.js +22 -0
- package/dist/scripts/zellij-right-column-manager-check.js +22 -0
- package/dist/scripts/zellij-slot-only-ui-check.js +22 -0
- package/dist/scripts/zellij-slot-pane-renderer-check.js +38 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +2 -1
- package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +7 -6
- package/package.json +23 -5
- package/schemas/zellij/zellij-right-column-state.schema.json +41 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { ensureDir, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
6
|
+
import { allocateWorkerWorktree } from '../git/git-worktree-manager.js';
|
|
7
|
+
import { cleanupGitWorktree } from '../git/git-worktree-cleanup.js';
|
|
8
|
+
export async function spawnActualNarutoWorker(input) {
|
|
9
|
+
const workerDir = path.join(input.root, '.sneakoscope', 'missions', input.missionId, 'agents', 'naruto-real-workers', input.item.id);
|
|
10
|
+
await ensureDir(workerDir);
|
|
11
|
+
let worktree = null;
|
|
12
|
+
if (input.worktreePolicy?.mode === 'git-worktree' && input.item.write_allowed === true) {
|
|
13
|
+
const allocation = await allocateWorkerWorktree({
|
|
14
|
+
repoRoot: input.worktreePolicy.main_repo_root || input.root,
|
|
15
|
+
missionId: input.missionId,
|
|
16
|
+
workerId: input.item.id,
|
|
17
|
+
slotId: input.item.id.replace(/[^A-Za-z0-9_-]/g, '-'),
|
|
18
|
+
generationIndex: 1
|
|
19
|
+
}).catch((err) => ({ ok: false, blockers: [`git_worktree_allocate_exception:${err?.message || String(err)}`] }));
|
|
20
|
+
await writeJsonAtomic(path.join(workerDir, 'git-worktree-allocation.json'), allocation);
|
|
21
|
+
if (allocation.ok)
|
|
22
|
+
worktree = allocation;
|
|
23
|
+
}
|
|
24
|
+
const resultPath = path.join(workerDir, 'worker-result.json');
|
|
25
|
+
const heartbeatPath = path.join(workerDir, 'worker-heartbeat.jsonl');
|
|
26
|
+
const intakePath = path.join(workerDir, 'worker-intake.json');
|
|
27
|
+
await writeJsonAtomic(intakePath, {
|
|
28
|
+
schema: 'sks.naruto-actual-worker-intake.v1',
|
|
29
|
+
generated_at: nowIso(),
|
|
30
|
+
mission_id: input.missionId,
|
|
31
|
+
item: input.item,
|
|
32
|
+
placement: input.placement,
|
|
33
|
+
backend: input.backend,
|
|
34
|
+
result_path: resultPath,
|
|
35
|
+
heartbeat_path: heartbeatPath,
|
|
36
|
+
worktree_path: worktree?.worktree_path || null,
|
|
37
|
+
zellij_session_name: input.zellijSessionName || null,
|
|
38
|
+
visible_pane_cap: input.visiblePaneCap
|
|
39
|
+
});
|
|
40
|
+
const child = spawn(process.execPath, [actualWorkerEntrypoint(), intakePath], {
|
|
41
|
+
cwd: worktree?.worktree_path || input.root,
|
|
42
|
+
stdio: ['ignore', 'ignore', 'ignore']
|
|
43
|
+
});
|
|
44
|
+
const exit = waitForExit(child, 30000);
|
|
45
|
+
return {
|
|
46
|
+
id: input.item.id,
|
|
47
|
+
item: input.item,
|
|
48
|
+
placement: input.placement,
|
|
49
|
+
started_at: Date.now(),
|
|
50
|
+
pid: child.pid || null,
|
|
51
|
+
child,
|
|
52
|
+
worker_artifact_dir: workerDir,
|
|
53
|
+
result_path: resultPath,
|
|
54
|
+
heartbeat_path: heartbeatPath,
|
|
55
|
+
worktree,
|
|
56
|
+
exit
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export async function collectActualNarutoWorker(handle) {
|
|
60
|
+
const exit = await handle.exit;
|
|
61
|
+
const result = await readJson(handle.result_path, null).catch(() => null);
|
|
62
|
+
const blockers = [
|
|
63
|
+
...(exit.code === 0 ? [] : [`naruto_actual_worker_exit_${exit.code ?? exit.signal ?? 'unknown'}`]),
|
|
64
|
+
...(result?.ok === false ? result.blockers || ['naruto_actual_worker_result_not_ok'] : []),
|
|
65
|
+
...(result ? [] : ['naruto_actual_worker_result_missing'])
|
|
66
|
+
];
|
|
67
|
+
if (handle.worktree?.worktree_path) {
|
|
68
|
+
const cleanup = await cleanupGitWorktree({
|
|
69
|
+
repoRoot: handle.worktree.main_repo_root || handle.worktree.repo_root || handle.worktree.repoRoot || '',
|
|
70
|
+
worktreePath: handle.worktree.worktree_path,
|
|
71
|
+
branch: handle.worktree.branch,
|
|
72
|
+
deleteBranch: true
|
|
73
|
+
}).catch((err) => ({ ok: false, blockers: [`git_worktree_cleanup_exception:${err?.message || String(err)}`] }));
|
|
74
|
+
await writeJsonAtomic(path.join(handle.worker_artifact_dir, 'git-worktree-cleanup.json'), cleanup);
|
|
75
|
+
blockers.push(...(cleanup.blockers || []));
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
id: handle.id,
|
|
79
|
+
ok: blockers.length === 0,
|
|
80
|
+
item: handle.item,
|
|
81
|
+
placement: handle.placement,
|
|
82
|
+
completed_at: Date.now(),
|
|
83
|
+
pid: handle.pid,
|
|
84
|
+
worker_artifact_dir: handle.worker_artifact_dir,
|
|
85
|
+
blockers
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function actualWorkerEntrypoint() {
|
|
89
|
+
return fileURLToPath(new URL('./naruto-real-worker-child.js', import.meta.url));
|
|
90
|
+
}
|
|
91
|
+
function waitForExit(child, timeoutMs) {
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
let settled = false;
|
|
94
|
+
let killTimer = null;
|
|
95
|
+
const finish = (code, signal) => {
|
|
96
|
+
if (settled)
|
|
97
|
+
return;
|
|
98
|
+
settled = true;
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
if (killTimer)
|
|
101
|
+
clearTimeout(killTimer);
|
|
102
|
+
resolve({ code, signal });
|
|
103
|
+
};
|
|
104
|
+
const timer = setTimeout(() => {
|
|
105
|
+
if (!child.killed)
|
|
106
|
+
child.kill();
|
|
107
|
+
killTimer = setTimeout(() => {
|
|
108
|
+
if (!settled)
|
|
109
|
+
child.kill('SIGKILL');
|
|
110
|
+
finish(null, 'SIGKILL');
|
|
111
|
+
}, 5000);
|
|
112
|
+
}, Math.max(1000, timeoutMs));
|
|
113
|
+
child.on('close', (code, signal) => {
|
|
114
|
+
finish(code, signal);
|
|
115
|
+
});
|
|
116
|
+
child.on('error', () => {
|
|
117
|
+
finish(1, null);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=naruto-real-worker-runtime.js.map
|
|
@@ -28,7 +28,8 @@ export function buildNarutoWorkGraph(input = {}) {
|
|
|
28
28
|
const requestedClones = normalizePositiveInt(input.requestedClones, 12);
|
|
29
29
|
const readonly = input.readonly === true;
|
|
30
30
|
const writeCapable = input.writeCapable !== false && !readonly;
|
|
31
|
-
const
|
|
31
|
+
const minimumFanout = writeCapable ? requestedClones * 2 : requestedClones;
|
|
32
|
+
const totalWorkItems = Math.max(minimumFanout, normalizePositiveInt(input.totalWorkItems, minimumFanout));
|
|
32
33
|
const kindCycle = writeCapable ? WRITE_CAPABLE_KIND_CYCLE : READONLY_KIND_CYCLE;
|
|
33
34
|
const basePath = normalizeNarutoPath(input.leaseBasePath || '.sneakoscope/naruto/patch-envelopes');
|
|
34
35
|
const targetPaths = normalizePaths(input.targetPaths || []);
|
|
@@ -19,14 +19,59 @@ export function releaseGateCacheKey(root, gate) {
|
|
|
19
19
|
hashFileIfPresent(hash, path.join(root, 'package.json'));
|
|
20
20
|
hashFileIfPresent(hash, path.join(root, 'dist', 'build-manifest.json'));
|
|
21
21
|
for (const input of gate.cache.inputs) {
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const expanded = expandGlob(root, input);
|
|
23
|
+
hash.update(`input:${input}`);
|
|
24
|
+
if (!expanded.length) {
|
|
25
|
+
hash.update(`missing_or_empty:${input}`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
for (const file of expanded) {
|
|
29
|
+
hash.update(path.relative(root, file));
|
|
24
30
|
hashFileIfPresent(hash, file);
|
|
25
|
-
|
|
26
|
-
hash.update(input);
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
return hash.digest('hex');
|
|
29
34
|
}
|
|
35
|
+
export function expandGlob(root, input) {
|
|
36
|
+
const absolute = path.join(root, input);
|
|
37
|
+
if (!/[*!?[\]{}]/.test(input)) {
|
|
38
|
+
if (!fs.existsSync(absolute))
|
|
39
|
+
return [];
|
|
40
|
+
const stat = fs.statSync(absolute);
|
|
41
|
+
if (stat.isDirectory())
|
|
42
|
+
return hashDirectoryRecursive(absolute);
|
|
43
|
+
return stat.isFile() ? [absolute] : [];
|
|
44
|
+
}
|
|
45
|
+
if (input.endsWith('/**')) {
|
|
46
|
+
const dir = path.join(root, input.slice(0, -3));
|
|
47
|
+
return fs.existsSync(dir) && fs.statSync(dir).isDirectory() ? hashDirectoryRecursive(dir) : [];
|
|
48
|
+
}
|
|
49
|
+
const firstWildcard = input.search(/[*!?[\]{}]/);
|
|
50
|
+
const prefix = input.slice(0, firstWildcard);
|
|
51
|
+
const base = path.join(root, prefix.includes('/') ? prefix.slice(0, prefix.lastIndexOf('/')) : '');
|
|
52
|
+
if (!fs.existsSync(base))
|
|
53
|
+
return [];
|
|
54
|
+
const re = globToRegExp(input);
|
|
55
|
+
return hashDirectoryRecursive(base).filter((file) => re.test(path.relative(root, file)));
|
|
56
|
+
}
|
|
57
|
+
export function hashDirectoryRecursive(dir) {
|
|
58
|
+
if (!fs.existsSync(dir))
|
|
59
|
+
return [];
|
|
60
|
+
const out = [];
|
|
61
|
+
const stack = [dir];
|
|
62
|
+
while (stack.length) {
|
|
63
|
+
const current = stack.pop();
|
|
64
|
+
for (const name of fs.readdirSync(current).sort()) {
|
|
65
|
+
const file = path.join(current, name);
|
|
66
|
+
const stat = fs.statSync(file);
|
|
67
|
+
if (stat.isDirectory())
|
|
68
|
+
stack.push(file);
|
|
69
|
+
else if (stat.isFile())
|
|
70
|
+
out.push(file);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return out.sort();
|
|
74
|
+
}
|
|
30
75
|
export function readReleaseGateCacheHit(root, gate) {
|
|
31
76
|
try {
|
|
32
77
|
const parsed = JSON.parse(fs.readFileSync(releaseGateCacheFile(root), 'utf8'));
|
|
@@ -60,4 +105,13 @@ function hashFileIfPresent(hash, file) {
|
|
|
60
105
|
if (fs.existsSync(file) && fs.statSync(file).isFile())
|
|
61
106
|
hash.update(fs.readFileSync(file));
|
|
62
107
|
}
|
|
108
|
+
function globToRegExp(input) {
|
|
109
|
+
const escaped = input
|
|
110
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
111
|
+
.replace(/\*\*/g, '\u0000')
|
|
112
|
+
.replace(/\*/g, '[^/]*')
|
|
113
|
+
.replace(/\?/g, '[^/]')
|
|
114
|
+
.replace(/\u0000/g, '.*');
|
|
115
|
+
return new RegExp(`^${escaped}$`);
|
|
116
|
+
}
|
|
63
117
|
//# sourceMappingURL=release-gate-cache-v2.js.map
|
|
@@ -35,6 +35,39 @@ export async function runReleaseGateDag(input) {
|
|
|
35
35
|
let cached = 0;
|
|
36
36
|
let sumGateMs = 0;
|
|
37
37
|
let peakRunning = 0;
|
|
38
|
+
const writeSummarySnapshot = (finished = false) => {
|
|
39
|
+
const wallMs = Date.now() - started;
|
|
40
|
+
const failures = [...failed.values()].map((row) => ({ id: row.id, exit_code: row.exit_code, stderr_tail: row.stderr_tail }));
|
|
41
|
+
const snapshot = {
|
|
42
|
+
schema: 'sks.release-gate-dag-run.v1',
|
|
43
|
+
ok: failures.length === 0,
|
|
44
|
+
run_id: runId,
|
|
45
|
+
selected_preset: preset,
|
|
46
|
+
total_gates: manifest.gates.length,
|
|
47
|
+
selected_gates: selected.length,
|
|
48
|
+
completed: completed.size,
|
|
49
|
+
failed: failed.size,
|
|
50
|
+
cached,
|
|
51
|
+
wall_ms: wallMs,
|
|
52
|
+
sum_gate_ms: sumGateMs,
|
|
53
|
+
cpu_time_saved_ms: Math.max(0, sumGateMs - wallMs),
|
|
54
|
+
parallelism_gain: wallMs > 0 ? Number((sumGateMs / wallMs).toFixed(2)) : 1,
|
|
55
|
+
critical_path_ms: estimateCriticalPath(selected, completed),
|
|
56
|
+
peak_running: peakRunning,
|
|
57
|
+
peak_resources: peakResources,
|
|
58
|
+
budget_snapshot: budget,
|
|
59
|
+
budget_summary: summarizeReleaseGateBudget(budget),
|
|
60
|
+
report_dir: reportDir,
|
|
61
|
+
failures
|
|
62
|
+
};
|
|
63
|
+
if (!finished) {
|
|
64
|
+
snapshot.in_progress = true;
|
|
65
|
+
snapshot.pending = pending.size;
|
|
66
|
+
snapshot.running = running.size;
|
|
67
|
+
}
|
|
68
|
+
writeReleaseGateJson(path.join(reportDir, 'summary.json'), snapshot);
|
|
69
|
+
return snapshot;
|
|
70
|
+
};
|
|
38
71
|
if (input.explain) {
|
|
39
72
|
writeReleaseGateJson(path.join(reportDir, 'explain.json'), { schema: RELEASE_GATE_NODE_SCHEMA, preset, budget, gates: selected.map((gate) => ({ id: gate.id, deps: gate.deps, resource: gate.resource, command: gate.command })) });
|
|
40
73
|
}
|
|
@@ -51,6 +84,7 @@ export async function runReleaseGateDag(input) {
|
|
|
51
84
|
cached += 1;
|
|
52
85
|
progressed = true;
|
|
53
86
|
appendReleaseGateJsonl(timeline, { event: 'cache_hit', gate_id: gate.id, at: new Date().toISOString() });
|
|
87
|
+
writeSummarySnapshot(false);
|
|
54
88
|
continue;
|
|
55
89
|
}
|
|
56
90
|
appendReleaseGateJsonl(timeline, { event: 'start', gate_id: gate.id, resource: gate.resource, at: new Date().toISOString() });
|
|
@@ -102,32 +136,9 @@ export async function runReleaseGateDag(input) {
|
|
|
102
136
|
}
|
|
103
137
|
}
|
|
104
138
|
appendReleaseGateJsonl(timeline, { event: result.ok ? 'pass' : 'fail', gate_id: result.id, duration_ms: result.duration_ms, at: new Date().toISOString() });
|
|
139
|
+
writeSummarySnapshot(false);
|
|
105
140
|
}
|
|
106
|
-
const
|
|
107
|
-
const failures = [...failed.values()].map((row) => ({ id: row.id, exit_code: row.exit_code, stderr_tail: row.stderr_tail }));
|
|
108
|
-
const result = {
|
|
109
|
-
schema: 'sks.release-gate-dag-run.v1',
|
|
110
|
-
ok: failures.length === 0,
|
|
111
|
-
run_id: runId,
|
|
112
|
-
selected_preset: preset,
|
|
113
|
-
total_gates: manifest.gates.length,
|
|
114
|
-
selected_gates: selected.length,
|
|
115
|
-
completed: completed.size,
|
|
116
|
-
failed: failed.size,
|
|
117
|
-
cached,
|
|
118
|
-
wall_ms: wallMs,
|
|
119
|
-
sum_gate_ms: sumGateMs,
|
|
120
|
-
cpu_time_saved_ms: Math.max(0, sumGateMs - wallMs),
|
|
121
|
-
parallelism_gain: wallMs > 0 ? Number((sumGateMs / wallMs).toFixed(2)) : 1,
|
|
122
|
-
critical_path_ms: estimateCriticalPath(selected, completed),
|
|
123
|
-
peak_running: peakRunning,
|
|
124
|
-
peak_resources: peakResources,
|
|
125
|
-
budget_snapshot: budget,
|
|
126
|
-
budget_summary: summarizeReleaseGateBudget(budget),
|
|
127
|
-
report_dir: reportDir,
|
|
128
|
-
failures
|
|
129
|
-
};
|
|
130
|
-
writeReleaseGateJson(path.join(reportDir, 'summary.json'), result);
|
|
141
|
+
const result = writeSummarySnapshot(true);
|
|
131
142
|
return result;
|
|
132
143
|
}
|
|
133
144
|
function selectPreset(manifest, preset) {
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '2.0.
|
|
1
|
+
export const PACKAGE_VERSION = '2.0.10';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -6,6 +6,10 @@ export function buildZellijDashboardSnapshot(input) {
|
|
|
6
6
|
generated_at: nowIso(),
|
|
7
7
|
mission_id: input.mission_id,
|
|
8
8
|
mode: input.mode || 'naruto',
|
|
9
|
+
route: input.route || input.mode || '$Naruto',
|
|
10
|
+
provider: input.provider || 'unknown',
|
|
11
|
+
service_tier: input.service_tier || 'fast',
|
|
12
|
+
backpressure: input.backpressure || 'normal',
|
|
9
13
|
backend_counts: input.backend_counts || { 'codex-sdk': 1 },
|
|
10
14
|
placement_counts: input.placement_counts || { 'zellij-pane': 1 },
|
|
11
15
|
active_workers: Number(input.active_workers || 0),
|
|
@@ -16,6 +20,15 @@ export function buildZellijDashboardSnapshot(input) {
|
|
|
16
20
|
local_llm: input.local_llm || { tps: 0, queue: 0 },
|
|
17
21
|
gpt_final_status: input.gpt_final_status || 'not_started',
|
|
18
22
|
gate_progress: input.gate_progress || 'not_release',
|
|
23
|
+
patch_verify: input.patch_verify || {
|
|
24
|
+
patches: 0,
|
|
25
|
+
gpt_approved: 0,
|
|
26
|
+
conflicts: 0,
|
|
27
|
+
verification_running: 0,
|
|
28
|
+
verification_passed: 0,
|
|
29
|
+
verification_failed: 0
|
|
30
|
+
},
|
|
31
|
+
workers: input.workers || [],
|
|
19
32
|
latest_blockers: input.latest_blockers || []
|
|
20
33
|
};
|
|
21
34
|
}
|
|
@@ -25,18 +38,21 @@ export function renderZellijDashboardText(snapshot) {
|
|
|
25
38
|
return [
|
|
26
39
|
'SKS Dashboard',
|
|
27
40
|
`Mission: ${snapshot.mission_id}`,
|
|
28
|
-
`
|
|
41
|
+
`Route / mode: ${snapshot.route} / ${snapshot.mode} / ${snapshot.service_tier}`,
|
|
42
|
+
`Provider: ${snapshot.provider}`,
|
|
29
43
|
`Backend counts: ${backendCounts || 'none'}`,
|
|
30
44
|
`Placement counts: ${placementCounts || 'none'}`,
|
|
31
|
-
`Active
|
|
32
|
-
`
|
|
33
|
-
`Headless workers: ${snapshot.headless_workers}`,
|
|
34
|
-
`Queue depth: ${snapshot.queue_depth}`,
|
|
45
|
+
`Active / visible / headless / queued: ${snapshot.active_workers}/${snapshot.visible_panes}/${snapshot.headless_workers}/${snapshot.queue_depth}`,
|
|
46
|
+
`Backpressure: ${snapshot.backpressure}`,
|
|
35
47
|
`Worktrees active/completed/retained: ${snapshot.worktrees.active}/${snapshot.worktrees.completed}/${snapshot.worktrees.retained}`,
|
|
36
48
|
`Local LLM TPS / queue: ${snapshot.local_llm.tps}/${snapshot.local_llm.queue}`,
|
|
37
49
|
`GPT final status: ${snapshot.gpt_final_status}`,
|
|
38
50
|
`Gate progress: ${snapshot.gate_progress}`,
|
|
39
|
-
`
|
|
51
|
+
`Patch / verify: patches ${snapshot.patch_verify.patches} · approved ${snapshot.patch_verify.gpt_approved} · conflicts ${snapshot.patch_verify.conflicts} · verify ${snapshot.patch_verify.verification_running}/${snapshot.patch_verify.verification_passed}/${snapshot.patch_verify.verification_failed}`,
|
|
52
|
+
'Workers:',
|
|
53
|
+
...(snapshot.workers.length ? snapshot.workers.slice(0, 12).map((worker) => `${worker.slot_id} gen-${worker.generation_index} ${worker.role} ${worker.backend}/${worker.provider}/${worker.service_tier} WT:${worker.worktree_id || '-'} ${worker.status} ${worker.current_file || ''} ${worker.latest_heartbeat || ''}`) : ['none']),
|
|
54
|
+
`Latest blockers: ${snapshot.latest_blockers.length ? snapshot.latest_blockers.join(', ') : 'none'}`,
|
|
55
|
+
'Controls: q detach | /stop selected | /focus slot | /logs'
|
|
40
56
|
].join('\n');
|
|
41
57
|
}
|
|
42
58
|
//# sourceMappingURL=zellij-dashboard-renderer.js.map
|
|
@@ -19,7 +19,7 @@ export async function launchZellijLayout(opts = {}) {
|
|
|
19
19
|
ledgerRoot,
|
|
20
20
|
cwd: opts.cwd || root,
|
|
21
21
|
kind: opts.kind || 'agent',
|
|
22
|
-
slotCount: opts.slotCount
|
|
22
|
+
slotCount: opts.slotCount ?? 1,
|
|
23
23
|
title: `SKS ${opts.kind || 'agent'} ${missionId}`,
|
|
24
24
|
codexArgs: opts.codexArgs || [],
|
|
25
25
|
launchEnv: opts.launchEnv || {}
|
|
@@ -124,7 +124,7 @@ export async function launchMadZellijUi(args = [], opts = {}) {
|
|
|
124
124
|
const launchOpts = {
|
|
125
125
|
...opts,
|
|
126
126
|
kind: 'mad',
|
|
127
|
-
slotCount: opts.slotCount
|
|
127
|
+
slotCount: opts.slotCount ?? 1
|
|
128
128
|
};
|
|
129
129
|
const resolvedSession = session || opts.session;
|
|
130
130
|
if (resolvedSession)
|
|
@@ -135,7 +135,7 @@ export async function launchTeamZellijView(opts = {}) {
|
|
|
135
135
|
return launchZellijLayout({
|
|
136
136
|
...opts,
|
|
137
137
|
kind: 'team',
|
|
138
|
-
slotCount: opts.slotCount
|
|
138
|
+
slotCount: opts.slotCount ?? 5
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
/**
|
|
@@ -3,7 +3,7 @@ import { ensureDir, nowIso, packageRoot, writeTextAtomic } from '../fsx.js';
|
|
|
3
3
|
import { writeZellijLaneRuntimeManifest } from './zellij-lane-runtime.js';
|
|
4
4
|
export const ZELLIJ_LAYOUT_SCHEMA = 'sks.zellij-layout.v1';
|
|
5
5
|
export function buildZellijLayoutKdl(input) {
|
|
6
|
-
const slotCount = Math.max(
|
|
6
|
+
const slotCount = Math.max(0, Number(input.slotCount ?? 1));
|
|
7
7
|
const sessionName = input.sessionName || `sks-${input.missionId}`;
|
|
8
8
|
const cwd = path.resolve(input.cwd || process.cwd());
|
|
9
9
|
const ledgerRoot = path.resolve(input.ledgerRoot);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function evaluateZellijRightColumnGeometry(input) {
|
|
2
|
+
const tolerance = Math.max(0, Number(input.tolerance ?? 2));
|
|
3
|
+
const main = input.panes.find((pane) => pane.role === 'main') || null;
|
|
4
|
+
const dashboard = input.panes.find((pane) => pane.role === 'dashboard') || null;
|
|
5
|
+
const workers = input.panes.filter((pane) => pane.role === 'worker');
|
|
6
|
+
const workerGeometries = workers.map((pane) => pane.geometry).filter(Boolean);
|
|
7
|
+
const baseX = workerGeometries[0]?.x;
|
|
8
|
+
const sameRightX = baseX == null ? null : workerGeometries.every((geometry) => geometry.x != null && Math.abs(geometry.x - baseX) <= tolerance);
|
|
9
|
+
const rightOfMain = !main?.geometry ? null : workerGeometries.every((geometry) => {
|
|
10
|
+
if (geometry.x == null || main.geometry?.x == null || main.geometry?.width == null)
|
|
11
|
+
return false;
|
|
12
|
+
return geometry.x >= main.geometry.x + main.geometry.width - tolerance;
|
|
13
|
+
});
|
|
14
|
+
const increasingY = workerGeometries.every((geometry, index) => {
|
|
15
|
+
if (index === 0)
|
|
16
|
+
return true;
|
|
17
|
+
const previous = workerGeometries[index - 1];
|
|
18
|
+
return geometry.y != null && previous?.y != null && geometry.y > previous.y;
|
|
19
|
+
});
|
|
20
|
+
const visibleCapOk = workers.length <= Math.max(0, Math.floor(Number(input.visiblePaneCap || 0)));
|
|
21
|
+
const blockers = [
|
|
22
|
+
...(!main ? ['right_column_main_pane_missing'] : []),
|
|
23
|
+
...(!dashboard ? ['right_column_dashboard_missing'] : []),
|
|
24
|
+
...(sameRightX === false ? ['right_column_worker_x_range_mismatch'] : []),
|
|
25
|
+
...(rightOfMain === false ? ['right_column_workers_not_right_of_main'] : []),
|
|
26
|
+
...(increasingY === false ? ['right_column_workers_not_stacked_down'] : []),
|
|
27
|
+
...(visibleCapOk ? [] : ['right_column_visible_cap_exceeded'])
|
|
28
|
+
];
|
|
29
|
+
return {
|
|
30
|
+
schema: 'sks.zellij-right-column-layout-proof.v1',
|
|
31
|
+
ok: blockers.length === 0,
|
|
32
|
+
main_pane_id: main?.pane_id || null,
|
|
33
|
+
dashboard_pane_id: dashboard?.pane_id || null,
|
|
34
|
+
worker_pane_ids: workers.map((pane) => pane.pane_id),
|
|
35
|
+
same_right_x: sameRightX,
|
|
36
|
+
right_of_main: rightOfMain,
|
|
37
|
+
increasing_y: increasingY,
|
|
38
|
+
visible_cap_ok: visibleCapOk,
|
|
39
|
+
blockers
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=zellij-right-column-layout-proof.js.map
|