sneakoscope 2.0.7 → 2.0.8
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 +1 -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/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +32 -8
- package/dist/core/agents/agent-command-surface.js +4 -2
- package/dist/core/agents/agent-orchestrator.js +2 -1
- package/dist/core/agents/agent-patch-schema.js +4 -2
- package/dist/core/agents/native-cli-session-swarm.js +7 -2
- package/dist/core/commands/mad-sks-command.js +25 -0
- package/dist/core/commands/naruto-command.js +29 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/git/git-repo-detection.js +7 -0
- package/dist/core/git/git-worktree-cleanup.js +14 -3
- package/dist/core/git/git-worktree-diff.js +7 -2
- package/dist/core/git/git-worktree-manager.js +9 -2
- package/dist/core/git/git-worktree-patch-envelope.js +5 -5
- package/dist/core/release/release-gate-cache-v2.js +63 -0
- package/dist/core/release/release-gate-dag.js +179 -0
- package/dist/core/release/release-gate-hermetic-env.js +32 -0
- package/dist/core/release/release-gate-node.js +62 -0
- package/dist/core/release/release-gate-report.js +11 -0
- package/dist/core/release/release-gate-resource-governor.js +54 -0
- package/dist/core/release/release-gate-scheduler.js +15 -0
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-dashboard-pane.js +71 -0
- package/dist/core/zellij/zellij-dashboard-renderer.js +42 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +63 -3
- package/dist/scripts/git-worktree-diff-envelope-check.js +17 -0
- package/dist/scripts/git-worktree-dirty-lock-check.js +17 -0
- package/dist/scripts/git-worktree-dirty-main-detection-check.js +14 -0
- package/dist/scripts/git-worktree-integration-primary-check.js +22 -0
- package/dist/scripts/git-worktree-manifest-append-check.js +18 -0
- package/dist/scripts/git-worktree-untracked-diff-check.js +18 -0
- package/dist/scripts/naruto-worktree-coding-blackbox.js +29 -0
- package/dist/scripts/release-gate-dag-runner-check.js +17 -0
- package/dist/scripts/release-gate-dag-runner.js +32 -0
- package/dist/scripts/release-gate-worker.js +10 -0
- package/dist/scripts/release-metadata-1-19-check.js +8 -2
- package/dist/scripts/release-parallel-speed-budget-check.js +25 -0
- package/dist/scripts/release-stability-report-check.js +99 -0
- package/dist/scripts/zellij-dashboard-pane-check.js +68 -0
- package/dist/scripts/zellij-dashboard-watch.js +41 -0
- package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +185 -0
- package/package.json +22 -5
- package/schemas/release/release-gate-node.schema.json +52 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
16
16
|
|
|
17
17
|
## Current Release
|
|
18
18
|
|
|
19
|
-
SKS **2.0.
|
|
19
|
+
SKS **2.0.8** makes the release path DAG-parallel by default and hardens the Naruto/MAD-SKS worktree proof chain. `release:check` now runs a manifest-backed DAG runner with resource-aware scheduling, hermetic per-gate environments, bounded logs, per-gate reports, cache proof, and parallel speed-budget evidence. Git worktree coding now preserves allocation manifests, detects dirty main worktrees, includes untracked content in exported diffs, emits one `git_apply_patch` envelope operation, applies worktree diffs through an integration queue, and locks retained dirty worktrees.
|
|
20
20
|
|
|
21
21
|
What changed:
|
|
22
22
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 2.0.
|
|
7
|
+
Some("--version") => println!("sks-rs 2.0.8"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build-stamp.v1",
|
|
3
3
|
"package_name": "sneakoscope",
|
|
4
|
-
"package_version": "2.0.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
4
|
+
"package_version": "2.0.8",
|
|
5
|
+
"source_digest": "5009938d20628f783aa7ac19da170d2737fef89481b49d9c8dc3f2f97c0dcf9d",
|
|
6
|
+
"source_file_count": 2004,
|
|
7
|
+
"built_at_source_time": 1780739165211
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
package/dist/build-manifest.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build.v2",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"package_version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
|
+
"package_version": "2.0.8",
|
|
5
5
|
"typescript": true,
|
|
6
6
|
"mjs_runtime_files": 0,
|
|
7
|
-
"compiled_file_count":
|
|
8
|
-
"compiled_js_count":
|
|
7
|
+
"compiled_file_count": 1067,
|
|
8
|
+
"compiled_js_count": 1067,
|
|
9
9
|
"compiled_dts_count": 0,
|
|
10
|
-
"source_digest": "
|
|
11
|
-
"source_file_count":
|
|
12
|
-
"source_files_hash": "
|
|
13
|
-
"source_list_hash": "
|
|
10
|
+
"source_digest": "5009938d20628f783aa7ac19da170d2737fef89481b49d9c8dc3f2f97c0dcf9d",
|
|
11
|
+
"source_file_count": 2004,
|
|
12
|
+
"source_files_hash": "045de0cf4e0b27b18838e5826c77f19ee281908588bbfde4a00a448b5a3c03eb",
|
|
13
|
+
"source_list_hash": "045de0cf4e0b27b18838e5826c77f19ee281908588bbfde4a00a448b5a3c03eb",
|
|
14
14
|
"src_mjs_runtime_files": 0,
|
|
15
15
|
"dist_stamp_schema": "sks.dist-build-stamp.v1",
|
|
16
16
|
"files": [
|
|
@@ -519,6 +519,13 @@
|
|
|
519
519
|
"core/release-parallel-full-coverage.js",
|
|
520
520
|
"core/release/gate-cache.js",
|
|
521
521
|
"core/release/gate-manifest.js",
|
|
522
|
+
"core/release/release-gate-cache-v2.js",
|
|
523
|
+
"core/release/release-gate-dag.js",
|
|
524
|
+
"core/release/release-gate-hermetic-env.js",
|
|
525
|
+
"core/release/release-gate-node.js",
|
|
526
|
+
"core/release/release-gate-report.js",
|
|
527
|
+
"core/release/release-gate-resource-governor.js",
|
|
528
|
+
"core/release/release-gate-scheduler.js",
|
|
522
529
|
"core/reporting/markdown-table.js",
|
|
523
530
|
"core/research.js",
|
|
524
531
|
"core/responses-retry-policy.js",
|
|
@@ -616,6 +623,8 @@
|
|
|
616
623
|
"core/zellij/zellij-capability.js",
|
|
617
624
|
"core/zellij/zellij-clipboard-config.js",
|
|
618
625
|
"core/zellij/zellij-command.js",
|
|
626
|
+
"core/zellij/zellij-dashboard-pane.js",
|
|
627
|
+
"core/zellij/zellij-dashboard-renderer.js",
|
|
619
628
|
"core/zellij/zellij-lane-renderer.js",
|
|
620
629
|
"core/zellij/zellij-lane-runtime.js",
|
|
621
630
|
"core/zellij/zellij-launcher.js",
|
|
@@ -830,10 +839,16 @@
|
|
|
830
839
|
"scripts/git-worktree-cache-performance-check.js",
|
|
831
840
|
"scripts/git-worktree-capability-check.js",
|
|
832
841
|
"scripts/git-worktree-cleanup-check.js",
|
|
842
|
+
"scripts/git-worktree-diff-envelope-check.js",
|
|
833
843
|
"scripts/git-worktree-diff-export-check.js",
|
|
844
|
+
"scripts/git-worktree-dirty-lock-check.js",
|
|
845
|
+
"scripts/git-worktree-dirty-main-detection-check.js",
|
|
846
|
+
"scripts/git-worktree-integration-primary-check.js",
|
|
834
847
|
"scripts/git-worktree-manager-check.js",
|
|
848
|
+
"scripts/git-worktree-manifest-append-check.js",
|
|
835
849
|
"scripts/git-worktree-merge-queue-check.js",
|
|
836
850
|
"scripts/git-worktree-pool-performance-check.js",
|
|
851
|
+
"scripts/git-worktree-untracked-diff-check.js",
|
|
837
852
|
"scripts/goal-mode-official-default-check.js",
|
|
838
853
|
"scripts/gpt-final-arbiter-check.js",
|
|
839
854
|
"scripts/gpt-final-arbiter-performance-check.js",
|
|
@@ -915,6 +930,7 @@
|
|
|
915
930
|
"scripts/naruto-shadow-clone-swarm-check.js",
|
|
916
931
|
"scripts/naruto-verification-pool-check.js",
|
|
917
932
|
"scripts/naruto-work-graph-check.js",
|
|
933
|
+
"scripts/naruto-worktree-coding-blackbox.js",
|
|
918
934
|
"scripts/naruto-worktree-coding-check.js",
|
|
919
935
|
"scripts/naruto-worktree-gpt-final-check.js",
|
|
920
936
|
"scripts/naruto-worktree-zellij-ui-check.js",
|
|
@@ -963,8 +979,11 @@
|
|
|
963
979
|
"scripts/release-dist-freshness-check.js",
|
|
964
980
|
"scripts/release-dynamic-performance-check.js",
|
|
965
981
|
"scripts/release-gate-budget-check.js",
|
|
982
|
+
"scripts/release-gate-dag-runner-check.js",
|
|
983
|
+
"scripts/release-gate-dag-runner.js",
|
|
966
984
|
"scripts/release-gate-existence-audit.js",
|
|
967
985
|
"scripts/release-gate-planner.js",
|
|
986
|
+
"scripts/release-gate-worker.js",
|
|
968
987
|
"scripts/release-metadata-1-11-check.js",
|
|
969
988
|
"scripts/release-metadata-1-12-check.js",
|
|
970
989
|
"scripts/release-metadata-1-13-check.js",
|
|
@@ -976,11 +995,13 @@
|
|
|
976
995
|
"scripts/release-native-agent-fixture-check.js",
|
|
977
996
|
"scripts/release-parallel-check.js",
|
|
978
997
|
"scripts/release-parallel-full-coverage-check.js",
|
|
998
|
+
"scripts/release-parallel-speed-budget-check.js",
|
|
979
999
|
"scripts/release-provenance-check.js",
|
|
980
1000
|
"scripts/release-readiness-report.js",
|
|
981
1001
|
"scripts/release-real-check.js",
|
|
982
1002
|
"scripts/release-registry-check.js",
|
|
983
1003
|
"scripts/release-runtime-truth-matrix-check.js",
|
|
1004
|
+
"scripts/release-stability-report-check.js",
|
|
984
1005
|
"scripts/release-version-truth-check.js",
|
|
985
1006
|
"scripts/repo-audit.js",
|
|
986
1007
|
"scripts/research-actual-route-backfill-check.js",
|
|
@@ -1044,6 +1065,8 @@
|
|
|
1044
1065
|
"scripts/wrongness-fixture-check.js",
|
|
1045
1066
|
"scripts/xai-mcp-capability-check.js",
|
|
1046
1067
|
"scripts/zellij-capability-check.js",
|
|
1068
|
+
"scripts/zellij-dashboard-pane-check.js",
|
|
1069
|
+
"scripts/zellij-dashboard-watch.js",
|
|
1047
1070
|
"scripts/zellij-doctor-readiness-check.js",
|
|
1048
1071
|
"scripts/zellij-lane-renderer-check.js",
|
|
1049
1072
|
"scripts/zellij-launch-command-truth-check.js",
|
|
@@ -1057,6 +1080,7 @@
|
|
|
1057
1080
|
"scripts/zellij-ui-design-check.js",
|
|
1058
1081
|
"scripts/zellij-worker-pane-manager-check.js",
|
|
1059
1082
|
"scripts/zellij-worker-pane-manager-single-owner-check.js",
|
|
1083
|
+
"scripts/zellij-worker-pane-real-ui-blackbox.js",
|
|
1060
1084
|
"scripts/zellij-worker-pane-spawn-order-check.js",
|
|
1061
1085
|
"vendor/openai-codex/latest/hooks/permission-request.command.input.schema.json",
|
|
1062
1086
|
"vendor/openai-codex/latest/hooks/permission-request.command.output.schema.json",
|
|
@@ -33,6 +33,8 @@ export function parseAgentCommandArgs(command, args = []) {
|
|
|
33
33
|
const ollamaBaseUrl = String(readOption(args, '--ollama-base-url', readOption(args, '--local-model-base-url', '')) || '') || null;
|
|
34
34
|
const zellijSessionName = String(readOption(args, '--zellij-session-name', '') || '') || null;
|
|
35
35
|
const zellijPaneWorker = hasFlag(args, '--no-zellij-pane-worker') ? false : hasFlag(args, '--zellij-pane-worker') ? true : undefined;
|
|
36
|
+
const workerPlacement = String(readOption(args, '--worker-placement', zellijPaneWorker === true ? 'zellij-pane' : '') || '') || undefined;
|
|
37
|
+
const zellijVisiblePaneCap = Number(readOption(args, '--zellij-visible-pane-cap', process.env.SKS_ZELLIJ_VISIBLE_PANE_CAP || 12));
|
|
36
38
|
const apply = hasFlag(args, '--apply');
|
|
37
39
|
const dryRun = hasFlag(args, '--dry-run') || hasFlag(args, '--dryrun');
|
|
38
40
|
const drain = hasFlag(args, '--drain');
|
|
@@ -40,7 +42,7 @@ export function parseAgentCommandArgs(command, args = []) {
|
|
|
40
42
|
const graceMs = Number(readOption(args, '--grace-ms', 750));
|
|
41
43
|
const killEscalation = hasFlag(args, '--kill-escalation') || !hasFlag(args, '--no-kill-escalation');
|
|
42
44
|
const codexApp = hasFlag(args, '--codex-app');
|
|
43
|
-
const positionals = positionalArgs(rest, new Set(['--agents', '--target-active-slots', '--work-items', '--minimum-work-items', '--max-queue-expansion', '--concurrency', '--backend', '--route', '--mission', '--mission-id', '--agent', '--lane', '--stale-ms', '--grace-ms', '--profile', '--write-mode', '--max-write-agents', '--patch-entry-id', '--patch-entry', '--service-tier', '--zellij-session-name', '--intake', '--agent-root', '--artifact-dir', '--result-path', '--heartbeat-path', '--patch-envelope-path', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url']));
|
|
45
|
+
const positionals = positionalArgs(rest, new Set(['--agents', '--target-active-slots', '--work-items', '--minimum-work-items', '--max-queue-expansion', '--concurrency', '--backend', '--route', '--mission', '--mission-id', '--agent', '--lane', '--stale-ms', '--grace-ms', '--profile', '--write-mode', '--max-write-agents', '--patch-entry-id', '--patch-entry', '--service-tier', '--zellij-session-name', '--worker-placement', '--zellij-visible-pane-cap', '--intake', '--agent-root', '--artifact-dir', '--result-path', '--heartbeat-path', '--patch-envelope-path', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url']));
|
|
44
46
|
const missionDefault = action === 'run' || action === 'spawn' || action === 'plan' ? '' : 'latest';
|
|
45
47
|
const positionalMission = action === 'run' || action === 'spawn' || action === 'plan' ? '' : (positionals[0] || '');
|
|
46
48
|
const missionId = String(readOption(args, '--mission', readOption(args, '--mission-id', positionalMission || missionDefault)));
|
|
@@ -48,7 +50,7 @@ export function parseAgentCommandArgs(command, args = []) {
|
|
|
48
50
|
const patchEntryId = String(readOption(args, '--patch-entry-id', readOption(args, '--patch-entry', '')));
|
|
49
51
|
const promptPositionals = positionalMission ? positionals.slice(1) : positionals;
|
|
50
52
|
const prompt = promptPositionals.join(' ').trim() || 'Native agent run';
|
|
51
|
-
return { command, action, prompt, route, agents, targetActiveSlots, desiredWorkItemCount, minimumWorkItems, maxQueueExpansion, concurrency, backend, backendExplicit, mock, real, readonly, profile, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, zellijSessionName, zellijPaneWorker, apply, dryRun, drain, staleMs, graceMs, killEscalation, json, missionId, lane, codexApp, patchEntryId };
|
|
53
|
+
return { command, action, prompt, route, agents, targetActiveSlots, desiredWorkItemCount, minimumWorkItems, maxQueueExpansion, concurrency, backend, backendExplicit, mock, real, readonly, profile, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, zellijSessionName, zellijPaneWorker, workerPlacement, zellijVisiblePaneCap, apply, dryRun, drain, staleMs, graceMs, killEscalation, json, missionId, lane, codexApp, patchEntryId };
|
|
52
54
|
}
|
|
53
55
|
function hasFlag(args, flag) {
|
|
54
56
|
return args.includes(flag);
|
|
@@ -280,7 +280,8 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
280
280
|
backendExplicit: opts.backendExplicit === true,
|
|
281
281
|
noOllama: opts.noOllama === true,
|
|
282
282
|
route,
|
|
283
|
-
fastModePolicy
|
|
283
|
+
fastModePolicy,
|
|
284
|
+
...(opts.workerPlacement === undefined ? {} : { workerPlacement: String(opts.workerPlacement) })
|
|
284
285
|
});
|
|
285
286
|
await nativeCliSwarm.initialize();
|
|
286
287
|
await setCurrent(root, { mission_id: missionId, mode: 'AGENT', phase: 'AGENT_NATIVE_KERNEL_RUNNING', route_command: routeCommand, native_agent_backend: backend });
|
|
@@ -68,8 +68,10 @@ export function validateAgentPatchEnvelope(envelope) {
|
|
|
68
68
|
violations.push(`write_content_missing:${operation.path}`);
|
|
69
69
|
if (operation.op === 'unified_diff' && typeof operation.diff !== 'string')
|
|
70
70
|
violations.push(`unified_diff_missing:${operation.path}`);
|
|
71
|
+
if (operation.op === 'git_apply_patch' && typeof operation.diff !== 'string')
|
|
72
|
+
violations.push(`git_apply_patch_missing:${operation.path}`);
|
|
71
73
|
const allowedPaths = envelope.allowed_paths?.length ? envelope.allowed_paths : envelope.lease_proof?.allowed_paths;
|
|
72
|
-
if (allowedPaths?.length && !pathAllowedByLease(operation.path, allowedPaths)) {
|
|
74
|
+
if (operation.op !== 'git_apply_patch' && allowedPaths?.length && !pathAllowedByLease(operation.path, allowedPaths)) {
|
|
73
75
|
violations.push(`lease_path_not_allowed:${operation.path}`);
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -83,7 +85,7 @@ function hasFiniteNumber(value) {
|
|
|
83
85
|
return value !== null && value !== undefined && value !== '' && Number.isFinite(Number(value));
|
|
84
86
|
}
|
|
85
87
|
function normalizeOperation(input) {
|
|
86
|
-
const op = input?.op === 'write' ? 'write' : input?.op === 'unified_diff' || input?.op === 'patch' ? 'unified_diff' : 'replace';
|
|
88
|
+
const op = input?.op === 'write' ? 'write' : input?.op === 'git_apply_patch' ? 'git_apply_patch' : input?.op === 'unified_diff' || input?.op === 'patch' ? 'unified_diff' : 'replace';
|
|
87
89
|
return {
|
|
88
90
|
op,
|
|
89
91
|
path: String(input?.path || ''),
|
|
@@ -101,7 +101,11 @@ class NativeCliSessionSwarmRecorder {
|
|
|
101
101
|
};
|
|
102
102
|
const stdout = fs.createWriteStream(path.join(this.root, stdoutRel), { flags: 'a' });
|
|
103
103
|
const stderr = fs.createWriteStream(path.join(this.root, stderrRel), { flags: 'a' });
|
|
104
|
-
|
|
104
|
+
const placement = String(ctx.opts.workerPlacement || this.input.workerPlacement || (this.input.backend === 'zellij' ? 'zellij-pane' : 'process'));
|
|
105
|
+
const useZellijPane = placement === 'zellij-pane'
|
|
106
|
+
&& ctx.opts.zellijPaneWorker !== false
|
|
107
|
+
&& (ctx.opts.zellijSessionName || this.input.missionId);
|
|
108
|
+
if (useZellijPane) {
|
|
105
109
|
stdout.end();
|
|
106
110
|
stderr.end();
|
|
107
111
|
return this.launchWorkerInZellijPane({
|
|
@@ -233,7 +237,8 @@ class NativeCliSessionSwarmRecorder {
|
|
|
233
237
|
cwd: workerCwd,
|
|
234
238
|
providerContext,
|
|
235
239
|
serviceTier: this.input.fastModePolicy.service_tier,
|
|
236
|
-
worktree: worktree ? { id: worktree.id, path: worktree.path, branch: worktree.branch } : null
|
|
240
|
+
worktree: worktree ? { id: worktree.id, path: worktree.path, branch: worktree.branch } : null,
|
|
241
|
+
backend: this.input.backend
|
|
237
242
|
});
|
|
238
243
|
const launchBlockers = paneRecord.blockers || [];
|
|
239
244
|
input.record.command_line = ['zellij', '--session', sessionName, 'action', 'new-pane', '--direction', paneRecord.direction_applied, '--name', paneRecord.pane_name, '--', 'sh', '-lc', '<native-cli-worker-command>'];
|
|
@@ -7,6 +7,7 @@ import { createMission, setCurrent } from '../mission.js';
|
|
|
7
7
|
import { buildMadHighLaunchProfileNoWrite, madHighProfileName } from '../auto-review.js';
|
|
8
8
|
import { permissionGateSummary } from '../permission-gates.js';
|
|
9
9
|
import { attachZellijSessionInteractive, launchMadZellijUi, sanitizeZellijSessionName } from '../zellij/zellij-launcher.js';
|
|
10
|
+
import { openZellijDashboardPane } from '../zellij/zellij-dashboard-pane.js';
|
|
10
11
|
import { createMadSksAuthorizationManifest, validateMadSksAuthorizationManifest } from '../mad-sks/authorization-manifest.js';
|
|
11
12
|
import { createMadSksAuditLedger, madSksAuditAction, writeMadSksAuditLedger } from '../mad-sks/audit-ledger.js';
|
|
12
13
|
import { compareProtectedCoreSnapshots, evaluateMadSksWrite, resolveProtectedCore, snapshotProtectedCore } from '../mad-sks/immutable-harness-guard.js';
|
|
@@ -113,6 +114,30 @@ export async function madHighCommand(args = [], deps = {}) {
|
|
|
113
114
|
console.log(`MAD Zellij action: ${formatMadZellijAction(launch)}`);
|
|
114
115
|
return launch;
|
|
115
116
|
}
|
|
117
|
+
launch.dashboard_pane = await openZellijDashboardPane({
|
|
118
|
+
root: madLaunch.root,
|
|
119
|
+
missionId: madLaunch.mission_id,
|
|
120
|
+
sessionName: launch.session_name,
|
|
121
|
+
cwd: process.cwd(),
|
|
122
|
+
snapshot: {
|
|
123
|
+
mode: 'mad-sks',
|
|
124
|
+
backend_counts: { zellij: 1 },
|
|
125
|
+
placement_counts: { 'zellij-pane': 1, headless: 0 },
|
|
126
|
+
active_workers: 1,
|
|
127
|
+
visible_panes: 1,
|
|
128
|
+
headless_workers: 0,
|
|
129
|
+
queue_depth: 0,
|
|
130
|
+
worktrees: { active: 0, completed: 0, retained: 0 },
|
|
131
|
+
local_llm: { tps: 0, queue: 0 },
|
|
132
|
+
gpt_final_status: 'pending',
|
|
133
|
+
gate_progress: 'mad-sks:session-open'
|
|
134
|
+
}
|
|
135
|
+
}).catch((err) => ({
|
|
136
|
+
ok: false,
|
|
137
|
+
pane_kind: 'dashboard',
|
|
138
|
+
worker_pane: false,
|
|
139
|
+
blockers: [`zellij_dashboard_exception:${err?.message || String(err)}`]
|
|
140
|
+
}));
|
|
116
141
|
const madNativeSwarm = await startMadNativeSwarm(madLaunch.root, madLaunch, args, profile, {
|
|
117
142
|
env: {
|
|
118
143
|
...madSksEnv,
|
|
@@ -14,6 +14,7 @@ import { runNarutoActivePool } from '../naruto/naruto-active-pool.js';
|
|
|
14
14
|
import { buildNarutoVerificationDag } from '../naruto/naruto-verification-dag.js';
|
|
15
15
|
import { buildNarutoGptFinalPack } from '../naruto/naruto-gpt-final-pack.js';
|
|
16
16
|
import { planNarutoZellijDashboard } from '../zellij/zellij-naruto-dashboard.js';
|
|
17
|
+
import { openZellijDashboardPane } from '../zellij/zellij-dashboard-pane.js';
|
|
17
18
|
import { checkPromptPlaceholders } from '../prompt/prompt-placeholder-guard.js';
|
|
18
19
|
import { evaluateGitWorktreeCapability } from '../git/git-worktree-capability.js';
|
|
19
20
|
const NARUTO_RESULT_SCHEMA = 'sks.naruto-command-result.v1';
|
|
@@ -149,6 +150,34 @@ async function narutoRun(parsed) {
|
|
|
149
150
|
attach: false
|
|
150
151
|
});
|
|
151
152
|
if (liveZellij?.ok && liveZellij.capability?.status === 'ok') {
|
|
153
|
+
liveZellij.dashboard_pane = await openZellijDashboardPane({
|
|
154
|
+
root,
|
|
155
|
+
missionId: mission.id,
|
|
156
|
+
sessionName: liveZellij.session_name,
|
|
157
|
+
cwd: root,
|
|
158
|
+
snapshot: {
|
|
159
|
+
mode: 'naruto',
|
|
160
|
+
backend_counts: { [schedulerBackend]: activeSlots },
|
|
161
|
+
placement_counts: { 'zellij-pane': zellijVisiblePanes, headless: Math.max(0, activeSlots - zellijVisiblePanes) },
|
|
162
|
+
active_workers: activeSlots,
|
|
163
|
+
visible_panes: zellijVisiblePanes,
|
|
164
|
+
headless_workers: Math.max(0, activeSlots - zellijVisiblePanes),
|
|
165
|
+
queue_depth: Math.max(0, workGraph.total_work_items - activeSlots),
|
|
166
|
+
worktrees: {
|
|
167
|
+
active: worktreePolicy.mode === 'git-worktree' ? activeSlots : 0,
|
|
168
|
+
completed: 0,
|
|
169
|
+
retained: 0
|
|
170
|
+
},
|
|
171
|
+
local_llm: { tps: 0, queue: localWorker.auto_select_eligible ? Math.max(0, activeSlots - zellijVisiblePanes) : 0 },
|
|
172
|
+
gpt_final_status: 'pending',
|
|
173
|
+
gate_progress: 'naruto:pre-orchestrator'
|
|
174
|
+
}
|
|
175
|
+
}).catch((err) => ({
|
|
176
|
+
ok: false,
|
|
177
|
+
pane_kind: 'dashboard',
|
|
178
|
+
worker_pane: false,
|
|
179
|
+
blockers: [`zellij_dashboard_exception:${err?.message || String(err)}`]
|
|
180
|
+
}));
|
|
152
181
|
console.log('Zellij: prepared ' + zellijVisiblePanes + ' visible active clone lane(s) in ' + liveZellij.session_name + ' with ' + Math.max(0, activeSlots - zellijVisiblePanes) + ' headless active worker(s). Attach with: ' + (liveZellij.attach_command_with_env || liveZellij.attach_command));
|
|
153
182
|
if (parsed.attach)
|
|
154
183
|
attachZellijSessionInteractive(liveZellij.session_name, { cwd: process.cwd(), configPath: liveZellij.clipboard_config_path });
|
package/dist/core/fsx.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '2.0.
|
|
8
|
+
export const PACKAGE_VERSION = '2.0.8';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
|
@@ -18,6 +18,7 @@ export async function detectGitRepo(root = process.cwd()) {
|
|
|
18
18
|
const bare = await runGitCommand(cwd, ['rev-parse', '--is-bare-repository']);
|
|
19
19
|
const branch = await runGitCommand(cwd, ['branch', '--show-current']);
|
|
20
20
|
const head = await runGitCommand(cwd, ['rev-parse', 'HEAD']);
|
|
21
|
+
const status = await runGitCommand(cwd, ['status', '--porcelain=v1', '--untracked-files=all']);
|
|
21
22
|
if (!top.ok)
|
|
22
23
|
blockers.push(gitBlocker('git_root_unresolved', top));
|
|
23
24
|
if (!gitDir.ok)
|
|
@@ -26,6 +27,8 @@ export async function detectGitRepo(root = process.cwd()) {
|
|
|
26
27
|
blockers.push(gitBlocker('git_common_dir_unresolved', commonDir));
|
|
27
28
|
if (!head.ok)
|
|
28
29
|
blockers.push(gitBlocker('git_head_unresolved', head));
|
|
30
|
+
if (!status.ok)
|
|
31
|
+
blockers.push(gitBlocker('git_status_unresolved', status));
|
|
29
32
|
const repoRoot = top.ok ? path.resolve(gitOutputLine(top)) : null;
|
|
30
33
|
const resolvedGitDir = gitDir.ok ? absolutizeGitPath(cwd, gitOutputLine(gitDir)) : null;
|
|
31
34
|
const resolvedCommonDir = commonDir.ok ? absolutizeGitPath(cwd, gitOutputLine(commonDir)) : null;
|
|
@@ -43,6 +46,8 @@ export async function detectGitRepo(root = process.cwd()) {
|
|
|
43
46
|
worktree_git_dir: resolvedGitDir && resolvedCommonDir && resolvedGitDir !== resolvedCommonDir ? resolvedGitDir : null,
|
|
44
47
|
branch: gitOutputLine(branch) || null,
|
|
45
48
|
head: gitOutputLine(head) || null,
|
|
49
|
+
main_worktree_dirty: status.ok && status.stdout.trim().length > 0,
|
|
50
|
+
status_porcelain: status.stdout || '',
|
|
46
51
|
blockers
|
|
47
52
|
};
|
|
48
53
|
}
|
|
@@ -61,6 +66,8 @@ function baseDetection(cwd, gitBinary, gitAvailable, blockers) {
|
|
|
61
66
|
worktree_git_dir: null,
|
|
62
67
|
branch: null,
|
|
63
68
|
head: null,
|
|
69
|
+
main_worktree_dirty: false,
|
|
70
|
+
status_porcelain: '',
|
|
64
71
|
blockers
|
|
65
72
|
};
|
|
66
73
|
}
|
|
@@ -7,6 +7,8 @@ export async function cleanupGitWorktree(input) {
|
|
|
7
7
|
const status = await runGitCommand(worktreePath, ['status', '--porcelain=v1', '--untracked-files=all']);
|
|
8
8
|
const clean = status.ok && status.stdout.trim().length === 0;
|
|
9
9
|
if (!clean) {
|
|
10
|
+
const reason = `SKS retained dirty failed worker ${path.basename(worktreePath)}`;
|
|
11
|
+
const lock = await runGitCommand(repoRoot, ['worktree', 'lock', '--reason', reason, worktreePath]);
|
|
10
12
|
const lockPath = `${worktreePath}.retained.json`;
|
|
11
13
|
await writeJsonAtomic(lockPath, {
|
|
12
14
|
schema: 'sks.git-worktree-retention-lock.v1',
|
|
@@ -15,7 +17,10 @@ export async function cleanupGitWorktree(input) {
|
|
|
15
17
|
worktree_path: worktreePath,
|
|
16
18
|
branch: input.branch || null,
|
|
17
19
|
reason: status.ok ? 'dirty_worktree_retained' : 'status_failed_retained',
|
|
18
|
-
status_porcelain: status.stdout || null
|
|
20
|
+
status_porcelain: status.stdout || null,
|
|
21
|
+
git_locked: lock.ok,
|
|
22
|
+
unlock_command: `git worktree unlock ${JSON.stringify(worktreePath)}`,
|
|
23
|
+
cleanup_command: 'sks worktree cleanup --mission <id>'
|
|
19
24
|
});
|
|
20
25
|
return {
|
|
21
26
|
schema: 'sks.git-worktree-cleanup.v1',
|
|
@@ -27,7 +32,10 @@ export async function cleanupGitWorktree(input) {
|
|
|
27
32
|
clean: false,
|
|
28
33
|
action: 'retained_dirty',
|
|
29
34
|
retention_lock_path: lockPath,
|
|
30
|
-
blockers: []
|
|
35
|
+
blockers: lock.ok ? [] : ['git_worktree_lock_failed'],
|
|
36
|
+
git_locked: lock.ok,
|
|
37
|
+
unlock_command: `git worktree unlock ${JSON.stringify(worktreePath)}`,
|
|
38
|
+
cleanup_command: 'sks worktree cleanup --mission <id>'
|
|
31
39
|
};
|
|
32
40
|
}
|
|
33
41
|
const remove = await runGitCommand(repoRoot, ['worktree', 'remove', worktreePath]);
|
|
@@ -45,7 +53,10 @@ export async function cleanupGitWorktree(input) {
|
|
|
45
53
|
clean: true,
|
|
46
54
|
action: remove.ok ? 'removed' : 'remove_failed',
|
|
47
55
|
retention_lock_path: null,
|
|
48
|
-
blockers
|
|
56
|
+
blockers,
|
|
57
|
+
git_locked: false,
|
|
58
|
+
unlock_command: null,
|
|
59
|
+
cleanup_command: null
|
|
49
60
|
};
|
|
50
61
|
}
|
|
51
62
|
//# sourceMappingURL=git-worktree-cleanup.js.map
|
|
@@ -7,14 +7,19 @@ export async function exportGitWorktreeDiff(input) {
|
|
|
7
7
|
const branch = await runGitCommand(worktreePath, ['branch', '--show-current']);
|
|
8
8
|
const head = await runGitCommand(worktreePath, ['rev-parse', 'HEAD']);
|
|
9
9
|
const status = await runGitCommand(worktreePath, ['status', '--porcelain=v1', '--untracked-files=all']);
|
|
10
|
+
const untracked = await runGitCommand(worktreePath, ['ls-files', '--others', '--exclude-standard']);
|
|
11
|
+
const untrackedFiles = lines(untracked.stdout);
|
|
12
|
+
if (untrackedFiles.length) {
|
|
13
|
+
const addIntent = await runGitCommand(worktreePath, ['add', '-N', '--', ...untrackedFiles]);
|
|
14
|
+
if (!addIntent.ok)
|
|
15
|
+
blockers.push('git_worktree_untracked_intent_to_add_failed');
|
|
16
|
+
}
|
|
10
17
|
const diff = await runGitCommand(worktreePath, ['diff', '--binary', '--full-index', 'HEAD']);
|
|
11
18
|
const names = await runGitCommand(worktreePath, ['diff', '--name-only', 'HEAD']);
|
|
12
|
-
const untracked = await runGitCommand(worktreePath, ['ls-files', '--others', '--exclude-standard']);
|
|
13
19
|
if (!status.ok)
|
|
14
20
|
blockers.push('git_worktree_status_failed');
|
|
15
21
|
if (!diff.ok)
|
|
16
22
|
blockers.push('git_worktree_diff_failed');
|
|
17
|
-
const untrackedFiles = lines(untracked.stdout);
|
|
18
23
|
const trackedChanged = lines(names.stdout);
|
|
19
24
|
const changedFiles = [...new Set([...trackedChanged, ...untrackedFiles, ...statusFiles(status.stdout)])];
|
|
20
25
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fsp from 'node:fs/promises';
|
|
3
|
-
import { ensureDir, nowIso, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { ensureDir, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { evaluateGitWorktreeCapability } from './git-worktree-capability.js';
|
|
5
5
|
import { gitBlocker, gitOutputLine, runGitCommand } from './git-worktree-runner.js';
|
|
6
6
|
import { sanitizePathPart } from './git-worktree-root.js';
|
|
@@ -67,13 +67,20 @@ export async function allocateWorkerWorktree(input) {
|
|
|
67
67
|
return allocation;
|
|
68
68
|
}
|
|
69
69
|
async function appendWorktreeManifest(allocation) {
|
|
70
|
+
const current = await readJson(allocation.manifest_path, null).catch(() => null);
|
|
71
|
+
const allocations = Array.isArray(current?.allocations) ? current.allocations : [];
|
|
72
|
+
const key = (row) => `${row.mission_id}:${row.slot_id}:${row.generation_index}:${row.worker_id}`;
|
|
73
|
+
const nextAllocations = [
|
|
74
|
+
...allocations.filter((row) => key(row) !== key(allocation)),
|
|
75
|
+
allocation
|
|
76
|
+
];
|
|
70
77
|
const manifest = {
|
|
71
78
|
schema: 'sks.git-worktree-manifest.v1',
|
|
72
79
|
updated_at: nowIso(),
|
|
73
80
|
mission_id: allocation.mission_id,
|
|
74
81
|
repo_root: allocation.repo_root,
|
|
75
82
|
root: path.dirname(allocation.worktree_path),
|
|
76
|
-
allocations:
|
|
83
|
+
allocations: nextAllocations
|
|
77
84
|
};
|
|
78
85
|
await writeJsonAtomic(allocation.manifest_path, manifest);
|
|
79
86
|
}
|
|
@@ -20,11 +20,11 @@ export function buildGitWorktreePatchEnvelope(input) {
|
|
|
20
20
|
changed_files: changedFiles,
|
|
21
21
|
diff_bytes: input.diff.diff_bytes
|
|
22
22
|
},
|
|
23
|
-
operations:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
operations: [{
|
|
24
|
+
op: 'git_apply_patch',
|
|
25
|
+
path: '.',
|
|
26
|
+
diff: input.diff.diff
|
|
27
|
+
}],
|
|
28
28
|
rationale: 'Process-generated patch envelope exported from an isolated Git worktree diff.',
|
|
29
29
|
verification_hint: {
|
|
30
30
|
command: 'git apply --3way --check <diff>',
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
export const RELEASE_GATE_CACHE_V2_SCHEMA = 'sks.release-gate-cache.v2';
|
|
5
|
+
export function releaseGateCacheFile(root) {
|
|
6
|
+
return path.join(root, '.sneakoscope', 'reports', 'release-gates', 'cache-v2.json');
|
|
7
|
+
}
|
|
8
|
+
export function releaseGateCacheKey(root, gate) {
|
|
9
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
10
|
+
const hash = crypto.createHash('sha256');
|
|
11
|
+
hash.update(gate.id);
|
|
12
|
+
hash.update(gate.command);
|
|
13
|
+
hash.update(String(pkg.version || ''));
|
|
14
|
+
hash.update(process.version);
|
|
15
|
+
hash.update(String(process.env.npm_config_user_agent || ''));
|
|
16
|
+
hash.update(JSON.stringify(gate.resource || []));
|
|
17
|
+
hash.update(JSON.stringify(gate.preset || []));
|
|
18
|
+
hashFileIfPresent(hash, path.join(root, 'release-gates.v2.json'));
|
|
19
|
+
hashFileIfPresent(hash, path.join(root, 'package.json'));
|
|
20
|
+
hashFileIfPresent(hash, path.join(root, 'dist', 'build-manifest.json'));
|
|
21
|
+
for (const input of gate.cache.inputs) {
|
|
22
|
+
const file = path.join(root, input);
|
|
23
|
+
if (fs.existsSync(file) && fs.statSync(file).isFile())
|
|
24
|
+
hashFileIfPresent(hash, file);
|
|
25
|
+
else
|
|
26
|
+
hash.update(input);
|
|
27
|
+
}
|
|
28
|
+
return hash.digest('hex');
|
|
29
|
+
}
|
|
30
|
+
export function readReleaseGateCacheHit(root, gate) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(fs.readFileSync(releaseGateCacheFile(root), 'utf8'));
|
|
33
|
+
return parsed.schema === RELEASE_GATE_CACHE_V2_SCHEMA && parsed.records?.[releaseGateCacheKey(root, gate)]?.ok === true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function writeReleaseGateCacheHit(root, gate) {
|
|
40
|
+
const file = releaseGateCacheFile(root);
|
|
41
|
+
let parsed = { schema: RELEASE_GATE_CACHE_V2_SCHEMA, records: {} };
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
parsed.schema = RELEASE_GATE_CACHE_V2_SCHEMA;
|
|
47
|
+
parsed.records ||= {};
|
|
48
|
+
parsed.records[releaseGateCacheKey(root, gate)] = {
|
|
49
|
+
ok: true,
|
|
50
|
+
gate_id: gate.id,
|
|
51
|
+
command: gate.command,
|
|
52
|
+
resource: gate.resource,
|
|
53
|
+
preset: gate.preset,
|
|
54
|
+
recorded_at: new Date().toISOString()
|
|
55
|
+
};
|
|
56
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
57
|
+
fs.writeFileSync(file, `${JSON.stringify(parsed, null, 2)}\n`);
|
|
58
|
+
}
|
|
59
|
+
function hashFileIfPresent(hash, file) {
|
|
60
|
+
if (fs.existsSync(file) && fs.statSync(file).isFile())
|
|
61
|
+
hash.update(fs.readFileSync(file));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=release-gate-cache-v2.js.map
|