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.
Files changed (48) hide show
  1. package/README.md +1 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +32 -8
  8. package/dist/core/agents/agent-command-surface.js +4 -2
  9. package/dist/core/agents/agent-orchestrator.js +2 -1
  10. package/dist/core/agents/agent-patch-schema.js +4 -2
  11. package/dist/core/agents/native-cli-session-swarm.js +7 -2
  12. package/dist/core/commands/mad-sks-command.js +25 -0
  13. package/dist/core/commands/naruto-command.js +29 -0
  14. package/dist/core/fsx.js +1 -1
  15. package/dist/core/git/git-repo-detection.js +7 -0
  16. package/dist/core/git/git-worktree-cleanup.js +14 -3
  17. package/dist/core/git/git-worktree-diff.js +7 -2
  18. package/dist/core/git/git-worktree-manager.js +9 -2
  19. package/dist/core/git/git-worktree-patch-envelope.js +5 -5
  20. package/dist/core/release/release-gate-cache-v2.js +63 -0
  21. package/dist/core/release/release-gate-dag.js +179 -0
  22. package/dist/core/release/release-gate-hermetic-env.js +32 -0
  23. package/dist/core/release/release-gate-node.js +62 -0
  24. package/dist/core/release/release-gate-report.js +11 -0
  25. package/dist/core/release/release-gate-resource-governor.js +54 -0
  26. package/dist/core/release/release-gate-scheduler.js +15 -0
  27. package/dist/core/version.js +1 -1
  28. package/dist/core/zellij/zellij-dashboard-pane.js +71 -0
  29. package/dist/core/zellij/zellij-dashboard-renderer.js +42 -0
  30. package/dist/core/zellij/zellij-worker-pane-manager.js +63 -3
  31. package/dist/scripts/git-worktree-diff-envelope-check.js +17 -0
  32. package/dist/scripts/git-worktree-dirty-lock-check.js +17 -0
  33. package/dist/scripts/git-worktree-dirty-main-detection-check.js +14 -0
  34. package/dist/scripts/git-worktree-integration-primary-check.js +22 -0
  35. package/dist/scripts/git-worktree-manifest-append-check.js +18 -0
  36. package/dist/scripts/git-worktree-untracked-diff-check.js +18 -0
  37. package/dist/scripts/naruto-worktree-coding-blackbox.js +29 -0
  38. package/dist/scripts/release-gate-dag-runner-check.js +17 -0
  39. package/dist/scripts/release-gate-dag-runner.js +32 -0
  40. package/dist/scripts/release-gate-worker.js +10 -0
  41. package/dist/scripts/release-metadata-1-19-check.js +8 -2
  42. package/dist/scripts/release-parallel-speed-budget-check.js +25 -0
  43. package/dist/scripts/release-stability-report-check.js +99 -0
  44. package/dist/scripts/zellij-dashboard-pane-check.js +68 -0
  45. package/dist/scripts/zellij-dashboard-watch.js +41 -0
  46. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +185 -0
  47. package/package.json +22 -5
  48. 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.7** adds Git worktree parallel coding foundations for Naruto/MAD-SKS-style high-throughput work. Write-capable Git missions can now declare `git-worktree` mode, allocate worker worktrees outside the main checkout by default, export full-index diffs into patch envelopes, apply them through an integration worktree queue, and retain dirty worktrees instead of deleting evidence. Non-Git projects degrade to patch-envelope-only without calling `git worktree`.
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
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "2.0.7"
79
+ version = "2.0.8"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "2.0.7"
3
+ version = "2.0.8"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -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"),
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.7",
5
- "source_digest": "fd9b0bc3d9a5a09bcd2b90988719b11e844448de39f28cf27e98c1b4cae82a0f",
6
- "source_file_count": 1979,
7
- "built_at_source_time": 1780686416915
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '2.0.7';
2
+ const FAST_PACKAGE_VERSION = '2.0.8';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schema": "sks.dist-build.v2",
3
- "version": "2.0.7",
4
- "package_version": "2.0.7",
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": 1043,
8
- "compiled_js_count": 1043,
7
+ "compiled_file_count": 1067,
8
+ "compiled_js_count": 1067,
9
9
  "compiled_dts_count": 0,
10
- "source_digest": "fd9b0bc3d9a5a09bcd2b90988719b11e844448de39f28cf27e98c1b4cae82a0f",
11
- "source_file_count": 1979,
12
- "source_files_hash": "319ce7cd86fb58c825b14eb717a1d7b9c85be197206852aee9646fbfd5d356c6",
13
- "source_list_hash": "319ce7cd86fb58c825b14eb717a1d7b9c85be197206852aee9646fbfd5d356c6",
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
- if (this.input.backend === 'zellij' && ctx.opts.real === true && ctx.opts.zellijPaneWorker !== false) {
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.7';
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: [allocation]
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: changedFiles.map((file) => ({
24
- op: 'unified_diff',
25
- path: file,
26
- diff: input.diff.diff
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