sneakoscope 2.0.10 → 2.0.12

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 (65) hide show
  1. package/README.md +5 -3
  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 +27 -8
  8. package/dist/cli/command-registry.js +1 -0
  9. package/dist/cli/install-helpers.js +8 -20
  10. package/dist/commands/doctor.js +5 -9
  11. package/dist/commands/zellij-slot-column-anchor.js +23 -0
  12. package/dist/core/agents/agent-orchestrator.js +338 -12
  13. package/dist/core/agents/agent-patch-schema.js +8 -1
  14. package/dist/core/agents/agent-scheduler.js +12 -1
  15. package/dist/core/agents/agent-slot-pane-binding-proof.js +3 -3
  16. package/dist/core/agents/agent-work-queue.js +26 -2
  17. package/dist/core/agents/agent-worker-pipeline.js +2 -0
  18. package/dist/core/agents/native-cli-session-swarm.js +2 -2
  19. package/dist/core/commands/naruto-command.js +191 -39
  20. package/dist/core/fsx.js +1 -1
  21. package/dist/core/git/git-worktree-checkpoint.js +52 -0
  22. package/dist/core/git/git-worktree-cross-rebase.js +54 -0
  23. package/dist/core/git/git-worktree-merge-queue.js +92 -3
  24. package/dist/core/git/git-worktree-patch-envelope.js +8 -1
  25. package/dist/core/init.js +2 -2
  26. package/dist/core/naruto/naruto-allocation-policy.js +99 -0
  27. package/dist/core/naruto/naruto-real-worker-child.js +110 -11
  28. package/dist/core/naruto/naruto-rebalance-policy.js +48 -0
  29. package/dist/core/naruto/naruto-task-hints.js +71 -0
  30. package/dist/core/naruto/naruto-work-graph.js +13 -0
  31. package/dist/core/pipeline/finalize-pipeline-result.js +3 -1
  32. package/dist/core/pipeline/gpt-final-required.js +22 -2
  33. package/dist/core/version.js +1 -1
  34. package/dist/core/zellij/zellij-right-column-manager.js +45 -2
  35. package/dist/core/zellij/zellij-slot-column-anchor.js +218 -0
  36. package/dist/core/zellij/zellij-worker-pane-manager.js +81 -14
  37. package/dist/scripts/agent-real-codex-in-zellij-worker-pane-check.js +8 -2
  38. package/dist/scripts/agent-slot-pane-binding-proof-check.js +4 -4
  39. package/dist/scripts/codex-sdk-release-review-pipeline-check.js +2 -1
  40. package/dist/scripts/codex-sdk-zellij-pane-binding-check.js +2 -2
  41. package/dist/scripts/git-worktree-checkpoint-check.js +20 -0
  42. package/dist/scripts/git-worktree-cross-rebase-check.js +39 -0
  43. package/dist/scripts/git-worktree-merge-queue-check.js +1 -0
  44. package/dist/scripts/local-collab-worktree-gpt-final-apply-policy-check.js +63 -0
  45. package/dist/scripts/naruto-actual-worker-control-plane-check.js +56 -0
  46. package/dist/scripts/naruto-allocation-policy-check.js +33 -0
  47. package/dist/scripts/naruto-allocation-runtime-wiring-check.js +92 -0
  48. package/dist/scripts/naruto-extreme-parallelism-real-check.js +5 -4
  49. package/dist/scripts/naruto-orchestrator-runtime-source-check.js +70 -0
  50. package/dist/scripts/naruto-real-active-pool-runtime-check.js +4 -2
  51. package/dist/scripts/naruto-rebalance-policy-check.js +41 -0
  52. package/dist/scripts/naruto-shadow-clone-swarm-check.js +8 -4
  53. package/dist/scripts/release-dag-full-coverage-check.js +19 -1
  54. package/dist/scripts/release-readiness-report.js +1 -1
  55. package/dist/scripts/release-real-check.js +258 -77
  56. package/dist/scripts/zellij-first-slot-down-stack-check.js +20 -0
  57. package/dist/scripts/zellij-first-slot-down-stack-real-check.js +356 -0
  58. package/dist/scripts/zellij-right-column-manager-check.js +7 -2
  59. package/dist/scripts/zellij-slot-column-anchor-check.js +45 -0
  60. package/dist/scripts/zellij-slot-only-ui-check.js +6 -2
  61. package/dist/scripts/zellij-slot-renderer-proof-semantics-check.js +59 -0
  62. package/dist/scripts/zellij-worker-pane-manager-check.js +23 -1
  63. package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +11 -4
  64. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +21 -4
  65. package/package.json +15 -3
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { createMission, findLatestMission, loadMission } from '../mission.js';
3
- import { readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
3
+ import { nowIso, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
4
4
  import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
5
5
  import { classifyOllamaWorkerSlice } from '../agents/agent-runner-ollama.js';
6
6
  import { buildNarutoCloneRoster, systemSafeNarutoConcurrency } from '../agents/agent-roster.js';
@@ -12,6 +12,8 @@ import { buildNarutoRoleDistribution } from '../naruto/naruto-role-policy.js';
12
12
  import { decideNarutoConcurrency } from '../naruto/naruto-concurrency-governor.js';
13
13
  import { runNarutoActivePool, runNarutoRealActivePool } from '../naruto/naruto-active-pool.js';
14
14
  import { collectActualNarutoWorker, spawnActualNarutoWorker } from '../naruto/naruto-real-worker-runtime.js';
15
+ import { allocateNarutoTasksToWorkers } from '../naruto/naruto-allocation-policy.js';
16
+ import { rebalanceNarutoReadyWork } from '../naruto/naruto-rebalance-policy.js';
15
17
  import { buildNarutoVerificationDag } from '../naruto/naruto-verification-dag.js';
16
18
  import { buildNarutoGptFinalPack } from '../naruto/naruto-gpt-final-pack.js';
17
19
  import { planNarutoZellijDashboard } from '../zellij/zellij-naruto-dashboard.js';
@@ -92,7 +94,7 @@ async function narutoRun(parsed) {
92
94
  const localWorker = await resolveNarutoLocalWorkerMode(parsed);
93
95
  const schedulerBackend = localWorker.auto_select_eligible ? 'ollama' : parsed.backend;
94
96
  const safe = systemSafeNarutoConcurrency({ backend: schedulerBackend });
95
- const workGraph = buildNarutoWorkGraph({
97
+ const baseWorkGraph = buildNarutoWorkGraph({
96
98
  prompt: parsed.prompt,
97
99
  requestedClones: roster.agent_count,
98
100
  totalWorkItems: parsed.workItems,
@@ -102,7 +104,63 @@ async function narutoRun(parsed) {
102
104
  maxActiveWorkers: parsed.concurrency || safe.cap,
103
105
  worktreePolicy
104
106
  });
107
+ const baseRoleDistribution = buildNarutoRoleDistribution(baseWorkGraph.work_items, { readonly: parsed.readonly });
108
+ const allocationWorkers = buildNarutoAllocationWorkers(baseWorkGraph, baseRoleDistribution, roster);
109
+ const allocationAssignments = allocateNarutoTasksToWorkers(baseWorkGraph.work_items, allocationWorkers);
110
+ const workGraph = buildNarutoWorkGraph({
111
+ prompt: parsed.prompt,
112
+ requestedClones: roster.agent_count,
113
+ totalWorkItems: parsed.workItems,
114
+ readonly: parsed.readonly,
115
+ writeCapable,
116
+ leaseBasePath: patchEnvelopeBasePath,
117
+ maxActiveWorkers: parsed.concurrency || safe.cap,
118
+ worktreePolicy,
119
+ allocationAssignments
120
+ });
105
121
  const roleDistribution = buildNarutoRoleDistribution(workGraph.work_items, { readonly: parsed.readonly });
122
+ const allocationPolicy = {
123
+ schema: 'sks.naruto-allocation-policy.v1',
124
+ generated_at: nowIso(),
125
+ ok: allocationWorkers.length > 0 && allocationAssignments.length === workGraph.work_items.length,
126
+ scoring_model: {
127
+ same_primary_role: 18,
128
+ declared_role: 12,
129
+ same_path_lane: 12,
130
+ overlap_each: 4,
131
+ assigned_task_penalty_each: -4,
132
+ write_conflict_penalty: -20,
133
+ dependency_incomplete: '-Infinity'
134
+ },
135
+ workers: allocationWorkers,
136
+ assignments: allocationAssignments.map((row) => ({
137
+ task_id: row.id,
138
+ owner: row.owner,
139
+ score: Number.isFinite(row.allocation_score) ? row.allocation_score : '-Infinity',
140
+ reason: row.allocation_reason,
141
+ role: row.required_role,
142
+ kind: row.kind,
143
+ paths: row.hints.paths,
144
+ domains: row.hints.domains,
145
+ write_paths: row.hints.writePaths
146
+ })),
147
+ blockers: allocationWorkers.length ? [] : ['naruto_allocation_workers_missing']
148
+ };
149
+ const rebalanceDecisions = rebalanceNarutoReadyWork({
150
+ tasks: workGraph.work_items.map((item) => ({ ...item, status: 'pending' })),
151
+ workers: allocationWorkers.map((worker) => ({ ...worker, alive: true, state: 'idle' })),
152
+ completedTaskIds: [],
153
+ reclaimedTaskIds: []
154
+ });
155
+ const rebalancePolicy = {
156
+ schema: 'sks.naruto-rebalance-policy.v1',
157
+ generated_at: nowIso(),
158
+ ok: true,
159
+ trigger: 'idle_worker_ready_queue',
160
+ decisions: rebalanceDecisions,
161
+ blocked_by_dependency_count: workGraph.work_items.filter((item) => item.dependencies.length > 0).length,
162
+ blockers: []
163
+ };
106
164
  const governor = decideNarutoConcurrency({
107
165
  requestedClones: roster.agent_count,
108
166
  totalWorkItems: workGraph.total_work_items,
@@ -113,32 +171,38 @@ async function narutoRun(parsed) {
113
171
  const activeSlots = Math.max(1, Math.min(roster.agent_count, parsed.concurrency || Math.max(governor.safe_active_workers, backendMinimum), safe.cap));
114
172
  const zellijVisiblePanes = Math.max(1, Math.min(activeSlots, governor.safe_zellij_visible_panes));
115
173
  const activePool = await runNarutoActivePool({ graph: workGraph, governor: { ...governor, safe_active_workers: activeSlots } });
116
- const realRuntimeWorktreePolicy = schedulerBackend === 'fake'
117
- ? {
118
- mode: 'patch-envelope-only',
119
- required: false,
120
- main_repo_root: worktreePolicy.main_repo_root,
121
- worktree_root: null,
122
- fallback_reason: 'fake_backend_fixture_skips_real_worktree_cleanup'
123
- }
124
- : worktreePolicy;
125
- const realActivePool = await runNarutoRealActivePool({
126
- graph: workGraph,
127
- governor: { ...governor, safe_active_workers: activeSlots },
128
- spawnWorker: async (item, placement) => spawnActualNarutoWorker({
174
+ const runPreRunSmoke = parsed.smoke === true || process.env.SKS_NARUTO_PRE_RUN_SMOKE === '1';
175
+ const realActivePoolSmoke = runPreRunSmoke
176
+ ? await runNarutoControlPlaneSmoke({
129
177
  root,
130
178
  missionId: mission.id,
131
- item,
132
- placement,
133
- backend: schedulerBackend,
134
- worktreePolicy: realRuntimeWorktreePolicy,
135
- zellijSessionName: `sks-${mission.id}`,
136
- visiblePaneCap: zellijVisiblePanes
137
- }),
138
- collectWorker: async (handle) => collectActualNarutoWorker(handle),
139
- enqueueVerification: async () => undefined,
140
- updateDashboard: async () => undefined
141
- });
179
+ prompt: parsed.prompt,
180
+ rosterCount: roster.agent_count,
181
+ totalWorkItems: workGraph.total_work_items,
182
+ patchEnvelopeBasePath,
183
+ worktreePolicy,
184
+ governor,
185
+ activeSlots,
186
+ zellijVisiblePanes
187
+ })
188
+ : {
189
+ schema: 'sks.naruto-active-pool.v1',
190
+ ok: true,
191
+ status: 'skipped',
192
+ runtime_source_of_truth: 'agent-orchestrator-scheduler',
193
+ production_runtime_source_of_truth: 'agent-orchestrator-scheduler',
194
+ fallback_reason: 'pre_run_smoke_never_owns_production_runtime',
195
+ reason: 'pre_run_smoke_disabled_for_production',
196
+ active_cap: 0,
197
+ max_observed_active_workers: 0,
198
+ average_active_workers: 0,
199
+ active_pool_utilization: 0,
200
+ refill_latency_ms_p95: 0,
201
+ visible_workers: 0,
202
+ headless_workers: 0,
203
+ worker_lifecycle: [],
204
+ smoke_graph_total_work_items: 0
205
+ };
142
206
  const verificationDag = buildNarutoVerificationDag(workGraph, { cwd: root });
143
207
  const gptFinalPack = buildNarutoGptFinalPack({
144
208
  missionId: mission.id,
@@ -162,7 +226,9 @@ async function narutoRun(parsed) {
162
226
  roleDistribution,
163
227
  governor,
164
228
  activePool,
165
- realActivePool,
229
+ realActivePool: realActivePoolSmoke,
230
+ allocationPolicy,
231
+ rebalancePolicy,
166
232
  verificationDag,
167
233
  gptFinalPack,
168
234
  zellijDashboard,
@@ -216,7 +282,8 @@ async function narutoRun(parsed) {
216
282
  concurrency: activeSlots,
217
283
  targetActiveSlots: activeSlots,
218
284
  visualLaneCount: zellijVisiblePanes,
219
- desiredWorkItemCount: parsed.workItems,
285
+ desiredWorkItemCount: workGraph.total_work_items,
286
+ minimumWorkItems: workGraph.total_work_items,
220
287
  maxAgentCount: MAX_NARUTO_AGENT_COUNT,
221
288
  narutoMode: true,
222
289
  clones: roster.agent_count,
@@ -239,6 +306,9 @@ async function narutoRun(parsed) {
239
306
  noFast: false,
240
307
  writeMode: writeCapable ? parsed.writeMode || 'parallel' : 'off',
241
308
  gitWorktreePolicy: worktreePolicy,
309
+ narutoWorkGraph: workGraph,
310
+ narutoAllocationPolicy: allocationPolicy,
311
+ narutoRebalancePolicy: rebalancePolicy,
242
312
  json: parsed.json
243
313
  });
244
314
  const clones = result.roster?.agent_count ?? roster.agent_count;
@@ -254,6 +324,8 @@ async function narutoRun(parsed) {
254
324
  max_clones: MAX_NARUTO_AGENT_COUNT,
255
325
  concurrency: result.target_active_slots ?? activeSlots,
256
326
  target_active_slots: result.target_active_slots ?? activeSlots,
327
+ runtime_source_of_truth: 'agent-orchestrator-scheduler',
328
+ pre_run_real_active_pool_source: runPreRunSmoke ? 'smoke_only' : 'skipped',
257
329
  concurrency_capped: clones > (result.target_active_slots ?? activeSlots),
258
330
  system: { cores: safe.cores, free_gb: safe.free_gb, safe_concurrency: safe.cap, heavy_backend: safe.heavy },
259
331
  work_graph: {
@@ -267,6 +339,8 @@ async function narutoRun(parsed) {
267
339
  },
268
340
  git_worktree: gitWorktreeCapability,
269
341
  role_distribution: roleDistribution,
342
+ allocation_policy: allocationPolicy,
343
+ rebalance_policy: rebalancePolicy,
270
344
  concurrency_governor: governor,
271
345
  active_pool: {
272
346
  ok: activePool.ok,
@@ -274,16 +348,18 @@ async function narutoRun(parsed) {
274
348
  refill_events: activePool.refill_events,
275
349
  completed_count: activePool.completed_count,
276
350
  real_runtime: {
277
- ok: realActivePool.ok,
278
- active_cap: realActivePool.active_cap,
279
- max_observed_active_workers: realActivePool.max_observed_active_workers,
280
- average_active_workers: realActivePool.average_active_workers,
281
- active_pool_utilization: realActivePool.active_pool_utilization,
282
- refill_latency_ms_p95: realActivePool.refill_latency_ms_p95,
283
- visible_workers: realActivePool.visible_workers,
284
- headless_workers: realActivePool.headless_workers,
285
- worker_lifecycle_count: realActivePool.worker_lifecycle.length,
286
- worker_lifecycle_sample: realActivePool.worker_lifecycle.slice(0, 5)
351
+ ok: realActivePoolSmoke.ok,
352
+ runtime_source_of_truth: realActivePoolSmoke.runtime_source_of_truth,
353
+ production_runtime_source_of_truth: realActivePoolSmoke.production_runtime_source_of_truth,
354
+ active_cap: realActivePoolSmoke.active_cap,
355
+ max_observed_active_workers: realActivePoolSmoke.max_observed_active_workers,
356
+ average_active_workers: realActivePoolSmoke.average_active_workers,
357
+ active_pool_utilization: realActivePoolSmoke.active_pool_utilization,
358
+ refill_latency_ms_p95: realActivePoolSmoke.refill_latency_ms_p95,
359
+ visible_workers: realActivePoolSmoke.visible_workers,
360
+ headless_workers: realActivePoolSmoke.headless_workers,
361
+ worker_lifecycle_count: realActivePoolSmoke.worker_lifecycle.length,
362
+ worker_lifecycle_sample: realActivePoolSmoke.worker_lifecycle.slice(0, 5)
287
363
  }
288
364
  },
289
365
  local_worker: localWorkerSummary,
@@ -335,6 +411,77 @@ function compactNarutoRunResult(result) {
335
411
  }
336
412
  };
337
413
  }
414
+ async function runNarutoControlPlaneSmoke(input) {
415
+ const smokeGraph = buildNarutoWorkGraph({
416
+ prompt: input.prompt,
417
+ requestedClones: Math.min(2, input.rosterCount),
418
+ totalWorkItems: Math.min(2, input.totalWorkItems),
419
+ readonly: true,
420
+ writeCapable: false,
421
+ leaseBasePath: input.patchEnvelopeBasePath,
422
+ maxActiveWorkers: Math.min(2, input.activeSlots),
423
+ worktreePolicy: {
424
+ mode: 'patch-envelope-only',
425
+ required: false,
426
+ main_repo_root: input.worktreePolicy.main_repo_root,
427
+ worktree_root: null,
428
+ fallback_reason: 'pre_run_smoke_never_owns_production_runtime'
429
+ }
430
+ });
431
+ const smokeWorktreePolicy = {
432
+ mode: 'patch-envelope-only',
433
+ required: false,
434
+ main_repo_root: input.worktreePolicy.main_repo_root,
435
+ worktree_root: null,
436
+ fallback_reason: 'pre_run_smoke_never_owns_production_runtime'
437
+ };
438
+ const realActivePool = await runNarutoRealActivePool({
439
+ graph: smokeGraph,
440
+ governor: { ...input.governor, safe_active_workers: Math.min(2, input.activeSlots), safe_zellij_visible_panes: Math.min(1, input.zellijVisiblePanes) },
441
+ spawnWorker: async (item, placement) => spawnActualNarutoWorker({
442
+ root: input.root,
443
+ missionId: input.missionId,
444
+ item,
445
+ placement,
446
+ backend: 'fake',
447
+ worktreePolicy: smokeWorktreePolicy,
448
+ zellijSessionName: `sks-${input.missionId}`,
449
+ visiblePaneCap: input.zellijVisiblePanes
450
+ }),
451
+ collectWorker: async (handle) => collectActualNarutoWorker(handle),
452
+ enqueueVerification: async () => undefined,
453
+ updateDashboard: async () => undefined
454
+ });
455
+ return {
456
+ ...realActivePool,
457
+ status: 'smoke_completed',
458
+ runtime_source_of_truth: 'pre_run_smoke_only',
459
+ production_runtime_source_of_truth: 'agent-orchestrator-scheduler',
460
+ fallback_reason: 'pre_run_smoke_never_owns_production_runtime',
461
+ smoke_graph_total_work_items: smokeGraph.total_work_items
462
+ };
463
+ }
464
+ function buildNarutoAllocationWorkers(workGraph, roleDistribution, roster) {
465
+ const workItems = Array.isArray(workGraph?.work_items) ? workGraph.work_items : [];
466
+ const roleByWorkItem = new Map((roleDistribution?.work_item_roles || []).map((row) => [String(row.work_item_id), String(row.role || '')]));
467
+ const rosterRows = Array.isArray(roster?.roster) ? roster.roster : [];
468
+ const count = Math.max(1, Math.min(Number(roster?.agent_count || rosterRows.length || workItems.length || 1), Math.max(1, workItems.length || 1)));
469
+ return Array.from({ length: count }, (_unused, index) => {
470
+ const agent = rosterRows[index] || {};
471
+ const item = workItems[index % Math.max(1, workItems.length)] || {};
472
+ const role = String(agent.naruto_role || agent.role || roleByWorkItem.get(String(item.id || '')) || item.required_role || 'worker');
473
+ return {
474
+ id: String(agent.id || `clone-${String(index + 1).padStart(3, '0')}`),
475
+ role,
476
+ lane: narutoAllocationLane(item)
477
+ };
478
+ });
479
+ }
480
+ function narutoAllocationLane(item) {
481
+ const firstPath = String((item?.write_paths || item?.target_paths || item?.readonly_paths || [])[0] || '');
482
+ const parts = firstPath.replace(/\\/g, '/').split('/').filter(Boolean);
483
+ return parts.slice(0, Math.min(2, parts.length)).join('/') || null;
484
+ }
338
485
  function summarizeNarutoLocalWorkerResult(localWorker, result) {
339
486
  const backendCounts = {};
340
487
  const rows = Array.isArray(result?.results) ? result.results : [];
@@ -487,9 +634,10 @@ function parseNarutoArgs(args = []) {
487
634
  const ollamaBaseUrl = String(readOption(args, '--ollama-base-url', readOption(args, '--local-model-base-url', '')) || '') || null;
488
635
  const noOpenZellij = hasFlag(args, '--no-open-zellij') || hasFlag(args, '--no-zellij');
489
636
  const attach = hasFlag(args, '--attach');
637
+ const smoke = hasFlag(args, '--smoke');
490
638
  const valueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--mission', '--mission-id', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url']);
491
639
  const prompt = positionalArgs(rest, valueFlags).join(' ').trim() || 'Naruto shadow clone swarm run';
492
- return { action, prompt, clones, workItems, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, json, missionId, noOpenZellij, attach };
640
+ return { action, prompt, clones, workItems, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, json, missionId, noOpenZellij, attach, smoke };
493
641
  }
494
642
  async function writeNarutoArtifacts(ledgerRoot, artifacts) {
495
643
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-work-graph.json'), artifacts.workGraph);
@@ -498,6 +646,10 @@ async function writeNarutoArtifacts(ledgerRoot, artifacts) {
498
646
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-active-pool.json'), artifacts.activePool);
499
647
  if (artifacts.realActivePool)
500
648
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-real-active-pool.json'), artifacts.realActivePool);
649
+ if (artifacts.allocationPolicy)
650
+ await writeJsonAtomic(path.join(ledgerRoot, 'naruto-allocation-policy.json'), artifacts.allocationPolicy);
651
+ if (artifacts.rebalancePolicy)
652
+ await writeJsonAtomic(path.join(ledgerRoot, 'naruto-rebalance-policy.json'), artifacts.rebalancePolicy);
501
653
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-verification-dag.json'), artifacts.verificationDag);
502
654
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-gpt-final-pack.json'), artifacts.gptFinalPack);
503
655
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-zellij-dashboard.json'), artifacts.zellijDashboard);
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.10';
8
+ export const PACKAGE_VERSION = '2.0.12';
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() {
@@ -0,0 +1,52 @@
1
+ import { nowIso } from '../fsx.js';
2
+ import { gitBlocker, gitOutputLine, runGitCommand } from './git-worktree-runner.js';
3
+ export async function checkpointWorkerWorktree(input) {
4
+ const status = await runGitCommand(input.worktreePath, ['status', '--porcelain=v1', '--untracked-files=all']);
5
+ const names = await runGitCommand(input.worktreePath, ['diff', '--name-only', 'HEAD']);
6
+ const untracked = await runGitCommand(input.worktreePath, ['ls-files', '--others', '--exclude-standard']);
7
+ const changedFiles = [...new Set([...lines(names.stdout), ...lines(untracked.stdout), ...statusFiles(status.stdout)])];
8
+ const blockers = [...(status.ok ? [] : [gitBlocker('git_worktree_status_failed', status)])];
9
+ const requested = input.mode || 'auto';
10
+ const commitMode = requested === 'checkpoint-commit' || (requested === 'auto' && changedFiles.length > 1);
11
+ if (!changedFiles.length || blockers.length) {
12
+ return report(input, requested, 'noop', null, changedFiles, blockers);
13
+ }
14
+ if (!commitMode)
15
+ return report(input, requested, 'diff-envelope', null, changedFiles, blockers);
16
+ const add = await runGitCommand(input.worktreePath, ['add', '-A'], { timeoutMs: 30000 });
17
+ if (!add.ok)
18
+ blockers.push(gitBlocker('git_worktree_checkpoint_add_failed', add));
19
+ const commit = blockers.length ? null : await runGitCommand(input.worktreePath, ['commit', '--no-verify', '-m', `sks(worker): checkpoint ${input.workerId}/${input.taskId}`], { timeoutMs: 120000 });
20
+ if (commit && !commit.ok)
21
+ blockers.push(gitBlocker('git_worktree_checkpoint_commit_failed', commit));
22
+ const head = blockers.length ? null : await runGitCommand(input.worktreePath, ['rev-parse', 'HEAD']);
23
+ const hash = head?.ok ? gitOutputLine(head) : null;
24
+ return report(input, requested, blockers.length ? 'noop' : 'checkpoint-commit', hash, changedFiles, blockers);
25
+ }
26
+ function report(input, mode, applied, commitHash, changedFiles, blockers) {
27
+ return {
28
+ schema: 'sks.git-worktree-checkpoint.v1',
29
+ ok: blockers.length === 0,
30
+ generated_at: nowIso(),
31
+ worktree_path: input.worktreePath,
32
+ repo_root: input.repoRoot,
33
+ worker_id: input.workerId,
34
+ task_id: input.taskId,
35
+ mode_requested: mode,
36
+ mode_applied: applied,
37
+ commit_hash: commitHash,
38
+ changed_files: changedFiles,
39
+ blockers
40
+ };
41
+ }
42
+ function lines(text) {
43
+ return String(text || '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
44
+ }
45
+ function statusFiles(text) {
46
+ return lines(text).map((line) => {
47
+ const match = line.match(/^.{2}\s+(.*)$/) || line.match(/^\S+\s+(.*)$/);
48
+ const file = (match?.[1] || line).trim();
49
+ return file.includes(' -> ') ? file.split(' -> ').pop()?.trim() || file : file;
50
+ }).filter(Boolean);
51
+ }
52
+ //# sourceMappingURL=git-worktree-checkpoint.js.map
@@ -0,0 +1,54 @@
1
+ import { nowIso } from '../fsx.js';
2
+ import { gitBlocker, gitOutputLine, runGitCommand } from './git-worktree-runner.js';
3
+ export async function crossRebaseIdleWorktrees(input) {
4
+ const records = [];
5
+ for (const worker of input.workers) {
6
+ const before = await runGitCommand(worker.worktree_path, ['rev-parse', 'HEAD']);
7
+ const beforeHead = before.ok ? gitOutputLine(before) : null;
8
+ if (!['idle', 'done', 'failed', 'unknown'].includes(worker.state)) {
9
+ records.push(record(worker, 'skipped', 'worker_not_idle', beforeHead, beforeHead, []));
10
+ continue;
11
+ }
12
+ const status = await runGitCommand(worker.worktree_path, ['status', '--porcelain=v1', '--untracked-files=all']);
13
+ if (!status.ok) {
14
+ records.push(record(worker, 'failed', 'status_failed', beforeHead, beforeHead, [gitBlocker('git_worktree_cross_rebase_status_failed', status)]));
15
+ continue;
16
+ }
17
+ if (status.stdout.trim()) {
18
+ records.push(record(worker, 'skipped', 'dirty_worktree_skipped', beforeHead, beforeHead, []));
19
+ continue;
20
+ }
21
+ const rebase = await runGitCommand(worker.worktree_path, ['rebase', input.integrationHead], { timeoutMs: 120000 });
22
+ if (!rebase.ok) {
23
+ await runGitCommand(worker.worktree_path, ['rebase', '--abort'], { timeoutMs: 30000 }).catch(() => null);
24
+ records.push(record(worker, 'failed', 'rebase_failed', beforeHead, beforeHead, [gitBlocker('git_worktree_cross_rebase_failed', rebase)]));
25
+ continue;
26
+ }
27
+ const after = await runGitCommand(worker.worktree_path, ['rev-parse', 'HEAD']);
28
+ records.push(record(worker, 'applied', 'rebased_to_integration_head', beforeHead, after.ok ? gitOutputLine(after) : null, []));
29
+ }
30
+ const blockers = records.flatMap((row) => row.blockers);
31
+ return {
32
+ schema: 'sks.git-worktree-cross-rebase.v1',
33
+ ok: blockers.length === 0,
34
+ generated_at: nowIso(),
35
+ integration_head: input.integrationHead,
36
+ applied_count: records.filter((row) => row.status === 'applied').length,
37
+ skipped_count: records.filter((row) => row.status === 'skipped').length,
38
+ records,
39
+ blockers
40
+ };
41
+ }
42
+ function record(worker, status, reason, beforeHead, afterHead, blockers) {
43
+ return {
44
+ worker_id: worker.worker_id,
45
+ worktree_path: worker.worktree_path,
46
+ state: worker.state,
47
+ status,
48
+ reason,
49
+ before_head: beforeHead,
50
+ after_head: afterHead,
51
+ blockers
52
+ };
53
+ }
54
+ //# sourceMappingURL=git-worktree-cross-rebase.js.map
@@ -3,10 +3,35 @@ import { runGitCommand } from './git-worktree-runner.js';
3
3
  import { summarizeGitWorktreeConflict } from './git-worktree-conflict-resolver.js';
4
4
  export async function applyGitWorktreeMergeQueue(input) {
5
5
  const conflicts = [];
6
+ const strategyResults = [];
6
7
  const changedFiles = new Set();
7
8
  let appliedCount = 0;
8
9
  let skippedCleanCount = 0;
10
+ let checkpointCommitCount = 0;
11
+ for (const checkpoint of input.checkpoints || []) {
12
+ for (const file of checkpoint.changed_files || [])
13
+ changedFiles.add(file);
14
+ if (checkpoint.mode_applied !== 'checkpoint-commit' || !checkpoint.commit_hash)
15
+ continue;
16
+ checkpointCommitCount += 1;
17
+ const merged = await applyCheckpointCommit(input.integrationWorktreePath, checkpoint);
18
+ strategyResults.push(merged);
19
+ if (merged.ok)
20
+ appliedCount += 1;
21
+ else
22
+ conflicts.push({
23
+ worker_id: checkpoint.worker_id,
24
+ changed_files: checkpoint.changed_files,
25
+ strategy: 'checkpoint-commit',
26
+ blockers: merged.blockers,
27
+ conflict_files: merged.conflict_files
28
+ });
29
+ }
9
30
  for (const diff of input.diffs) {
31
+ if ((input.checkpoints || []).some((checkpoint) => checkpoint.worker_id === diff.worker_id && checkpoint.mode_applied === 'checkpoint-commit' && checkpoint.commit_hash)) {
32
+ skippedCleanCount += 1;
33
+ continue;
34
+ }
10
35
  for (const file of diff.changed_files)
11
36
  changedFiles.add(file);
12
37
  if (diff.clean || !diff.diff.trim()) {
@@ -29,14 +54,34 @@ export async function applyGitWorktreeMergeQueue(input) {
29
54
  input: diff.diff,
30
55
  timeoutMs: 30000
31
56
  });
32
- if (apply.ok)
57
+ if (apply.ok) {
33
58
  appliedCount += 1;
59
+ strategyResults.push({
60
+ ok: true,
61
+ worker_id: diff.worker_id,
62
+ strategy: 'diff-apply-3way',
63
+ commit_hash: null,
64
+ conflict_files: [],
65
+ changed_files: diff.changed_files,
66
+ blockers: []
67
+ });
68
+ }
34
69
  else {
35
- conflicts.push(summarizeGitWorktreeConflict({
70
+ const conflict = summarizeGitWorktreeConflict({
36
71
  workerId: diff.worker_id,
37
72
  changedFiles: diff.changed_files,
38
73
  stderr: apply.stderr || apply.stdout
39
- }));
74
+ });
75
+ strategyResults.push({
76
+ ok: false,
77
+ worker_id: diff.worker_id,
78
+ strategy: 'diff-apply-3way',
79
+ commit_hash: null,
80
+ conflict_files: conflict.conflict_files || diff.changed_files,
81
+ changed_files: diff.changed_files,
82
+ blockers: conflict.blockers || ['git_worktree_diff_apply_failed']
83
+ });
84
+ conflicts.push(conflict);
40
85
  }
41
86
  }
42
87
  const blockers = conflicts.length ? ['git_worktree_merge_queue_conflicts'] : [];
@@ -46,10 +91,54 @@ export async function applyGitWorktreeMergeQueue(input) {
46
91
  generated_at: nowIso(),
47
92
  integration_worktree_path: input.integrationWorktreePath,
48
93
  applied_count: appliedCount,
94
+ checkpoint_commit_count: checkpointCommitCount,
49
95
  skipped_clean_count: skippedCleanCount,
50
96
  conflicts,
97
+ strategy_results: strategyResults,
51
98
  changed_files: [...changedFiles],
52
99
  blockers
53
100
  };
54
101
  }
102
+ async function applyCheckpointCommit(integrationWorktreePath, checkpoint) {
103
+ const cherryPick = await runGitCommand(integrationWorktreePath, ['cherry-pick', '--allow-empty', '-X', 'theirs', checkpoint.commit_hash || ''], {
104
+ timeoutMs: 120000
105
+ });
106
+ if (cherryPick.ok) {
107
+ return {
108
+ ok: true,
109
+ worker_id: checkpoint.worker_id,
110
+ strategy: 'checkpoint-cherry-pick',
111
+ commit_hash: checkpoint.commit_hash,
112
+ conflict_files: [],
113
+ blockers: []
114
+ };
115
+ }
116
+ await runGitCommand(integrationWorktreePath, ['cherry-pick', '--abort'], { timeoutMs: 30000 }).catch(() => null);
117
+ const merge = await runGitCommand(integrationWorktreePath, ['merge', '--no-ff', '--no-edit', '-X', 'theirs', checkpoint.commit_hash || ''], {
118
+ timeoutMs: 120000
119
+ });
120
+ if (merge.ok) {
121
+ return {
122
+ ok: true,
123
+ worker_id: checkpoint.worker_id,
124
+ strategy: 'checkpoint-merge',
125
+ commit_hash: checkpoint.commit_hash,
126
+ conflict_files: [],
127
+ blockers: []
128
+ };
129
+ }
130
+ const conflictFiles = await runGitCommand(integrationWorktreePath, ['diff', '--name-only', '--diff-filter=U'], { timeoutMs: 30000 }).catch(() => null);
131
+ await runGitCommand(integrationWorktreePath, ['merge', '--abort'], { timeoutMs: 30000 }).catch(() => null);
132
+ return {
133
+ ok: false,
134
+ worker_id: checkpoint.worker_id,
135
+ strategy: 'checkpoint-cherry-pick-then-merge',
136
+ commit_hash: checkpoint.commit_hash,
137
+ conflict_files: String(conflictFiles?.stdout || '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean),
138
+ blockers: [
139
+ `git_worktree_checkpoint_cherry_pick_failed:${cherryPick.stderr_tail || cherryPick.stdout_tail}`,
140
+ `git_worktree_checkpoint_merge_failed:${merge.stderr_tail || merge.stdout_tail}`
141
+ ]
142
+ };
143
+ }
55
144
  //# sourceMappingURL=git-worktree-merge-queue.js.map
@@ -18,7 +18,14 @@ export function buildGitWorktreePatchEnvelope(input) {
18
18
  base_head: input.diff.base_head,
19
19
  worktree_head: input.diff.worktree_head,
20
20
  changed_files: changedFiles,
21
- diff_bytes: input.diff.diff_bytes
21
+ diff_bytes: input.diff.diff_bytes,
22
+ checkpoint: input.checkpoint ? {
23
+ schema: input.checkpoint.schema,
24
+ mode_applied: input.checkpoint.mode_applied,
25
+ commit_hash: input.checkpoint.commit_hash,
26
+ changed_files: input.checkpoint.changed_files,
27
+ blockers: input.checkpoint.blockers
28
+ } : null
22
29
  },
23
30
  operations: [{
24
31
  op: 'git_apply_patch',
package/dist/core/init.js CHANGED
@@ -237,7 +237,7 @@ function isSksManagedHook(hook) {
237
237
  const command = String(hook.command || '');
238
238
  return hook.type === 'command' && /\bhook\s+(?:session-start|user-prompt-submit|pre-tool|post-tool|permission-request|pre-compact|post-compact|subagent-start|subagent-stop|stop)\b/.test(command) && /\b(?:sks|sneakoscope|sks\.js)\b/.test(command);
239
239
  }
240
- const AGENTS_BLOCK = "\n# Sneakoscope Codex Managed Rules\n\nThis repository uses Sneakoscope Codex.\n\n## Core Rules\n\n- Codex native `/goal` workflows are the persisted continuation surface; Ralph is removed from the user-facing SKS surface.\n- Keep runtime state bounded: raw logs go to files, prompts get tails/summaries, and `sks gc` may prune stale artifacts.\n- Codex App hooks do not force SKS update prompts during ordinary work. CLI update surfaces (`sks update-check`, `sks update check`, and launch notices) show latest-version guidance, and `sks doctor --fix` runs the guarded global SKS update path.\n- Versioning is explicit: use `sks versioning bump` when preparing release metadata. SKS must not install Git pre-commit hooks.\n- Installed harness files are immutable to LLM edits: `.codex/*`, `.agents/skills/`, `.codex/agents/`, `.sneakoscope/*policy*.json`, `AGENTS.md`, and `node_modules/sneakoscope`. The Sneakoscope engine source repo is the only automatic exception.\n- OMX/DCodex conflicts block setup/doctor. Show `sks conflicts prompt`; cleanup requires explicit human approval.\n- Do not stop at a plan when implementation was requested. Finish, verify, or report the hard blocker.\n- Do not create unrequested fallback implementation code. If the requested path is impossible, block with evidence instead of inventing substitute behavior.\n\n## Routes\n\n- General execution/code-changing prompts default to `$Team`: native agent intake agents, TriWiki refresh/validate, read-only debate, consensus, concrete runtime task graph/inboxes, fresh executor team, minimum five-lane Team review, integration, Honest Mode.\n- `$Computer-Use` / `$CU` is the maximum-speed Codex Computer Use lane for native macOS, desktop-app, OS-settings, and non-web visual tasks only. Web, browser, localhost, website, webapp, and web-based app verification must use the Codex Chrome Extension path first and halt rapidly if the extension is not installed/enabled.\n- `$Goal` is a fast bridge/overlay for Codex native `/goal` create/pause/resume/clear persistence controls; implementation continues through the selected SKS execution route.\n- TriWiki recall must stay bounded. Use `sks wiki sweep` to record demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim.\n- Team missions must keep schema-backed evidence current: `work-order-ledger.json`, `effort-decision.json`, `team-dashboard-state.json`, and route-specific visual/dogfood artifacts where applicable. Team completion requires at least five independent reviewer/QA validation lanes before integration or final, even when a prompt requests fewer reviewers. Use `sks validate-artifacts latest` before claiming those artifacts pass.\n- `$DFix` is Direct Fix: only tiny copy/config/docs/labels/spacing/translation/simple mechanical edits, bypassing the main pipeline, Team, TriWiki/TriFix/reflection recording, and persistent route state; it still uses a one-line DFix-specific Honest check before final. Broad implementation stays on `$Team`, while UI design specifics follow the relevant design/UI route rules. `$PPT` is the restrained, information-first HTML/PDF presentation route and must seal delivery context, audience profile, STP, decision context, and 3+ pain-point/solution/aha mappings before design/render work. It must avoid over-designed visuals, carry detail through hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents, preserve editable source HTML under `source-html/`, record `ppt-parallel-report.json`, and clean PPT-only temporary build files before completion. `$Image-UX-Review` / `$UX-Review` is the imagegen/gpt-image-2 UI/UX review route: source screenshots must become generated annotated review images, those generated images must be extracted into issue ledgers, and text-only critique cannot pass the route gate. `$Answer`, `$Help`, and `$Wiki` stay lightweight.\n- For code work, surface route/guard/write scopes first, split independent worker scopes when available, and keep parent-owned integration and verification.\n- Design work reads `design.md` as the only design decision SSOT. If missing, create it through `design-system-builder` from `docs/Design-Sys-Prompt.md`; getdesign.md, getdesign-reference, and curated DESIGN.md examples from https://github.com/VoltAgent/awesome-design-md are source inputs to fuse into that SSOT or route-local style tokens, not parallel design authorities. Image/logo/raster assets use `imagegen`, which must prefer official Codex App built-in image generation via `$imagegen` / `gpt-image-2`; for newest-model image requests prompt explicitly for ChatGPT Images 2.0 / GPT Image 2.0 with `gpt-image-2`. Do not replace required raster evidence with placeholder SVG/HTML/CSS, prose-only reviews, or fabricated files.\n- Research, AutoResearch, performance, token, accuracy, SEO/GEO, or workflow-improvement claims need experiment/eval evidence. Do not claim live model accuracy without a scored dataset.\n- Treat handwritten files above 3000 lines as split-review risks. Run `sks code-structure scan` and prefer extraction before adding substantial logic.\n- Skill dreaming stays lightweight: route use records JSON counters in `.sneakoscope/skills/dream-state.json`, and full skill inventory/recommendation runs only after the configured 10-route-event threshold and cooldown. Reports are recommendation-only; deleting or merging skills needs explicit user approval.\n\n## Evidence And Context\n\n- Context7 is required for external libraries, APIs, MCPs, package managers, SDKs, and generated docs: resolve-library-id then query-docs.\n- When tech stack, framework, package, runtime, or deployment-platform versions change, use Context7 or official vendor web docs, record current syntax/security/limit guidance as high-priority TriWiki claims, then refresh and validate before coding.\n- TriWiki is the context-tracking SSOT for long-running missions, Team handoffs, and context-pressure recovery. Read `.sneakoscope/wiki/context-pack.json` before each stage, use `attention.use_first` for compact high-trust recall, hydrate `attention.hydrate_first` from source before risky or lower-trust decisions, refresh after findings or artifact changes, and validate before handoffs/final claims.\n- Source priority: current code/tests/config, decision contract, vgraph, beta, GX render/snapshot metadata, LLM Wiki coordinate index, then model knowledge only if allowed.\n- Final response before stop: summarize what was done, what changed for the user/repo, what was verified, and what remains unverified or blocked; then run Honest Mode. Say what passed and what was not verified.\n- `$From-Chat-IMG` uses forensic visual effort, not ordinary Team effort. Completion is blocked until source inventory, visual mapping, work-order coverage, scoped dogfood/QA, and post-fix verification artifacts are present and valid.\n\n## Safety\n\n- Database access is high risk. Use read-only inspection by default; live data mutation is out of scope unless a sealed contract allows local or branch-only migration files.\n- MAD and MAD-SKS widen only explicit scoped permissions; they still do not authorize unrequested fallback implementation code.\n- Task completion requires relevant tests or justification, zero unsupported critical claims, accepted visual/wiki drift, and final evidence.\n\n## Codex App\n\nUse `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, and SKS dollar commands (`$sks`, `$team`, `$computer-use`, `$cu`, `$ppt`, `$image-ux-review`, `$ux-review`, `$goal`, `$dfix`, `$qa-loop`, etc.) as the app control surface.\n";
240
+ const AGENTS_BLOCK = "\n# Sneakoscope Codex Managed Rules\n\nThis repository uses Sneakoscope Codex.\n\n## Core Rules\n\n- Codex native `/goal` workflows are the persisted continuation surface; Ralph is removed from the user-facing SKS surface.\n- Keep runtime state bounded: raw logs go to files, prompts get tails/summaries, and `sks gc` may prune stale artifacts.\n- Codex App hooks, launch paths, and `sks doctor --fix` do not force SKS update prompts during ordinary work. Manual CLI update surfaces (`sks update-check`, `sks update check`, and `sks update now`) remain available when the operator explicitly asks for them.\n- Versioning is explicit: use `sks versioning bump` when preparing release metadata. SKS must not install Git pre-commit hooks.\n- Installed harness files are immutable to LLM edits: `.codex/*`, `.agents/skills/`, `.codex/agents/`, `.sneakoscope/*policy*.json`, `AGENTS.md`, and `node_modules/sneakoscope`. The Sneakoscope engine source repo is the only automatic exception.\n- OMX/DCodex conflicts block setup/doctor. Show `sks conflicts prompt`; cleanup requires explicit human approval.\n- Do not stop at a plan when implementation was requested. Finish, verify, or report the hard blocker.\n- Do not create unrequested fallback implementation code. If the requested path is impossible, block with evidence instead of inventing substitute behavior.\n\n## Routes\n\n- General execution/code-changing prompts default to `$Team`: native agent intake agents, TriWiki refresh/validate, read-only debate, consensus, concrete runtime task graph/inboxes, fresh executor team, minimum five-lane Team review, integration, Honest Mode.\n- `$Computer-Use` / `$CU` is the maximum-speed Codex Computer Use lane for native macOS, desktop-app, OS-settings, and non-web visual tasks only. Web, browser, localhost, website, webapp, and web-based app verification must use the Codex Chrome Extension path first and halt rapidly if the extension is not installed/enabled.\n- `$Goal` is a fast bridge/overlay for Codex native `/goal` create/pause/resume/clear persistence controls; implementation continues through the selected SKS execution route.\n- TriWiki recall must stay bounded. Use `sks wiki sweep` to record demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim.\n- Team missions must keep schema-backed evidence current: `work-order-ledger.json`, `effort-decision.json`, `team-dashboard-state.json`, and route-specific visual/dogfood artifacts where applicable. Team completion requires at least five independent reviewer/QA validation lanes before integration or final, even when a prompt requests fewer reviewers. Use `sks validate-artifacts latest` before claiming those artifacts pass.\n- `$DFix` is Direct Fix: only tiny copy/config/docs/labels/spacing/translation/simple mechanical edits, bypassing the main pipeline, Team, TriWiki/TriFix/reflection recording, and persistent route state; it still uses a one-line DFix-specific Honest check before final. Broad implementation stays on `$Team`, while UI design specifics follow the relevant design/UI route rules. `$PPT` is the restrained, information-first HTML/PDF presentation route and must seal delivery context, audience profile, STP, decision context, and 3+ pain-point/solution/aha mappings before design/render work. It must avoid over-designed visuals, carry detail through hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents, preserve editable source HTML under `source-html/`, record `ppt-parallel-report.json`, and clean PPT-only temporary build files before completion. `$Image-UX-Review` / `$UX-Review` is the imagegen/gpt-image-2 UI/UX review route: source screenshots must become generated annotated review images, those generated images must be extracted into issue ledgers, and text-only critique cannot pass the route gate. `$Answer`, `$Help`, and `$Wiki` stay lightweight.\n- For code work, surface route/guard/write scopes first, split independent worker scopes when available, and keep parent-owned integration and verification.\n- Design work reads `design.md` as the only design decision SSOT. If missing, create it through `design-system-builder` from `docs/Design-Sys-Prompt.md`; getdesign.md, getdesign-reference, and curated DESIGN.md examples from https://github.com/VoltAgent/awesome-design-md are source inputs to fuse into that SSOT or route-local style tokens, not parallel design authorities. Image/logo/raster assets use `imagegen`, which must prefer official Codex App built-in image generation via `$imagegen` / `gpt-image-2`; for newest-model image requests prompt explicitly for ChatGPT Images 2.0 / GPT Image 2.0 with `gpt-image-2`. Do not replace required raster evidence with placeholder SVG/HTML/CSS, prose-only reviews, or fabricated files.\n- Research, AutoResearch, performance, token, accuracy, SEO/GEO, or workflow-improvement claims need experiment/eval evidence. Do not claim live model accuracy without a scored dataset.\n- Treat handwritten files above 3000 lines as split-review risks. Run `sks code-structure scan` and prefer extraction before adding substantial logic.\n- Skill dreaming stays lightweight: route use records JSON counters in `.sneakoscope/skills/dream-state.json`, and full skill inventory/recommendation runs only after the configured 10-route-event threshold and cooldown. Reports are recommendation-only; deleting or merging skills needs explicit user approval.\n\n## Evidence And Context\n\n- Context7 is required for external libraries, APIs, MCPs, package managers, SDKs, and generated docs: resolve-library-id then query-docs.\n- When tech stack, framework, package, runtime, or deployment-platform versions change, use Context7 or official vendor web docs, record current syntax/security/limit guidance as high-priority TriWiki claims, then refresh and validate before coding.\n- TriWiki is the context-tracking SSOT for long-running missions, Team handoffs, and context-pressure recovery. Read `.sneakoscope/wiki/context-pack.json` before each stage, use `attention.use_first` for compact high-trust recall, hydrate `attention.hydrate_first` from source before risky or lower-trust decisions, refresh after findings or artifact changes, and validate before handoffs/final claims.\n- Source priority: current code/tests/config, decision contract, vgraph, beta, GX render/snapshot metadata, LLM Wiki coordinate index, then model knowledge only if allowed.\n- Final response before stop: summarize what was done, what changed for the user/repo, what was verified, and what remains unverified or blocked; then run Honest Mode. Say what passed and what was not verified.\n- `$From-Chat-IMG` uses forensic visual effort, not ordinary Team effort. Completion is blocked until source inventory, visual mapping, work-order coverage, scoped dogfood/QA, and post-fix verification artifacts are present and valid.\n\n## Safety\n\n- Database access is high risk. Use read-only inspection by default; live data mutation is out of scope unless a sealed contract allows local or branch-only migration files.\n- MAD and MAD-SKS widen only explicit scoped permissions; they still do not authorize unrequested fallback implementation code.\n- Task completion requires relevant tests or justification, zero unsupported critical claims, accepted visual/wiki drift, and final evidence.\n\n## Codex App\n\nUse `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, and SKS dollar commands (`$sks`, `$team`, `$computer-use`, `$cu`, `$ppt`, `$image-ux-review`, `$ux-review`, `$goal`, `$dfix`, `$qa-loop`, etc.) as the app control surface.\n";
241
241
  function agentsBlockText() {
242
242
  return AGENTS_BLOCK;
243
243
  }
@@ -1011,7 +1011,7 @@ function codexAppQuickReference(scope, commandPrefix) {
1011
1011
  stackCurrentDocsPolicyText(commandPrefix),
1012
1012
  `Team review: ${MIN_TEAM_REVIEW_POLICY_TEXT}`,
1013
1013
  `Team Zellij view: ${commandPrefix} team "task" prepares live watch/lane commands and reconciles managed Team panes inside the current SKS-owned Zellij session when available; add --no-open-zellij for artifact-only creation. Existing hook-created Team missions can be opened later with ${commandPrefix} team open-zellij latest. The view keeps the main Codex pane alive, adds an overview watch pane plus color-coded split per-agent lanes, and closes only SKS-managed Team panes as agent lanes finish or cleanup is requested; ${commandPrefix} team lane latest --agent native_agent_1 --follow shows one agent's status, assigned runtime tasks, recent agent events, direct messages, and fallback global tail; ${commandPrefix} team message latest --from native_agent_1 --to executor_1 --message "handoff note" mirrors bounded agent communication into transcript/lane panes; ${commandPrefix} team cleanup-zellij latest marks the SKS session record complete and asks managed panes/follow loops to close or show a cleanup summary.`,
1014
- `Runtime: open Codex App once, then run ${commandPrefix} bootstrap and ${commandPrefix} deps check. Zellij is the interactive lane runtime for ${commandPrefix} --mad and Team lane UI; ${commandPrefix} bootstrap --yes, ${commandPrefix} deps check --yes, and ${commandPrefix} --mad --yes can install or repair Codex CLI/Zellij on macOS/Homebrew. npm postinstall reports missing CLI tools but does not mutate Homebrew/npm globals unless SKS_POSTINSTALL_AUTO_INSTALL_CLI_TOOLS=1 is set. Before launch SKS prints non-blocking latest-version notices for sneakoscope and checks npm @openai/codex@latest, prompting Y/n only when the installed Codex CLI is missing or outdated. ${commandPrefix} doctor --fix runs the guarded global SKS update path before repair. ${commandPrefix} codex-app remote-control wraps the Codex CLI 0.130.0+ headless remote-control entrypoint. ${commandPrefix} team open-zellij latest is the explicit Team lane view command.`,
1014
+ `Runtime: open Codex App once, then run ${commandPrefix} bootstrap and ${commandPrefix} deps check. Zellij is the interactive lane runtime for ${commandPrefix} --mad and Team lane UI; ${commandPrefix} bootstrap --yes, ${commandPrefix} deps check --yes, and ${commandPrefix} --mad --yes can install or repair Codex CLI/Zellij on macOS/Homebrew. npm postinstall reports missing CLI tools but does not mutate Homebrew/npm globals unless SKS_POSTINSTALL_AUTO_INSTALL_CLI_TOOLS=1 is set. Launch paths do not run sneakoscope npm update checks; use ${commandPrefix} update-check or ${commandPrefix} update now explicitly when you want that. Codex CLI latest checks remain dependency-readiness guidance and prompt Y/n only when the installed Codex CLI is missing or outdated. ${commandPrefix} doctor --fix repairs the local SKS/Codex setup without running a global SKS package update. ${commandPrefix} codex-app remote-control wraps the Codex CLI 0.130.0+ headless remote-control entrypoint. ${commandPrefix} team open-zellij latest is the explicit Team lane view command.`,
1015
1015
  `Guard: generated harness files are immutable outside the engine source repo; check ${commandPrefix} guard check; conflicts use ${commandPrefix} conflicts prompt with human approval.`
1016
1016
  ].join('\n') + '\n';
1017
1017
  }