sneakoscope 2.0.8 → 2.0.10
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 +8 -4
- 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 +33 -8
- package/dist/cli/command-registry.js +1 -0
- package/dist/commands/doctor.js +18 -1
- package/dist/commands/zellij-slot-pane.js +26 -0
- package/dist/commands/zellij.js +144 -1
- package/dist/core/agents/agent-orchestrator.js +202 -9
- package/dist/core/agents/agent-role-config.js +92 -0
- package/dist/core/agents/native-cli-session-swarm.js +230 -48
- package/dist/core/commands/mad-sks-command.js +17 -26
- package/dist/core/commands/naruto-command.js +155 -37
- package/dist/core/doctor/doctor-readiness-matrix.js +6 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +4 -0
- package/dist/core/init.js +1 -0
- package/dist/core/naruto/naruto-active-pool.js +141 -0
- package/dist/core/naruto/naruto-concurrency-governor.js +17 -2
- package/dist/core/naruto/naruto-real-worker-child.js +35 -0
- package/dist/core/naruto/naruto-real-worker-runtime.js +121 -0
- package/dist/core/naruto/naruto-work-graph.js +2 -1
- package/dist/core/release/release-gate-cache-v2.js +58 -4
- package/dist/core/release/release-gate-dag.js +36 -25
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-dashboard-renderer.js +22 -6
- package/dist/core/zellij/zellij-launcher.js +3 -3
- package/dist/core/zellij/zellij-layout-builder.js +1 -1
- package/dist/core/zellij/zellij-right-column-layout-proof.js +42 -0
- package/dist/core/zellij/zellij-right-column-manager.js +304 -0
- package/dist/core/zellij/zellij-slot-pane-renderer.js +82 -0
- package/dist/core/zellij/zellij-ui-mode.js +16 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +152 -17
- package/dist/scripts/agent-role-config-repair-check.js +33 -0
- package/dist/scripts/codex-sdk-release-review-pipeline-check.js +5 -5
- package/dist/scripts/doctor-fix-proves-codex-read-check.js +26 -5
- package/dist/scripts/git-worktree-integration-primary-check.js +4 -2
- package/dist/scripts/git-worktree-integration-primary-runtime-check.js +20 -0
- package/dist/scripts/lib/codex-sdk-gate-lib.js +4 -0
- package/dist/scripts/mad-sks-zellij-default-pane-worker-check.js +2 -2
- package/dist/scripts/mutation-callsite-coverage-check.js +2 -1
- package/dist/scripts/naruto-concurrency-governor-check.js +2 -1
- package/dist/scripts/naruto-extreme-parallelism-check.js +22 -0
- package/dist/scripts/naruto-extreme-parallelism-real-check.js +42 -0
- package/dist/scripts/naruto-real-active-pool-check.js +39 -0
- package/dist/scripts/naruto-real-active-pool-runtime-check.js +53 -0
- package/dist/scripts/naruto-work-graph-check.js +1 -1
- package/dist/scripts/naruto-zellij-dynamic-right-column-check.js +48 -0
- package/dist/scripts/product-design-auto-install-check.js +3 -3
- package/dist/scripts/product-design-plugin-routing-check.js +3 -3
- package/dist/scripts/readme-architecture-imagegen-official-check.js +4 -3
- package/dist/scripts/release-cache-glob-hashing-check.js +42 -0
- package/dist/scripts/release-check-dynamic-execute.js +27 -1
- package/dist/scripts/release-check-dynamic.js +38 -11
- package/dist/scripts/release-check-stamp.js +7 -2
- package/dist/scripts/release-dag-full-coverage-check.js +35 -0
- package/dist/scripts/release-dynamic-performance-check.js +31 -1
- package/dist/scripts/release-gate-existence-audit.js +29 -33
- package/dist/scripts/release-parallel-speed-budget-check.js +67 -13
- package/dist/scripts/release-readiness-report.js +14 -3
- package/dist/scripts/zellij-dashboard-pane-check.js +6 -4
- package/dist/scripts/zellij-developer-controls-check.js +20 -0
- package/dist/scripts/zellij-dynamic-pane-lifecycle-check.js +21 -0
- package/dist/scripts/zellij-initial-main-only-blackbox.js +28 -0
- package/dist/scripts/zellij-right-column-geometry-proof.js +162 -0
- package/dist/scripts/zellij-right-column-headless-overflow-check.js +22 -0
- package/dist/scripts/zellij-right-column-manager-check.js +22 -0
- package/dist/scripts/zellij-slot-only-ui-check.js +22 -0
- package/dist/scripts/zellij-slot-pane-renderer-check.js +38 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +2 -1
- package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +7 -6
- package/package.json +23 -5
- package/schemas/zellij/zellij-right-column-state.schema.json +41 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { assertGate, emitGate, importDist } from './sks-1-18-gate-lib.js';
|
|
7
|
+
const mod = await importDist('core/agents/agent-role-config.js');
|
|
8
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-role-repair-'));
|
|
9
|
+
fs.mkdirSync(path.join(root, '.sneakoscope', 'reports'), { recursive: true });
|
|
10
|
+
const plan = await mod.repairAgentRoleConfigs({ root, apply: false, codexHome: path.join(root, 'codex-home') });
|
|
11
|
+
const repair = await mod.repairAgentRoleConfigs({ root, apply: true, codexHome: path.join(root, 'codex-home'), reportPath: path.join(root, '.sneakoscope', 'reports', 'agent-role-config-repair.json') });
|
|
12
|
+
const analysisScout = path.join(root, '.codex', 'agents', 'analysis-scout.toml');
|
|
13
|
+
const staleRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-role-stale-'));
|
|
14
|
+
fs.mkdirSync(path.join(staleRoot, '.codex', 'agents'), { recursive: true });
|
|
15
|
+
fs.writeFileSync(path.join(staleRoot, '.codex', 'agents', 'analysis-scout.toml'), 'model = "gpt-5-codex"\nsandbox_mode = "read-only"\n');
|
|
16
|
+
const stalePlan = await mod.repairAgentRoleConfigs({ root: staleRoot, apply: false, codexHome: path.join(staleRoot, 'codex-home') });
|
|
17
|
+
const staleRepair = await mod.repairAgentRoleConfigs({ root: staleRoot, apply: true, codexHome: path.join(staleRoot, 'codex-home') });
|
|
18
|
+
const repairedText = fs.readFileSync(path.join(staleRoot, '.codex', 'agents', 'analysis-scout.toml'), 'utf8');
|
|
19
|
+
const report = {
|
|
20
|
+
schema: 'sks.agent-role-config-repair-check.v1',
|
|
21
|
+
plan_ok: plan.ok === true && plan.missing.includes('analysis-scout.toml'),
|
|
22
|
+
repair_ok: repair.ok === true,
|
|
23
|
+
analysis_scout_created: fs.existsSync(analysisScout),
|
|
24
|
+
created_matches_model: fs.readFileSync(analysisScout, 'utf8').includes('model = "gpt-5.5"'),
|
|
25
|
+
stale_detected: stalePlan.stale.includes('analysis-scout.toml'),
|
|
26
|
+
stale_repaired: staleRepair.repaired.includes('.codex/agents/analysis-scout.toml') && repairedText.includes('name = "analysis_scout"') && repairedText.includes('model = "gpt-5.5"'),
|
|
27
|
+
warnings_suppressed: repair.warnings_suppressed === true,
|
|
28
|
+
artifact_written: fs.existsSync(path.join(root, '.sneakoscope', 'reports', 'agent-role-config-repair.json'))
|
|
29
|
+
};
|
|
30
|
+
const ok = report.plan_ok && report.repair_ok && report.analysis_scout_created && report.created_matches_model && report.stale_detected && report.stale_repaired && report.warnings_suppressed && report.artifact_written;
|
|
31
|
+
assertGate(ok, 'doctor --fix must repair missing SKS-owned agent role configs', report);
|
|
32
|
+
emitGate('agent:role-config-repair', report);
|
|
33
|
+
//# sourceMappingURL=agent-role-config-repair-check.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @ts-nocheck
|
|
3
|
-
import { assertGate, emitGate, packageScripts, readText } from './lib/codex-sdk-gate-lib.js';
|
|
3
|
+
import { assertGate, emitGate, packageScripts, readText, releaseGateIds } from './lib/codex-sdk-gate-lib.js';
|
|
4
4
|
const scripts = packageScripts();
|
|
5
|
-
const releaseCheck = String(scripts['release:check'] || '');
|
|
6
5
|
const releaseRealCheck = String(scripts['release:real-check'] || '');
|
|
7
|
-
|
|
8
|
-
assertGate(
|
|
6
|
+
const releaseGates = releaseGateIds();
|
|
7
|
+
assertGate(releaseGates.has('codex-sdk:capability'), 'release gate DAG must include Codex SDK capability gate');
|
|
8
|
+
assertGate(releaseGates.has('codex-sdk:all-pipelines'), 'release gate DAG must include Codex SDK all-pipelines gate');
|
|
9
9
|
assertGate(releaseRealCheck.includes('codex-sdk:real-smoke'), 'release:real-check must include Codex SDK real smoke');
|
|
10
10
|
assertGate(readText('src/core/agents/agent-orchestrator.ts').includes('legacy_codex_exec_runtime_removed'), 'orchestrator must block legacy codex-exec requests');
|
|
11
|
-
emitGate('codex-sdk:release-review-pipeline', {
|
|
11
|
+
emitGate('codex-sdk:release-review-pipeline', { release_gate_dag_contains_sdk: true });
|
|
12
12
|
//# sourceMappingURL=codex-sdk-release-review-pipeline-check.js.map
|
|
@@ -25,19 +25,40 @@ const run = spawnSync(process.execPath, [
|
|
|
25
25
|
SKS_DISABLE_UPDATE_CHECK: '1'
|
|
26
26
|
},
|
|
27
27
|
encoding: 'utf8',
|
|
28
|
-
timeout:
|
|
28
|
+
timeout: 180000
|
|
29
29
|
});
|
|
30
30
|
const parsed = parseLastJson(run.stdout || '{}');
|
|
31
31
|
const ok = run.status !== 0
|
|
32
32
|
&& parsed.ready?.ready === false
|
|
33
33
|
&& parsed.ready?.blockers?.includes('codex_cli_config_eperm')
|
|
34
34
|
&& parsed.ready?.next_actions?.length > 0;
|
|
35
|
-
console.log(JSON.stringify({
|
|
35
|
+
console.log(JSON.stringify({
|
|
36
|
+
schema: 'sks.doctor-fix-proves-codex-read-check.v1',
|
|
37
|
+
ok,
|
|
38
|
+
status: run.status,
|
|
39
|
+
signal: run.signal,
|
|
40
|
+
error: run.error ? String(run.error.message || run.error) : null,
|
|
41
|
+
parsed,
|
|
42
|
+
stdout_tail: String(run.stdout || '').slice(-1000),
|
|
43
|
+
stderr_tail: String(run.stderr || '').slice(-1000)
|
|
44
|
+
}, null, 2));
|
|
36
45
|
if (!ok)
|
|
37
46
|
process.exitCode = 1;
|
|
38
47
|
function parseLastJson(text) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
const source = String(text || '').trim();
|
|
49
|
+
if (!source)
|
|
50
|
+
return {};
|
|
51
|
+
const starts = [];
|
|
52
|
+
for (let index = source.indexOf('{'); index >= 0; index = source.indexOf('{', index + 1))
|
|
53
|
+
starts.push(index);
|
|
54
|
+
for (let i = starts.length - 1; i >= 0; i -= 1) {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(source.slice(starts[i]));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Continue searching for the outer JSON object; pretty JSON may contain nested objects.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {};
|
|
42
63
|
}
|
|
43
64
|
//# sourceMappingURL=doctor-fix-proves-codex-read-check.js.map
|
|
@@ -17,6 +17,8 @@ const integrationPath = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-integration-'
|
|
|
17
17
|
fs.rmSync(integrationPath, { recursive: true, force: true });
|
|
18
18
|
const integration = await managerMod.allocateWorkerWorktree({ repoRoot: repo, missionId: 'M-integrate', workerId: 'integration', slotId: 'integration' });
|
|
19
19
|
const report = await mergeMod.applyGitWorktreeMergeQueue({ integrationWorktreePath: integration.worktree_path, diffs: [diff] });
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const mainReport = await mergeMod.applyGitWorktreeMergeQueue({ integrationWorktreePath: repo, diffs: [diff] });
|
|
21
|
+
const mainContent = fs.readFileSync(path.join(repo, 'a.txt'), 'utf8');
|
|
22
|
+
assertGate(report.ok === true && report.applied_count === 1 && mainReport.ok === true && mainReport.applied_count === 1 && mainContent.includes('integrated'), 'git-worktree-diff must prevalidate in integration worktree and apply to main repo', { report, mainReport, mainContent });
|
|
23
|
+
emitGate('git:worktree-integration-primary', { applied_count: report.applied_count, main_applied_count: mainReport.applied_count });
|
|
22
24
|
//# sourceMappingURL=git-worktree-integration-primary-check.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
|
|
6
|
+
const orchestrator = fs.readFileSync(path.join(root, 'src/core/agents/agent-orchestrator.ts'), 'utf8');
|
|
7
|
+
const report = {
|
|
8
|
+
schema: 'sks.git-worktree-integration-primary-runtime-check.v1',
|
|
9
|
+
splits_worktree_entries: orchestrator.includes("entry.envelope?.source === 'git-worktree-diff'"),
|
|
10
|
+
uses_integration_worktree: orchestrator.includes('createGitIntegrationWorktree'),
|
|
11
|
+
uses_merge_queue: orchestrator.includes('applyGitWorktreeMergeQueue'),
|
|
12
|
+
applies_to_main_repo: orchestrator.includes('main_repo_apply') && orchestrator.includes('integrationWorktreePath: repoRoot'),
|
|
13
|
+
rollback_evidence: orchestrator.includes('rollback_evidence') && orchestrator.includes('captureGitWorktreeRollbackPlan') && orchestrator.includes('completeGitWorktreeRollbackPlan'),
|
|
14
|
+
bypasses_normal_apply: orchestrator.includes('const normalEntries') && orchestrator.includes('applyAgentPatchQueueEntry(root, entry') && orchestrator.includes('parallelEntries = disjointEntries'),
|
|
15
|
+
writes_report: orchestrator.includes('git-worktree-merge-queue-report.json')
|
|
16
|
+
};
|
|
17
|
+
const ok = report.splits_worktree_entries && report.uses_integration_worktree && report.uses_merge_queue && report.applies_to_main_repo && report.rollback_evidence && report.bypasses_normal_apply && report.writes_report;
|
|
18
|
+
assertGate(ok, 'git-worktree-diff entries must use integration merge queue primary path', report);
|
|
19
|
+
emitGate('git:worktree-integration-primary-runtime', report);
|
|
20
|
+
//# sourceMappingURL=git-worktree-integration-primary-runtime-check.js.map
|
|
@@ -59,6 +59,10 @@ export async function runFakeCodexSdkTaskFixture(label = 'fixture', extra = {})
|
|
|
59
59
|
export function packageScripts() {
|
|
60
60
|
return readJson('package.json').scripts || {};
|
|
61
61
|
}
|
|
62
|
+
export function releaseGateIds() {
|
|
63
|
+
const manifest = readJson('release-gates.v2.json');
|
|
64
|
+
return new Set((manifest.gates || []).map((gate) => gate.id));
|
|
65
|
+
}
|
|
62
66
|
export function assertSourceIncludes(file, tokens) {
|
|
63
67
|
const text = readText(file);
|
|
64
68
|
for (const token of tokens)
|
|
@@ -17,10 +17,10 @@ const checks = {
|
|
|
17
17
|
zellij_session_before_swarm: launchIndex >= 0 && swarmIndex >= 0 && launchIndex < swarmIndex,
|
|
18
18
|
main_only_session: mad.includes('slotCount: 0'),
|
|
19
19
|
zellij_default_backend: /return 'zellij'/.test(mad) && mad.includes("list.includes('--json')") && mad.includes("list.includes('--no-attach')"),
|
|
20
|
-
worker_command_real_zellij: mad.includes("command.push('--real')") && mad.includes("command.push('--zellij-session-name'") && mad.includes("command.push('--zellij-pane-worker')"),
|
|
20
|
+
worker_command_real_zellij: mad.includes("command.push('--real')") && mad.includes("command.push('--zellij-session-name'") && mad.includes("command.push('--zellij-pane-worker')") && mad.includes("command.push('--worker-placement'"),
|
|
21
21
|
parser_accepts_worker_flags: parser.includes('--zellij-session-name') && parser.includes('--zellij-pane-worker') && parser.includes('--no-zellij-pane-worker'),
|
|
22
22
|
native_worker_pane_path: swarm.includes("this.input.backend === 'zellij'") && swarm.includes('ctx.opts.zellijPaneWorker !== false') && swarm.includes('openWorkerPane({'),
|
|
23
|
-
right_pane_requested: manager.includes("'--direction', '
|
|
23
|
+
right_pane_requested: manager.includes("'--direction', directionRequested") && manager.includes("'--near-current-pane'")
|
|
24
24
|
};
|
|
25
25
|
const ok = Object.values(checks).every(Boolean);
|
|
26
26
|
emit({
|
|
@@ -17,6 +17,7 @@ const uncovered = [];
|
|
|
17
17
|
const GUARD_CALL = /\bguarded(WriteFile|Rm|Rename|Chmod|Xattr|Chflags|GlobalCodexConfigWrite|ProcessKill|PackageInstall|SkillSnapshotPromotion|Apply)\(/;
|
|
18
18
|
const RISKY = [
|
|
19
19
|
{ kind: 'write_file', token: 'fs.writeFile', re: /\bfs\.writeFile\(/ },
|
|
20
|
+
{ kind: 'write_file', token: 'fs.promises.writeFile', re: /\bfs\.promises\.writeFile\(/ },
|
|
20
21
|
{ kind: 'write_file', token: 'fsp.writeFile', re: /\bfsp\.writeFile\(/ },
|
|
21
22
|
{ kind: 'write_file', token: 'writeFileSync', re: /\b(?:fs\.)?writeFileSync\(/ },
|
|
22
23
|
{ kind: 'rm', token: 'fs.rm', re: /\bfs\.rm\(/ },
|
|
@@ -167,7 +168,7 @@ function processKillIsLivenessProbe(line) {
|
|
|
167
168
|
return /\bprocess\.kill\([^,\n]+,\s*0\s*\)/.test(line);
|
|
168
169
|
}
|
|
169
170
|
function codexHomeMutationOnLine(line) {
|
|
170
|
-
return /\b(?:writeTextAtomic|writeJsonAtomic|writeFileSync|fs\.writeFile|fsp\.writeFile|fs\.rm|fsp\.rm|fs\.rename|fsp\.rename|fs\.chmod|fsp\.chmod|copyFile|open)\b/.test(line)
|
|
171
|
+
return /\b(?:writeTextAtomic|writeJsonAtomic|writeFileSync|fs\.writeFile|fs\.promises\.writeFile|fsp\.writeFile|fs\.rm|fsp\.rm|fs\.rename|fsp\.rename|fs\.chmod|fsp\.chmod|copyFile|open)\b/.test(line)
|
|
171
172
|
&& /(?:~\/\.codex|CODEX_HOME|codexHome|codexLbHome|auth\.json|config\.toml)/.test(line);
|
|
172
173
|
}
|
|
173
174
|
function snippet(line) {
|
|
@@ -10,6 +10,7 @@ const normal = governorMod.decideNarutoConcurrency({
|
|
|
10
10
|
zellijVisiblePaneCap: 12,
|
|
11
11
|
hardware: {
|
|
12
12
|
cores: 32,
|
|
13
|
+
loadAverage: [0, 0, 0],
|
|
13
14
|
freeMemoryBytes: 48 * 1024 * 1024 * 1024,
|
|
14
15
|
totalMemoryBytes: 64 * 1024 * 1024 * 1024,
|
|
15
16
|
fileDescriptorLimit: 4096,
|
|
@@ -37,7 +38,7 @@ const pressure = governorMod.decideNarutoConcurrency({
|
|
|
37
38
|
diskIoPressure: 0.9
|
|
38
39
|
}
|
|
39
40
|
});
|
|
40
|
-
assertGate(normal.safe_active_workers <=
|
|
41
|
+
assertGate(normal.safe_active_workers >= 32 && normal.safe_active_workers <= 100, 'requested_clones=200 fixture must cap active workers safely while allowing aggressive process-pool fanout', { normal });
|
|
41
42
|
assertGate(normal.safe_zellij_visible_panes === 12 && normal.headless_workers === normal.safe_active_workers - 12, 'zellij visible panes must stay within UI cap', { normal });
|
|
42
43
|
assertGate(normal.local_llm_parallel <= 4, 'local LLM active requests must respect max_parallel_requests=4', { normal });
|
|
43
44
|
assertGate(pressure.safe_active_workers < normal.safe_active_workers, 'memory/load pressure fixture must decrease active workers', { normal: normal.safe_active_workers, pressure: pressure.safe_active_workers });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
4
|
+
import { buildNarutoWorkGraph } from '../core/naruto/naruto-work-graph.js';
|
|
5
|
+
import { decideNarutoConcurrency } from '../core/naruto/naruto-concurrency-governor.js';
|
|
6
|
+
const graph = buildNarutoWorkGraph({ requestedClones: 100, writeCapable: true, maxActiveWorkers: 32 });
|
|
7
|
+
const governor = decideNarutoConcurrency({
|
|
8
|
+
requestedClones: 100,
|
|
9
|
+
totalWorkItems: graph.total_work_items,
|
|
10
|
+
pendingWorkQueueSize: graph.total_work_items,
|
|
11
|
+
backend: 'codex-sdk',
|
|
12
|
+
hardware: { cores: 16, loadAverage: [1, 1, 1], freeMemoryBytes: 64 * 1024 * 1024 * 1024, totalMemoryBytes: 128 * 1024 * 1024 * 1024, fileDescriptorLimit: 8192, processCount: 100, terminalRows: 48, remoteApiRateLimitBudget: 32, localLlmMaxParallelRequests: 8 }
|
|
13
|
+
});
|
|
14
|
+
const report = {
|
|
15
|
+
schema: 'sks.naruto-extreme-parallelism-check.v1',
|
|
16
|
+
ok: graph.total_work_items >= 200 && governor.safe_active_workers >= 16 && governor.safe_zellij_visible_panes <= governor.safe_active_workers && governor.safe_zellij_visible_panes <= 8 && graph.mixed_work_kinds.length >= 6,
|
|
17
|
+
graph: { total_work_items: graph.total_work_items, mixed_work_kinds: graph.mixed_work_kinds, write_allowed_count: graph.write_allowed_count },
|
|
18
|
+
governor
|
|
19
|
+
};
|
|
20
|
+
assertGate(report.ok, 'Naruto extreme parallelism must fan out >=2x clones and keep a high safe active pool', report);
|
|
21
|
+
emitGate('naruto:extreme-parallelism', report);
|
|
22
|
+
//# sourceMappingURL=naruto-extreme-parallelism-check.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
7
|
+
import { buildNarutoWorkGraph } from '../core/naruto/naruto-work-graph.js';
|
|
8
|
+
import { decideNarutoConcurrency } from '../core/naruto/naruto-concurrency-governor.js';
|
|
9
|
+
import { runNarutoRealActivePool } from '../core/naruto/naruto-active-pool.js';
|
|
10
|
+
import { collectActualNarutoWorker, spawnActualNarutoWorker } from '../core/naruto/naruto-real-worker-runtime.js';
|
|
11
|
+
const graph = buildNarutoWorkGraph({ requestedClones: 100, totalWorkItems: 200, writeCapable: true, maxActiveWorkers: 32 });
|
|
12
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-naruto-extreme-real-'));
|
|
13
|
+
const missionId = `M-naruto-extreme-real-${process.pid}`;
|
|
14
|
+
const governor = decideNarutoConcurrency({
|
|
15
|
+
requestedClones: 100,
|
|
16
|
+
totalWorkItems: graph.total_work_items,
|
|
17
|
+
pendingWorkQueueSize: graph.total_work_items,
|
|
18
|
+
backend: 'codex-sdk',
|
|
19
|
+
hardware: { cores: 16, loadAverage: [1, 1, 1], freeMemoryBytes: 64 * 1024 * 1024 * 1024, totalMemoryBytes: 128 * 1024 * 1024 * 1024, fileDescriptorLimit: 8192, processCount: 100, terminalRows: 48, remoteApiRateLimitBudget: 32, localLlmMaxParallelRequests: 8 }
|
|
20
|
+
});
|
|
21
|
+
const target = { ...governor, safe_active_workers: Math.min(32, governor.safe_active_workers), safe_zellij_visible_panes: Math.min(8, governor.safe_zellij_visible_panes) };
|
|
22
|
+
const report = await runNarutoRealActivePool({
|
|
23
|
+
graph,
|
|
24
|
+
governor: target,
|
|
25
|
+
spawnWorker: async (item, placement) => spawnActualNarutoWorker({
|
|
26
|
+
root: tempRoot,
|
|
27
|
+
missionId,
|
|
28
|
+
item,
|
|
29
|
+
placement,
|
|
30
|
+
backend: 'codex-sdk',
|
|
31
|
+
visiblePaneCap: target.safe_zellij_visible_panes,
|
|
32
|
+
zellijSessionName: `sks-${missionId}`
|
|
33
|
+
}),
|
|
34
|
+
collectWorker: async (handle) => collectActualNarutoWorker(handle),
|
|
35
|
+
enqueueVerification: async () => undefined,
|
|
36
|
+
updateDashboard: async () => undefined
|
|
37
|
+
});
|
|
38
|
+
const actualArtifacts = report.worker_lifecycle.every((row) => row.pid && row.worker_artifact_dir && fs.existsSync(path.join(row.worker_artifact_dir, 'worker-result.json')));
|
|
39
|
+
const ok = report.ok && report.max_observed_active_workers >= Math.ceil(target.safe_active_workers * 0.8) && report.active_pool_utilization >= 0.8 && report.headless_workers > 0 && report.visible_workers <= graph.total_work_items && actualArtifacts;
|
|
40
|
+
assertGate(ok, 'Naruto extreme parallelism must use actual child process active-pool lifecycle near cap with headless overflow', { report, target, actualArtifacts, tempRoot });
|
|
41
|
+
emitGate('naruto:extreme-parallelism-real', report);
|
|
42
|
+
//# sourceMappingURL=naruto-extreme-parallelism-real-check.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
4
|
+
import { buildNarutoWorkGraph } from '../core/naruto/naruto-work-graph.js';
|
|
5
|
+
import { decideNarutoConcurrency } from '../core/naruto/naruto-concurrency-governor.js';
|
|
6
|
+
import { runNarutoRealActivePool } from '../core/naruto/naruto-active-pool.js';
|
|
7
|
+
const graph = buildNarutoWorkGraph({ requestedClones: 12, totalWorkItems: 24, writeCapable: true, maxActiveWorkers: 6 });
|
|
8
|
+
const governor = decideNarutoConcurrency({
|
|
9
|
+
requestedClones: 12,
|
|
10
|
+
totalWorkItems: graph.total_work_items,
|
|
11
|
+
pendingWorkQueueSize: graph.total_work_items,
|
|
12
|
+
backend: 'codex-sdk',
|
|
13
|
+
hardware: { cpuCoreCount: 8, freeMemoryBytes: 32 * 1024 * 1024 * 1024, totalMemoryBytes: 64 * 1024 * 1024 * 1024, fileDescriptorLimit: 4096, processCount: 100, terminalRows: 40, remoteApiRateLimitBudget: 8 }
|
|
14
|
+
});
|
|
15
|
+
const target = { ...governor, safe_active_workers: Math.min(6, governor.safe_active_workers), safe_zellij_visible_panes: 3 };
|
|
16
|
+
let spawned = 0;
|
|
17
|
+
let collected = 0;
|
|
18
|
+
let dashboardEvents = 0;
|
|
19
|
+
const report = await runNarutoRealActivePool({
|
|
20
|
+
graph,
|
|
21
|
+
governor: target,
|
|
22
|
+
spawnWorker: async (item, placement) => {
|
|
23
|
+
spawned += 1;
|
|
24
|
+
return { id: item.id, item, placement, started_at: Date.now() };
|
|
25
|
+
},
|
|
26
|
+
collectWorker: async (handle) => {
|
|
27
|
+
collected += 1;
|
|
28
|
+
return { id: handle.id, ok: true, item: handle.item, placement: handle.placement, completed_at: Date.now() };
|
|
29
|
+
},
|
|
30
|
+
enqueueVerification: async () => undefined,
|
|
31
|
+
updateDashboard: async () => {
|
|
32
|
+
dashboardEvents += 1;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const processEvidence = report.worker_lifecycle.every((row) => row.pid == null || row.worker_artifact_dir != null);
|
|
36
|
+
const ok = report.ok && spawned === graph.total_work_items && collected === graph.total_work_items && report.max_observed_active_workers >= target.safe_active_workers && dashboardEvents > graph.total_work_items && report.active_cap === target.safe_active_workers && processEvidence;
|
|
37
|
+
assertGate(ok, 'Naruto real active pool must run spawn/collect lifecycle and refill to cap', { report, spawned, collected, dashboardEvents, target });
|
|
38
|
+
emitGate('naruto:real-active-pool', { spawned, collected, active_cap: report.active_cap, max_observed_active_workers: report.max_observed_active_workers, refill_latency_ms_p95: report.refill_latency_ms_p95, active_pool_utilization: report.active_pool_utilization });
|
|
39
|
+
//# sourceMappingURL=naruto-real-active-pool-check.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
7
|
+
import { buildNarutoWorkGraph } from '../core/naruto/naruto-work-graph.js';
|
|
8
|
+
import { decideNarutoConcurrency } from '../core/naruto/naruto-concurrency-governor.js';
|
|
9
|
+
import { runNarutoRealActivePool } from '../core/naruto/naruto-active-pool.js';
|
|
10
|
+
import { collectActualNarutoWorker, spawnActualNarutoWorker } from '../core/naruto/naruto-real-worker-runtime.js';
|
|
11
|
+
const graph = buildNarutoWorkGraph({ requestedClones: 12, totalWorkItems: 24, writeCapable: false, maxActiveWorkers: 6 });
|
|
12
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-naruto-real-runtime-'));
|
|
13
|
+
const missionId = `M-naruto-real-runtime-${process.pid}`;
|
|
14
|
+
const governor = decideNarutoConcurrency({
|
|
15
|
+
requestedClones: 12,
|
|
16
|
+
totalWorkItems: graph.total_work_items,
|
|
17
|
+
pendingWorkQueueSize: graph.total_work_items,
|
|
18
|
+
backend: 'codex-sdk',
|
|
19
|
+
hardware: { cores: 8, freeMemoryBytes: 32 * 1024 * 1024 * 1024, totalMemoryBytes: 64 * 1024 * 1024 * 1024, fileDescriptorLimit: 4096, processCount: 100, terminalRows: 40, remoteApiRateLimitBudget: 8 }
|
|
20
|
+
});
|
|
21
|
+
let spawned = 0;
|
|
22
|
+
let collected = 0;
|
|
23
|
+
const target = { ...governor, safe_active_workers: Math.min(6, governor.safe_active_workers), safe_zellij_visible_panes: 3 };
|
|
24
|
+
const report = await runNarutoRealActivePool({
|
|
25
|
+
graph,
|
|
26
|
+
governor: target,
|
|
27
|
+
spawnWorker: async (item, placement) => {
|
|
28
|
+
spawned += 1;
|
|
29
|
+
return spawnActualNarutoWorker({
|
|
30
|
+
root: tempRoot,
|
|
31
|
+
missionId,
|
|
32
|
+
item,
|
|
33
|
+
placement,
|
|
34
|
+
backend: 'codex-sdk',
|
|
35
|
+
visiblePaneCap: target.safe_zellij_visible_panes,
|
|
36
|
+
zellijSessionName: `sks-${missionId}`
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
collectWorker: async (handle) => {
|
|
40
|
+
collected += 1;
|
|
41
|
+
return collectActualNarutoWorker(handle);
|
|
42
|
+
},
|
|
43
|
+
enqueueVerification: async () => undefined,
|
|
44
|
+
updateDashboard: async () => undefined
|
|
45
|
+
});
|
|
46
|
+
const processEvidence = report.worker_lifecycle.every((row) => row.pid && row.worker_artifact_dir);
|
|
47
|
+
const artifactEvidence = report.worker_lifecycle.every((row) => row.worker_artifact_dir
|
|
48
|
+
&& fs.existsSync(path.join(row.worker_artifact_dir, 'worker-heartbeat.jsonl'))
|
|
49
|
+
&& fs.existsSync(path.join(row.worker_artifact_dir, 'worker-result.json')));
|
|
50
|
+
const ok = report.ok && spawned === graph.total_work_items && collected === graph.total_work_items && report.max_observed_active_workers >= target.safe_active_workers && report.active_pool_utilization >= 0.8 && processEvidence && artifactEvidence;
|
|
51
|
+
assertGate(ok, 'Naruto real active pool runtime must include actual child process, heartbeat, and result evidence', { report, spawned, collected, processEvidence, artifactEvidence, tempRoot });
|
|
52
|
+
emitGate('naruto:real-active-pool-runtime', report);
|
|
53
|
+
//# sourceMappingURL=naruto-real-active-pool-runtime-check.js.map
|
|
@@ -11,7 +11,7 @@ const graph = workGraph.buildNarutoWorkGraph({
|
|
|
11
11
|
});
|
|
12
12
|
const validation = workGraph.validateNarutoWorkGraph(graph);
|
|
13
13
|
assertGate(graph.ok === true && validation.ok === true, 'Naruto work graph must validate', { graph_blockers: graph.blockers, validation });
|
|
14
|
-
assertGate(graph.total_work_items >= graph.requested_clones, 'work graph must create at least requested clone count', { total: graph.total_work_items, requested: graph.requested_clones });
|
|
14
|
+
assertGate(graph.total_work_items >= graph.requested_clones * 2, 'write-capable work graph must create at least 2x requested clone count', { total: graph.total_work_items, requested: graph.requested_clones });
|
|
15
15
|
assertGate(graph.mixed_work_kinds.length > 4, 'work graph must contain mixed work kinds, not only verification', { kinds: graph.mixed_work_kinds });
|
|
16
16
|
assertGate(graph.write_allowed_count > 0, 'write-capable Naruto graph must include write_allowed work items', { write_allowed_count: graph.write_allowed_count });
|
|
17
17
|
assertGate(graph.active_waves.every((wave) => wave.conflict_count === 0), 'active waves must not overlap write leases', { waves: graph.active_waves });
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
|
|
7
|
+
const requireReal = process.argv.includes('--require-real') || process.env.SKS_REQUIRE_ZELLIJ === '1';
|
|
8
|
+
const naruto = fs.readFileSync(path.join(root, 'src/core/commands/naruto-command.ts'), 'utf8');
|
|
9
|
+
const worker = fs.readFileSync(path.join(root, 'src/core/zellij/zellij-worker-pane-manager.ts'), 'utf8');
|
|
10
|
+
const swarm = fs.readFileSync(path.join(root, 'src/core/agents/native-cli-session-swarm.ts'), 'utf8');
|
|
11
|
+
const realGeometryProof = requireReal ? runRealGeometryProof() : null;
|
|
12
|
+
const report = {
|
|
13
|
+
schema: 'sks.naruto-zellij-dynamic-right-column-check.v1',
|
|
14
|
+
ok: true,
|
|
15
|
+
require_real: requireReal,
|
|
16
|
+
initial_main_only: /slotCount:\s*0/.test(naruto),
|
|
17
|
+
passes_session_name: naruto.includes('zellijSessionName: liveZellij?.session_name'),
|
|
18
|
+
passes_worker_placement: naruto.includes("workerPlacement: parsed.json || parsed.noOpenZellij ? 'process' : 'zellij-pane'"),
|
|
19
|
+
passes_visible_cap: naruto.includes('zellijVisiblePaneCap: zellijVisiblePanes'),
|
|
20
|
+
worker_uses_right_column: worker.includes("rightColumnMode: 'spawn-on-first-worker'") || swarm.includes("rightColumnMode: 'spawn-on-first-worker'"),
|
|
21
|
+
real_geometry_proof: realGeometryProof
|
|
22
|
+
};
|
|
23
|
+
report.ok = report.initial_main_only
|
|
24
|
+
&& report.passes_session_name
|
|
25
|
+
&& report.passes_worker_placement
|
|
26
|
+
&& report.passes_visible_cap
|
|
27
|
+
&& report.worker_uses_right_column
|
|
28
|
+
&& (!requireReal || realGeometryProof?.ok === true);
|
|
29
|
+
assertGate(report.ok, 'Naruto must use dynamic right-column worker panes in interactive mode', report);
|
|
30
|
+
emitGate('naruto:zellij-dynamic-right-column', report);
|
|
31
|
+
function runRealGeometryProof() {
|
|
32
|
+
const res = spawnSync('npm', ['run', 'zellij:right-column-real-geometry', '--silent', '--', '--require-real'], {
|
|
33
|
+
cwd: root,
|
|
34
|
+
env: { ...process.env, SKS_REQUIRE_ZELLIJ: '1' },
|
|
35
|
+
encoding: 'utf8',
|
|
36
|
+
maxBuffer: 20 * 1024 * 1024
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
ok: res.status === 0,
|
|
40
|
+
exit_code: res.status,
|
|
41
|
+
stdout_tail: tail(res.stdout),
|
|
42
|
+
stderr_tail: tail(res.stderr)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function tail(text) {
|
|
46
|
+
return String(text || '').split('\n').slice(-30).join('\n');
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=naruto-zellij-dynamic-right-column-check.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @ts-nocheck
|
|
3
3
|
import { ensureProductDesignPluginInstalledWithRequest, findProductDesignPluginSummaryFromMarketplaces, productDesignAutoInstallRequested } from '../core/product-design-app-server.js';
|
|
4
4
|
import { PRODUCT_DESIGN_PLUGIN, PRODUCT_DESIGN_REQUIRED_SKILLS } from '../core/product-design-plugin.js';
|
|
5
|
-
import { assertGate, emitGate, readText } from './lib/codex-sdk-gate-lib.js';
|
|
5
|
+
import { assertGate, emitGate, readText, releaseGateIds } from './lib/codex-sdk-gate-lib.js';
|
|
6
6
|
function pluginReadResponse({ installed, enabled }) {
|
|
7
7
|
return {
|
|
8
8
|
plugin: {
|
|
@@ -92,7 +92,7 @@ const codexAppSource = readText('src/core/codex-app.ts');
|
|
|
92
92
|
const commandSource = readText('src/commands/codex-app.ts');
|
|
93
93
|
const appServerSource = readText('src/core/product-design-app-server.ts');
|
|
94
94
|
const pkg = JSON.parse(readText('package.json'));
|
|
95
|
-
const
|
|
95
|
+
const releaseGates = releaseGateIds();
|
|
96
96
|
for (const token of [
|
|
97
97
|
'ensureProductDesignPluginInstalled',
|
|
98
98
|
'PRODUCT_DESIGN_AUTO_INSTALL_ENV',
|
|
@@ -109,7 +109,7 @@ for (const token of [
|
|
|
109
109
|
assertGate(commandSource.includes(token), `sks codex-app command missing Product Design token: ${token}`);
|
|
110
110
|
}
|
|
111
111
|
assertGate(Boolean(pkg.scripts?.['codex:product-design-auto-install']), 'package script missing codex:product-design-auto-install');
|
|
112
|
-
assertGate(
|
|
112
|
+
assertGate(releaseGates.has('codex:product-design-auto-install'), 'release gate DAG must include Product Design auto-install gate');
|
|
113
113
|
emitGate('codex:product-design-auto-install', {
|
|
114
114
|
install_calls: installCalls.map((call) => call.method),
|
|
115
115
|
dry_calls: dryCalls.map((call) => call.method),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @ts-nocheck
|
|
3
3
|
import { PRODUCT_DESIGN_PIPELINE_STAGES, PRODUCT_DESIGN_PLUGIN, PRODUCT_DESIGN_REQUIRED_SKILLS, normalizeProductDesignPluginEvidence, productDesignPluginVisibilityFromCodexPluginList } from '../core/product-design-plugin.js';
|
|
4
|
-
import { assertGate, emitGate, readText } from './lib/codex-sdk-gate-lib.js';
|
|
4
|
+
import { assertGate, emitGate, readText, releaseGateIds } from './lib/codex-sdk-gate-lib.js';
|
|
5
5
|
const routesSource = readText('src/core/routes.ts');
|
|
6
6
|
const productDesignSource = readText('src/core/product-design-plugin.ts');
|
|
7
7
|
const runtimeSource = readText('src/core/pipeline-internals/runtime-core.ts');
|
|
@@ -9,7 +9,7 @@ const pptSource = readText('src/core/ppt.ts');
|
|
|
9
9
|
const codexAppSource = readText('src/core/codex-app.ts');
|
|
10
10
|
const initSource = readText('src/core/init.ts');
|
|
11
11
|
const pkg = JSON.parse(readText('package.json'));
|
|
12
|
-
const
|
|
12
|
+
const releaseGates = releaseGateIds();
|
|
13
13
|
assertGate(PRODUCT_DESIGN_PLUGIN.id === 'product-design@openai-curated-remote', 'Product Design plugin id must use the remote marketplace');
|
|
14
14
|
assertGate(PRODUCT_DESIGN_PLUGIN.marketplace === 'openai-curated-remote', 'Product Design marketplace must be openai-curated-remote');
|
|
15
15
|
assertGate(PRODUCT_DESIGN_PLUGIN.marketplace_kind === 'vertical', 'Product Design marketplace kind must be vertical');
|
|
@@ -90,7 +90,7 @@ for (const token of [
|
|
|
90
90
|
assertGate(codexAppSource.includes(token), `codex-app.ts missing Product Design readiness token: ${token}`);
|
|
91
91
|
}
|
|
92
92
|
assertGate(Boolean(pkg.scripts?.['codex:product-design-plugin-routing']), 'package script missing codex:product-design-plugin-routing');
|
|
93
|
-
assertGate(
|
|
93
|
+
assertGate(releaseGates.has('codex:product-design-plugin-routing'), 'release gate DAG must include Product Design routing gate');
|
|
94
94
|
emitGate('codex:product-design-plugin-routing', {
|
|
95
95
|
plugin_id: PRODUCT_DESIGN_PLUGIN.id,
|
|
96
96
|
remote_plugin_id: PRODUCT_DESIGN_PLUGIN.remote_plugin_id,
|
|
@@ -5,6 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
7
|
import { execFileSync } from 'node:child_process';
|
|
8
|
+
import { writeTextAtomic } from '../core/fsx.js';
|
|
8
9
|
const root = process.cwd();
|
|
9
10
|
const args = process.argv.slice(2);
|
|
10
11
|
const reportPath = path.join(root, '.sneakoscope', 'reports', 'readme-architecture-imagegen-attempt-1.18.8.json');
|
|
@@ -139,12 +140,12 @@ else {
|
|
|
139
140
|
}
|
|
140
141
|
async function ensurePromptContract() {
|
|
141
142
|
if (!promptExistedBeforeRun) {
|
|
142
|
-
await
|
|
143
|
+
await writeTextAtomic(promptPath, prompt);
|
|
143
144
|
return { changed: true, reason: 'created' };
|
|
144
145
|
}
|
|
145
146
|
const existing = await fs.promises.readFile(promptPath, 'utf8').catch(() => null);
|
|
146
147
|
if (existing !== prompt) {
|
|
147
|
-
await
|
|
148
|
+
await writeTextAtomic(promptPath, prompt);
|
|
148
149
|
return { changed: true, reason: 'refreshed_prompt_changed' };
|
|
149
150
|
}
|
|
150
151
|
return { changed: false, reason: 'unchanged' };
|
|
@@ -334,7 +335,7 @@ async function textArtifactMeta(file, promptWrite = null) {
|
|
|
334
335
|
};
|
|
335
336
|
}
|
|
336
337
|
async function writeReport(report) {
|
|
337
|
-
await
|
|
338
|
+
await writeTextAtomic(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
338
339
|
}
|
|
339
340
|
async function sha256File(file) {
|
|
340
341
|
return new Promise((resolve, reject) => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
7
|
+
import { expandGlob, releaseGateCacheKey } from '../core/release/release-gate-cache-v2.js';
|
|
8
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-cache-glob-'));
|
|
9
|
+
fs.mkdirSync(path.join(tmp, 'src/core/release'), { recursive: true });
|
|
10
|
+
fs.writeFileSync(path.join(tmp, 'package.json'), JSON.stringify({ version: '0.0.0' }));
|
|
11
|
+
fs.writeFileSync(path.join(tmp, 'release-gates.v2.json'), JSON.stringify({ schema: 'sks.release-gates.v2', gates: [] }));
|
|
12
|
+
fs.writeFileSync(path.join(tmp, 'src/core/release/a.ts'), 'a');
|
|
13
|
+
const gate = {
|
|
14
|
+
id: 'release:cache-glob-hashing-fixture',
|
|
15
|
+
command: 'node fixture',
|
|
16
|
+
deps: [],
|
|
17
|
+
resource: ['cpu-light'],
|
|
18
|
+
side_effect: 'hermetic',
|
|
19
|
+
timeout_ms: 1000,
|
|
20
|
+
cache: { enabled: true, inputs: ['src/core/release/**'] },
|
|
21
|
+
isolation: { home: 'temp', codex_home: 'temp', report_dir: 'per-gate' },
|
|
22
|
+
preset: ['release']
|
|
23
|
+
};
|
|
24
|
+
const before = releaseGateCacheKey(tmp, gate);
|
|
25
|
+
const expandedBefore = expandGlob(tmp, 'src/core/release/**');
|
|
26
|
+
fs.writeFileSync(path.join(tmp, 'src/core/release/b.ts'), 'b');
|
|
27
|
+
const afterAdd = releaseGateCacheKey(tmp, gate);
|
|
28
|
+
fs.writeFileSync(path.join(tmp, 'src/core/release/a.ts'), 'changed');
|
|
29
|
+
const afterChange = releaseGateCacheKey(tmp, gate);
|
|
30
|
+
const expandedAfter = expandGlob(tmp, 'src/core/release/**');
|
|
31
|
+
const report = {
|
|
32
|
+
schema: 'sks.release-cache-glob-hashing-check.v1',
|
|
33
|
+
ok: expandedBefore.length === 1 && expandedAfter.length === 2 && before !== afterAdd && afterAdd !== afterChange,
|
|
34
|
+
expanded_before: expandedBefore.map((file) => path.basename(file)),
|
|
35
|
+
expanded_after: expandedAfter.map((file) => path.basename(file)),
|
|
36
|
+
before,
|
|
37
|
+
after_add: afterAdd,
|
|
38
|
+
after_change: afterChange
|
|
39
|
+
};
|
|
40
|
+
assertGate(report.ok, 'release cache key must hash recursive glob file paths and contents', report);
|
|
41
|
+
emitGate('release:cache-glob-hashing', report);
|
|
42
|
+
//# sourceMappingURL=release-cache-glob-hashing-check.js.map
|
|
@@ -40,7 +40,7 @@ const invariants = {
|
|
|
40
40
|
};
|
|
41
41
|
const distHash = distHashValue();
|
|
42
42
|
const gitCommit = gitHead();
|
|
43
|
-
const manifestHash = fileHash('release-gates.json');
|
|
43
|
+
const manifestHash = fileHash(fs.existsSync(path.join(root, 'release-gates.v2.json')) ? 'release-gates.v2.json' : 'release-gates.json');
|
|
44
44
|
const packageScriptsHash = sha256(JSON.stringify(pkg.scripts || {}));
|
|
45
45
|
const nodeVersion = process.version;
|
|
46
46
|
const npmVersion = npmVersionValue();
|
|
@@ -134,6 +134,25 @@ emitGate('release:check:dynamic:execute', {
|
|
|
134
134
|
});
|
|
135
135
|
// ---- helpers ----------------------------------------------------------------
|
|
136
136
|
function loadManifest() {
|
|
137
|
+
const v2Path = path.join(root, 'release-gates.v2.json');
|
|
138
|
+
if (fs.existsSync(v2Path)) {
|
|
139
|
+
const parsed = JSON.parse(fs.readFileSync(v2Path, 'utf8'));
|
|
140
|
+
const releaseNodes = (Array.isArray(parsed.gates) ? parsed.gates : []).filter((gate) => Array.isArray(gate.preset) && gate.preset.includes('release'));
|
|
141
|
+
const byId = new Map(releaseNodes.map((gate) => [gate.id, gate]));
|
|
142
|
+
const dynamic = buildGateManifest(releaseNodes.map((gate) => gate.id));
|
|
143
|
+
return {
|
|
144
|
+
schema: 'sks.release-gate-manifest.v1.from-v2',
|
|
145
|
+
gates: dynamic.gates.map((entry) => {
|
|
146
|
+
const node = byId.get(entry.id);
|
|
147
|
+
const resource = Array.isArray(node?.resource) ? node.resource.join(',') : '';
|
|
148
|
+
return {
|
|
149
|
+
...entry,
|
|
150
|
+
affected_by: usefulCacheInputs(node?.cache?.inputs, entry.affected_by),
|
|
151
|
+
cost: node?.side_effect === 'real-env' || resource.includes('real') ? 'real' : entry.cost
|
|
152
|
+
};
|
|
153
|
+
})
|
|
154
|
+
};
|
|
155
|
+
}
|
|
137
156
|
const p = path.join(root, 'release-gates.json');
|
|
138
157
|
if (fs.existsSync(p))
|
|
139
158
|
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
@@ -143,6 +162,13 @@ function loadManifest() {
|
|
|
143
162
|
const ids = [...new Set([...dagIds, ...releaseCheckIds])].filter((id) => id && id !== 'build' && id !== 'release:check:parallel');
|
|
144
163
|
return buildGateManifest(ids);
|
|
145
164
|
}
|
|
165
|
+
function usefulCacheInputs(inputs, fallback) {
|
|
166
|
+
if (!Array.isArray(inputs) || !inputs.length)
|
|
167
|
+
return fallback;
|
|
168
|
+
if (inputs.some((input) => ['src/**', 'package.json', 'release-gates.v2.json', 'schemas/**'].includes(input)))
|
|
169
|
+
return fallback;
|
|
170
|
+
return inputs;
|
|
171
|
+
}
|
|
146
172
|
function hashAffectedFiles(globs) {
|
|
147
173
|
const regexes = (globs || []).map(globToRegExp);
|
|
148
174
|
const hashes = [];
|