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.
- package/README.md +5 -3
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +27 -8
- package/dist/cli/command-registry.js +1 -0
- package/dist/cli/install-helpers.js +8 -20
- package/dist/commands/doctor.js +5 -9
- package/dist/commands/zellij-slot-column-anchor.js +23 -0
- package/dist/core/agents/agent-orchestrator.js +338 -12
- package/dist/core/agents/agent-patch-schema.js +8 -1
- package/dist/core/agents/agent-scheduler.js +12 -1
- package/dist/core/agents/agent-slot-pane-binding-proof.js +3 -3
- package/dist/core/agents/agent-work-queue.js +26 -2
- package/dist/core/agents/agent-worker-pipeline.js +2 -0
- package/dist/core/agents/native-cli-session-swarm.js +2 -2
- package/dist/core/commands/naruto-command.js +191 -39
- package/dist/core/fsx.js +1 -1
- package/dist/core/git/git-worktree-checkpoint.js +52 -0
- package/dist/core/git/git-worktree-cross-rebase.js +54 -0
- package/dist/core/git/git-worktree-merge-queue.js +92 -3
- package/dist/core/git/git-worktree-patch-envelope.js +8 -1
- package/dist/core/init.js +2 -2
- package/dist/core/naruto/naruto-allocation-policy.js +99 -0
- package/dist/core/naruto/naruto-real-worker-child.js +110 -11
- package/dist/core/naruto/naruto-rebalance-policy.js +48 -0
- package/dist/core/naruto/naruto-task-hints.js +71 -0
- package/dist/core/naruto/naruto-work-graph.js +13 -0
- package/dist/core/pipeline/finalize-pipeline-result.js +3 -1
- package/dist/core/pipeline/gpt-final-required.js +22 -2
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-right-column-manager.js +45 -2
- package/dist/core/zellij/zellij-slot-column-anchor.js +218 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +81 -14
- package/dist/scripts/agent-real-codex-in-zellij-worker-pane-check.js +8 -2
- package/dist/scripts/agent-slot-pane-binding-proof-check.js +4 -4
- package/dist/scripts/codex-sdk-release-review-pipeline-check.js +2 -1
- package/dist/scripts/codex-sdk-zellij-pane-binding-check.js +2 -2
- package/dist/scripts/git-worktree-checkpoint-check.js +20 -0
- package/dist/scripts/git-worktree-cross-rebase-check.js +39 -0
- package/dist/scripts/git-worktree-merge-queue-check.js +1 -0
- package/dist/scripts/local-collab-worktree-gpt-final-apply-policy-check.js +63 -0
- package/dist/scripts/naruto-actual-worker-control-plane-check.js +56 -0
- package/dist/scripts/naruto-allocation-policy-check.js +33 -0
- package/dist/scripts/naruto-allocation-runtime-wiring-check.js +92 -0
- package/dist/scripts/naruto-extreme-parallelism-real-check.js +5 -4
- package/dist/scripts/naruto-orchestrator-runtime-source-check.js +70 -0
- package/dist/scripts/naruto-real-active-pool-runtime-check.js +4 -2
- package/dist/scripts/naruto-rebalance-policy-check.js +41 -0
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +8 -4
- package/dist/scripts/release-dag-full-coverage-check.js +19 -1
- package/dist/scripts/release-readiness-report.js +1 -1
- package/dist/scripts/release-real-check.js +258 -77
- package/dist/scripts/zellij-first-slot-down-stack-check.js +20 -0
- package/dist/scripts/zellij-first-slot-down-stack-real-check.js +356 -0
- package/dist/scripts/zellij-right-column-manager-check.js +7 -2
- package/dist/scripts/zellij-slot-column-anchor-check.js +45 -0
- package/dist/scripts/zellij-slot-only-ui-check.js +6 -2
- package/dist/scripts/zellij-slot-renderer-proof-semantics-check.js +59 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +23 -1
- package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +11 -4
- package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +21 -4
- 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
|
|
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
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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:
|
|
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:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
}
|