sneakoscope 2.0.6 → 2.0.7

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 (51) hide show
  1. package/README.md +6 -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-orchestrator.js +138 -3
  9. package/dist/core/agents/agent-patch-schema.js +16 -2
  10. package/dist/core/agents/agent-proof-evidence.js +3 -0
  11. package/dist/core/agents/native-cli-session-swarm.js +25 -4
  12. package/dist/core/agents/native-cli-worker.js +28 -1
  13. package/dist/core/codex-control/python-codex-sdk-adapter.js +28 -4
  14. package/dist/core/commands/naruto-command.js +39 -10
  15. package/dist/core/feature-registry.js +2 -0
  16. package/dist/core/fsx.js +1 -1
  17. package/dist/core/git/git-integration-worktree.js +15 -0
  18. package/dist/core/git/git-repo-detection.js +72 -0
  19. package/dist/core/git/git-worktree-cache-policy.js +36 -0
  20. package/dist/core/git/git-worktree-capability.js +54 -0
  21. package/dist/core/git/git-worktree-cleanup.js +51 -0
  22. package/dist/core/git/git-worktree-conflict-resolver.js +13 -0
  23. package/dist/core/git/git-worktree-diff.js +50 -0
  24. package/dist/core/git/git-worktree-manager.js +86 -0
  25. package/dist/core/git/git-worktree-merge-queue.js +55 -0
  26. package/dist/core/git/git-worktree-patch-envelope.js +35 -0
  27. package/dist/core/git/git-worktree-pool.js +23 -0
  28. package/dist/core/git/git-worktree-root.js +52 -0
  29. package/dist/core/git/git-worktree-runner.js +40 -0
  30. package/dist/core/naruto/naruto-active-pool.js +35 -0
  31. package/dist/core/naruto/naruto-gpt-final-pack.js +2 -0
  32. package/dist/core/naruto/naruto-work-graph.js +16 -1
  33. package/dist/core/version.js +1 -1
  34. package/dist/core/zellij/zellij-naruto-dashboard.js +10 -1
  35. package/dist/core/zellij/zellij-worker-pane-manager.js +6 -4
  36. package/dist/scripts/git-worktree-cache-performance-check.js +25 -0
  37. package/dist/scripts/git-worktree-capability-check.js +27 -0
  38. package/dist/scripts/git-worktree-cleanup-check.js +27 -0
  39. package/dist/scripts/git-worktree-diff-export-check.js +43 -0
  40. package/dist/scripts/git-worktree-manager-check.js +37 -0
  41. package/dist/scripts/git-worktree-merge-queue-check.js +30 -0
  42. package/dist/scripts/git-worktree-pool-performance-check.js +20 -0
  43. package/dist/scripts/lib/git-worktree-fixture.js +33 -0
  44. package/dist/scripts/naruto-shadow-clone-swarm-check.js +9 -5
  45. package/dist/scripts/naruto-worktree-coding-check.js +44 -0
  46. package/dist/scripts/naruto-worktree-gpt-final-check.js +45 -0
  47. package/dist/scripts/naruto-worktree-zellij-ui-check.js +28 -0
  48. package/dist/scripts/release-parallel-check.js +1 -1
  49. package/package.json +14 -3
  50. package/schemas/git/git-worktree-capability.schema.json +19 -0
  51. package/schemas/git/git-worktree-manifest.schema.json +36 -0
package/README.md CHANGED
@@ -16,10 +16,15 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
16
16
 
17
17
  ## Current Release
18
18
 
19
- SKS **2.0.6** wires Codex App Product Design into the design pipeline and hardens Naruto read-only routing on top of the 2.0 execution layer. Design routes can now discover, install, verify, and prefer the remote `product-design@openai-curated-remote` plugin, while read-only native worker runs keep write mode off and avoid treating pre-existing dirty files as generated patches.
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`.
20
20
 
21
21
  What changed:
22
22
 
23
+ - Git capability checks detect repo roots, Git dirs, worktree support, and safe cache roots while blocking in-repo worktree roots unless `SKS_ALLOW_IN_REPO_WORKTREES=1` is explicit.
24
+ - Worker worktree allocation creates isolated branches/paths under `$SKS_WORKTREE_ROOT`, `$XDG_CACHE_HOME/sks/worktrees`, or `~/.cache/sks/worktrees`; main checkouts stay untouched until integration.
25
+ - Worktree diff export records status, changed files, full-index binary diffs, and `git-worktree-diff` patch envelopes for parent-owned integration.
26
+ - Integration worktrees apply worker diffs with `git apply --3way`, while cleanup removes only clean worktrees and retains dirty ones with a lock artifact.
27
+ - Naruto work graphs, active-pool artifacts, Zellij dashboard titles, native worker intake, and GPT Final packs now carry worktree policy and WT/branch context.
23
28
  - Product Design plugin readiness now checks both local and remote Codex App catalogs, auto-installs the remote plugin when needed, and records the installed/enabled skill surface.
24
29
  - UI/design/PPT runtime routes prefer Product Design for research, ideation, audit, design QA, prototype, URL-to-code, image-to-code, share, and user-context steps.
25
30
  - Naruto read-only runs force write mode off, propagate no-patch reasons through worker proof, and skip changed-file lease checks when no write-capable patch envelope exists.
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "2.0.6"
79
+ version = "2.0.7"
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.6"
3
+ version = "2.0.7"
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.6"),
7
+ Some("--version") => println!("sks-rs 2.0.7"),
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.6",
5
- "source_digest": "3947e39c47a565506e9ffd37568ae889bc7943f6dc342b5c357976b840770f94",
6
- "source_file_count": 1953,
7
- "built_at_source_time": 1780662014263
4
+ "package_version": "2.0.7",
5
+ "source_digest": "fd9b0bc3d9a5a09bcd2b90988719b11e844448de39f28cf27e98c1b4cae82a0f",
6
+ "source_file_count": 1979,
7
+ "built_at_source_time": 1780686416915
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.6';
2
+ const FAST_PACKAGE_VERSION = '2.0.7';
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.6",
4
- "package_version": "2.0.6",
3
+ "version": "2.0.7",
4
+ "package_version": "2.0.7",
5
5
  "typescript": true,
6
6
  "mjs_runtime_files": 0,
7
- "compiled_file_count": 1019,
8
- "compiled_js_count": 1019,
7
+ "compiled_file_count": 1043,
8
+ "compiled_js_count": 1043,
9
9
  "compiled_dts_count": 0,
10
- "source_digest": "3947e39c47a565506e9ffd37568ae889bc7943f6dc342b5c357976b840770f94",
11
- "source_file_count": 1953,
12
- "source_files_hash": "5d6f0e4186fc37279e1cf6de0c6b3c7230123c4a588966f8fda2a9324eb75192",
13
- "source_list_hash": "5d6f0e4186fc37279e1cf6de0c6b3c7230123c4a588966f8fda2a9324eb75192",
10
+ "source_digest": "fd9b0bc3d9a5a09bcd2b90988719b11e844448de39f28cf27e98c1b4cae82a0f",
11
+ "source_file_count": 1979,
12
+ "source_files_hash": "319ce7cd86fb58c825b14eb717a1d7b9c85be197206852aee9646fbfd5d356c6",
13
+ "source_list_hash": "319ce7cd86fb58c825b14eb717a1d7b9c85be197206852aee9646fbfd5d356c6",
14
14
  "src_mjs_runtime_files": 0,
15
15
  "dist_stamp_schema": "sks.dist-build-stamp.v1",
16
16
  "files": [
@@ -342,6 +342,19 @@
342
342
  "core/git-hygiene/shared-memory-security.js",
343
343
  "core/git-hygiene/validators.js",
344
344
  "core/git-simple.js",
345
+ "core/git/git-integration-worktree.js",
346
+ "core/git/git-repo-detection.js",
347
+ "core/git/git-worktree-cache-policy.js",
348
+ "core/git/git-worktree-capability.js",
349
+ "core/git/git-worktree-cleanup.js",
350
+ "core/git/git-worktree-conflict-resolver.js",
351
+ "core/git/git-worktree-diff.js",
352
+ "core/git/git-worktree-manager.js",
353
+ "core/git/git-worktree-merge-queue.js",
354
+ "core/git/git-worktree-patch-envelope.js",
355
+ "core/git/git-worktree-pool.js",
356
+ "core/git/git-worktree-root.js",
357
+ "core/git/git-worktree-runner.js",
345
358
  "core/goal-workflow.js",
346
359
  "core/gx-renderer.js",
347
360
  "core/harness-conflicts.js",
@@ -814,6 +827,13 @@
814
827
  "scripts/flagship-proof-graph-v3-check.js",
815
828
  "scripts/flagship-proof-graph-v4-check.js",
816
829
  "scripts/git-precommit-fixture-check.js",
830
+ "scripts/git-worktree-cache-performance-check.js",
831
+ "scripts/git-worktree-capability-check.js",
832
+ "scripts/git-worktree-cleanup-check.js",
833
+ "scripts/git-worktree-diff-export-check.js",
834
+ "scripts/git-worktree-manager-check.js",
835
+ "scripts/git-worktree-merge-queue-check.js",
836
+ "scripts/git-worktree-pool-performance-check.js",
817
837
  "scripts/goal-mode-official-default-check.js",
818
838
  "scripts/gpt-final-arbiter-check.js",
819
839
  "scripts/gpt-final-arbiter-performance-check.js",
@@ -841,6 +861,7 @@
841
861
  "scripts/legacy-upgrade-matrix-check.js",
842
862
  "scripts/lib/codex-sdk-gate-lib.js",
843
863
  "scripts/lib/ensure-dist-fresh.js",
864
+ "scripts/lib/git-worktree-fixture.js",
844
865
  "scripts/lib/mad-sks-actual-executor-check-lib.js",
845
866
  "scripts/lib/native-cli-session-swarm-check-lib.js",
846
867
  "scripts/lib/real-codex-parallel-gate.js",
@@ -894,6 +915,9 @@
894
915
  "scripts/naruto-shadow-clone-swarm-check.js",
895
916
  "scripts/naruto-verification-pool-check.js",
896
917
  "scripts/naruto-work-graph-check.js",
918
+ "scripts/naruto-worktree-coding-check.js",
919
+ "scripts/naruto-worktree-gpt-final-check.js",
920
+ "scripts/naruto-worktree-zellij-ui-check.js",
897
921
  "scripts/naruto-zellij-massive-ui-check.js",
898
922
  "scripts/non-recursive-pipeline-check.js",
899
923
  "scripts/npm-publish-performance-check.js",
@@ -52,6 +52,10 @@ import { CODEX_AGENT_WORKER_RESULT_SCHEMA_ID, codexAgentWorkerResultSchema } fro
52
52
  import { resolveLocalCollaborationPolicy, localCollaborationParticipated } from '../local-llm/local-collaboration-policy.js';
53
53
  import { runFinalGptReviewStage } from '../pipeline/final-gpt-review-stage.js';
54
54
  import { selectFinalGptPatchSource } from '../pipeline/final-gpt-patch-stage.js';
55
+ import { allocateWorkerWorktree } from '../git/git-worktree-manager.js';
56
+ import { exportGitWorktreeDiff } from '../git/git-worktree-diff.js';
57
+ import { buildGitWorktreePatchEnvelope } from '../git/git-worktree-patch-envelope.js';
58
+ import { cleanupGitWorktree } from '../git/git-worktree-cleanup.js';
55
59
  export async function runNativeAgentOrchestrator(opts = {}) {
56
60
  const root = path.resolve(opts.root || process.cwd());
57
61
  const prompt = String(opts.prompt || 'Native agent run');
@@ -253,6 +257,21 @@ export async function runNativeAgentOrchestrator(opts = {}) {
253
257
  strategy_gate: strategyRef
254
258
  };
255
259
  await writeJsonAtomic(path.join(ledgerRoot, 'agent-parallel-write-policy.json'), parallelWritePolicy);
260
+ const gitWorktreePolicy = opts.gitWorktreePolicy || null;
261
+ const gitWorktreeRuntime = {
262
+ schema: 'sks.agent-git-worktree-runtime.v1',
263
+ generated_at: nowIso(),
264
+ ok: true,
265
+ mode: gitWorktreePolicy?.mode || 'patch-envelope-only',
266
+ required: gitWorktreePolicy?.required === true,
267
+ main_repo_root: gitWorktreePolicy?.main_repo_root || root,
268
+ worktree_root: gitWorktreePolicy?.worktree_root || null,
269
+ allocations: [],
270
+ diffs: [],
271
+ cleanup: [],
272
+ blockers: []
273
+ };
274
+ await writeJsonAtomic(path.join(ledgerRoot, 'agent-git-worktree-runtime.json'), gitWorktreeRuntime);
256
275
  const nativeCliSwarm = createNativeCliSessionSwarmRecorder(ledgerRoot, {
257
276
  missionId,
258
277
  requestedAgents: Number(opts.agents || roster.agent_count || targetActiveSlots),
@@ -280,6 +299,17 @@ export async function runNativeAgentOrchestrator(opts = {}) {
280
299
  goalModeRef,
281
300
  launchSession: async ({ agent, workItem }) => {
282
301
  const slice = workItem.slice || { id: workItem.id, description: workItem.description || prompt };
302
+ const workerWorktree = await prepareWorkerGitWorktree({
303
+ root,
304
+ ledgerRoot,
305
+ missionId,
306
+ agent,
307
+ slice,
308
+ policy: gitWorktreePolicy,
309
+ runtime: gitWorktreeRuntime
310
+ });
311
+ const runtimeAgent = workerWorktree ? { ...agent, worktree: workerWorktree.context } : agent;
312
+ const runtimeSlice = workerWorktree ? { ...slice, worktree: workerWorktree.context } : slice;
283
313
  await openAgentSession(ledgerRoot, agent);
284
314
  await heartbeatAgentSession(ledgerRoot, agent);
285
315
  await appendAgentCodexCockpitHookEvent(dir, {
@@ -303,10 +333,21 @@ export async function runNativeAgentOrchestrator(opts = {}) {
303
333
  generationIndex: agent.generation_index,
304
334
  requireGeneration: true
305
335
  });
306
- const backendOpts = { ...opts, missionId, agentRoot: ledgerRoot, cwd: root, route, prompt, fastMode: fastModePolicy.fast_mode, serviceTier: fastModePolicy.service_tier };
336
+ const backendOpts = { ...opts, missionId, agentRoot: ledgerRoot, cwd: workerWorktree?.context.path || root, route, prompt, fastMode: fastModePolicy.fast_mode, serviceTier: fastModePolicy.service_tier, ...(workerWorktree ? { worktree: workerWorktree.context } : {}) };
307
337
  const result = opts.nativeCliSwarm === false
308
- ? await runAgentByBackend(backend, agent, slice, backendOpts)
309
- : await nativeCliSwarm.launchWorker({ agent, slice, opts: backendOpts });
338
+ ? await runAgentByBackend(backend, runtimeAgent, runtimeSlice, backendOpts)
339
+ : await nativeCliSwarm.launchWorker({ agent: runtimeAgent, slice: runtimeSlice, opts: backendOpts });
340
+ if (workerWorktree)
341
+ await finalizeWorkerGitWorktree({
342
+ root,
343
+ ledgerRoot,
344
+ missionId,
345
+ agent: runtimeAgent,
346
+ slice: runtimeSlice,
347
+ result,
348
+ workerWorktree,
349
+ runtime: gitWorktreeRuntime
350
+ });
310
351
  const terminalClose = await closeAgentTerminalSession(ledgerRoot, agent, {
311
352
  exitCode: result.status === 'done' ? 0 : 1,
312
353
  status: result.status,
@@ -444,6 +485,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
444
485
  ...(nativeCliSessionProof.ok ? [] : nativeCliSessionProof.blockers),
445
486
  ...(noSubagentScalingPolicy.ok ? [] : noSubagentScalingPolicy.blockers),
446
487
  ...(fastModePropagation.ok ? [] : fastModePropagation.blockers),
488
+ ...(gitWorktreeRuntime.required === true && gitWorktreeRuntime.ok === false ? gitWorktreeRuntime.blockers || ['git_worktree_runtime_not_ok'] : []),
447
489
  ...(localParticipated && gptFinalArbiter?.ok !== true ? gptFinalArbiter?.blockers || ['gpt_final_arbiter_not_ok'] : []),
448
490
  ...(localParticipated && finalGptPatchStage?.ok === false ? finalGptPatchStage.blockers || ['final_gpt_patch_stage_not_ok'] : []),
449
491
  ...(patchSwarm.ok ? [] : patchSwarm.blockers),
@@ -480,6 +522,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
480
522
  noSubagentScalingPolicy,
481
523
  fastModePolicy,
482
524
  fastModePropagation,
525
+ gitWorktreeRuntime,
483
526
  triwikiContext,
484
527
  selectedCoreSkill,
485
528
  localCollaborationPolicy,
@@ -524,6 +567,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
524
567
  no_subagent_scaling_policy: noSubagentScalingPolicy,
525
568
  fast_mode_policy: fastModePolicy,
526
569
  fast_mode_propagation: fastModePropagation,
570
+ git_worktree_runtime: gitWorktreeRuntime,
527
571
  local_collaboration_policy: localCollaborationPolicy,
528
572
  gpt_final_arbiter: gptFinalArbiter,
529
573
  final_gpt_patch_stage: finalGptPatchStage,
@@ -548,6 +592,97 @@ function withFinalGptPatchEnvelopes(results, patchEnvelopes = []) {
548
592
  next[0] = { ...next[0], patch_envelopes: patchEnvelopes };
549
593
  return next;
550
594
  }
595
+ async function prepareWorkerGitWorktree(input) {
596
+ if (input.policy?.mode !== 'git-worktree')
597
+ return null;
598
+ const sliceHasWritePaths = Array.isArray(input.slice.write_paths) && input.slice.write_paths.length > 0;
599
+ const agentWriteCapable = input.agent.write_allowed === true || /write|lease|required/i.test(String(input.agent.write_policy || ''));
600
+ if (!sliceHasWritePaths && !agentWriteCapable)
601
+ return null;
602
+ const generationIndex = Math.max(1, Math.floor(Number(input.agent.generation_index || 1)));
603
+ const allocation = await allocateWorkerWorktree({
604
+ repoRoot: input.policy.main_repo_root || input.root,
605
+ missionId: input.missionId,
606
+ workerId: String(input.agent.id || input.slice.id || 'worker'),
607
+ slotId: String(input.agent.slot_id || input.agent.id || 'slot-001'),
608
+ generationIndex
609
+ });
610
+ const artifactDir = path.join(input.ledgerRoot, input.agent.session_artifact_dir || path.join('sessions', input.agent.id || input.slice.id || 'worker'), 'worker');
611
+ await writeJsonAtomic(path.join(artifactDir, 'git-worktree-allocation.json'), allocation);
612
+ input.runtime.allocations.push({
613
+ agent_id: input.agent.id,
614
+ slice_id: input.slice.id,
615
+ ok: allocation.ok,
616
+ worktree_path: allocation.worktree_path,
617
+ branch: allocation.branch,
618
+ blockers: allocation.blockers
619
+ });
620
+ input.runtime.blockers.push(...allocation.blockers);
621
+ input.runtime.ok = input.runtime.blockers.length === 0;
622
+ await writeJsonAtomic(path.join(input.ledgerRoot, 'agent-git-worktree-runtime.json'), input.runtime);
623
+ if (!allocation.ok)
624
+ return null;
625
+ return {
626
+ allocation,
627
+ artifactDir,
628
+ context: {
629
+ id: `${allocation.slot_id}-gen-${allocation.generation_index}`,
630
+ path: allocation.worktree_path,
631
+ branch: allocation.branch,
632
+ main_repo_root: allocation.main_repo_root
633
+ }
634
+ };
635
+ }
636
+ async function finalizeWorkerGitWorktree(input) {
637
+ const allocation = input.workerWorktree.allocation;
638
+ const diff = await exportGitWorktreeDiff({
639
+ mainRepoRoot: allocation.main_repo_root || input.root,
640
+ worktreePath: allocation.worktree_path,
641
+ missionId: input.missionId,
642
+ workerId: String(input.agent.id || input.slice.id || 'worker')
643
+ });
644
+ await writeJsonAtomic(path.join(input.workerWorktree.artifactDir, 'git-worktree-diff.json'), diff);
645
+ input.runtime.diffs.push({
646
+ agent_id: input.agent.id,
647
+ slice_id: input.slice.id,
648
+ ok: diff.ok,
649
+ clean: diff.clean,
650
+ changed_files: diff.changed_files,
651
+ diff_bytes: diff.diff_bytes,
652
+ blockers: diff.blockers
653
+ });
654
+ if (!diff.clean && diff.ok) {
655
+ const envelope = buildGitWorktreePatchEnvelope({
656
+ diff,
657
+ agentId: String(input.agent.id || 'agent'),
658
+ sessionId: String(input.agent.session_id || ''),
659
+ slotId: String(input.agent.slot_id || ''),
660
+ generationIndex: Math.max(1, Math.floor(Number(input.agent.generation_index || 1)))
661
+ });
662
+ input.result.patch_envelopes = [...(Array.isArray(input.result.patch_envelopes) ? input.result.patch_envelopes : []), envelope];
663
+ input.result.changed_files = [...new Set([...(input.result.changed_files || []), ...diff.changed_files])];
664
+ input.result.artifacts = [...new Set([...(input.result.artifacts || []), path.relative(input.ledgerRoot, path.join(input.workerWorktree.artifactDir, 'git-worktree-diff.json'))])];
665
+ input.result.git_worktree_diff = diff;
666
+ }
667
+ const cleanup = await cleanupGitWorktree({
668
+ repoRoot: allocation.main_repo_root || input.root,
669
+ worktreePath: allocation.worktree_path,
670
+ branch: allocation.branch,
671
+ deleteBranch: diff.clean
672
+ });
673
+ await writeJsonAtomic(path.join(input.workerWorktree.artifactDir, 'git-worktree-cleanup.json'), cleanup);
674
+ input.runtime.cleanup.push({
675
+ agent_id: input.agent.id,
676
+ slice_id: input.slice.id,
677
+ action: cleanup.action,
678
+ clean: cleanup.clean,
679
+ retention_lock_path: cleanup.retention_lock_path,
680
+ blockers: cleanup.blockers
681
+ });
682
+ input.runtime.blockers.push(...diff.blockers, ...cleanup.blockers);
683
+ input.runtime.ok = input.runtime.blockers.length === 0;
684
+ await writeJsonAtomic(path.join(input.ledgerRoot, 'agent-git-worktree-runtime.json'), input.runtime);
685
+ }
551
686
  async function legacyCodexExecBlockedRun(input) {
552
687
  const blockers = ['legacy_codex_exec_runtime_removed'];
553
688
  const ledgerRoot = path.join(input.dir, 'agents');
@@ -27,6 +27,7 @@ export function normalizeAgentPatchEnvelope(input) {
27
27
  ...(input?.verification_node_id === undefined ? {} : { verification_node_id: String(input.verification_node_id) }),
28
28
  ...(input?.rollback_node_id === undefined ? {} : { rollback_node_id: String(input.rollback_node_id) }),
29
29
  ...(input?.lease_proof ? { lease_proof: normalizeLeaseProof(input.lease_proof) } : {}),
30
+ ...(input?.git_worktree ? { git_worktree: normalizeGitWorktreeMetadata(input.git_worktree) } : {}),
30
31
  ...(input?.rationale ? { rationale: String(input.rationale) } : {}),
31
32
  ...(input?.verification_hint ? { verification_hint: normalizeHint(input.verification_hint) } : {}),
32
33
  ...(input?.rollback_hint ? { rollback_hint: normalizeHint(input.rollback_hint) } : {}),
@@ -49,12 +50,14 @@ export function validateAgentPatchEnvelope(envelope) {
49
50
  violations.push('lease_id_missing');
50
51
  if (!envelope.operations.length)
51
52
  violations.push('operations_missing');
52
- if (envelope.source && !['fixture', 'model_authored', 'process_generated', 'zellij_generated'].includes(envelope.source))
53
+ if (envelope.source && !['fixture', 'model_authored', 'process_generated', 'zellij_generated', 'git-worktree-diff'].includes(envelope.source))
53
54
  violations.push('source_invalid');
54
55
  if (envelope.source === 'model_authored' && !hasFiniteNumber(envelope.backend_child_process_id) && !envelope.backend_sdk_thread_id && !envelope.backend_ollama_request_id)
55
56
  violations.push('model_authored_backend_proof_missing');
56
57
  if (envelope.source === 'fixture' && envelope.backend_child_process_id !== undefined)
57
58
  violations.push('fixture_backend_child_process_id_present');
59
+ if (envelope.source === 'git-worktree-diff' && !envelope.git_worktree?.worktree_path)
60
+ violations.push('git_worktree_metadata_missing');
58
61
  for (const operation of envelope.operations) {
59
62
  if (!operation.path || operation.path.includes('\0') || operation.path.startsWith('/') || operation.path.split(/[\\/]/).includes('..')) {
60
63
  violations.push(`invalid_path:${operation.path || 'missing'}`);
@@ -74,7 +77,7 @@ export function validateAgentPatchEnvelope(envelope) {
74
77
  }
75
78
  function normalizeEnvelopeSource(value) {
76
79
  const text = String(value || '');
77
- return text === 'fixture' || text === 'model_authored' || text === 'process_generated' || text === 'zellij_generated' ? text : null;
80
+ return text === 'fixture' || text === 'model_authored' || text === 'process_generated' || text === 'zellij_generated' || text === 'git-worktree-diff' ? text : null;
78
81
  }
79
82
  function hasFiniteNumber(value) {
80
83
  return value !== null && value !== undefined && value !== '' && Number.isFinite(Number(value));
@@ -104,6 +107,17 @@ function normalizeLeaseProof(input) {
104
107
  ...(input?.rollback_node_id === undefined ? {} : { rollback_node_id: String(input.rollback_node_id) })
105
108
  };
106
109
  }
110
+ function normalizeGitWorktreeMetadata(input) {
111
+ return {
112
+ main_repo_root: String(input?.main_repo_root || ''),
113
+ worktree_path: String(input?.worktree_path || ''),
114
+ branch: input?.branch == null ? null : String(input.branch),
115
+ base_head: input?.base_head == null ? null : String(input.base_head),
116
+ worktree_head: input?.worktree_head == null ? null : String(input.worktree_head),
117
+ changed_files: Array.isArray(input?.changed_files) ? input.changed_files.map(String) : [],
118
+ diff_bytes: Number(input?.diff_bytes || 0)
119
+ };
120
+ }
107
121
  function normalizeHint(input) {
108
122
  return {
109
123
  ...(input?.command === undefined ? {} : { command: String(input.command) }),
@@ -21,6 +21,7 @@ export async function writeAgentProofEvidence(root, input) {
21
21
  const scheduler = input.scheduler || await readJson(path.join(root, 'agent-scheduler-state.json'), null);
22
22
  const taskGraph = input.partition?.task_graph || await readJson(path.join(root, 'agent-task-graph.json'), null);
23
23
  const parallelWritePolicy = input.parallelWritePolicy || await readJson(path.join(root, 'agent-parallel-write-policy.json'), null);
24
+ const gitWorktreeRuntime = input.gitWorktreeRuntime || await readJson(path.join(root, 'agent-git-worktree-runtime.json'), null);
24
25
  const patchQueue = await readJson(path.join(root, 'agent-patch-queue.json'), null);
25
26
  const patchQueueEvents = await readTextSafe(path.join(root, 'agent-patch-queue-events.jsonl'));
26
27
  const patchMerge = await readJson(path.join(root, 'agent-merge-coordinator-report.json'), null);
@@ -180,6 +181,7 @@ export async function writeAgentProofEvidence(root, input) {
180
181
  ...(noSubagentScalingPolicy?.ok === false ? noSubagentScalingPolicy.blockers || ['no_subagent_scaling_policy_not_ok'] : []),
181
182
  ...(fastModePropagation?.ok === false ? fastModePropagation.blockers || ['fast_mode_propagation_not_ok'] : []),
182
183
  ...(patchSwarm?.ok === false ? patchSwarm.blockers || ['patch_swarm_not_ok'] : []),
184
+ ...(gitWorktreeRuntime?.required === true && gitWorktreeRuntime?.ok === false ? gitWorktreeRuntime.blockers || ['git_worktree_runtime_not_ok'] : []),
183
185
  ...(patchSwarm && !patchQueue ? ['patch_queue_missing'] : []),
184
186
  ...(patchSwarm && !patchMerge ? ['patch_merge_report_missing'] : []),
185
187
  ...(patchSwarm && !patchApplyResults ? ['patch_apply_results_missing'] : []),
@@ -217,6 +219,7 @@ export async function writeAgentProofEvidence(root, input) {
217
219
  // Deployed Core Skill snapshot consulted at route start (read-only; never
218
220
  // confers mutation rights). Null when no snapshot is deployed for this route.
219
221
  selected_core_skill: input.selectedCoreSkill || null,
222
+ git_worktree_runtime: gitWorktreeRuntime,
220
223
  route_blackbox_kind: input.routeBlackboxKind || (realRouteCommandUsed ? 'actual_route_command' : 'generic_agent_route_standin'),
221
224
  real_route_command_used: realRouteCommandUsed,
222
225
  native_cli_session_proof: nativeCliSessionProof ? 'native-cli-session-proof.json' : null,
@@ -26,6 +26,8 @@ class NativeCliSessionSwarmRecorder {
26
26
  await this.persist();
27
27
  }
28
28
  async launchWorker(ctx) {
29
+ const worktree = normalizeWorkerWorktree(ctx.agent?.worktree || ctx.slice?.worktree || ctx.opts?.worktree || null);
30
+ const workerCwd = worktree?.path || ctx.opts.cwd || packageRoot();
29
31
  const workerDirRel = path.join(ctx.agent.session_artifact_dir || path.join('sessions', ctx.agent.id), 'worker');
30
32
  const workerDir = path.join(this.root, workerDirRel);
31
33
  await ensureDir(workerDir);
@@ -45,6 +47,9 @@ class NativeCliSessionSwarmRecorder {
45
47
  backend_explicit: this.input.backendExplicit === true,
46
48
  no_ollama: this.input.noOllama === true || ctx.opts.noOllama === true,
47
49
  agent_root: this.root,
50
+ main_repo_root: worktree?.main_repo_root || ctx.opts.cwd || packageRoot(),
51
+ cwd: workerCwd,
52
+ worktree,
48
53
  agent: ctx.agent,
49
54
  slice: ctx.slice,
50
55
  worker_artifact_dir: workerDirRel,
@@ -89,6 +94,7 @@ class NativeCliSessionSwarmRecorder {
89
94
  patch_envelope_path: patchRel,
90
95
  fast_mode: this.input.fastModePolicy.fast_mode,
91
96
  service_tier: this.input.fastModePolicy.service_tier,
97
+ cwd: workerCwd,
92
98
  status: 'launching',
93
99
  exit_code: null,
94
100
  blockers: []
@@ -110,7 +116,7 @@ class NativeCliSessionSwarmRecorder {
110
116
  });
111
117
  }
112
118
  const child = spawn(process.execPath, args, {
113
- cwd: ctx.opts.cwd || packageRoot(),
119
+ cwd: workerCwd,
114
120
  env: {
115
121
  ...process.env,
116
122
  ...(ctx.opts.env || {}),
@@ -179,6 +185,8 @@ class NativeCliSessionSwarmRecorder {
179
185
  async launchWorkerInZellijPane(input) {
180
186
  const sessionName = String(input.ctx.opts.zellijSessionName || (this.input.missionId ? `sks-${this.input.missionId}` : 'sks-agent-runtime'));
181
187
  const slotId = String(input.ctx.agent.slot_id || input.ctx.agent.id || 'slot-001');
188
+ const worktree = normalizeWorkerWorktree(input.ctx.agent?.worktree || input.ctx.slice?.worktree || input.ctx.opts?.worktree || null);
189
+ const workerCwd = worktree?.path || input.ctx.opts.cwd || packageRoot();
182
190
  const activeToken = this.nextPaneToken--;
183
191
  this.active.add(activeToken);
184
192
  this.maxObserved = Math.max(this.maxObserved, this.active.size);
@@ -222,9 +230,10 @@ class NativeCliSessionSwarmRecorder {
222
230
  patchEnvelopePath: input.record.patch_envelope_path,
223
231
  stdoutLog: input.stdoutRel,
224
232
  stderrLog: input.stderrRel,
225
- cwd: input.ctx.opts.cwd || packageRoot(),
233
+ cwd: workerCwd,
226
234
  providerContext,
227
- serviceTier: this.input.fastModePolicy.service_tier
235
+ serviceTier: this.input.fastModePolicy.service_tier,
236
+ worktree: worktree ? { id: worktree.id, path: worktree.path, branch: worktree.branch } : null
228
237
  });
229
238
  const launchBlockers = paneRecord.blockers || [];
230
239
  input.record.command_line = ['zellij', '--session', sessionName, 'action', 'new-pane', '--direction', paneRecord.direction_applied, '--name', paneRecord.pane_name, '--', 'sh', '-lc', '<native-cli-worker-command>'];
@@ -239,6 +248,7 @@ class NativeCliSessionSwarmRecorder {
239
248
  input.record.provider = paneRecord.provider;
240
249
  input.record.service_tier = paneRecord.service_tier;
241
250
  input.record.provider_context = paneRecord.provider_context;
251
+ input.record.worktree = worktree;
242
252
  input.record.status = launchBlockers.length ? 'failed' : 'running';
243
253
  input.record.blockers = launchBlockers;
244
254
  await this.record(input.record);
@@ -323,7 +333,7 @@ class NativeCliSessionSwarmRecorder {
323
333
  paneRecord = await closeWorkerPane({
324
334
  root: this.root,
325
335
  paneRecord,
326
- cwd: input.ctx.opts.cwd || packageRoot(),
336
+ cwd: workerCwd,
327
337
  status: input.record.status === 'closed' ? 'closed' : 'failed',
328
338
  blockers: input.record.blockers,
329
339
  sdkThreadId,
@@ -462,4 +472,15 @@ function redactWorkerArgs(args) {
462
472
  function shellQuote(value) {
463
473
  return `'${String(value).replace(/'/g, `'\\''`)}'`;
464
474
  }
475
+ function normalizeWorkerWorktree(value) {
476
+ const pathValue = value?.path || value?.worktree_path;
477
+ if (!pathValue)
478
+ return null;
479
+ return {
480
+ id: String(value?.id || value?.worktree_id || value?.slot_id || 'worktree'),
481
+ path: String(pathValue),
482
+ branch: String(value?.branch || 'unknown'),
483
+ main_repo_root: value?.main_repo_root == null ? null : String(value.main_repo_root)
484
+ };
485
+ }
465
486
  //# sourceMappingURL=native-cli-session-swarm.js.map
@@ -27,6 +27,8 @@ export async function runNativeCliWorker(input = {}) {
27
27
  };
28
28
  const slice = intake.slice || {};
29
29
  const backend = String(input.backend || intake.backend || 'fake');
30
+ const workerCwd = path.resolve(String(input.cwd || intake.cwd || process.cwd()));
31
+ const worktree = normalizeWorkerWorktree(input.worktree || intake.worktree || null);
30
32
  const policy = resolveFastModePolicy({
31
33
  fastMode: intake.fast_mode ?? input.fastMode,
32
34
  serviceTier: intake.service_tier ?? input.serviceTier
@@ -37,6 +39,10 @@ export async function runNativeCliWorker(input = {}) {
37
39
  const heartbeatRel = String(input.heartbeatPath || intake.heartbeat_path || path.join(workerDirRel, 'worker-heartbeat.jsonl'));
38
40
  const patchRel = String(input.patchEnvelopePath || intake.patch_envelope_path || path.join(workerDirRel, 'worker-patch-envelope.json'));
39
41
  await ensureDir(workerDir);
42
+ try {
43
+ process.chdir(workerCwd);
44
+ }
45
+ catch { }
40
46
  const recursion = scanAgentTextForRecursion(JSON.stringify({ agent, slice, backend }));
41
47
  const guard = {
42
48
  schema: 'sks.native-cli-worker-recursion-guard.v1',
@@ -56,6 +62,9 @@ export async function runNativeCliWorker(input = {}) {
56
62
  persona_id: String(agent.persona_id || input.personaId || ''),
57
63
  lease_id: String(intake.lease_id || input.leaseId || ''),
58
64
  agent_root: agentRoot,
65
+ main_repo_root: String(input.mainRepoRoot || intake.main_repo_root || worktree?.main_repo_root || agentRoot),
66
+ cwd: workerCwd,
67
+ worktree,
59
68
  worker_artifact_dir: workerDirRel,
60
69
  result_path: resultRel,
61
70
  heartbeat_path: heartbeatRel,
@@ -65,7 +74,9 @@ export async function runNativeCliWorker(input = {}) {
65
74
  backend: backend === 'zellij' ? 'codex-sdk' : backend,
66
75
  output_schema_id: 'sks.agent-worker-result.v1',
67
76
  sandbox_policy: Array.isArray(slice.write_paths) && slice.write_paths.length > 0 ? 'workspace-write' : 'read-only',
68
- thread_policy: 'new_thread_per_generation'
77
+ thread_policy: 'new_thread_per_generation',
78
+ worktree,
79
+ cwd: workerCwd
69
80
  },
70
81
  service_tier: policy.service_tier,
71
82
  fast_mode: policy.fast_mode,
@@ -143,6 +154,8 @@ export async function runNativeCliWorker(input = {}) {
143
154
  generated_at: nowIso(),
144
155
  ok: guard.ok,
145
156
  backend,
157
+ cwd: workerCwd,
158
+ worktree,
146
159
  agent_id: agent.id,
147
160
  session_id: agent.session_id,
148
161
  slot_id: agent.slot_id || null,
@@ -221,6 +234,9 @@ export async function runNativeCliWorker(input = {}) {
221
234
  slot_id: agent.slot_id || null,
222
235
  generation_index: agent.generation_index || null,
223
236
  process_id: process.pid,
237
+ main_repo_root: String(input.mainRepoRoot || intake.main_repo_root || worktree?.main_repo_root || agentRoot),
238
+ cwd: workerCwd,
239
+ worktree,
224
240
  artifact_dir: workerDirRel,
225
241
  patch_envelope: patchEnvelopes.length ? patchRel : null,
226
242
  no_patch_reason: patchEnvelopes.length ? null : path.join(workerDirRel, 'worker-no-patch-reason.json'),
@@ -290,4 +306,15 @@ function redactCommandLine(argv) {
290
306
  return part;
291
307
  });
292
308
  }
309
+ function normalizeWorkerWorktree(value) {
310
+ const pathValue = value?.path || value?.worktree_path;
311
+ if (!pathValue)
312
+ return null;
313
+ return {
314
+ id: String(value?.id || value?.worktree_id || value?.slot_id || 'worktree'),
315
+ path: String(pathValue),
316
+ branch: String(value?.branch || 'unknown'),
317
+ main_repo_root: value?.main_repo_root == null ? null : String(value.main_repo_root)
318
+ };
319
+ }
293
320
  //# sourceMappingURL=native-cli-worker.js.map