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.
- package/README.md +1 -1
- 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/cli/install-helpers.js +6 -7
- package/dist/commands/zellij-slot-column-anchor.js +3 -1
- package/dist/commands/zellij-slot-pane.js +19 -2
- package/dist/core/agents/agent-janitor.js +10 -1
- package/dist/core/agents/agent-orchestrator.js +8 -2
- package/dist/core/agents/agent-proof-evidence.js +20 -0
- package/dist/core/agents/agent-runner-ollama.js +11 -4
- package/dist/core/agents/fast-mode-policy.js +7 -5
- package/dist/core/agents/intelligent-work-graph.js +93 -14
- package/dist/core/agents/native-cli-session-swarm.js +115 -9
- package/dist/core/agents/no-subagent-scaling-policy.js +10 -1
- package/dist/core/agents/official-subagent-helper-policy.js +62 -0
- package/dist/core/codex-app.js +0 -2
- package/dist/core/codex-control/codex-task-runner.js +9 -0
- package/dist/core/commands/fast-mode-command.js +1 -1
- package/dist/core/commands/loop-command.js +86 -13
- package/dist/core/commands/naruto-command.js +34 -21
- package/dist/core/commands/team-command.js +1 -0
- package/dist/core/commands/wiki-command.js +35 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +1 -2
- package/dist/core/locks/file-lock.js +88 -0
- package/dist/core/loops/loop-artifacts.js +54 -2
- package/dist/core/loops/loop-checkpoint.js +22 -0
- package/dist/core/loops/loop-concurrency-budget.js +55 -0
- package/dist/core/loops/loop-final-arbiter-contract.js +28 -0
- package/dist/core/loops/loop-finalizer.js +55 -7
- package/dist/core/loops/loop-fixture-policy.js +58 -0
- package/dist/core/loops/loop-gate-registry.js +96 -0
- package/dist/core/loops/loop-gate-runner.js +206 -17
- package/dist/core/loops/loop-gpt-final-arbiter.js +81 -0
- package/dist/core/loops/loop-integration-merge.js +80 -0
- package/dist/core/loops/loop-interrupt-registry.js +118 -0
- package/dist/core/loops/loop-lease.js +35 -20
- package/dist/core/loops/loop-merge-strategy.js +105 -0
- package/dist/core/loops/loop-mutation-ledger.js +103 -0
- package/dist/core/loops/loop-planner.js +36 -5
- package/dist/core/loops/loop-runtime-control.js +27 -0
- package/dist/core/loops/loop-runtime.js +254 -96
- package/dist/core/loops/loop-scheduler.js +14 -5
- package/dist/core/loops/loop-side-effect-scanner.js +91 -0
- package/dist/core/loops/loop-worker-prompts.js +43 -0
- package/dist/core/loops/loop-worker-runtime.js +281 -0
- package/dist/core/loops/loop-worktree-runtime.js +92 -0
- package/dist/core/naruto/naruto-finalizer.js +7 -2
- package/dist/core/naruto/naruto-loop-mesh.js +10 -1
- package/dist/core/proof/auto-finalize.js +3 -2
- package/dist/core/proof/proof-schema.js +6 -0
- package/dist/core/proof/proof-writer.js +5 -2
- package/dist/core/proof/root-cause-policy.js +70 -0
- package/dist/core/proof/route-adapter.js +18 -1
- package/dist/core/proof/route-finalizer.js +71 -6
- package/dist/core/proof/route-proof-gate.js +4 -0
- package/dist/core/release/release-gate-batch-runner.js +56 -10
- package/dist/core/release/release-gate-cache-v2.js +18 -3
- package/dist/core/release/release-gate-dag.js +121 -18
- package/dist/core/release/release-gate-node.js +2 -1
- package/dist/core/release/release-gate-resource-governor.js +27 -6
- package/dist/core/skills/core-skill-meta-update.js +24 -0
- package/dist/core/skills/core-skill-reflection.js +94 -0
- package/dist/core/skills/core-skill-trainer.js +103 -0
- package/dist/core/trust-kernel/completion-contract.js +4 -0
- package/dist/core/trust-kernel/route-contract.js +4 -1
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-right-column-manager.js +13 -2
- package/dist/core/zellij/zellij-slot-column-anchor.js +40 -3
- package/dist/core/zellij/zellij-slot-pane-renderer.js +36 -11
- package/dist/core/zellij/zellij-slot-telemetry.js +96 -44
- package/dist/core/zellij/zellij-worker-pane-manager.js +42 -4
- package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +14 -2
- package/dist/scripts/loop-directive-check-lib.js +225 -2
- package/dist/scripts/loop-hardening-check-lib.js +289 -0
- package/dist/scripts/loop-worker-fixture-child.js +53 -0
- package/dist/scripts/naruto-real-local-gpt-final-smoke.js +10 -1
- package/dist/scripts/prepublish-release-check-or-fast.js +38 -10
- package/dist/scripts/release-check-stamp.js +29 -4
- package/dist/scripts/release-gate-existence-audit.js +1 -0
- package/package.json +32 -2
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **3.1.
|
|
38
|
+
SKS **3.1.2** hardens Loop Mesh for production use: fixture workers/gates/final arbiters are test-only, `gpt:final-arbiter` is explicitly finalizer-owned, integration merge uses a strategy ladder, side-effect reports come from loop diffs and mutation ledgers, loop kill interrupts active worker sessions, and global loop concurrency budgets prevent worker/model-call oversubscription. It remains Codex 0.139-aware while bundling @openai/codex-sdk 0.138.0 at this release boundary; see [docs/codex-0.139-compat.md](docs/codex-0.139-compat.md), [docs/codex-0.139-real-probes.md](docs/codex-0.139-real-probes.md), [docs/loop-fixture-policy.md](docs/loop-fixture-policy.md), and [docs/loop-merge-strategy.md](docs/loop-merge-strategy.md).
|
|
39
39
|
|
|
40
40
|
SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
|
|
41
41
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 3.1.
|
|
7
|
+
Some("--version") => println!("sks-rs 3.1.2"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build-stamp.v1",
|
|
3
3
|
"package_name": "sneakoscope",
|
|
4
|
-
"package_version": "3.1.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
4
|
+
"package_version": "3.1.2",
|
|
5
|
+
"source_digest": "0312e2e27bae702aee9d6c07009b9492e43274c3d26e3b63a5140196997fea39",
|
|
6
|
+
"source_file_count": 2448,
|
|
7
|
+
"built_at_source_time": 1781367909038
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
|
@@ -1515,7 +1515,7 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1515
1515
|
next = upsertTopLevelTomlStringIfAbsent(next, 'model', 'gpt-5.5');
|
|
1516
1516
|
next = opts.forceFastMode === true
|
|
1517
1517
|
? upsertTopLevelTomlString(next, 'service_tier', 'fast')
|
|
1518
|
-
:
|
|
1518
|
+
: next;
|
|
1519
1519
|
// Codex App feature flags / fast-mode UI / suppress-warning are SET-IF-ABSENT: a fresh
|
|
1520
1520
|
// config still gets SKS's defaults, but SKS NEVER overrides (re-enables) a feature the
|
|
1521
1521
|
// user disabled in the App, and never rejects-then-hides UI by forcing an unrecognized
|
|
@@ -1536,11 +1536,10 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1536
1536
|
else {
|
|
1537
1537
|
next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'visible = true');
|
|
1538
1538
|
next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'enabled = true');
|
|
1539
|
-
next =
|
|
1539
|
+
next = removeTomlTableKey(next, 'user.fast_mode', 'default_profile');
|
|
1540
1540
|
}
|
|
1541
|
-
// Keep ONLY the sks-fast-high config-profile table
|
|
1542
|
-
//
|
|
1543
|
-
// codex-app:ui-preservation gate still expect it. The other SKS config profiles are
|
|
1541
|
+
// Keep ONLY the sks-fast-high config-profile table for explicit fast-mode opt-in
|
|
1542
|
+
// and CLI `--profile` use. The other SKS config profiles are
|
|
1544
1543
|
// no longer written as `[profiles.sks-*]` tables here (Codex 0.134+ deprecates them);
|
|
1545
1544
|
// they are managed as per-file `<name>.config.toml` overlays by
|
|
1546
1545
|
// migrateSksProfilesToPerFile (src/core/auto-review.ts), which also writes the
|
|
@@ -2953,8 +2952,8 @@ export async function selftestCodexLb(tmp) {
|
|
|
2953
2952
|
});
|
|
2954
2953
|
if (brokenChain.ok || brokenChain.status !== 'previous_response_not_found' || brokenChain.chain_unhealthy !== true)
|
|
2955
2954
|
throw new Error('selftest: codex-lb response chain health check did not detect previous_response_not_found');
|
|
2956
|
-
if (!/^model = "gpt-5\.5"/m.test(codexLbConfig) || !codexLbConfig.includes('
|
|
2957
|
-
throw new Error('selftest: codex-lb setup did not preserve Codex App feature flags, default plugins, profile-scoped reasoning effort, Fast
|
|
2955
|
+
if (!/^model = "gpt-5\.5"/m.test(codexLbConfig) || !codexLbConfig.includes('hooks = true') || hasDeprecatedCodexHooksFeatureFlag(codexLbConfig) || !codexLbConfig.includes('remote_control = true') || !codexLbConfig.includes('multi_agent = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('codex_git_commit = true') || !codexLbConfig.includes('computer_use = true') || !codexLbConfig.includes('browser_use = true') || !codexLbConfig.includes('browser_use_external = true') || !codexLbConfig.includes('guardian_approval = true') || !codexLbConfig.includes('tool_suggest = true') || !codexLbConfig.includes('apps = true') || !codexLbConfig.includes('plugins = true') || !codexLbConfig.includes('[plugins."latex@openai-bundled"]') || !codexLbConfig.includes('[plugins."documents@openai-primary-runtime"]') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !/\[profiles\.custom\][\s\S]*?model_reasoning_effort = "low"/.test(codexLbConfig) || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig))
|
|
2956
|
+
throw new Error('selftest: codex-lb setup did not preserve Codex App feature flags, default plugins, profile-scoped reasoning effort, explicit Fast profile, Codex Git commit generation, force GPT-5.5, or migrate the hooks feature flag');
|
|
2958
2957
|
if (!hasCodexUnstableFeatureWarningSuppression(codexLbConfig))
|
|
2959
2958
|
throw new Error('selftest: codex-lb setup did not suppress Codex unstable feature warning');
|
|
2960
2959
|
const codexLbLaunch = `source ${path.join(tmp, '.codex', 'sks-codex-lb.env')} && codex '--model' 'gpt-5.5'`;
|
|
@@ -7,7 +7,9 @@ export async function run(_command = 'zellij-slot-column-anchor', args = []) {
|
|
|
7
7
|
const intervalMs = Math.max(250, Number(readOption(args, '--interval-ms', '1000') || 1000));
|
|
8
8
|
for (;;) {
|
|
9
9
|
const text = await renderZellijSlotColumnAnchorFromArtifacts({ artifactRoot, missionId, mode });
|
|
10
|
-
|
|
10
|
+
// Cursor-home + clear-to-end redraw; `\x1Bc` (RIS) resets the pane's
|
|
11
|
+
// scrollback/modes every tick and intermittently breaks scrolling.
|
|
12
|
+
process.stdout.write('\x1b[H' + text + '\n\x1b[0J');
|
|
11
13
|
if (!watch)
|
|
12
14
|
break;
|
|
13
15
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderZellijSlotPaneFromArtifacts, renderZellijSlotPaneStatusFromArtifacts } from '../core/zellij/zellij-slot-pane-renderer.js';
|
|
1
|
+
import { renderZellijSlotPaneFromArtifacts, renderZellijSlotPaneStatusFromArtifacts, resolveZellijSlotPaneExit } from '../core/zellij/zellij-slot-pane-renderer.js';
|
|
2
2
|
export async function run(_command = 'zellij-slot-pane', args = []) {
|
|
3
3
|
const artifactDir = readOption(args, '--artifact-dir', process.cwd()) || process.cwd();
|
|
4
4
|
const artifactRoot = readOption(args, '--artifact-root', artifactDir) || artifactDir;
|
|
@@ -18,9 +18,19 @@ export async function run(_command = 'zellij-slot-pane', args = []) {
|
|
|
18
18
|
}
|
|
19
19
|
for (;;) {
|
|
20
20
|
const text = await renderZellijSlotPaneFromArtifacts({ artifactDir, artifactRoot, missionId, slotId, generationIndex, backend, role, mode });
|
|
21
|
-
process.stdout.write(
|
|
21
|
+
process.stdout.write(redrawFrame(text));
|
|
22
22
|
if (!watch)
|
|
23
23
|
break;
|
|
24
|
+
// Root-cause-3 fix: exit the pane once the worker has reached a terminal state and written its
|
|
25
|
+
// result, so the pane closes (or shows the final exited frame) instead of looping forever and
|
|
26
|
+
// perpetually re-reporting telemetry staleness.
|
|
27
|
+
const shouldExit = await resolveZellijSlotPaneExit({ artifactDir, artifactRoot, missionId, slotId, generationIndex }).catch(() => false);
|
|
28
|
+
if (shouldExit) {
|
|
29
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
30
|
+
const finalText = await renderZellijSlotPaneFromArtifacts({ artifactDir, artifactRoot, missionId, slotId, generationIndex, backend, role, mode });
|
|
31
|
+
process.stdout.write(redrawFrame(finalText));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
24
34
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
25
35
|
}
|
|
26
36
|
}
|
|
@@ -28,6 +38,13 @@ function readOption(args, name, fallback) {
|
|
|
28
38
|
const index = args.indexOf(name);
|
|
29
39
|
return index >= 0 && args[index + 1] ? String(args[index + 1]) : fallback;
|
|
30
40
|
}
|
|
41
|
+
// Redraw in place with cursor-home + clear-to-end instead of `\x1Bc` (RIS).
|
|
42
|
+
// RIS performs a full terminal reset every tick, which wipes the Zellij pane's
|
|
43
|
+
// scrollback and resets scroll position/modes — the cause of intermittent
|
|
44
|
+
// "scrolling stops working" while a watch pane is refreshing.
|
|
45
|
+
function redrawFrame(text) {
|
|
46
|
+
return '\x1b[H' + text + '\n\x1b[0J';
|
|
47
|
+
}
|
|
31
48
|
function hasFlag(args, flag) {
|
|
32
49
|
return args.includes(flag);
|
|
33
50
|
}
|
|
@@ -164,7 +164,16 @@ async function listFiles(dir) {
|
|
|
164
164
|
const out = [];
|
|
165
165
|
if (!(await exists(dir)))
|
|
166
166
|
return out;
|
|
167
|
-
|
|
167
|
+
let entries;
|
|
168
|
+
try {
|
|
169
|
+
entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR')
|
|
173
|
+
return out;
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
for (const entry of entries) {
|
|
168
177
|
const file = path.join(dir, entry.name);
|
|
169
178
|
if (entry.isDirectory())
|
|
170
179
|
out.push(...await listFiles(file));
|
|
@@ -49,6 +49,7 @@ import { applyFastModeToRoster, resolveFastModePolicy, writeFastModePropagationP
|
|
|
49
49
|
import { createNativeCliSessionSwarmRecorder } from './native-cli-session-swarm.js';
|
|
50
50
|
import { writeNativeCliSessionProof } from './native-cli-session-proof.js';
|
|
51
51
|
import { writeNoSubagentScalingPolicy } from './no-subagent-scaling-policy.js';
|
|
52
|
+
import { writeOfficialSubagentHelperPolicy } from './official-subagent-helper-policy.js';
|
|
52
53
|
import { runCodexTask } from '../codex-control/codex-control-plane.js';
|
|
53
54
|
import { CODEX_AGENT_WORKER_RESULT_SCHEMA_ID, codexAgentWorkerResultSchema } from '../codex-control/schemas/agent-worker-result.schema.js';
|
|
54
55
|
import { resolveLocalCollaborationPolicy, localCollaborationParticipated } from '../local-llm/local-collaboration-policy.js';
|
|
@@ -255,7 +256,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
255
256
|
total_work_items: partition.task_graph?.total_work_items || partition.slices.length,
|
|
256
257
|
service_tier: fastModePolicy.service_tier,
|
|
257
258
|
fast_mode: fastModePolicy.fast_mode,
|
|
258
|
-
fast_mode_default:
|
|
259
|
+
fast_mode_default: fastModePolicy.default_fast_mode,
|
|
259
260
|
backpressure: 'dynamic scheduler maintains target active slots until the work queue drains',
|
|
260
261
|
rate_limit_delay_ms: backend === 'codex-sdk' ? 250 : 0,
|
|
261
262
|
resource_pressure_warnings: roster.agent_count > roster.concurrency ? ['agents_exceed_concurrency_batches'] : []
|
|
@@ -477,6 +478,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
477
478
|
visiblePanes: visualLaneCount,
|
|
478
479
|
expectedWorkerRuntimeMs: targetActiveSlots >= 10 ? 8000 : targetActiveSlots >= 2 ? 2000 : 25,
|
|
479
480
|
minActiveWorkers: Math.min(targetActiveSlots, desiredWorkItemCount),
|
|
481
|
+
...(backend === 'codex-sdk' && opts.real === true ? { minSpeedupRatio: 3 } : {}),
|
|
480
482
|
proofMode: opts.mock === true ? 'mock-process' : 'production',
|
|
481
483
|
requireWorkerPids: opts.nativeCliSwarm !== false && targetActiveSlots >= 16
|
|
482
484
|
});
|
|
@@ -486,7 +488,8 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
486
488
|
targetActiveSlots,
|
|
487
489
|
totalWorkItems: partition.task_graph?.total_work_items || partition.slices.length
|
|
488
490
|
});
|
|
489
|
-
const
|
|
491
|
+
const officialSubagentHelperPolicy = await writeOfficialSubagentHelperPolicy(ledgerRoot, { nativeProof: nativeCliSessionProof });
|
|
492
|
+
const noSubagentScalingPolicy = await writeNoSubagentScalingPolicy(ledgerRoot, { nativeProof: nativeCliSessionProof, officialSubagentHelperPolicy });
|
|
490
493
|
const fastModePropagation = await writeFastModePropagationProof(ledgerRoot, { policy: fastModePolicy, backend, results });
|
|
491
494
|
const localCollaborationPolicy = resolveLocalCollaborationPolicy();
|
|
492
495
|
await writeJsonAtomic(path.join(ledgerRoot, 'local-collaboration-policy.json'), localCollaborationPolicy);
|
|
@@ -559,6 +562,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
559
562
|
...(timeoutKill.killed_sessions || []).map((id) => 'timeout_killed:' + id),
|
|
560
563
|
...(recursion.ok ? [] : recursion.violations.map((id) => 'recursion:' + id)),
|
|
561
564
|
...(nativeCliSessionProof.ok ? [] : nativeCliSessionProof.blockers),
|
|
565
|
+
...(officialSubagentHelperPolicy.ok ? [] : officialSubagentHelperPolicy.blockers),
|
|
562
566
|
...(noSubagentScalingPolicy.ok ? [] : noSubagentScalingPolicy.blockers),
|
|
563
567
|
...(fastModePropagation.ok ? [] : fastModePropagation.blockers),
|
|
564
568
|
...(gitWorktreeRuntime.required === true && gitWorktreeRuntime.ok === false ? gitWorktreeRuntime.blockers || ['git_worktree_runtime_not_ok'] : []),
|
|
@@ -596,6 +600,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
596
600
|
strategyGate,
|
|
597
601
|
nativeCliSessionProof,
|
|
598
602
|
noSubagentScalingPolicy,
|
|
603
|
+
officialSubagentHelperPolicy,
|
|
599
604
|
fastModePolicy,
|
|
600
605
|
fastModePropagation,
|
|
601
606
|
gitWorktreeRuntime,
|
|
@@ -641,6 +646,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
641
646
|
parallel_write_policy: parallelWritePolicy,
|
|
642
647
|
native_cli_session_proof: nativeCliSessionProof,
|
|
643
648
|
no_subagent_scaling_policy: noSubagentScalingPolicy,
|
|
649
|
+
official_subagent_helper_policy: officialSubagentHelperPolicy,
|
|
644
650
|
fast_mode_policy: fastModePolicy,
|
|
645
651
|
fast_mode_propagation: fastModePropagation,
|
|
646
652
|
git_worktree_runtime: gitWorktreeRuntime,
|
|
@@ -51,6 +51,7 @@ export async function writeAgentProofEvidence(root, input) {
|
|
|
51
51
|
});
|
|
52
52
|
const nativeCliSessionProof = input.nativeCliSessionProof || await readJson(path.join(root, 'native-cli-session-proof.json'), null);
|
|
53
53
|
const noSubagentScalingPolicy = input.noSubagentScalingPolicy || await readJson(path.join(root, 'no-subagent-scaling-policy.json'), null);
|
|
54
|
+
const officialSubagentHelperPolicy = input.officialSubagentHelperPolicy || await readJson(path.join(root, 'official-subagent-helper-policy.json'), null);
|
|
54
55
|
const fastModePropagation = input.fastModePropagation || await readJson(path.join(root, 'fast-mode-propagation-proof.json'), null);
|
|
55
56
|
const zellijPaneProof = await readJson(path.join(root, 'zellij-pane-proof.json'), null);
|
|
56
57
|
const cleanupProof = await readJson(path.join(root, 'agent-cleanup-proof.json'), null);
|
|
@@ -179,6 +180,7 @@ export async function writeAgentProofEvidence(root, input) {
|
|
|
179
180
|
...(input.results || []).flatMap((result) => result.blockers || []),
|
|
180
181
|
...(nativeCliSessionProof?.ok === false ? nativeCliSessionProof.blockers || ['native_cli_session_proof_not_ok'] : []),
|
|
181
182
|
...(noSubagentScalingPolicy?.ok === false ? noSubagentScalingPolicy.blockers || ['no_subagent_scaling_policy_not_ok'] : []),
|
|
183
|
+
...(officialSubagentHelperPolicy?.ok === false ? officialSubagentHelperPolicy.blockers || ['official_subagent_helper_policy_not_ok'] : []),
|
|
182
184
|
...(fastModePropagation?.ok === false ? fastModePropagation.blockers || ['fast_mode_propagation_not_ok'] : []),
|
|
183
185
|
...(patchSwarm?.ok === false ? patchSwarm.blockers || ['patch_swarm_not_ok'] : []),
|
|
184
186
|
...(gitWorktreeRuntime?.required === true && gitWorktreeRuntime?.ok === false ? gitWorktreeRuntime.blockers || ['git_worktree_runtime_not_ok'] : []),
|
|
@@ -225,6 +227,23 @@ export async function writeAgentProofEvidence(root, input) {
|
|
|
225
227
|
native_cli_session_proof: nativeCliSessionProof ? 'native-cli-session-proof.json' : null,
|
|
226
228
|
native_cli_session_swarm: nativeCliSessionProof ? 'agent-native-cli-session-swarm.json' : null,
|
|
227
229
|
no_subagent_scaling_policy: noSubagentScalingPolicy ? 'no-subagent-scaling-policy.json' : null,
|
|
230
|
+
official_subagent_helper_policy: officialSubagentHelperPolicy ? 'official-subagent-helper-policy.json' : null,
|
|
231
|
+
official_subagent_helper_lane_enabled: officialSubagentHelperPolicy?.official_codex_subagent_helper_lane_enabled === true,
|
|
232
|
+
official_subagent_helper_lane_ok: officialSubagentHelperPolicy?.ok === true,
|
|
233
|
+
official_subagent_helper_worker_capacity_credit: Number(officialSubagentHelperPolicy?.worker_capacity_credit || 0),
|
|
234
|
+
official_subagent_helper_observed_subagent_event_count: Number(officialSubagentHelperPolicy?.observed_subagent_event_count || 0),
|
|
235
|
+
official_subagent_helper_events_counted_as_worker_sessions: officialSubagentHelperPolicy?.subagent_events_counted_as_worker_sessions === true,
|
|
236
|
+
official_subagent_helper_capacity_source: officialSubagentHelperPolicy?.worker_capacity_source || null,
|
|
237
|
+
official_subagent_helper_parallel_allowed: officialSubagentHelperPolicy?.helper_lane_may_run_in_parallel_with_native_workers === true,
|
|
238
|
+
official_subagent_helper_capabilities: officialSubagentHelperPolicy?.codex_app_capabilities_allowed || [],
|
|
239
|
+
official_subagent_helper_builtin_agents: officialSubagentHelperPolicy?.built_in_agents_allowed || [],
|
|
240
|
+
official_subagent_helper_required_output_proof_for_generated_images: officialSubagentHelperPolicy?.required_output_proof_for_generated_images || [],
|
|
241
|
+
codex_builtin_imagegen_helper_allowed: officialSubagentHelperPolicy?.codex_builtin_imagegen_helper_allowed === true,
|
|
242
|
+
codex_builtin_imagegen_required_surface: officialSubagentHelperPolicy?.preferred_image_generation_surface || null,
|
|
243
|
+
codex_builtin_imagegen_evidence_class: officialSubagentHelperPolicy?.codex_app_builtin_evidence_class || null,
|
|
244
|
+
imagegen_api_fallback_evidence_class: officialSubagentHelperPolicy?.api_fallback_evidence_class || null,
|
|
245
|
+
imagegen_provider_surface_evidence_required: officialSubagentHelperPolicy?.provider_surface_evidence_required === true,
|
|
246
|
+
imagegen_api_fallback_counts_as_codex_app_evidence: officialSubagentHelperPolicy?.imagegen_api_fallback_counts_as_codex_app_evidence === true,
|
|
228
247
|
fast_mode_policy: input.fastModePolicy || null,
|
|
229
248
|
fast_mode_propagation: fastModePropagation ? 'fast-mode-propagation-proof.json' : null,
|
|
230
249
|
native_cli_worker_process_count: Number(nativeCliSessionProof?.spawned_worker_process_count || 0),
|
|
@@ -403,6 +422,7 @@ export async function writeAgentProofEvidence(root, input) {
|
|
|
403
422
|
'gpt-final-arbiter/gpt-final-arbiter.json': gptFinalArbiter,
|
|
404
423
|
'native-cli-session-proof.json': nativeCliSessionProof,
|
|
405
424
|
'no-subagent-scaling-policy.json': noSubagentScalingPolicy,
|
|
425
|
+
'official-subagent-helper-policy.json': officialSubagentHelperPolicy,
|
|
406
426
|
'fast-mode-propagation-proof.json': fastModePropagation
|
|
407
427
|
}
|
|
408
428
|
});
|
|
@@ -132,19 +132,25 @@ export function classifyOllamaWorkerSlice(slice, input = {}) {
|
|
|
132
132
|
input.agent?.persona_id,
|
|
133
133
|
slice?.role,
|
|
134
134
|
slice?.domain,
|
|
135
|
+
slice?.kind,
|
|
135
136
|
slice?.title,
|
|
136
137
|
slice?.description,
|
|
137
138
|
...(Array.isArray(slice?.target_paths) ? slice.target_paths : [])
|
|
138
139
|
].map((value) => String(value || '')).join('\n');
|
|
139
|
-
const bannedRole = /(?:^|\b)(architect|verifier|safety|integrator|schema|release|ux|db)(?:\b|$)/i.test(String(input.agent?.role || slice?.role || ''));
|
|
140
|
+
const bannedRole = /(?:^|\b)(architect|verifier|checker|reviewer|researcher|safety|integrator|schema|release|ux|db)(?:\b|$)/i.test(String(input.agent?.role || slice?.role || ''));
|
|
140
141
|
const collection = /\b(collect|gather|extract|inventory|list|scan|grep|tail|summarize|catalog)\b|수집|추출|목록|스캔|인벤토리/i.test(text);
|
|
141
142
|
const coding = /\b(code|implement|patch|write|edit|fix|mechanical|simple)\b|코드|작성|수정|구현|패치|단순/i.test(text);
|
|
142
|
-
const banned = /\b(strategy|strategize|planning|plan|architecture|architect|design|review|verify|verification|safety|risk|consensus|debate|orchestrate|policy|decide|decision|migration|database|schema)\b
|
|
143
|
-
|
|
143
|
+
const banned = /\b(strategy|strategize|planning|plan|architecture|architect|design|review|verify|verification|audit|inspect|safety|risk|consensus|debate|orchestrate|policy|decide|decision|migration|database|schema)\b|전략|기획|설계|디자인|검증|검수|리뷰|감사|안전|위험|합의|토론|결정|마이그레이션|데이터베이스/i.test(text);
|
|
144
|
+
// Web research / external lookup must run on GPT, never on the local model:
|
|
145
|
+
// local LLMs hallucinate sources and cannot browse. This wins even over the
|
|
146
|
+
// collection allowlist (e.g. "web research and collect docs" stays on GPT).
|
|
147
|
+
const research = /\b(web|research|browse|browser|crawl|fetch docs|websearch|web search|search the web|investigate|context7)\b|웹|리서치|조사|웹서치|웹 ?검색|검색/i.test(text);
|
|
148
|
+
const allowed = !bannedRole && !banned && !research && (writePaths.length > 0 || collection || coding);
|
|
144
149
|
const blockers = [
|
|
145
150
|
...(allowed ? [] : ['ollama_worker_task_not_simple_code_or_collection']),
|
|
146
151
|
...(bannedRole ? ['ollama_worker_role_blocked'] : []),
|
|
147
|
-
...(banned ? ['ollama_worker_strategy_planning_design_blocked'] : [])
|
|
152
|
+
...(banned ? ['ollama_worker_strategy_planning_design_blocked'] : []),
|
|
153
|
+
...(research ? ['ollama_worker_web_research_blocked'] : [])
|
|
148
154
|
];
|
|
149
155
|
return {
|
|
150
156
|
schema: OLLAMA_WORKER_POLICY_SCHEMA,
|
|
@@ -156,6 +162,7 @@ export function classifyOllamaWorkerSlice(slice, input = {}) {
|
|
|
156
162
|
write_path_count: writePaths.length,
|
|
157
163
|
collection_detected: collection,
|
|
158
164
|
coding_detected: coding,
|
|
165
|
+
research_detected: research,
|
|
159
166
|
blockers
|
|
160
167
|
};
|
|
161
168
|
}
|
|
@@ -18,15 +18,17 @@ export function resolveFastModePolicy(input = {}) {
|
|
|
18
18
|
? 'standard'
|
|
19
19
|
: preference?.mode === 'standard'
|
|
20
20
|
? 'standard'
|
|
21
|
-
: 'fast'
|
|
21
|
+
: explicitFast || explicitTier === 'fast' || preference?.mode === 'fast'
|
|
22
|
+
? 'fast'
|
|
23
|
+
: 'standard';
|
|
22
24
|
return {
|
|
23
25
|
schema: FAST_MODE_POLICY_SCHEMA,
|
|
24
26
|
generated_at: nowIso(),
|
|
25
27
|
fast_mode: serviceTier === 'fast',
|
|
26
28
|
service_tier: serviceTier,
|
|
27
29
|
codex_desktop_service_tier: codexDesktopServiceTier(serviceTier),
|
|
28
|
-
default_fast_mode:
|
|
29
|
-
disabled_by: explicitNoFast ? 'no-fast' : explicitTier === 'standard' ? 'service-tier-standard' : preference?.mode === 'standard' ? 'preference-standard' : 'none',
|
|
30
|
+
default_fast_mode: false,
|
|
31
|
+
disabled_by: explicitNoFast ? 'no-fast' : explicitTier === 'standard' ? 'service-tier-standard' : preference?.mode === 'standard' ? 'preference-standard' : serviceTier === 'standard' ? 'default-standard' : 'none',
|
|
30
32
|
explicit_fast: explicitFast,
|
|
31
33
|
explicit_no_fast: explicitNoFast,
|
|
32
34
|
explicit_service_tier: explicitTier,
|
|
@@ -125,7 +127,7 @@ export async function writeFastModePropagationProof(root, input = { policy: reso
|
|
|
125
127
|
const agentProcessReports = await collectNamedJson(root, 'agent-process-report.json');
|
|
126
128
|
const zellijReports = await collectNamedJson(root, 'agent-zellij-report.json');
|
|
127
129
|
const madReports = await collectNamedJson(root, 'mad-sks-worker-report.json');
|
|
128
|
-
const defaultFastExpected = input.policy.
|
|
130
|
+
const defaultFastExpected = input.policy.fast_mode === true;
|
|
129
131
|
const childReports = [...workerFastReports, ...workerProcessReports, ...agentProcessReports, ...zellijReports, ...madReports];
|
|
130
132
|
const missingFast = defaultFastExpected
|
|
131
133
|
? childReports.filter((row) => row.json?.fast_mode !== true && row.json?.fast_mode !== 'true')
|
|
@@ -144,7 +146,7 @@ export async function writeFastModePropagationProof(root, input = { policy: reso
|
|
|
144
146
|
ok: missingFast.length === 0 && missingTier.length === 0 && workerMissing.length === 0 && missingCliOverride.length === 0,
|
|
145
147
|
policy: input.policy,
|
|
146
148
|
backend: input.backend || null,
|
|
147
|
-
default_fast_mode:
|
|
149
|
+
default_fast_mode: input.policy.default_fast_mode,
|
|
148
150
|
service_tier: input.policy.service_tier,
|
|
149
151
|
fast_mode: input.policy.fast_mode,
|
|
150
152
|
worker_fast_report_count: workerFastReports.length,
|
|
@@ -112,6 +112,12 @@ export function enhanceTaskGraphWithIntelligence(taskGraph, graph) {
|
|
|
112
112
|
}
|
|
113
113
|
export async function writeIntelligentWorkGraphArtifacts(root, graph) {
|
|
114
114
|
const compact = compactIntelligentWorkGraph(graph);
|
|
115
|
+
const symbolOwnership = compactSymbolOwnershipMap(graph);
|
|
116
|
+
const routeOwnership = limitRecordOfArrays(graph.route_to_module_ownership, graphRecordLimit(), graphArrayLimit());
|
|
117
|
+
const commandOwnership = limitRecordOfArrays(graph.command_to_module_ownership, graphRecordLimit(), graphArrayLimit());
|
|
118
|
+
const testOwnership = compactTestOwnershipMap(graph.test_ownership_map);
|
|
119
|
+
const criticalPath = compactCriticalPath(graph.critical_path);
|
|
120
|
+
const integrationBottlenecks = compactIntegrationBottlenecks(graph.integration_bottlenecks);
|
|
115
121
|
await writeJsonAtomic(path.join(root, 'agent-intelligent-work-graph.json'), compact);
|
|
116
122
|
await writeJsonAtomic(path.join(root, 'agent-intelligent-work-graph-v2.json'), compact);
|
|
117
123
|
await writeJsonAtomic(path.join(root, 'agent-symbol-ownership-map.json'), {
|
|
@@ -119,23 +125,19 @@ export async function writeIntelligentWorkGraphArtifacts(root, graph) {
|
|
|
119
125
|
generated_at: graph.generated_at,
|
|
120
126
|
ok: Object.keys(graph.symbol_to_files).length > 0,
|
|
121
127
|
ast_coverage: graph.ast_coverage,
|
|
122
|
-
|
|
123
|
-
symbol_to_files: graph.symbol_to_files,
|
|
124
|
-
exported_symbols: graph.exported_symbols,
|
|
125
|
-
imported_symbols: graph.imported_symbols,
|
|
126
|
-
exported_api_ownership: graph.exported_api_ownership
|
|
128
|
+
...symbolOwnership
|
|
127
129
|
});
|
|
128
130
|
await writeJsonAtomic(path.join(root, 'agent-route-ownership-map.json'), {
|
|
129
131
|
schema: 'sks.agent-route-ownership-map.v1',
|
|
130
132
|
generated_at: graph.generated_at,
|
|
131
133
|
ok: Object.keys(graph.route_to_module_ownership).length > 0,
|
|
132
|
-
route_to_module_ownership:
|
|
134
|
+
route_to_module_ownership: routeOwnership
|
|
133
135
|
});
|
|
134
136
|
await writeJsonAtomic(path.join(root, 'agent-command-ownership-map.json'), {
|
|
135
137
|
schema: 'sks.agent-command-ownership-map.v1',
|
|
136
138
|
generated_at: graph.generated_at,
|
|
137
139
|
ok: Object.keys(graph.command_to_module_ownership).length > 0,
|
|
138
|
-
command_to_module_ownership:
|
|
140
|
+
command_to_module_ownership: commandOwnership
|
|
139
141
|
});
|
|
140
142
|
await writeJsonAtomic(path.join(root, 'agent-test-ownership-map.json'), {
|
|
141
143
|
schema: 'sks.agent-test-ownership-map.v1',
|
|
@@ -143,7 +145,7 @@ export async function writeIntelligentWorkGraphArtifacts(root, graph) {
|
|
|
143
145
|
ok: graph.test_ownership_map.unmapped_sources.length < Math.max(10, graph.source_inventory_count),
|
|
144
146
|
ast_coverage: graph.ast_coverage,
|
|
145
147
|
test_ownership_confidence: graph.test_ownership_confidence,
|
|
146
|
-
...
|
|
148
|
+
...testOwnership
|
|
147
149
|
});
|
|
148
150
|
await writeJsonAtomic(path.join(root, 'agent-source-test-ownership-v2.json'), {
|
|
149
151
|
schema: 'sks.agent-source-test-ownership.v2',
|
|
@@ -151,14 +153,14 @@ export async function writeIntelligentWorkGraphArtifacts(root, graph) {
|
|
|
151
153
|
ok: graph.test_ownership_confidence > 0,
|
|
152
154
|
ast_coverage: graph.ast_coverage,
|
|
153
155
|
test_ownership_confidence: graph.test_ownership_confidence,
|
|
154
|
-
source_to_test_relations:
|
|
155
|
-
...
|
|
156
|
+
source_to_test_relations: testOwnership.relations,
|
|
157
|
+
...testOwnership
|
|
156
158
|
});
|
|
157
159
|
await writeJsonAtomic(path.join(root, 'agent-critical-path.json'), {
|
|
158
160
|
schema: 'sks.agent-critical-path.v1',
|
|
159
161
|
generated_at: graph.generated_at,
|
|
160
162
|
ok: graph.critical_path.path.length > 0,
|
|
161
|
-
...
|
|
163
|
+
...criticalPath
|
|
162
164
|
});
|
|
163
165
|
await writeJsonAtomic(path.join(root, 'agent-critical-path-v2.json'), {
|
|
164
166
|
schema: 'sks.agent-critical-path.v2',
|
|
@@ -166,21 +168,21 @@ export async function writeIntelligentWorkGraphArtifacts(root, graph) {
|
|
|
166
168
|
ok: graph.critical_path.path.length > 0,
|
|
167
169
|
ast_coverage: graph.ast_coverage,
|
|
168
170
|
critical_path_confidence: graph.critical_path.confidence || graph.test_ownership_confidence,
|
|
169
|
-
...
|
|
171
|
+
...criticalPath
|
|
170
172
|
});
|
|
171
173
|
await writeJsonAtomic(path.join(root, 'agent-integration-bottlenecks.json'), {
|
|
172
174
|
schema: 'sks.agent-integration-bottlenecks.v1',
|
|
173
175
|
generated_at: graph.generated_at,
|
|
174
176
|
ok: true,
|
|
175
177
|
critical_path_confidence: graph.critical_path.confidence || null,
|
|
176
|
-
...
|
|
178
|
+
...integrationBottlenecks
|
|
177
179
|
});
|
|
178
180
|
await writeJsonAtomic(path.join(root, 'agent-integration-bottlenecks-v2.json'), {
|
|
179
181
|
schema: 'sks.agent-integration-bottlenecks.v2',
|
|
180
182
|
generated_at: graph.generated_at,
|
|
181
183
|
ok: true,
|
|
182
184
|
critical_path_confidence: graph.critical_path.confidence || null,
|
|
183
|
-
...
|
|
185
|
+
...integrationBottlenecks
|
|
184
186
|
});
|
|
185
187
|
}
|
|
186
188
|
export function compactIntelligentWorkGraph(graph) {
|
|
@@ -222,6 +224,83 @@ export function compactIntelligentWorkGraph(graph) {
|
|
|
222
224
|
function limitArray(items, limit) {
|
|
223
225
|
return Array.isArray(items) ? items.slice(0, limit) : [];
|
|
224
226
|
}
|
|
227
|
+
function graphRecordLimit() {
|
|
228
|
+
return Math.max(50, Math.floor(Number(process.env.SKS_WORK_GRAPH_RECORD_LIMIT || 500) || 500));
|
|
229
|
+
}
|
|
230
|
+
function graphArrayLimit() {
|
|
231
|
+
return Math.max(10, Math.floor(Number(process.env.SKS_WORK_GRAPH_ARRAY_LIMIT || 50) || 50));
|
|
232
|
+
}
|
|
233
|
+
function graphRelationLimit() {
|
|
234
|
+
return Math.max(100, Math.floor(Number(process.env.SKS_WORK_GRAPH_RELATION_LIMIT || 1000) || 1000));
|
|
235
|
+
}
|
|
236
|
+
function limitRecordOfArrays(record, entryLimit, arrayLimit) {
|
|
237
|
+
const out = {};
|
|
238
|
+
for (const [key, value] of Object.entries(record || {}).slice(0, entryLimit)) {
|
|
239
|
+
out[key] = Array.isArray(value) ? value.slice(0, arrayLimit) : value;
|
|
240
|
+
}
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
function compactSymbolOwnershipMap(graph) {
|
|
244
|
+
const entryLimit = graphRecordLimit();
|
|
245
|
+
const arrayLimit = graphArrayLimit();
|
|
246
|
+
const exportedApiOwnership = {};
|
|
247
|
+
for (const [symbol, file] of Object.entries(graph.exported_api_ownership || {}).slice(0, entryLimit))
|
|
248
|
+
exportedApiOwnership[symbol] = String(file);
|
|
249
|
+
return {
|
|
250
|
+
compact: true,
|
|
251
|
+
limits: { record_entries: entryLimit, array_items: arrayLimit },
|
|
252
|
+
original_counts: {
|
|
253
|
+
file_to_symbols: Object.keys(graph.file_to_symbols || {}).length,
|
|
254
|
+
symbol_to_files: Object.keys(graph.symbol_to_files || {}).length,
|
|
255
|
+
exported_symbols: Object.keys(graph.exported_symbols || {}).length,
|
|
256
|
+
imported_symbols: Object.keys(graph.imported_symbols || {}).length,
|
|
257
|
+
exported_api_ownership: Object.keys(graph.exported_api_ownership || {}).length
|
|
258
|
+
},
|
|
259
|
+
file_to_symbols: limitRecordOfArrays(graph.file_to_symbols, entryLimit, arrayLimit),
|
|
260
|
+
symbol_to_files: limitRecordOfArrays(graph.symbol_to_files, entryLimit, arrayLimit),
|
|
261
|
+
exported_symbols: limitRecordOfArrays(graph.exported_symbols, entryLimit, arrayLimit),
|
|
262
|
+
imported_symbols: limitRecordOfArrays(graph.imported_symbols, entryLimit, arrayLimit),
|
|
263
|
+
exported_api_ownership: exportedApiOwnership
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function compactTestOwnershipMap(map = {}) {
|
|
267
|
+
const entryLimit = graphRecordLimit();
|
|
268
|
+
const arrayLimit = graphArrayLimit();
|
|
269
|
+
const relationLimit = graphRelationLimit();
|
|
270
|
+
const relations = limitArray((Array.isArray(map.relations) ? map.relations : []).map((row) => ({
|
|
271
|
+
...row,
|
|
272
|
+
symbol_overlap: limitArray(row?.symbol_overlap, arrayLimit)
|
|
273
|
+
})), relationLimit);
|
|
274
|
+
return {
|
|
275
|
+
compact: true,
|
|
276
|
+
limits: { owner_entries: entryLimit, owner_items: arrayLimit, relations: relationLimit },
|
|
277
|
+
original_counts: {
|
|
278
|
+
owner_by_source: Object.keys(map.owner_by_source || {}).length,
|
|
279
|
+
unmapped_sources: Array.isArray(map.unmapped_sources) ? map.unmapped_sources.length : 0,
|
|
280
|
+
relations: Array.isArray(map.relations) ? map.relations.length : 0
|
|
281
|
+
},
|
|
282
|
+
owner_by_source: limitRecordOfArrays(map.owner_by_source, entryLimit, arrayLimit),
|
|
283
|
+
mapped_source_count: Number(map.mapped_source_count || 0),
|
|
284
|
+
unmapped_sources: limitArray(map.unmapped_sources, entryLimit),
|
|
285
|
+
relations
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function compactCriticalPath(criticalPath = {}) {
|
|
289
|
+
return {
|
|
290
|
+
...criticalPath,
|
|
291
|
+
path: limitArray(criticalPath.path, 500),
|
|
292
|
+
compact: true,
|
|
293
|
+
original_path_length: Array.isArray(criticalPath.path) ? criticalPath.path.length : 0
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function compactIntegrationBottlenecks(bottlenecks = {}) {
|
|
297
|
+
return {
|
|
298
|
+
...bottlenecks,
|
|
299
|
+
bottlenecks: limitArray(bottlenecks.bottlenecks, 200),
|
|
300
|
+
changed_bottlenecks: limitArray(bottlenecks.changed_bottlenecks, 200),
|
|
301
|
+
compact: true
|
|
302
|
+
};
|
|
303
|
+
}
|
|
225
304
|
function buildTestOwnershipMap(sourceFiles, testFiles, ast, dependencyGraph = {}) {
|
|
226
305
|
const ownerBySource = {};
|
|
227
306
|
const relations = [];
|