sneakoscope 2.0.4 → 2.0.6
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 +18 -11
- 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 +78 -8
- package/dist/cli/install-helpers.js +23 -0
- package/dist/commands/codex-app.js +25 -3
- package/dist/commands/doctor.js +33 -4
- package/dist/commands/mad-sks.js +2 -2
- package/dist/core/agents/agent-orchestrator.js +22 -3
- package/dist/core/agents/agent-proof-evidence.js +59 -2
- package/dist/core/agents/agent-roster.js +35 -6
- package/dist/core/agents/agent-schema.js +1 -1
- package/dist/core/agents/agent-worker-pipeline.js +9 -1
- package/dist/core/agents/native-worker-backend-router.js +50 -10
- package/dist/core/agents/ollama-worker-config.js +164 -15
- package/dist/core/codex/codex-0-137-compat.js +119 -0
- package/dist/core/codex-app.js +124 -2
- package/dist/core/codex-control/codex-control-proof.js +4 -1
- package/dist/core/codex-control/codex-sdk-capability.js +1 -1
- package/dist/core/codex-control/codex-task-runner.js +329 -5
- package/dist/core/codex-control/python-codex-sdk-adapter.js +197 -0
- package/dist/core/codex-control/python-codex-sdk-event-translator.js +14 -0
- package/dist/core/commands/local-model-command.js +65 -19
- package/dist/core/commands/naruto-command.js +124 -8
- package/dist/core/commands/run-command.js +1 -1
- package/dist/core/doctor/doctor-readiness-matrix.js +21 -2
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +2 -233
- package/dist/core/init.js +8 -8
- package/dist/core/local-llm/local-llm-backpressure.js +20 -0
- package/dist/core/local-llm/local-llm-capability.js +29 -0
- package/dist/core/local-llm/local-llm-client.js +100 -0
- package/dist/core/local-llm/local-llm-config.js +6 -1
- package/dist/core/local-llm/local-llm-context-cache.js +21 -0
- package/dist/core/local-llm/local-llm-control-adapter.js +101 -0
- package/dist/core/local-llm/local-llm-json-repair.js +52 -0
- package/dist/core/local-llm/local-llm-metrics.js +42 -0
- package/dist/core/local-llm/local-llm-ollama-client.js +67 -0
- package/dist/core/local-llm/local-llm-openai-compatible-client.js +30 -0
- package/dist/core/local-llm/local-llm-prompt-cache.js +12 -0
- package/dist/core/local-llm/local-llm-scheduler.js +29 -0
- package/dist/core/local-llm/local-llm-schema-enforcer.js +15 -0
- package/dist/core/local-llm/local-llm-smoke.js +83 -0
- package/dist/core/local-llm/local-llm-warmup.js +20 -0
- package/dist/core/local-llm/local-worker-eligibility.js +27 -0
- package/dist/core/naruto/hardware-capacity-probe.js +36 -0
- package/dist/core/naruto/naruto-active-pool.js +134 -0
- package/dist/core/naruto/naruto-backpressure.js +13 -0
- package/dist/core/naruto/naruto-concurrency-governor.js +65 -0
- package/dist/core/naruto/naruto-finalizer.js +18 -0
- package/dist/core/naruto/naruto-generation-scheduler.js +18 -0
- package/dist/core/naruto/naruto-gpt-final-pack.js +49 -0
- package/dist/core/naruto/naruto-parallel-patch-apply.js +95 -0
- package/dist/core/naruto/naruto-patch-transaction-batch.js +42 -0
- package/dist/core/naruto/naruto-role-policy.js +107 -0
- package/dist/core/naruto/naruto-verification-dag.js +42 -0
- package/dist/core/naruto/naruto-verification-pool.js +18 -0
- package/dist/core/naruto/naruto-work-graph.js +198 -0
- package/dist/core/naruto/naruto-work-item.js +40 -0
- package/dist/core/naruto/naruto-work-stealing.js +11 -0
- package/dist/core/naruto/resource-pressure-monitor.js +32 -0
- package/dist/core/pipeline/finalize-pipeline-result.js +58 -0
- package/dist/core/pipeline/gpt-final-required.js +12 -0
- package/dist/core/pipeline-internals/runtime-core.js +1 -1
- package/dist/core/ppt.js +31 -8
- package/dist/core/product-design-app-server.js +410 -0
- package/dist/core/product-design-plugin.js +139 -0
- package/dist/core/prompt/prompt-placeholder-guard.js +30 -0
- package/dist/core/router/capability-card.js +13 -0
- package/dist/core/router/route-cache.js +3 -0
- package/dist/core/router/ultra-router.js +2 -1
- package/dist/core/routes.js +12 -12
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-lane-runtime.js +2 -2
- package/dist/core/zellij/zellij-naruto-dashboard.js +36 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +4 -4
- package/dist/scripts/blackbox-command-import-smoke.js +10 -1
- package/dist/scripts/check-package-boundary.js +12 -3
- package/dist/scripts/codex-0-137-compat-check.js +27 -0
- package/dist/scripts/codex-environment-scoped-approvals-check.js +10 -0
- package/dist/scripts/codex-plugin-list-json-check.js +8 -0
- package/dist/scripts/codex-thread-runtime-choice-check.js +10 -0
- package/dist/scripts/local-collab-all-pipelines-final-gpt-check.js +21 -0
- package/dist/scripts/local-llm-all-pipelines-check.js +11 -0
- package/dist/scripts/local-llm-cache-performance-check.js +10 -0
- package/dist/scripts/local-llm-capability-check.js +14 -0
- package/dist/scripts/local-llm-smoke-check.js +23 -0
- package/dist/scripts/local-llm-structured-output-check.js +11 -0
- package/dist/scripts/local-llm-throughput-check.js +10 -0
- package/dist/scripts/local-llm-tool-call-repair-check.js +10 -0
- package/dist/scripts/local-llm-warmup-check.js +11 -0
- package/dist/scripts/naruto-active-pool-check.js +39 -0
- package/dist/scripts/naruto-concurrency-governor-check.js +52 -0
- package/dist/scripts/naruto-gpt-final-pack-check.js +34 -0
- package/dist/scripts/naruto-parallel-patch-apply-check.js +41 -0
- package/dist/scripts/naruto-readonly-routing-check.js +116 -0
- package/dist/scripts/naruto-real-local-gpt-final-smoke.js +16 -0
- package/dist/scripts/naruto-role-distribution-check.js +23 -0
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +13 -0
- package/dist/scripts/naruto-verification-pool-check.js +36 -0
- package/dist/scripts/naruto-work-graph-check.js +24 -0
- package/dist/scripts/naruto-zellij-massive-ui-check.js +23 -0
- package/dist/scripts/product-design-auto-install-check.js +119 -0
- package/dist/scripts/product-design-plugin-routing-check.js +101 -0
- package/dist/scripts/prompt-placeholder-guard-check.js +33 -0
- package/dist/scripts/python-codex-sdk-all-pipelines-check.js +47 -0
- package/dist/scripts/python-codex-sdk-capability-check.js +75 -0
- package/dist/scripts/python-codex-sdk-sandbox-policy-check.js +10 -0
- package/dist/scripts/python-codex-sdk-stream-bridge-check.js +12 -0
- package/dist/scripts/release-parallel-check.js +16 -2
- package/dist/scripts/release-provenance-check.js +21 -0
- package/dist/scripts/release-real-check.js +5 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +1 -1
- package/package.json +36 -4
- package/schemas/local-llm/local-model-config.schema.json +74 -0
- package/schemas/naruto/naruto-concurrency-governor.schema.json +21 -0
- package/schemas/naruto/naruto-work-graph.schema.json +22 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function applyNarutoBackpressure(requestedActiveWorkers, pressure) {
|
|
2
|
+
const requested = Math.max(1, Math.floor(requestedActiveWorkers));
|
|
3
|
+
const multiplier = pressure.state === 'saturated' ? 0.25 : pressure.state === 'throttled' ? 0.5 : 1;
|
|
4
|
+
const adjusted = Math.max(1, Math.floor(requested * multiplier));
|
|
5
|
+
return {
|
|
6
|
+
schema: 'sks.naruto-backpressure.v1',
|
|
7
|
+
requested_active_workers: requested,
|
|
8
|
+
adjusted_active_workers: adjusted,
|
|
9
|
+
backpressure: pressure.state,
|
|
10
|
+
reasons: pressure.reasons
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=naruto-backpressure.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { probeHardwareCapacity } from './hardware-capacity-probe.js';
|
|
2
|
+
import { applyNarutoBackpressure } from './naruto-backpressure.js';
|
|
3
|
+
import { monitorNarutoResourcePressure } from './resource-pressure-monitor.js';
|
|
4
|
+
export function decideNarutoConcurrency(input = {}) {
|
|
5
|
+
const requestedClones = normalizePositiveInt(input.requestedClones, 12);
|
|
6
|
+
const totalWorkItems = normalizePositiveInt(input.totalWorkItems, requestedClones);
|
|
7
|
+
const pending = normalizeNonNegativeInt(input.pendingWorkQueueSize, totalWorkItems);
|
|
8
|
+
const leaseConflicts = normalizeNonNegativeInt(input.activeLeaseConflicts, 0);
|
|
9
|
+
const hardware = probeHardwareCapacity(input.hardware || {});
|
|
10
|
+
const zellijVisiblePaneCap = normalizePositiveInt(input.zellijVisiblePaneCap, Math.min(12, Math.max(4, Math.floor(hardware.terminal_rows / 3))));
|
|
11
|
+
const backend = String(input.backend || 'codex-sdk');
|
|
12
|
+
const freeGb = hardware.free_memory_bytes / (1024 * 1024 * 1024);
|
|
13
|
+
const totalGb = hardware.total_memory_bytes / (1024 * 1024 * 1024);
|
|
14
|
+
const reclaimableFloorGb = totalGb >= 32 ? 16 : totalGb >= 16 ? 8 : totalGb >= 8 ? 4 : Math.max(1, freeGb);
|
|
15
|
+
const memoryBudgetGb = Math.max(freeGb, reclaimableFloorGb);
|
|
16
|
+
const heavy = backend === 'codex-sdk' || backend === 'zellij' || backend === 'process' || backend === 'ollama';
|
|
17
|
+
const gbPerWorker = heavy ? Number(process.env.SKS_NARUTO_GB_PER_WORKER || 0.25) : Number(process.env.SKS_NARUTO_LIGHT_GB_PER_WORKER || 0.1);
|
|
18
|
+
const memoryCap = Math.max(1, Math.floor(memoryBudgetGb / Math.max(0.05, gbPerWorker)));
|
|
19
|
+
const fdCap = Math.max(1, Math.floor((hardware.file_descriptor_limit - hardware.process_count) / 6));
|
|
20
|
+
const localLlmParallel = Math.max(1, Math.min(4, hardware.local_llm_max_parallel_requests));
|
|
21
|
+
const remoteCodexParallel = Math.max(1, Math.min(hardware.remote_api_rate_limit_budget, requestedClones));
|
|
22
|
+
const queueCap = Math.max(1, Math.min(requestedClones, pending || totalWorkItems));
|
|
23
|
+
const leaseCap = Math.max(1, requestedClones - leaseConflicts);
|
|
24
|
+
const rawSafe = Math.max(1, Math.min(requestedClones, totalWorkItems, memoryCap, fdCap, remoteCodexParallel, queueCap, leaseCap, 100));
|
|
25
|
+
const pressure = monitorNarutoResourcePressure(hardware, { activeWorkers: rawSafe, zellijVisiblePaneCap });
|
|
26
|
+
const backpressure = applyNarutoBackpressure(rawSafe, pressure);
|
|
27
|
+
const safeActiveWorkers = Math.max(1, Math.min(rawSafe, backpressure.adjusted_active_workers));
|
|
28
|
+
const safeVisible = Math.min(safeActiveWorkers, zellijVisiblePaneCap);
|
|
29
|
+
const reasons = [
|
|
30
|
+
...(memoryCap < requestedClones ? ['memory_cap'] : []),
|
|
31
|
+
...(fdCap < requestedClones ? ['file_descriptor_budget'] : []),
|
|
32
|
+
...(remoteCodexParallel < requestedClones ? ['remote_api_rate_limit_budget'] : []),
|
|
33
|
+
...(localLlmParallel <= 4 ? ['local_llm_max_parallel_requests'] : []),
|
|
34
|
+
...(safeVisible < safeActiveWorkers ? ['zellij_ui_pane_budget'] : []),
|
|
35
|
+
...(leaseConflicts > 0 ? ['active_lease_conflicts'] : []),
|
|
36
|
+
...pressure.reasons
|
|
37
|
+
];
|
|
38
|
+
return {
|
|
39
|
+
schema: 'sks.naruto-concurrency-governor.v1',
|
|
40
|
+
requested_clones: requestedClones,
|
|
41
|
+
total_work_items: totalWorkItems,
|
|
42
|
+
safe_active_workers: safeActiveWorkers,
|
|
43
|
+
safe_zellij_visible_panes: safeVisible,
|
|
44
|
+
headless_workers: Math.max(0, safeActiveWorkers - safeVisible),
|
|
45
|
+
local_llm_parallel: localLlmParallel,
|
|
46
|
+
remote_codex_parallel: remoteCodexParallel,
|
|
47
|
+
verification_parallel: Math.max(1, Math.min(hardware.cpu_core_count * 2, safeActiveWorkers, 16)),
|
|
48
|
+
reasons: [...new Set(reasons)],
|
|
49
|
+
backpressure: backpressure.backpressure,
|
|
50
|
+
hardware
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function normalizePositiveInt(value, fallback) {
|
|
54
|
+
const parsed = Number(value);
|
|
55
|
+
if (!Number.isFinite(parsed) || parsed < 1)
|
|
56
|
+
return Math.max(1, Math.floor(fallback));
|
|
57
|
+
return Math.floor(parsed);
|
|
58
|
+
}
|
|
59
|
+
function normalizeNonNegativeInt(value, fallback) {
|
|
60
|
+
const parsed = Number(value);
|
|
61
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
62
|
+
return Math.max(0, Math.floor(fallback));
|
|
63
|
+
return Math.floor(parsed);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=naruto-concurrency-governor.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function evaluateNarutoFinalizer(input = {}) {
|
|
2
|
+
const localParticipated = input.localParticipated === true;
|
|
3
|
+
const gptFinalRequired = localParticipated;
|
|
4
|
+
const gptFinalAccepted = input.gptFinalStatus === 'approved' || input.gptFinalStatus === 'modified';
|
|
5
|
+
const blockers = [
|
|
6
|
+
...(gptFinalRequired && !gptFinalAccepted ? ['naruto_local_worker_output_needs_gpt_final_arbiter'] : [])
|
|
7
|
+
];
|
|
8
|
+
return {
|
|
9
|
+
schema: 'sks.naruto-finalizer.v1',
|
|
10
|
+
local_participated: localParticipated,
|
|
11
|
+
gpt_final_required: gptFinalRequired,
|
|
12
|
+
final_status: blockers.length ? 'blocked' : input.applyPatches === true ? 'accepted' : 'draft',
|
|
13
|
+
final_patch_source: gptFinalRequired ? 'gpt_final_arbiter' : 'deterministic_no_local',
|
|
14
|
+
blockers,
|
|
15
|
+
ok: blockers.length === 0
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=naruto-finalizer.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function createNarutoGeneration(workItem, index, tick) {
|
|
2
|
+
return {
|
|
3
|
+
generation_id: `NG-${String(index).padStart(6, '0')}`,
|
|
4
|
+
work_item_id: workItem.id,
|
|
5
|
+
role: workItem.required_role,
|
|
6
|
+
status: 'active',
|
|
7
|
+
started_tick: tick,
|
|
8
|
+
completed_tick: null
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function completeNarutoGeneration(generation, tick, failed = false) {
|
|
12
|
+
return {
|
|
13
|
+
...generation,
|
|
14
|
+
status: failed ? 'failed' : 'completed',
|
|
15
|
+
completed_tick: tick
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=naruto-generation-scheduler.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function buildNarutoGptFinalPack(input) {
|
|
2
|
+
const maxPatchEnvelopes = Math.max(1, Math.floor(Number(input.maxPatchEnvelopes || 100)));
|
|
3
|
+
const maxLogs = Math.max(1, Math.floor(Number(input.maxLogs || 12)));
|
|
4
|
+
return {
|
|
5
|
+
schema: 'sks.naruto-gpt-final-pack.v1',
|
|
6
|
+
mission_id: input.missionId,
|
|
7
|
+
route: '$Naruto',
|
|
8
|
+
work_graph_summary: {
|
|
9
|
+
total_work_items: input.graph.total_work_items,
|
|
10
|
+
mixed_work_kinds: input.graph.mixed_work_kinds,
|
|
11
|
+
write_allowed_count: input.graph.write_allowed_count
|
|
12
|
+
},
|
|
13
|
+
role_distribution: input.roleDistribution,
|
|
14
|
+
changed_files: [...new Set((input.changedFiles || []).map(String))],
|
|
15
|
+
patch_envelopes: (input.patchEnvelopes || []).slice(0, maxPatchEnvelopes).map(redactSecrets),
|
|
16
|
+
verification_results: (input.verificationResults || []).slice(0, 200).map(redactSecrets),
|
|
17
|
+
failed_shards: (input.failedShards || []).slice(0, 100).map(redactSecrets),
|
|
18
|
+
conflict_map: (input.conflictMap || []).slice(0, 100).map(redactSecrets),
|
|
19
|
+
rollback_plan: redactSecrets(input.rollbackPlan || { status: 'not_required' }),
|
|
20
|
+
side_effect_report: redactSecrets(input.sideEffectReport || { status: 'not_recorded' }),
|
|
21
|
+
local_llm_metrics: redactSecrets(input.localLlmMetrics || { participated: false }),
|
|
22
|
+
representative_logs: (input.logs || []).slice(0, maxLogs).map((log) => redactSecretText(log).slice(-4000)),
|
|
23
|
+
bounded: true,
|
|
24
|
+
secrets_redacted: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function redactSecrets(value) {
|
|
28
|
+
if (typeof value === 'string')
|
|
29
|
+
return redactSecretText(value);
|
|
30
|
+
if (Array.isArray(value))
|
|
31
|
+
return value.map(redactSecrets);
|
|
32
|
+
if (value && typeof value === 'object') {
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [key, item] of Object.entries(value)) {
|
|
35
|
+
if (/token|secret|password|api[_-]?key|authorization/i.test(key))
|
|
36
|
+
out[key] = '[REDACTED]';
|
|
37
|
+
else
|
|
38
|
+
out[key] = redactSecrets(item);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function redactSecretText(value) {
|
|
45
|
+
return String(value)
|
|
46
|
+
.replace(/sk-[A-Za-z0-9_-]{16,}/g, 'sk-[REDACTED]')
|
|
47
|
+
.replace(/(api[_-]?key|authorization|token|password)\s*[:=]\s*\S+/gi, '$1=[REDACTED]');
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=naruto-gpt-final-pack.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { normalizeAgentPatchEnvelope, validateAgentPatchEnvelope } from '../agents/agent-patch-schema.js';
|
|
3
|
+
import { ensureDir, readText, sha256, writeTextAtomic } from '../fsx.js';
|
|
4
|
+
import { envelopeIdFor, planNarutoPatchTransactionBatches } from './naruto-patch-transaction-batch.js';
|
|
5
|
+
export async function applyNarutoPatchEnvelopes(root, rawEnvelopes, opts = {}) {
|
|
6
|
+
const dryRun = opts.dryRun === true;
|
|
7
|
+
const envelopes = rawEnvelopes.map(normalizeAgentPatchEnvelope);
|
|
8
|
+
const plan = planNarutoPatchTransactionBatches(envelopes);
|
|
9
|
+
const results = [];
|
|
10
|
+
for (const batch of plan.batches) {
|
|
11
|
+
const batchEnvelopes = envelopes.filter((envelope) => batch.envelope_ids.includes(envelopeIdFor(envelope)));
|
|
12
|
+
const batchResults = await Promise.all(batchEnvelopes.map((envelope) => applyEnvelope(root, envelope, dryRun)));
|
|
13
|
+
results.push(...batchResults);
|
|
14
|
+
}
|
|
15
|
+
const blockers = [
|
|
16
|
+
...plan.conflicts.map((conflict) => `naruto_patch_conflict:${conflict.envelope_id}`),
|
|
17
|
+
...results.flatMap((result) => result.blockers)
|
|
18
|
+
];
|
|
19
|
+
return {
|
|
20
|
+
schema: 'sks.naruto-parallel-patch-apply.v1',
|
|
21
|
+
ok: blockers.length === 0,
|
|
22
|
+
dry_run: dryRun,
|
|
23
|
+
batch_count: plan.batches.length,
|
|
24
|
+
parallel_apply_count: plan.batches.filter((batch) => batch.envelope_ids.length > 1).length,
|
|
25
|
+
conflicts: plan.conflicts,
|
|
26
|
+
results,
|
|
27
|
+
blockers
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function applyEnvelope(root, envelope, dryRun) {
|
|
31
|
+
const validation = validateAgentPatchEnvelope(envelope);
|
|
32
|
+
const beforeHashes = {};
|
|
33
|
+
const afterHashes = {};
|
|
34
|
+
const rollback = [];
|
|
35
|
+
const changedFiles = [];
|
|
36
|
+
const blockers = [...validation.violations];
|
|
37
|
+
if (!validation.ok) {
|
|
38
|
+
return { envelope_id: envelopeIdFor(envelope), ok: false, changed_files: [], before_hashes: {}, after_hashes: {}, rollback: [], blockers };
|
|
39
|
+
}
|
|
40
|
+
for (const operation of envelope.operations) {
|
|
41
|
+
const target = resolvePatchPath(root, operation.path);
|
|
42
|
+
const before = await readText(target, '');
|
|
43
|
+
beforeHashes[operation.path] = sha256(String(before));
|
|
44
|
+
rollback.push({ path: operation.path, content: String(before) });
|
|
45
|
+
const after = applyOperation(String(before), operation);
|
|
46
|
+
afterHashes[operation.path] = sha256(after);
|
|
47
|
+
if (after !== before) {
|
|
48
|
+
changedFiles.push(operation.path);
|
|
49
|
+
if (!dryRun) {
|
|
50
|
+
await ensureDir(path.dirname(target));
|
|
51
|
+
await writeTextAtomic(target, after);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
envelope_id: envelopeIdFor(envelope),
|
|
57
|
+
ok: blockers.length === 0,
|
|
58
|
+
changed_files: changedFiles,
|
|
59
|
+
before_hashes: beforeHashes,
|
|
60
|
+
after_hashes: afterHashes,
|
|
61
|
+
rollback,
|
|
62
|
+
blockers
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export async function rollbackNarutoPatchResult(root, result) {
|
|
66
|
+
const restored = [];
|
|
67
|
+
const blockers = [];
|
|
68
|
+
for (const entry of result.rollback) {
|
|
69
|
+
try {
|
|
70
|
+
const target = resolvePatchPath(root, entry.path);
|
|
71
|
+
await ensureDir(path.dirname(target));
|
|
72
|
+
await writeTextAtomic(target, entry.content);
|
|
73
|
+
restored.push(entry.path);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
blockers.push(error instanceof Error ? error.message : String(error));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { ok: blockers.length === 0, restored, blockers };
|
|
80
|
+
}
|
|
81
|
+
function applyOperation(before, operation) {
|
|
82
|
+
if (operation.op === 'write')
|
|
83
|
+
return String(operation.content || '');
|
|
84
|
+
if (operation.op === 'replace')
|
|
85
|
+
return before.replace(String(operation.search || ''), String(operation.replace || ''));
|
|
86
|
+
return before;
|
|
87
|
+
}
|
|
88
|
+
function resolvePatchPath(root, relativePath) {
|
|
89
|
+
const resolved = path.resolve(root, relativePath);
|
|
90
|
+
const base = path.resolve(root);
|
|
91
|
+
if (resolved !== base && !resolved.startsWith(base + path.sep))
|
|
92
|
+
throw new Error(`patch path escapes root: ${relativePath}`);
|
|
93
|
+
return resolved;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=naruto-parallel-patch-apply.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function planNarutoPatchTransactionBatches(envelopes) {
|
|
2
|
+
const batches = [];
|
|
3
|
+
const conflicts = [];
|
|
4
|
+
for (const envelope of envelopes) {
|
|
5
|
+
const envelopeId = envelopeIdFor(envelope);
|
|
6
|
+
const paths = envelope.operations.map((operation) => operation.path);
|
|
7
|
+
let placed = false;
|
|
8
|
+
for (const batch of batches) {
|
|
9
|
+
const overlaps = paths.filter((file) => batch.write_paths.includes(file));
|
|
10
|
+
if (overlaps.length)
|
|
11
|
+
continue;
|
|
12
|
+
batch.envelope_ids.push(envelopeId);
|
|
13
|
+
batch.write_paths.push(...paths);
|
|
14
|
+
batch.write_paths = [...new Set(batch.write_paths)];
|
|
15
|
+
placed = true;
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
if (!placed) {
|
|
19
|
+
const previousConflicts = envelopes
|
|
20
|
+
.filter((candidate) => envelopeIdFor(candidate) !== envelopeId)
|
|
21
|
+
.filter((candidate) => candidate.operations.some((operation) => paths.includes(operation.path)))
|
|
22
|
+
.map(envelopeIdFor);
|
|
23
|
+
if (previousConflicts.length)
|
|
24
|
+
conflicts.push({ envelope_id: envelopeId, conflicts_with: previousConflicts, write_paths: paths });
|
|
25
|
+
batches.push({
|
|
26
|
+
batch_id: `NPB-${String(batches.length + 1).padStart(4, '0')}`,
|
|
27
|
+
envelope_ids: [envelopeId],
|
|
28
|
+
write_paths: [...new Set(paths)],
|
|
29
|
+
parallel_apply_allowed: true
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
schema: 'sks.naruto-patch-transaction-batch.v1',
|
|
35
|
+
batches,
|
|
36
|
+
conflicts
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function envelopeIdFor(envelope) {
|
|
40
|
+
return `${envelope.agent_id}:${envelope.session_id}:${envelope.task_slice_id || envelope.generation_index}`;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=naruto-patch-transaction-batch.js.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export const NARUTO_WRITE_ROLES = new Set([
|
|
2
|
+
'implementer',
|
|
3
|
+
'modifier',
|
|
4
|
+
'test_writer',
|
|
5
|
+
'conflict_resolver',
|
|
6
|
+
'rollback_planner',
|
|
7
|
+
'integrator'
|
|
8
|
+
]);
|
|
9
|
+
export const NARUTO_IMPLEMENTATION_LIKE_ROLES = new Set([
|
|
10
|
+
'implementer',
|
|
11
|
+
'modifier',
|
|
12
|
+
'test_writer',
|
|
13
|
+
'conflict_resolver'
|
|
14
|
+
]);
|
|
15
|
+
export function narutoRoleAllowsWrite(role) {
|
|
16
|
+
return NARUTO_WRITE_ROLES.has(role);
|
|
17
|
+
}
|
|
18
|
+
export function mapWorkKindToNarutoRole(kind) {
|
|
19
|
+
switch (kind) {
|
|
20
|
+
case 'implementation':
|
|
21
|
+
return 'implementer';
|
|
22
|
+
case 'code_modification':
|
|
23
|
+
case 'refactor':
|
|
24
|
+
case 'patch_rebase':
|
|
25
|
+
return 'modifier';
|
|
26
|
+
case 'test_generation':
|
|
27
|
+
return 'test_writer';
|
|
28
|
+
case 'test_execution':
|
|
29
|
+
case 'verification':
|
|
30
|
+
case 'ux_review':
|
|
31
|
+
case 'ppt_review':
|
|
32
|
+
case 'image_review':
|
|
33
|
+
return 'verifier';
|
|
34
|
+
case 'research':
|
|
35
|
+
return 'researcher';
|
|
36
|
+
case 'documentation':
|
|
37
|
+
return 'modifier';
|
|
38
|
+
case 'conflict_resolution':
|
|
39
|
+
return 'conflict_resolver';
|
|
40
|
+
case 'rollback_preparation':
|
|
41
|
+
return 'rollback_planner';
|
|
42
|
+
case 'integration_support':
|
|
43
|
+
return 'integrator';
|
|
44
|
+
case 'final_review_input_pack':
|
|
45
|
+
return 'gpt_final_arbiter';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function mapNarutoRoleToAgentRole(role) {
|
|
49
|
+
switch (role) {
|
|
50
|
+
case 'implementer':
|
|
51
|
+
case 'modifier':
|
|
52
|
+
case 'test_writer':
|
|
53
|
+
return 'implementer';
|
|
54
|
+
case 'researcher':
|
|
55
|
+
return 'research';
|
|
56
|
+
case 'conflict_resolver':
|
|
57
|
+
case 'rollback_planner':
|
|
58
|
+
case 'integrator':
|
|
59
|
+
case 'gpt_final_arbiter':
|
|
60
|
+
return 'integrator';
|
|
61
|
+
case 'verifier':
|
|
62
|
+
return 'verifier';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function buildNarutoRoleDistribution(workItems, opts = {}) {
|
|
66
|
+
const readonly = opts.readonly === true;
|
|
67
|
+
const counts = new Map();
|
|
68
|
+
const workItemRoles = workItems.map((item) => {
|
|
69
|
+
const role = mapWorkKindToNarutoRole(item.kind);
|
|
70
|
+
counts.set(role, (counts.get(role) || 0) + 1);
|
|
71
|
+
return {
|
|
72
|
+
work_item_id: item.id,
|
|
73
|
+
kind: item.kind,
|
|
74
|
+
role,
|
|
75
|
+
write_allowed: item.write_allowed && narutoRoleAllowsWrite(role)
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
const entries = [...counts.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([role, count]) => ({
|
|
79
|
+
role,
|
|
80
|
+
count,
|
|
81
|
+
write_allowed: narutoRoleAllowsWrite(role)
|
|
82
|
+
}));
|
|
83
|
+
const implementationLikeWorkers = workItemRoles.filter((row) => NARUTO_IMPLEMENTATION_LIKE_ROLES.has(row.role)).length;
|
|
84
|
+
const totalWorkers = workItems.length;
|
|
85
|
+
const implementationLikeRatio = totalWorkers ? implementationLikeWorkers / totalWorkers : 0;
|
|
86
|
+
const verifierOnly = totalWorkers > 0 && workItemRoles.every((row) => row.role === 'verifier');
|
|
87
|
+
const writeAllowedCount = workItemRoles.filter((row) => row.write_allowed).length;
|
|
88
|
+
const blockers = [
|
|
89
|
+
...(totalWorkers === 0 ? ['naruto_role_distribution_empty'] : []),
|
|
90
|
+
...(!readonly && verifierOnly ? ['naruto_default_must_not_be_verifier_only'] : []),
|
|
91
|
+
...(!readonly && implementationLikeRatio < 0.4 ? ['naruto_write_capable_route_requires_40_percent_implementation_like_roles'] : []),
|
|
92
|
+
...(!readonly && writeAllowedCount === 0 ? ['naruto_write_capable_route_requires_write_roles'] : [])
|
|
93
|
+
];
|
|
94
|
+
return {
|
|
95
|
+
schema: 'sks.naruto-role-distribution.v1',
|
|
96
|
+
readonly,
|
|
97
|
+
total_workers: totalWorkers,
|
|
98
|
+
implementation_like_workers: implementationLikeWorkers,
|
|
99
|
+
implementation_like_ratio: Math.round(implementationLikeRatio * 1000) / 1000,
|
|
100
|
+
verifier_only: verifierOnly,
|
|
101
|
+
entries,
|
|
102
|
+
work_item_roles: workItemRoles,
|
|
103
|
+
ok: blockers.length === 0,
|
|
104
|
+
blockers
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=naruto-role-policy.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { buildVerificationDag } from '../verification/verification-dag.js';
|
|
2
|
+
const NODE_KIND_CYCLE = [
|
|
3
|
+
'typecheck_shard',
|
|
4
|
+
'unit_test_shard',
|
|
5
|
+
'route_gate_shard',
|
|
6
|
+
'lint_static_scan_shard',
|
|
7
|
+
'schema_validation_shard',
|
|
8
|
+
'patch_specific_test_shard',
|
|
9
|
+
'docs_changelog_check',
|
|
10
|
+
'side_effect_check',
|
|
11
|
+
'mutation_ledger_check',
|
|
12
|
+
'zellij_proof_check',
|
|
13
|
+
'local_llm_structured_output_check'
|
|
14
|
+
];
|
|
15
|
+
export function buildNarutoVerificationDag(graph, input = {}) {
|
|
16
|
+
const command = input.command || 'node -e "process.exit(0)"';
|
|
17
|
+
const verifiableItems = graph.work_items.filter((item) => item.verification_required);
|
|
18
|
+
const taskIdByWorkItemId = new Map(verifiableItems.map((item, index) => [item.id, `NV-${String(index + 1).padStart(6, '0')}`]));
|
|
19
|
+
const tasks = verifiableItems
|
|
20
|
+
.map((item, index) => {
|
|
21
|
+
const kind = NODE_KIND_CYCLE[index % NODE_KIND_CYCLE.length] || 'route_gate_shard';
|
|
22
|
+
const taskId = taskIdByWorkItemId.get(item.id) || `NV-${String(index + 1).padStart(6, '0')}`;
|
|
23
|
+
return {
|
|
24
|
+
id: taskId,
|
|
25
|
+
command,
|
|
26
|
+
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
27
|
+
inputs: [...item.target_paths],
|
|
28
|
+
outputs: [`.sneakoscope/reports/naruto-verification/${item.id}.json`],
|
|
29
|
+
dependencies: item.dependencies.map((dep) => taskIdByWorkItemId.get(dep)).filter((dep) => typeof dep === 'string' && dep !== taskId),
|
|
30
|
+
timeout_ms: 60000,
|
|
31
|
+
read_only: true
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
const dag = buildVerificationDag(tasks);
|
|
35
|
+
return {
|
|
36
|
+
...dag,
|
|
37
|
+
naruto_schema: 'sks.naruto-verification-dag.v1',
|
|
38
|
+
node_kinds: NODE_KIND_CYCLE,
|
|
39
|
+
starts_when_dependencies_ready: true
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=naruto-verification-dag.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { runVerificationDag } from '../verification/verification-worker-pool.js';
|
|
2
|
+
export async function runNarutoVerificationPool(dag, governor, opts = {}) {
|
|
3
|
+
const safeConcurrency = Math.max(1, governor.verification_parallel);
|
|
4
|
+
const result = await runVerificationDag(dag, {
|
|
5
|
+
...(opts.cwd ? { cwd: opts.cwd } : {}),
|
|
6
|
+
...(opts.logDir ? { logDir: opts.logDir } : {}),
|
|
7
|
+
concurrency: safeConcurrency
|
|
8
|
+
});
|
|
9
|
+
return {
|
|
10
|
+
...result,
|
|
11
|
+
naruto_schema: 'sks.naruto-verification-pool.v1',
|
|
12
|
+
safe_concurrency: safeConcurrency,
|
|
13
|
+
cpu_heavy_cap_respected: safeConcurrency <= Math.max(1, governor.hardware.cpu_core_count * 2),
|
|
14
|
+
io_heavy_cap_respected: governor.backpressure !== 'saturated',
|
|
15
|
+
api_rate_cap_respected: safeConcurrency <= Math.max(1, governor.remote_codex_parallel)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=naruto-verification-pool.js.map
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { mapWorkKindToNarutoRole } from './naruto-role-policy.js';
|
|
2
|
+
import { isNarutoWriteKind, normalizeNarutoPath } from './naruto-work-item.js';
|
|
3
|
+
const WRITE_CAPABLE_KIND_CYCLE = [
|
|
4
|
+
'implementation',
|
|
5
|
+
'code_modification',
|
|
6
|
+
'test_generation',
|
|
7
|
+
'verification',
|
|
8
|
+
'research',
|
|
9
|
+
'documentation',
|
|
10
|
+
'refactor',
|
|
11
|
+
'test_execution',
|
|
12
|
+
'conflict_resolution',
|
|
13
|
+
'patch_rebase',
|
|
14
|
+
'rollback_preparation',
|
|
15
|
+
'integration_support',
|
|
16
|
+
'final_review_input_pack'
|
|
17
|
+
];
|
|
18
|
+
const READONLY_KIND_CYCLE = [
|
|
19
|
+
'verification',
|
|
20
|
+
'research',
|
|
21
|
+
'test_execution',
|
|
22
|
+
'ux_review',
|
|
23
|
+
'ppt_review',
|
|
24
|
+
'image_review',
|
|
25
|
+
'final_review_input_pack'
|
|
26
|
+
];
|
|
27
|
+
export function buildNarutoWorkGraph(input = {}) {
|
|
28
|
+
const requestedClones = normalizePositiveInt(input.requestedClones, 12);
|
|
29
|
+
const readonly = input.readonly === true;
|
|
30
|
+
const writeCapable = input.writeCapable !== false && !readonly;
|
|
31
|
+
const totalWorkItems = Math.max(requestedClones, normalizePositiveInt(input.totalWorkItems, requestedClones));
|
|
32
|
+
const kindCycle = writeCapable ? WRITE_CAPABLE_KIND_CYCLE : READONLY_KIND_CYCLE;
|
|
33
|
+
const basePath = normalizeNarutoPath(input.leaseBasePath || '.sneakoscope/naruto/patch-envelopes');
|
|
34
|
+
const targetPaths = normalizePaths(input.targetPaths || []);
|
|
35
|
+
const readonlyPaths = normalizePaths(input.readonlyPaths || []);
|
|
36
|
+
const workItems = [];
|
|
37
|
+
for (let index = 0; index < totalWorkItems; index += 1) {
|
|
38
|
+
const id = `NW-${String(index + 1).padStart(6, '0')}`;
|
|
39
|
+
const kind = kindCycle[index % kindCycle.length] || 'verification';
|
|
40
|
+
const kindWrites = writeCapable && isNarutoWriteKind(kind);
|
|
41
|
+
const selectedTarget = targetPaths.length ? targetPaths[index % targetPaths.length] || targetPaths[0] || '' : `${basePath}/${id}.json`;
|
|
42
|
+
const writePaths = kindWrites ? [selectedTarget].filter(Boolean) : [];
|
|
43
|
+
const readPaths = readonlyPaths.length ? readonlyPaths : targetPaths.filter((item) => !writePaths.includes(item));
|
|
44
|
+
const leaseRequirements = [
|
|
45
|
+
...writePaths.map((file) => ({ path: file, kind: 'write' })),
|
|
46
|
+
...readPaths.map((file) => ({ path: file, kind: 'read' }))
|
|
47
|
+
];
|
|
48
|
+
workItems.push({
|
|
49
|
+
id,
|
|
50
|
+
kind,
|
|
51
|
+
title: titleForKind(kind, id),
|
|
52
|
+
target_paths: [...new Set([...(writePaths.length ? writePaths : [selectedTarget].filter(Boolean)), ...readPaths])],
|
|
53
|
+
readonly_paths: readPaths,
|
|
54
|
+
write_paths: writePaths,
|
|
55
|
+
required_role: mapWorkKindToNarutoRole(kind),
|
|
56
|
+
write_allowed: writePaths.length > 0,
|
|
57
|
+
verification_required: kind !== 'research' && kind !== 'final_review_input_pack',
|
|
58
|
+
dependencies: dependenciesForKind(kind, workItems),
|
|
59
|
+
can_run_in_parallel_with: [],
|
|
60
|
+
conflicts_with: [],
|
|
61
|
+
estimated_cost: estimateCost(kind),
|
|
62
|
+
lease_requirements: leaseRequirements,
|
|
63
|
+
acceptance: {
|
|
64
|
+
requires_patch_envelope: writePaths.length > 0,
|
|
65
|
+
requires_verification: kind !== 'research' && kind !== 'final_review_input_pack',
|
|
66
|
+
requires_gpt_final: writePaths.length > 0 || kind === 'final_review_input_pack'
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const activeWaves = planNarutoWorkWaves(workItems, Math.max(1, normalizePositiveInt(input.maxActiveWorkers, requestedClones)));
|
|
71
|
+
annotateParallelCompatibility(workItems, activeWaves);
|
|
72
|
+
const mixedWorkKinds = [...new Set(workItems.map((item) => item.kind))];
|
|
73
|
+
const writeAllowedCount = workItems.filter((item) => item.write_allowed).length;
|
|
74
|
+
const blockers = [
|
|
75
|
+
...(mixedWorkKinds.length < 2 ? ['naruto_work_graph_not_mixed'] : []),
|
|
76
|
+
...(!readonly && writeAllowedCount === 0 ? ['naruto_write_capable_graph_missing_write_items'] : []),
|
|
77
|
+
...(workItems.length < requestedClones ? ['naruto_work_graph_below_requested_clones'] : []),
|
|
78
|
+
...activeWaves.flatMap((wave) => wave.conflict_count > 0 ? [`naruto_wave_write_conflict:${wave.wave_id}`] : [])
|
|
79
|
+
];
|
|
80
|
+
return {
|
|
81
|
+
schema: 'sks.naruto-work-graph.v1',
|
|
82
|
+
route: '$Naruto',
|
|
83
|
+
requested_clones: requestedClones,
|
|
84
|
+
total_work_items: workItems.length,
|
|
85
|
+
readonly,
|
|
86
|
+
write_capable: writeCapable,
|
|
87
|
+
work_items: workItems,
|
|
88
|
+
active_waves: activeWaves,
|
|
89
|
+
mixed_work_kinds: mixedWorkKinds,
|
|
90
|
+
write_allowed_count: writeAllowedCount,
|
|
91
|
+
ok: blockers.length === 0,
|
|
92
|
+
blockers
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function validateNarutoWorkGraph(graph) {
|
|
96
|
+
const ids = new Set();
|
|
97
|
+
const blockers = [...graph.blockers];
|
|
98
|
+
for (const item of graph.work_items) {
|
|
99
|
+
if (ids.has(item.id))
|
|
100
|
+
blockers.push(`duplicate_work_item:${item.id}`);
|
|
101
|
+
ids.add(item.id);
|
|
102
|
+
for (const dep of item.dependencies) {
|
|
103
|
+
if (!ids.has(dep) && !graph.work_items.some((candidate) => candidate.id === dep))
|
|
104
|
+
blockers.push(`missing_dependency:${item.id}:${dep}`);
|
|
105
|
+
}
|
|
106
|
+
if (item.write_allowed && item.acceptance.requires_patch_envelope !== true)
|
|
107
|
+
blockers.push(`write_item_missing_patch_envelope_acceptance:${item.id}`);
|
|
108
|
+
}
|
|
109
|
+
if (graph.total_work_items !== graph.work_items.length)
|
|
110
|
+
blockers.push('naruto_work_graph_count_mismatch');
|
|
111
|
+
if (!graph.readonly && graph.write_allowed_count === 0)
|
|
112
|
+
blockers.push('naruto_write_capable_graph_missing_write_items');
|
|
113
|
+
return { ok: blockers.length === 0, blockers: [...new Set(blockers)] };
|
|
114
|
+
}
|
|
115
|
+
export function planNarutoWorkWaves(items, maxActiveWorkers) {
|
|
116
|
+
const pending = [...items];
|
|
117
|
+
const waves = [];
|
|
118
|
+
while (pending.length) {
|
|
119
|
+
const waveItems = [];
|
|
120
|
+
const writePaths = new Set();
|
|
121
|
+
for (let index = 0; index < pending.length && waveItems.length < maxActiveWorkers;) {
|
|
122
|
+
const item = pending[index];
|
|
123
|
+
if (!item) {
|
|
124
|
+
index += 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const conflicts = item.write_paths.some((file) => writePaths.has(file));
|
|
128
|
+
if (conflicts) {
|
|
129
|
+
index += 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
waveItems.push(item);
|
|
133
|
+
for (const file of item.write_paths)
|
|
134
|
+
writePaths.add(file);
|
|
135
|
+
pending.splice(index, 1);
|
|
136
|
+
}
|
|
137
|
+
if (!waveItems.length) {
|
|
138
|
+
const item = pending.shift();
|
|
139
|
+
if (item)
|
|
140
|
+
waveItems.push(item);
|
|
141
|
+
}
|
|
142
|
+
const paths = waveItems.flatMap((item) => item.write_paths);
|
|
143
|
+
waves.push({
|
|
144
|
+
wave_id: `NWAVE-${String(waves.length + 1).padStart(4, '0')}`,
|
|
145
|
+
work_item_ids: waveItems.map((item) => item.id),
|
|
146
|
+
write_paths: [...new Set(paths)],
|
|
147
|
+
conflict_count: paths.length - new Set(paths).size
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return waves;
|
|
151
|
+
}
|
|
152
|
+
function annotateParallelCompatibility(items, waves) {
|
|
153
|
+
const byId = new Map(items.map((item) => [item.id, item]));
|
|
154
|
+
for (const wave of waves) {
|
|
155
|
+
for (const id of wave.work_item_ids) {
|
|
156
|
+
const item = byId.get(id);
|
|
157
|
+
if (!item)
|
|
158
|
+
continue;
|
|
159
|
+
item.can_run_in_parallel_with = wave.work_item_ids.filter((other) => other !== id);
|
|
160
|
+
item.conflicts_with = items
|
|
161
|
+
.filter((other) => other.id !== id && other.write_paths.some((file) => item.write_paths.includes(file)))
|
|
162
|
+
.map((other) => other.id);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function dependenciesForKind(kind, previous) {
|
|
167
|
+
if (kind === 'verification' || kind === 'test_execution') {
|
|
168
|
+
const candidate = [...previous].reverse().find((item) => item.write_allowed);
|
|
169
|
+
return candidate ? [candidate.id] : [];
|
|
170
|
+
}
|
|
171
|
+
if (kind === 'final_review_input_pack') {
|
|
172
|
+
return previous.slice(-3).map((item) => item.id);
|
|
173
|
+
}
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
function estimateCost(kind) {
|
|
177
|
+
const heavy = kind === 'implementation' || kind === 'code_modification' || kind === 'refactor' || kind === 'conflict_resolution';
|
|
178
|
+
return {
|
|
179
|
+
tokens: heavy ? 8000 : 3000,
|
|
180
|
+
latency_ms: heavy ? 90000 : 30000,
|
|
181
|
+
cpu_weight: kind === 'test_execution' || kind === 'verification' ? 2 : 1,
|
|
182
|
+
memory_mb: heavy ? 512 : 256,
|
|
183
|
+
gpu_weight: 0
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function titleForKind(kind, id) {
|
|
187
|
+
return `${id} ${kind.replace(/_/g, ' ')}`;
|
|
188
|
+
}
|
|
189
|
+
function normalizePositiveInt(value, fallback) {
|
|
190
|
+
const parsed = Number(value);
|
|
191
|
+
if (!Number.isFinite(parsed) || parsed < 1)
|
|
192
|
+
return Math.max(1, Math.floor(fallback));
|
|
193
|
+
return Math.floor(parsed);
|
|
194
|
+
}
|
|
195
|
+
function normalizePaths(paths) {
|
|
196
|
+
return [...new Set(paths.map(normalizeNarutoPath).filter(Boolean))];
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=naruto-work-graph.js.map
|