sneakoscope 3.1.0 → 3.1.2

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 (84) hide show
  1. package/README.md +1 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/cli/install-helpers.js +6 -7
  8. package/dist/commands/zellij-slot-column-anchor.js +3 -1
  9. package/dist/commands/zellij-slot-pane.js +19 -2
  10. package/dist/core/agents/agent-janitor.js +10 -1
  11. package/dist/core/agents/agent-orchestrator.js +8 -2
  12. package/dist/core/agents/agent-proof-evidence.js +20 -0
  13. package/dist/core/agents/agent-runner-ollama.js +11 -4
  14. package/dist/core/agents/fast-mode-policy.js +7 -5
  15. package/dist/core/agents/intelligent-work-graph.js +93 -14
  16. package/dist/core/agents/native-cli-session-swarm.js +115 -9
  17. package/dist/core/agents/no-subagent-scaling-policy.js +10 -1
  18. package/dist/core/agents/official-subagent-helper-policy.js +62 -0
  19. package/dist/core/codex-app.js +0 -2
  20. package/dist/core/codex-control/codex-task-runner.js +9 -0
  21. package/dist/core/commands/fast-mode-command.js +1 -1
  22. package/dist/core/commands/loop-command.js +86 -13
  23. package/dist/core/commands/naruto-command.js +34 -21
  24. package/dist/core/commands/team-command.js +1 -0
  25. package/dist/core/commands/wiki-command.js +35 -1
  26. package/dist/core/fsx.js +1 -1
  27. package/dist/core/init.js +1 -2
  28. package/dist/core/locks/file-lock.js +88 -0
  29. package/dist/core/loops/loop-artifacts.js +54 -2
  30. package/dist/core/loops/loop-checkpoint.js +22 -0
  31. package/dist/core/loops/loop-concurrency-budget.js +55 -0
  32. package/dist/core/loops/loop-final-arbiter-contract.js +28 -0
  33. package/dist/core/loops/loop-finalizer.js +55 -7
  34. package/dist/core/loops/loop-fixture-policy.js +58 -0
  35. package/dist/core/loops/loop-gate-registry.js +96 -0
  36. package/dist/core/loops/loop-gate-runner.js +206 -17
  37. package/dist/core/loops/loop-gpt-final-arbiter.js +81 -0
  38. package/dist/core/loops/loop-integration-merge.js +80 -0
  39. package/dist/core/loops/loop-interrupt-registry.js +118 -0
  40. package/dist/core/loops/loop-lease.js +35 -20
  41. package/dist/core/loops/loop-merge-strategy.js +105 -0
  42. package/dist/core/loops/loop-mutation-ledger.js +103 -0
  43. package/dist/core/loops/loop-planner.js +36 -5
  44. package/dist/core/loops/loop-runtime-control.js +27 -0
  45. package/dist/core/loops/loop-runtime.js +254 -96
  46. package/dist/core/loops/loop-scheduler.js +14 -5
  47. package/dist/core/loops/loop-side-effect-scanner.js +91 -0
  48. package/dist/core/loops/loop-worker-prompts.js +43 -0
  49. package/dist/core/loops/loop-worker-runtime.js +281 -0
  50. package/dist/core/loops/loop-worktree-runtime.js +92 -0
  51. package/dist/core/naruto/naruto-finalizer.js +7 -2
  52. package/dist/core/naruto/naruto-loop-mesh.js +10 -1
  53. package/dist/core/proof/auto-finalize.js +3 -2
  54. package/dist/core/proof/proof-schema.js +6 -0
  55. package/dist/core/proof/proof-writer.js +5 -2
  56. package/dist/core/proof/root-cause-policy.js +70 -0
  57. package/dist/core/proof/route-adapter.js +18 -1
  58. package/dist/core/proof/route-finalizer.js +71 -6
  59. package/dist/core/proof/route-proof-gate.js +4 -0
  60. package/dist/core/release/release-gate-batch-runner.js +56 -10
  61. package/dist/core/release/release-gate-cache-v2.js +18 -3
  62. package/dist/core/release/release-gate-dag.js +121 -18
  63. package/dist/core/release/release-gate-node.js +2 -1
  64. package/dist/core/release/release-gate-resource-governor.js +27 -6
  65. package/dist/core/skills/core-skill-meta-update.js +24 -0
  66. package/dist/core/skills/core-skill-reflection.js +94 -0
  67. package/dist/core/skills/core-skill-trainer.js +103 -0
  68. package/dist/core/trust-kernel/completion-contract.js +4 -0
  69. package/dist/core/trust-kernel/route-contract.js +4 -1
  70. package/dist/core/version.js +1 -1
  71. package/dist/core/zellij/zellij-right-column-manager.js +13 -2
  72. package/dist/core/zellij/zellij-slot-column-anchor.js +40 -3
  73. package/dist/core/zellij/zellij-slot-pane-renderer.js +36 -11
  74. package/dist/core/zellij/zellij-slot-telemetry.js +96 -44
  75. package/dist/core/zellij/zellij-worker-pane-manager.js +42 -4
  76. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +14 -2
  77. package/dist/scripts/loop-directive-check-lib.js +225 -2
  78. package/dist/scripts/loop-hardening-check-lib.js +289 -0
  79. package/dist/scripts/loop-worker-fixture-child.js +53 -0
  80. package/dist/scripts/naruto-real-local-gpt-final-smoke.js +10 -1
  81. package/dist/scripts/prepublish-release-check-or-fast.js +38 -10
  82. package/dist/scripts/release-check-stamp.js +29 -4
  83. package/dist/scripts/release-gate-existence-audit.js +1 -0
  84. package/package.json +32 -2
@@ -0,0 +1,105 @@
1
+ import path from 'node:path';
2
+ import { exists, runProcess } from '../fsx.js';
3
+ import { gitBlocker, runGitCommand } from '../git/git-worktree-runner.js';
4
+ export async function mergeSingleLoopWorktree(input) {
5
+ const attempts = [];
6
+ const changedFiles = [...new Set(input.proof.changed_files)];
7
+ const diff = await runGitCommand(input.worktreePath, ['diff', '--binary', '--full-index', 'HEAD'], { timeoutMs: 60000 }).catch(() => null);
8
+ if (!diff?.ok) {
9
+ return result(input.proof.loop_id, false, null, attempts, changedFiles, [`loop_merge_diff_failed:${input.proof.loop_id}`]);
10
+ }
11
+ if (!diff.stdout.trim())
12
+ return result(input.proof.loop_id, true, 'already_applied', attempts, changedFiles, []);
13
+ const applyCheck = await gitAttempt('apply-check', input.root, ['apply', '--check', '--whitespace=nowarn', '-'], diff.stdout);
14
+ attempts.push(applyCheck);
15
+ if (applyCheck.ok) {
16
+ const apply = await gitAttempt('apply', input.root, ['apply', '--whitespace=nowarn', '-'], diff.stdout);
17
+ attempts.push(apply);
18
+ if (apply.ok)
19
+ return result(input.proof.loop_id, true, 'apply', attempts, changedFiles, []);
20
+ await rollbackApply(input.root, diff.stdout);
21
+ }
22
+ const alreadyApplied = await gitAttempt('apply-check', input.root, ['apply', '--reverse', '--check', '--whitespace=nowarn', '-'], diff.stdout);
23
+ if (alreadyApplied.ok) {
24
+ attempts.push({ ...alreadyApplied, strategy: 'apply-check', blockers: [] });
25
+ return result(input.proof.loop_id, true, 'already_applied', attempts, changedFiles, []);
26
+ }
27
+ const apply3Check = await gitAttempt('apply-3way', input.root, ['apply', '--3way', '--check', '--whitespace=nowarn', '-'], diff.stdout);
28
+ attempts.push(apply3Check);
29
+ if (apply3Check.ok) {
30
+ const apply3 = await gitAttempt('apply-3way', input.root, ['apply', '--3way', '--whitespace=nowarn', '-'], diff.stdout);
31
+ attempts.push(apply3);
32
+ if (apply3.ok)
33
+ return result(input.proof.loop_id, true, 'apply-3way', attempts, changedFiles, []);
34
+ await abortMergeLikeState(input.root);
35
+ }
36
+ const head = await runGitCommand(input.worktreePath, ['rev-parse', '--verify', 'HEAD'], { timeoutMs: 10000 }).catch(() => null);
37
+ const commit = head?.ok ? head.stdout.trim() : '';
38
+ if (commit) {
39
+ const cherry = await gitAttempt('cherry-pick', input.root, ['cherry-pick', '--no-commit', commit], undefined);
40
+ attempts.push(cherry);
41
+ if (cherry.ok)
42
+ return result(input.proof.loop_id, true, 'cherry-pick', attempts, changedFiles, []);
43
+ await runGitCommand(input.root, ['cherry-pick', '--abort'], { timeoutMs: 30000 }).catch(() => null);
44
+ await abortMergeLikeState(input.root);
45
+ }
46
+ if (input.allowBranchMerge && input.proof.worktree.branch) {
47
+ const branch = input.proof.worktree.branch;
48
+ const merge = await gitAttempt('merge-no-commit', input.root, ['merge', '--no-ff', '--no-commit', branch], undefined);
49
+ attempts.push(merge);
50
+ if (merge.ok)
51
+ return result(input.proof.loop_id, true, 'merge-no-commit', attempts, changedFiles, []);
52
+ await runGitCommand(input.root, ['merge', '--abort'], { timeoutMs: 30000 }).catch(() => null);
53
+ await abortMergeLikeState(input.root);
54
+ }
55
+ const handoff = {
56
+ strategy: 'handoff',
57
+ ok: false,
58
+ exit_code: null,
59
+ stdout_tail: '',
60
+ stderr_tail: 'all merge strategies failed',
61
+ duration_ms: 1,
62
+ blockers: [`loop_merge_conflict_handoff:${input.proof.loop_id}`]
63
+ };
64
+ attempts.push(handoff);
65
+ return result(input.proof.loop_id, false, 'handoff', attempts, changedFiles, handoff.blockers);
66
+ }
67
+ async function gitAttempt(strategy, cwd, args, input) {
68
+ const started = Date.now();
69
+ const res = await runGitCommand(cwd, args, { timeoutMs: 60000, ...(input === undefined ? {} : { input }) }).catch((err) => null);
70
+ if (!res) {
71
+ return { strategy, ok: false, exit_code: null, stdout_tail: '', stderr_tail: '', duration_ms: Math.max(1, Date.now() - started), blockers: [`loop_merge_${strategy}_exception`] };
72
+ }
73
+ return {
74
+ strategy,
75
+ ok: res.ok,
76
+ exit_code: res.code,
77
+ stdout_tail: res.stdout_tail,
78
+ stderr_tail: res.stderr_tail,
79
+ duration_ms: Math.max(1, Date.now() - started),
80
+ blockers: res.ok ? [] : [gitBlocker(`loop_merge_${strategy}_failed`, res)]
81
+ };
82
+ }
83
+ async function rollbackApply(root, diff) {
84
+ await runGitCommand(root, ['apply', '--reverse', '--whitespace=nowarn', '-'], { input: diff, timeoutMs: 60000 }).catch(() => null);
85
+ await abortMergeLikeState(root);
86
+ }
87
+ async function abortMergeLikeState(root) {
88
+ if (await exists(path.join(root, '.git', 'MERGE_HEAD')))
89
+ await runGitCommand(root, ['merge', '--abort'], { timeoutMs: 30000 }).catch(() => null);
90
+ if (await exists(path.join(root, '.git', 'CHERRY_PICK_HEAD')))
91
+ await runGitCommand(root, ['cherry-pick', '--abort'], { timeoutMs: 30000 }).catch(() => null);
92
+ await runProcess('git', ['reset', '--merge'], { cwd: root, timeoutMs: 30000, maxOutputBytes: 64 * 1024 }).catch(() => null);
93
+ }
94
+ function result(loopId, ok, selectedStrategy, attempts, changedFiles, blockers) {
95
+ return {
96
+ schema: 'sks.loop-merge-strategy-result.v1',
97
+ loop_id: loopId,
98
+ ok,
99
+ selected_strategy: selectedStrategy,
100
+ attempts,
101
+ changed_files: changedFiles,
102
+ blockers: [...new Set(blockers)]
103
+ };
104
+ }
105
+ //# sourceMappingURL=loop-merge-strategy.js.map
@@ -0,0 +1,103 @@
1
+ import { appendJsonl, readText } from '../fsx.js';
2
+ import { loopMutationLedgerPath } from './loop-artifacts.js';
3
+ import { enforceLoopOwnerScope } from './loop-worktree-runtime.js';
4
+ export async function appendLoopMutationEvent(root, missionId, event) {
5
+ await appendJsonl(loopMutationLedgerPath(root, missionId), {
6
+ schema: 'sks.loop-mutation-ledger-event.v1',
7
+ ts: event.ts || new Date().toISOString(),
8
+ mission_id: missionId,
9
+ loop_id: event.loop_id,
10
+ event_type: event.event_type,
11
+ file_path: event.file_path,
12
+ source: event.source,
13
+ allowed_by_owner_scope: event.allowed_by_owner_scope,
14
+ details: event.details
15
+ });
16
+ }
17
+ export async function readLoopMutationLedger(root, missionId) {
18
+ const text = await readText(loopMutationLedgerPath(root, missionId), '');
19
+ return String(text).split(/\r?\n/)
20
+ .map((line) => line.trim())
21
+ .filter(Boolean)
22
+ .map((line) => {
23
+ try {
24
+ return JSON.parse(line);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ })
30
+ .filter((row) => Boolean(row));
31
+ }
32
+ export async function mutationLedgerFromLoopProofs(input) {
33
+ const events = [];
34
+ for (const proof of input.proofs) {
35
+ const workerChanged = [...new Set([...(proof.maker_result.changed_files || []), ...proof.changed_files])];
36
+ for (const file of workerChanged) {
37
+ const violations = enforceLoopOwnerScope([file], proof.owner_scope);
38
+ const eventType = violations.length ? 'owner_scope_violation' : 'file_changed';
39
+ const event = {
40
+ schema: 'sks.loop-mutation-ledger-event.v1',
41
+ ts: new Date().toISOString(),
42
+ mission_id: input.missionId,
43
+ loop_id: proof.loop_id,
44
+ event_type: eventType,
45
+ file_path: file,
46
+ source: 'git-diff',
47
+ allowed_by_owner_scope: violations.length === 0,
48
+ details: { status: proof.status, blockers: violations }
49
+ };
50
+ events.push(event);
51
+ await appendJsonl(loopMutationLedgerPath(input.root, input.missionId), event);
52
+ }
53
+ if (proof.gate_result.blockers?.some((blocker) => blocker.includes('side_effect') || blocker.includes('mutation'))) {
54
+ const event = {
55
+ schema: 'sks.loop-mutation-ledger-event.v1',
56
+ ts: new Date().toISOString(),
57
+ mission_id: input.missionId,
58
+ loop_id: proof.loop_id,
59
+ event_type: 'gate_side_effect',
60
+ file_path: null,
61
+ source: 'gate-result',
62
+ allowed_by_owner_scope: false,
63
+ details: { blockers: proof.gate_result.blockers || [] }
64
+ };
65
+ events.push(event);
66
+ await appendJsonl(loopMutationLedgerPath(input.root, input.missionId), event);
67
+ }
68
+ }
69
+ if (input.integrationMerge) {
70
+ for (const loopId of input.integrationMerge.applied_loops) {
71
+ const event = {
72
+ schema: 'sks.loop-mutation-ledger-event.v1',
73
+ ts: new Date().toISOString(),
74
+ mission_id: input.missionId,
75
+ loop_id: loopId,
76
+ event_type: 'merge_applied',
77
+ file_path: null,
78
+ source: 'integration-merge',
79
+ allowed_by_owner_scope: true,
80
+ details: { changed_files: input.integrationMerge.changed_files }
81
+ };
82
+ events.push(event);
83
+ await appendJsonl(loopMutationLedgerPath(input.root, input.missionId), event);
84
+ }
85
+ for (const loopId of input.integrationMerge.conflict_loops) {
86
+ const event = {
87
+ schema: 'sks.loop-mutation-ledger-event.v1',
88
+ ts: new Date().toISOString(),
89
+ mission_id: input.missionId,
90
+ loop_id: loopId,
91
+ event_type: 'merge_conflict',
92
+ file_path: null,
93
+ source: 'integration-merge',
94
+ allowed_by_owner_scope: false,
95
+ details: { blockers: input.integrationMerge.blockers }
96
+ };
97
+ events.push(event);
98
+ await appendJsonl(loopMutationLedgerPath(input.root, input.missionId), event);
99
+ }
100
+ }
101
+ return events;
102
+ }
103
+ //# sourceMappingURL=loop-mutation-ledger.js.map
@@ -6,6 +6,7 @@ import { inferLoopOwnerScope } from './loop-owner-inference.js';
6
6
  import { classifyLoopRisk } from './loop-risk-classifier.js';
7
7
  import { defaultLoopBudget, validateLoopPlan } from './loop-schema.js';
8
8
  export async function planLoopsFromRequest(input) {
9
+ const parallelism = input.parallelism || 'balanced';
9
10
  const maxLoops = Math.max(1, Math.min(32, input.maxLoops || 8));
10
11
  const domains = decomposeRequestIntoLoopDomains(input.request).slice(0, maxLoops);
11
12
  const actionNodes = domains.map((domain) => {
@@ -21,7 +22,8 @@ export async function planLoopsFromRequest(input) {
21
22
  dependencies: [],
22
23
  route: domain.id === 'docs' ? '$Loop' : '$Naruto',
23
24
  level: domain.id === 'docs' ? 'L1-assisted' : 'L2-action',
24
- risk
25
+ risk,
26
+ parallelism
25
27
  });
26
28
  return { ...nodeBase, gates: selectLoopGates({ node: nodeBase, changedFiles: [...ownerScope.files, ...ownerScope.directories], risk }) };
27
29
  });
@@ -36,7 +38,8 @@ export async function planLoopsFromRequest(input) {
36
38
  dependencies: actionNodes.map((node) => node.loop_id),
37
39
  route: '$Integration',
38
40
  level: 'L1-assisted',
39
- risk: integrationRisk
41
+ risk: integrationRisk,
42
+ parallelism
40
43
  });
41
44
  const integrationNode = {
42
45
  ...integrationBase,
@@ -86,8 +89,10 @@ export async function planLoopsFromRequest(input) {
86
89
  return plan;
87
90
  }
88
91
  function makeNode(input) {
92
+ const makerWorkerCount = dynamicMakerWorkerCount(input);
93
+ const checkerWorkerCount = dynamicCheckerWorkerCount(input);
89
94
  const budget = defaultLoopBudget({
90
- max_subagents: input.route === '$Integration' ? 2 : 4,
95
+ max_subagents: input.route === '$Integration' ? 2 : Math.max(4, makerWorkerCount + checkerWorkerCount + 1),
91
96
  max_changed_files: input.ownerScope.files.length ? Math.max(4, input.ownerScope.files.length + 2) : 12
92
97
  });
93
98
  return {
@@ -105,14 +110,14 @@ function makeNode(input) {
105
110
  maker: {
106
111
  route: '$Naruto',
107
112
  role: input.route === '$Integration' ? 'planner' : input.loopId.includes('docs') ? 'writer' : 'implementer',
108
- worker_count: input.route === '$Integration' ? 1 : 2,
113
+ worker_count: makerWorkerCount,
109
114
  backend_preference: ['codex-sdk', 'python-codex-sdk', 'local-llm'],
110
115
  local_draft_allowed: input.risk.level !== 'critical',
111
116
  gpt_final_required: input.risk.requires_gpt_final
112
117
  },
113
118
  checker: {
114
119
  route: input.loopId.includes('research') ? '$Research' : input.loopId.includes('docs') ? '$DFix' : '$QA-LOOP',
115
- worker_count: input.route === '$Integration' ? 1 : 1,
120
+ worker_count: checkerWorkerCount,
116
121
  fresh_session_required: true,
117
122
  stronger_model_required: input.risk.level === 'high' || input.risk.level === 'critical',
118
123
  required_before_next_iteration: input.level === 'L2-action'
@@ -133,6 +138,32 @@ function makeNode(input) {
133
138
  risk: input.risk
134
139
  };
135
140
  }
141
+ // Maker parallelism scales with the loop's owned scope instead of a flat 2:
142
+ // Naruto can fan out far wider, and a fixed count starved wide scopes while
143
+ // over-provisioning single-file loops. Risk still clamps the ceiling so
144
+ // critical work cannot stampede, and 'safe' mode keeps the old behavior.
145
+ function dynamicMakerWorkerCount(input) {
146
+ if (input.route === '$Integration')
147
+ return 1;
148
+ const scopeSize = input.ownerScope.files.length + input.ownerScope.directories.length * 3;
149
+ const modeCap = input.parallelism === 'safe' ? 2 : input.parallelism === 'extreme' ? 8 : 6;
150
+ const riskCap = input.risk.level === 'critical' ? 2 : modeCap;
151
+ const riskFloor = input.risk.level === 'high' ? 3 : 2;
152
+ const scopeScaled = Math.max(riskFloor, Math.ceil(scopeSize / 3));
153
+ return Math.max(1, Math.min(modeCap, riskCap, scopeScaled));
154
+ }
155
+ // Checker workers are read-only GPT review lanes. They scale more conservatively
156
+ // than makers, but wide/high-risk owner scopes get more than one fresh reviewer.
157
+ function dynamicCheckerWorkerCount(input) {
158
+ if (input.route === '$Integration')
159
+ return 1;
160
+ const scopeSize = input.ownerScope.files.length + input.ownerScope.directories.length * 3;
161
+ const modeCap = input.parallelism === 'safe' ? 1 : input.parallelism === 'extreme' ? 4 : 3;
162
+ const riskFloor = input.risk.level === 'high' || input.risk.level === 'critical' ? 2 : 1;
163
+ const riskCap = input.risk.level === 'critical' ? Math.min(2, modeCap) : modeCap;
164
+ const scopeScaled = Math.max(riskFloor, Math.ceil(scopeSize / 6));
165
+ return Math.max(1, Math.min(modeCap, riskCap, scopeScaled));
166
+ }
136
167
  function titleFromDomain(domainId) {
137
168
  return domainId === 'loop-general-coding' ? 'General coding loop' : `${domainId} loop`;
138
169
  }
@@ -0,0 +1,27 @@
1
+ import { readJson, writeJsonAtomic } from '../fsx.js';
2
+ import { loopKillRequestPath, loopProofPath, loopStatePath } from './loop-artifacts.js';
3
+ import { writeLoopCheckpoint } from './loop-checkpoint.js';
4
+ import { interruptLoopWorkers } from './loop-interrupt-registry.js';
5
+ export async function writeLoopKillRequest(root, missionId, target) {
6
+ const request = { schema: 'sks.loop-kill-request.v1', mission_id: missionId, target, requested_at: new Date().toISOString() };
7
+ await writeJsonAtomic(loopKillRequestPath(root, missionId), request);
8
+ await interruptLoopWorkers({ root, missionId, target }).catch(() => undefined);
9
+ return request;
10
+ }
11
+ export async function shouldKillLoop(root, missionId, loopId) {
12
+ const request = await readJson(loopKillRequestPath(root, missionId), null);
13
+ return request?.target === 'all' || request?.target === loopId;
14
+ }
15
+ export async function checkpointCancelledLoop(root, node, iteration, phase) {
16
+ await writeLoopCheckpoint({
17
+ root,
18
+ mission_id: node.mission_id,
19
+ loop_id: node.loop_id,
20
+ iteration,
21
+ phase,
22
+ state_path: loopStatePath(root, node.mission_id, node.loop_id),
23
+ proof_path: loopProofPath(root, node.mission_id, node.loop_id),
24
+ resumable: true
25
+ });
26
+ }
27
+ //# sourceMappingURL=loop-runtime-control.js.map