sneakoscope 2.0.10 → 2.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +27 -8
- package/dist/cli/command-registry.js +1 -0
- package/dist/cli/install-helpers.js +8 -20
- package/dist/commands/doctor.js +5 -9
- package/dist/commands/zellij-slot-column-anchor.js +23 -0
- package/dist/core/agents/agent-orchestrator.js +338 -12
- package/dist/core/agents/agent-patch-schema.js +8 -1
- package/dist/core/agents/agent-scheduler.js +12 -1
- package/dist/core/agents/agent-slot-pane-binding-proof.js +3 -3
- package/dist/core/agents/agent-work-queue.js +26 -2
- package/dist/core/agents/agent-worker-pipeline.js +2 -0
- package/dist/core/agents/native-cli-session-swarm.js +2 -2
- package/dist/core/commands/naruto-command.js +191 -39
- package/dist/core/fsx.js +1 -1
- package/dist/core/git/git-worktree-checkpoint.js +52 -0
- package/dist/core/git/git-worktree-cross-rebase.js +54 -0
- package/dist/core/git/git-worktree-merge-queue.js +92 -3
- package/dist/core/git/git-worktree-patch-envelope.js +8 -1
- package/dist/core/init.js +2 -2
- package/dist/core/naruto/naruto-allocation-policy.js +99 -0
- package/dist/core/naruto/naruto-real-worker-child.js +110 -11
- package/dist/core/naruto/naruto-rebalance-policy.js +48 -0
- package/dist/core/naruto/naruto-task-hints.js +71 -0
- package/dist/core/naruto/naruto-work-graph.js +13 -0
- package/dist/core/pipeline/finalize-pipeline-result.js +3 -1
- package/dist/core/pipeline/gpt-final-required.js +22 -2
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-right-column-manager.js +45 -2
- package/dist/core/zellij/zellij-slot-column-anchor.js +218 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +81 -14
- package/dist/scripts/agent-real-codex-in-zellij-worker-pane-check.js +8 -2
- package/dist/scripts/agent-slot-pane-binding-proof-check.js +4 -4
- package/dist/scripts/codex-sdk-release-review-pipeline-check.js +2 -1
- package/dist/scripts/codex-sdk-zellij-pane-binding-check.js +2 -2
- package/dist/scripts/git-worktree-checkpoint-check.js +20 -0
- package/dist/scripts/git-worktree-cross-rebase-check.js +39 -0
- package/dist/scripts/git-worktree-merge-queue-check.js +1 -0
- package/dist/scripts/local-collab-worktree-gpt-final-apply-policy-check.js +63 -0
- package/dist/scripts/naruto-actual-worker-control-plane-check.js +56 -0
- package/dist/scripts/naruto-allocation-policy-check.js +33 -0
- package/dist/scripts/naruto-allocation-runtime-wiring-check.js +92 -0
- package/dist/scripts/naruto-extreme-parallelism-real-check.js +5 -4
- package/dist/scripts/naruto-orchestrator-runtime-source-check.js +70 -0
- package/dist/scripts/naruto-real-active-pool-runtime-check.js +4 -2
- package/dist/scripts/naruto-rebalance-policy-check.js +41 -0
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +8 -4
- package/dist/scripts/release-dag-full-coverage-check.js +19 -1
- package/dist/scripts/release-readiness-report.js +1 -1
- package/dist/scripts/release-real-check.js +258 -77
- package/dist/scripts/zellij-first-slot-down-stack-check.js +20 -0
- package/dist/scripts/zellij-first-slot-down-stack-real-check.js +356 -0
- package/dist/scripts/zellij-right-column-manager-check.js +7 -2
- package/dist/scripts/zellij-slot-column-anchor-check.js +45 -0
- package/dist/scripts/zellij-slot-only-ui-check.js +6 -2
- package/dist/scripts/zellij-slot-renderer-proof-semantics-check.js +59 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +23 -1
- package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +11 -4
- package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +21 -4
- package/package.json +15 -3
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function renderZellijSlotColumnAnchor(input = {}) {
|
|
4
|
+
const active = nonNegativeInt(input.activeWorkers, 0);
|
|
5
|
+
const visible = Math.max(1, nonNegativeInt(input.visiblePaneCap, active || 1));
|
|
6
|
+
const headless = nonNegativeInt(input.headlessWorkers, 0);
|
|
7
|
+
const queue = nonNegativeInt(input.queueDepth, 0);
|
|
8
|
+
const header = `SLOTS active ${active}/${visible} · headless ${headless} · q ${queue}`;
|
|
9
|
+
const workers = Array.isArray(input.workerRows) ? input.workerRows : [];
|
|
10
|
+
if (!workers.length)
|
|
11
|
+
return header;
|
|
12
|
+
const maxRows = Math.max(1, nonNegativeInt(input.maxWorkerRows, input.mode === 'full-debug' ? 24 : 12));
|
|
13
|
+
const visibleRows = workers.slice(0, maxRows);
|
|
14
|
+
const hidden = Math.max(0, workers.length - visibleRows.length);
|
|
15
|
+
return [
|
|
16
|
+
header,
|
|
17
|
+
...visibleRows.map((row, index) => renderWorkerRow(row, index + 1)),
|
|
18
|
+
...(hidden ? [`+${hidden} more worker${hidden === 1 ? '' : 's'}`] : [])
|
|
19
|
+
].join('\n');
|
|
20
|
+
}
|
|
21
|
+
export async function renderZellijSlotColumnAnchorFromArtifacts(input) {
|
|
22
|
+
const root = path.resolve(input.artifactRoot);
|
|
23
|
+
const missionDir = inferMissionDir(root, input.missionId);
|
|
24
|
+
const snapshot = await readJson(path.join(missionDir, 'zellij-dashboard-snapshot.json'));
|
|
25
|
+
const rightColumn = await readJson(path.join(missionDir, 'zellij-right-column-state.json'));
|
|
26
|
+
const swarm = await readJson(path.join(root, 'agent-native-cli-session-swarm.json'))
|
|
27
|
+
|| await readJson(path.join(missionDir, 'agents', 'agent-native-cli-session-swarm.json'));
|
|
28
|
+
const workerRows = await buildWorkerRows(root, missionDir, rightColumn, swarm);
|
|
29
|
+
const activeWorkers = Number(snapshot?.active_workers ?? workerRows.filter((row) => row.status === 'running' || row.status === 'launching').length ?? 0);
|
|
30
|
+
const visiblePaneCap = Number(snapshot?.visible_panes ?? Math.max(1, rightColumn?.visible_worker_panes?.length || activeWorkers || 1));
|
|
31
|
+
const headlessWorkers = Number(snapshot?.headless_workers ?? workerRows.filter((row) => row.placement === 'headless' && (!row.status || row.status === 'running')).length ?? 0);
|
|
32
|
+
const queueDepth = Number(snapshot?.queue_depth ?? 0);
|
|
33
|
+
const anchorInput = { activeWorkers, visiblePaneCap, headlessWorkers, queueDepth, workerRows };
|
|
34
|
+
if (input.mode !== undefined)
|
|
35
|
+
anchorInput.mode = input.mode;
|
|
36
|
+
return renderZellijSlotColumnAnchor(anchorInput);
|
|
37
|
+
}
|
|
38
|
+
export function buildZellijSlotColumnAnchorCommand(input) {
|
|
39
|
+
const args = [
|
|
40
|
+
input.cliPath,
|
|
41
|
+
'zellij-slot-column-anchor',
|
|
42
|
+
'--mission', input.missionId,
|
|
43
|
+
'--mode', input.mode,
|
|
44
|
+
'--artifact-root', input.artifactRoot,
|
|
45
|
+
...(input.watch ? ['--watch'] : [])
|
|
46
|
+
];
|
|
47
|
+
return [input.nodePath || process.execPath, ...args].map(shellQuote).join(' ');
|
|
48
|
+
}
|
|
49
|
+
function inferMissionDir(root, missionId) {
|
|
50
|
+
if (path.basename(root) === 'agents' && path.basename(path.dirname(root)) === missionId)
|
|
51
|
+
return path.dirname(root);
|
|
52
|
+
if (path.basename(root) === missionId && path.basename(path.dirname(root)) === 'missions')
|
|
53
|
+
return root;
|
|
54
|
+
return path.join(root, '.sneakoscope', 'missions', missionId);
|
|
55
|
+
}
|
|
56
|
+
async function readJson(file) {
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(await fs.promises.readFile(file, 'utf8'));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function buildWorkerRows(root, missionDir, rightColumn, swarm) {
|
|
65
|
+
const byKey = new Map();
|
|
66
|
+
const records = Array.isArray(swarm?.records) ? swarm.records : [];
|
|
67
|
+
const recordByKey = new Map();
|
|
68
|
+
for (const record of records) {
|
|
69
|
+
const key = workerKey(record?.slot_id, record?.generation_index);
|
|
70
|
+
if (key)
|
|
71
|
+
recordByKey.set(key, record);
|
|
72
|
+
}
|
|
73
|
+
for (const pane of Array.isArray(rightColumn?.visible_worker_panes) ? rightColumn.visible_worker_panes : []) {
|
|
74
|
+
const key = workerKey(pane?.slot_id, pane?.generation_index);
|
|
75
|
+
if (!key)
|
|
76
|
+
continue;
|
|
77
|
+
const record = recordByKey.get(key);
|
|
78
|
+
byKey.set(key, await hydrateWorkerRow(root, missionDir, {
|
|
79
|
+
slotId: String(pane.slot_id),
|
|
80
|
+
generationIndex: Number(pane.generation_index || 1),
|
|
81
|
+
placement: 'zellij-pane',
|
|
82
|
+
status: pane.status || record?.status || 'running',
|
|
83
|
+
paneId: pane.pane_id || record?.zellij_pane_id || null,
|
|
84
|
+
yOrder: Number(pane.y_order || 0)
|
|
85
|
+
}, record));
|
|
86
|
+
}
|
|
87
|
+
for (const row of Array.isArray(rightColumn?.headless_workers) ? rightColumn.headless_workers : []) {
|
|
88
|
+
const key = workerKey(row?.slot_id, row?.generation_index);
|
|
89
|
+
if (!key)
|
|
90
|
+
continue;
|
|
91
|
+
const record = recordByKey.get(key);
|
|
92
|
+
byKey.set(key, await hydrateWorkerRow(root, missionDir, {
|
|
93
|
+
slotId: String(row.slot_id),
|
|
94
|
+
generationIndex: Number(row.generation_index || 1),
|
|
95
|
+
placement: 'headless',
|
|
96
|
+
status: row.status || record?.status || 'running',
|
|
97
|
+
reason: row.reason || record?.headless_reason || null,
|
|
98
|
+
yOrder: 9000
|
|
99
|
+
}, record));
|
|
100
|
+
}
|
|
101
|
+
for (const record of records) {
|
|
102
|
+
const key = workerKey(record?.slot_id, record?.generation_index);
|
|
103
|
+
if (!key || byKey.has(key))
|
|
104
|
+
continue;
|
|
105
|
+
byKey.set(key, await hydrateWorkerRow(root, missionDir, {
|
|
106
|
+
slotId: String(record.slot_id || record.agent_id || 'slot-?'),
|
|
107
|
+
generationIndex: Number(record.generation_index || 1),
|
|
108
|
+
placement: record.worker_placement || (record.zellij_pane_id ? 'zellij-pane' : 'process'),
|
|
109
|
+
status: record.status || 'running',
|
|
110
|
+
paneId: record.zellij_pane_id || null,
|
|
111
|
+
yOrder: 5000
|
|
112
|
+
}, record));
|
|
113
|
+
}
|
|
114
|
+
return [...byKey.values()].sort((a, b) => {
|
|
115
|
+
const statusDelta = statusWeight(a.status) - statusWeight(b.status);
|
|
116
|
+
if (statusDelta)
|
|
117
|
+
return statusDelta;
|
|
118
|
+
const yDelta = Number(a.yOrder || 0) - Number(b.yOrder || 0);
|
|
119
|
+
if (yDelta)
|
|
120
|
+
return yDelta;
|
|
121
|
+
return String(a.slotId).localeCompare(String(b.slotId));
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async function hydrateWorkerRow(root, missionDir, base, record) {
|
|
125
|
+
const artifactDir = resolveArtifactDir(root, missionDir, record?.worker_artifact_dir);
|
|
126
|
+
const result = artifactDir ? await readJson(path.join(artifactDir, 'worker-result.json')) : null;
|
|
127
|
+
const intake = artifactDir ? await readJson(path.join(artifactDir, 'worker-intake.json')) : null;
|
|
128
|
+
const heartbeatPath = artifactDir ? path.join(artifactDir, 'worker-heartbeat.jsonl') : null;
|
|
129
|
+
return {
|
|
130
|
+
...base,
|
|
131
|
+
status: result?.status || base.status || record?.status || 'running',
|
|
132
|
+
backend: result?.backend || record?.backend || intake?.backend || null,
|
|
133
|
+
role: result?.persona_id || intake?.agent?.naruto_role || intake?.agent?.role || intake?.agent?.persona_id || null,
|
|
134
|
+
task: firstText([
|
|
135
|
+
result?.summary,
|
|
136
|
+
Array.isArray(result?.changed_files) ? result.changed_files[0] : null,
|
|
137
|
+
intake?.slice?.description,
|
|
138
|
+
intake?.slice?.title,
|
|
139
|
+
intake?.slice?.id,
|
|
140
|
+
base.reason
|
|
141
|
+
]),
|
|
142
|
+
worktreeId: result?.worktree?.id || record?.worktree?.id || intake?.worktree?.id || null,
|
|
143
|
+
heartbeatAgeMs: heartbeatPath ? await heartbeatAgeMs(heartbeatPath) : null
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function renderWorkerRow(row, index) {
|
|
147
|
+
const slot = `${trimInline(row.slotId || 'slot-?', 12)} g${Math.max(1, Math.floor(Number(row.generationIndex) || 1))}`;
|
|
148
|
+
const status = trimInline(row.status || 'running', 9);
|
|
149
|
+
const backend = trimInline(row.backend || row.placement || '-', 12);
|
|
150
|
+
const worktree = trimInline(row.worktreeId || row.role || '-', 10);
|
|
151
|
+
const task = trimInline(row.task || row.reason || '-', 38);
|
|
152
|
+
return `${String(index).padStart(2, '0')} ${slot} ${status} ${backend} ${worktree} · ${task} · hb ${formatHeartbeat(row.heartbeatAgeMs)}`;
|
|
153
|
+
}
|
|
154
|
+
function resolveArtifactDir(root, missionDir, value) {
|
|
155
|
+
if (!value)
|
|
156
|
+
return null;
|
|
157
|
+
const text = String(value);
|
|
158
|
+
if (path.isAbsolute(text))
|
|
159
|
+
return text;
|
|
160
|
+
return path.join(root, text);
|
|
161
|
+
}
|
|
162
|
+
async function heartbeatAgeMs(file) {
|
|
163
|
+
try {
|
|
164
|
+
return Date.now() - (await fs.promises.stat(file)).mtimeMs;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function formatHeartbeat(ageMs) {
|
|
171
|
+
if (ageMs == null)
|
|
172
|
+
return '?';
|
|
173
|
+
if (ageMs < 1000)
|
|
174
|
+
return 'now';
|
|
175
|
+
return `${Math.max(1, Math.round(ageMs / 1000))}s`;
|
|
176
|
+
}
|
|
177
|
+
function firstText(values) {
|
|
178
|
+
for (const value of values) {
|
|
179
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
180
|
+
if (text)
|
|
181
|
+
return text;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
function workerKey(slotId, generationIndex) {
|
|
186
|
+
const slot = String(slotId || '').trim();
|
|
187
|
+
if (!slot)
|
|
188
|
+
return null;
|
|
189
|
+
return `${slot}:g${Math.max(1, Math.floor(Number(generationIndex) || 1))}`;
|
|
190
|
+
}
|
|
191
|
+
function statusWeight(status) {
|
|
192
|
+
const text = String(status || '').toLowerCase();
|
|
193
|
+
if (text === 'running' || text === 'launching')
|
|
194
|
+
return 0;
|
|
195
|
+
if (text === 'failed')
|
|
196
|
+
return 1;
|
|
197
|
+
if (text === 'draining')
|
|
198
|
+
return 2;
|
|
199
|
+
if (text === 'closed')
|
|
200
|
+
return 3;
|
|
201
|
+
return 4;
|
|
202
|
+
}
|
|
203
|
+
function nonNegativeInt(value, fallback) {
|
|
204
|
+
const parsed = Number(value);
|
|
205
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
206
|
+
return fallback;
|
|
207
|
+
return Math.floor(parsed);
|
|
208
|
+
}
|
|
209
|
+
function trimInline(value, max) {
|
|
210
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
211
|
+
if (text.length <= max)
|
|
212
|
+
return text;
|
|
213
|
+
return text.slice(0, Math.max(1, max - 3)) + '...';
|
|
214
|
+
}
|
|
215
|
+
function shellQuote(value) {
|
|
216
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=zellij-slot-column-anchor.js.map
|
|
@@ -4,7 +4,8 @@ import { providerPaneLabel } from '../provider/provider-badge.js';
|
|
|
4
4
|
import { resolveProviderContext } from '../provider/provider-context.js';
|
|
5
5
|
import { runZellij } from './zellij-command.js';
|
|
6
6
|
import { extractZellijPaneIdFromOutput } from './zellij-lane-runtime.js';
|
|
7
|
-
import {
|
|
7
|
+
import { buildZellijSlotColumnAnchorCommand } from './zellij-slot-column-anchor.js';
|
|
8
|
+
import { closeWorkerInRightColumn, prepareWorkerInRightColumn, recordSlotColumnAnchorInRightColumn, recordWorkerPaneInRightColumn } from './zellij-right-column-manager.js';
|
|
8
9
|
export const ZELLIJ_WORKER_PANE_SCHEMA = 'sks.zellij-worker-pane.v1';
|
|
9
10
|
export const ZELLIJ_WORKER_PANE_EVENT_SCHEMA = 'sks.zellij-worker-pane-event.v1';
|
|
10
11
|
export function buildWorkerPaneName(slotId, generationIndex) {
|
|
@@ -25,6 +26,14 @@ export function buildWorkerPaneArtifact(input) {
|
|
|
25
26
|
const blockers = input.blockers || [];
|
|
26
27
|
const providerContext = normalizePaneProviderContext(input.providerContext, input.serviceTier);
|
|
27
28
|
const paneTitle = buildWorkerPaneTitle(input.slotId, input.generationIndex, providerContext, input.serviceTier, input.backend, input.statusLabel || input.status, input.worktree || null);
|
|
29
|
+
const commandText = String(input.workerCommand || '');
|
|
30
|
+
const isSlotRenderer = commandText.includes('zellij-slot-pane');
|
|
31
|
+
const paneKind = input.paneKind || (isSlotRenderer ? 'slot_status_renderer' : 'worker_codex_sdk');
|
|
32
|
+
const scalingPrimitive = input.scalingPrimitive || (isSlotRenderer
|
|
33
|
+
? 'native_cli_process_with_zellij_slot_renderer'
|
|
34
|
+
: 'native_cli_process_in_zellij_worker_pane');
|
|
35
|
+
const directionRequested = input.directionRequested || 'down';
|
|
36
|
+
const directionApplied = input.directionApplied || 'not_applied';
|
|
28
37
|
return {
|
|
29
38
|
schema: ZELLIJ_WORKER_PANE_SCHEMA,
|
|
30
39
|
generated_at: now,
|
|
@@ -38,7 +47,7 @@ export function buildWorkerPaneArtifact(input) {
|
|
|
38
47
|
session_id: input.sessionId,
|
|
39
48
|
pane_name: paneTitle,
|
|
40
49
|
pane_title: paneTitle,
|
|
41
|
-
pane_kind:
|
|
50
|
+
pane_kind: paneKind,
|
|
42
51
|
pane_id: input.paneId || null,
|
|
43
52
|
pane_id_source: paneIdSource,
|
|
44
53
|
provider: providerContext.provider,
|
|
@@ -52,16 +61,21 @@ export function buildWorkerPaneArtifact(input) {
|
|
|
52
61
|
stdout_log: input.stdoutLog,
|
|
53
62
|
stderr_log: input.stderrLog,
|
|
54
63
|
parent_child_transport: 'worker-result-json-and-heartbeat',
|
|
55
|
-
scaling_primitive:
|
|
56
|
-
command:
|
|
64
|
+
scaling_primitive: scalingPrimitive,
|
|
65
|
+
command: isSlotRenderer ? '<zellij-slot-pane-renderer-command>' : '<native-cli-worker-command>',
|
|
57
66
|
create_session: input.createSession || null,
|
|
58
67
|
launch: input.launch || null,
|
|
59
68
|
pane_reconciliation: input.paneReconciliation || null,
|
|
60
69
|
opened_at: now,
|
|
61
70
|
closed_at: null,
|
|
62
71
|
close: null,
|
|
63
|
-
direction_requested:
|
|
64
|
-
direction_applied:
|
|
72
|
+
direction_requested: directionRequested,
|
|
73
|
+
direction_applied: directionApplied,
|
|
74
|
+
column_creation_direction_requested: input.columnCreationDirectionRequested ?? null,
|
|
75
|
+
column_creation_direction_applied: input.columnCreationDirectionApplied ?? null,
|
|
76
|
+
worker_direction_requested: input.workerDirectionRequested || 'down',
|
|
77
|
+
worker_direction_applied: input.workerDirectionApplied || (directionApplied === 'down' ? 'down' : directionApplied === 'unknown' ? 'unknown' : 'not_applied'),
|
|
78
|
+
slot_column_anchor_pane_id: input.slotColumnAnchorPaneId || null,
|
|
65
79
|
right_column: input.rightColumn || null,
|
|
66
80
|
sdk_thread_id: input.sdkThreadId || null,
|
|
67
81
|
sdk_run_id: input.sdkRunId || null,
|
|
@@ -113,12 +127,18 @@ export async function openWorkerPane(input) {
|
|
|
113
127
|
createSession,
|
|
114
128
|
launch: null,
|
|
115
129
|
paneReconciliation: null,
|
|
116
|
-
directionRequested: '
|
|
130
|
+
directionRequested: 'down',
|
|
117
131
|
directionApplied: 'not_applied',
|
|
132
|
+
columnCreationDirectionRequested: null,
|
|
133
|
+
columnCreationDirectionApplied: 'not_applied',
|
|
134
|
+
workerDirectionRequested: 'down',
|
|
135
|
+
workerDirectionApplied: 'not_applied',
|
|
136
|
+
slotColumnAnchorPaneId: rightColumn.state.slot_column_anchor_pane_id || null,
|
|
118
137
|
rightColumn: {
|
|
119
138
|
mode: 'spawn-on-first-worker',
|
|
120
139
|
focus_pane_id: null,
|
|
121
|
-
y_order: null
|
|
140
|
+
y_order: null,
|
|
141
|
+
slot_column_anchor_pane_id: rightColumn.state.slot_column_anchor_pane_id || null
|
|
122
142
|
},
|
|
123
143
|
status: 'running',
|
|
124
144
|
providerContext,
|
|
@@ -135,9 +155,42 @@ export async function openWorkerPane(input) {
|
|
|
135
155
|
return record;
|
|
136
156
|
}
|
|
137
157
|
const paneName = buildWorkerPaneTitle(input.slotId, input.generationIndex, providerContext, input.serviceTier, input.backend, input.statusLabel || 'running', input.worktree || null);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
158
|
+
let slotColumnAnchorPaneId = rightColumn?.state.slot_column_anchor_pane_id || null;
|
|
159
|
+
let anchorLaunch = null;
|
|
160
|
+
let columnCreationDirectionRequested = null;
|
|
161
|
+
let columnCreationDirectionApplied = null;
|
|
162
|
+
if (rightColumn && !rightColumn.focusPaneId && !slotColumnAnchorPaneId) {
|
|
163
|
+
columnCreationDirectionRequested = 'right';
|
|
164
|
+
const anchorCommand = buildZellijSlotColumnAnchorCommand({
|
|
165
|
+
cliPath: path.join(packageRoot(), 'dist', 'bin', 'sks.js'),
|
|
166
|
+
missionId: input.missionId,
|
|
167
|
+
mode: input.uiMode || 'compact-slots',
|
|
168
|
+
artifactRoot: root,
|
|
169
|
+
watch: true
|
|
170
|
+
});
|
|
171
|
+
anchorLaunch = createSession.ok
|
|
172
|
+
? await runZellij(['--session', input.sessionName, 'action', 'new-pane', '--direction', 'right', '--name', 'SLOTS', '--', 'sh', '-lc', anchorCommand], {
|
|
173
|
+
cwd,
|
|
174
|
+
timeoutMs: 5000,
|
|
175
|
+
optional: false
|
|
176
|
+
})
|
|
177
|
+
: null;
|
|
178
|
+
slotColumnAnchorPaneId = anchorLaunch?.ok ? extractZellijPaneIdFromOutput(anchorLaunch.stdout_tail) : null;
|
|
179
|
+
columnCreationDirectionApplied = anchorLaunch?.ok ? (slotColumnAnchorPaneId ? 'right' : 'unknown') : 'not_applied';
|
|
180
|
+
if (slotColumnAnchorPaneId) {
|
|
181
|
+
await recordSlotColumnAnchorInRightColumn({
|
|
182
|
+
root,
|
|
183
|
+
...(input.projectRoot ? { projectRoot: input.projectRoot } : {}),
|
|
184
|
+
missionId: input.missionId,
|
|
185
|
+
sessionName: input.sessionName,
|
|
186
|
+
paneId: slotColumnAnchorPaneId
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const focusPaneId = rightColumn?.focusPaneId || slotColumnAnchorPaneId || null;
|
|
191
|
+
const directionRequested = 'down';
|
|
192
|
+
const focus = focusPaneId
|
|
193
|
+
? await focusZellijPaneById(input.sessionName, focusPaneId, cwd)
|
|
141
194
|
: null;
|
|
142
195
|
const newPaneArgs = ['--session', input.sessionName, 'action', 'new-pane', '--direction', directionRequested, ...(directionRequested === 'down' ? ['--near-current-pane'] : []), '--name', paneName, '--', 'sh', '-lc', input.workerCommand];
|
|
143
196
|
let launch = createSession.ok
|
|
@@ -176,6 +229,8 @@ export async function openWorkerPane(input) {
|
|
|
176
229
|
const blockers = [
|
|
177
230
|
...(createSession.ok ? [] : createSession.blockers.map((blocker) => `zellij_worker_session_${blocker}`)),
|
|
178
231
|
...(rightColumn && rightColumn.placement !== 'zellij-pane' ? [`zellij_worker_right_column_${rightColumn.placement}`] : []),
|
|
232
|
+
...(rightColumn && columnCreationDirectionRequested === 'right' && columnCreationDirectionApplied !== 'right' ? ['zellij_worker_slot_column_anchor_not_created'] : []),
|
|
233
|
+
...(anchorLaunch && !anchorLaunch.ok ? anchorLaunch.blockers.map((blocker) => `zellij_worker_slot_column_anchor_${blocker}`) : []),
|
|
179
234
|
...(launch && !launch.ok ? launch.blockers.map((blocker) => `zellij_worker_pane_${blocker}`) : []),
|
|
180
235
|
...(launch?.ok && !isRealZellijWorkerPaneIdSource(paneIdSource) ? ['zellij_worker_pane_id_real_source_missing'] : [])
|
|
181
236
|
];
|
|
@@ -189,11 +244,18 @@ export async function openWorkerPane(input) {
|
|
|
189
244
|
...(reconciledPane || {}),
|
|
190
245
|
focus_pane: focus,
|
|
191
246
|
focus_degraded: focus ? focus.ok !== true : false,
|
|
247
|
+
slot_column_anchor_pane_id: slotColumnAnchorPaneId,
|
|
248
|
+
slot_column_anchor_launch: anchorLaunch,
|
|
192
249
|
rename_pane: renamePane
|
|
193
250
|
},
|
|
194
251
|
directionRequested,
|
|
195
252
|
directionApplied,
|
|
196
|
-
|
|
253
|
+
columnCreationDirectionRequested,
|
|
254
|
+
columnCreationDirectionApplied,
|
|
255
|
+
workerDirectionRequested: 'down',
|
|
256
|
+
workerDirectionApplied: directionApplied === 'down' ? 'down' : directionApplied === 'unknown' ? 'unknown' : 'not_applied',
|
|
257
|
+
slotColumnAnchorPaneId,
|
|
258
|
+
rightColumn: rightColumn ? { mode: 'spawn-on-first-worker', focus_pane_id: focusPaneId, y_order: rightColumn.yOrder, slot_column_anchor_pane_id: slotColumnAnchorPaneId } : null,
|
|
197
259
|
status: blockers.length ? 'failed' : 'running',
|
|
198
260
|
providerContext,
|
|
199
261
|
serviceTier: input.serviceTier || providerContext.service_tier,
|
|
@@ -226,7 +288,7 @@ export async function openWorkerPane(input) {
|
|
|
226
288
|
session_name: input.sessionName,
|
|
227
289
|
pane_name: paneName,
|
|
228
290
|
pane_title: paneName,
|
|
229
|
-
pane_kind:
|
|
291
|
+
pane_kind: record.pane_kind,
|
|
230
292
|
pane_id: record.pane_id,
|
|
231
293
|
pane_id_source: record.pane_id_source,
|
|
232
294
|
provider: record.provider,
|
|
@@ -234,6 +296,11 @@ export async function openWorkerPane(input) {
|
|
|
234
296
|
provider_context: record.provider_context,
|
|
235
297
|
direction_requested: record.direction_requested,
|
|
236
298
|
direction_applied: record.direction_applied,
|
|
299
|
+
column_creation_direction_requested: record.column_creation_direction_requested || null,
|
|
300
|
+
column_creation_direction_applied: record.column_creation_direction_applied || null,
|
|
301
|
+
worker_direction_requested: record.worker_direction_requested,
|
|
302
|
+
worker_direction_applied: record.worker_direction_applied,
|
|
303
|
+
slot_column_anchor_pane_id: record.slot_column_anchor_pane_id || null,
|
|
237
304
|
command: record.command,
|
|
238
305
|
worker_artifact_dir: input.workerArtifactDir,
|
|
239
306
|
worker_result_path: input.resultPath,
|
|
@@ -241,7 +308,7 @@ export async function openWorkerPane(input) {
|
|
|
241
308
|
patch_envelope_path: input.patchEnvelopePath,
|
|
242
309
|
parent_child_transport: 'worker-result-json-and-heartbeat',
|
|
243
310
|
persistent_slot_lane: false,
|
|
244
|
-
scaling_primitive:
|
|
311
|
+
scaling_primitive: record.scaling_primitive,
|
|
245
312
|
blockers
|
|
246
313
|
});
|
|
247
314
|
return record;
|
|
@@ -123,7 +123,7 @@ async function runRealGate({ sourceOk, eventProof }) {
|
|
|
123
123
|
async function inspectRealLedger(ledgerRoot) {
|
|
124
124
|
const swarm = await readJson(path.join(ledgerRoot, 'agent-native-cli-session-swarm.json'), null);
|
|
125
125
|
const records = Array.isArray(swarm?.records) ? swarm.records : [];
|
|
126
|
-
const workerRecords = records.filter(
|
|
126
|
+
const workerRecords = records.filter(isZellijCodexWorkerRecord);
|
|
127
127
|
const eventsByWorker = [];
|
|
128
128
|
for (const record of workerRecords) {
|
|
129
129
|
const workerDir = String(record.worker_artifact_dir || '');
|
|
@@ -137,7 +137,7 @@ async function inspectRealLedger(ledgerRoot) {
|
|
|
137
137
|
const blockers = [
|
|
138
138
|
...(swarm?.ok === true ? [] : ['real_codex_zellij_swarm_not_ok']),
|
|
139
139
|
...(workerRecords.length >= 1 ? [] : ['real_codex_zellij_worker_record_missing']),
|
|
140
|
-
...(workerRecords.some((row) => row.pane_kind === 'worker_codex_sdk') ? [] : ['real_codex_zellij_worker_pane_kind_missing']),
|
|
140
|
+
...(workerRecords.some((row) => row.pane_kind === 'worker_codex_sdk' || row.pane_kind === 'slot_status_renderer') ? [] : ['real_codex_zellij_worker_pane_kind_missing']),
|
|
141
141
|
...(workerRecords.every((row) => manager.isRealZellijWorkerPaneIdSource(row.zellij_pane_id_source)) ? [] : ['real_codex_zellij_pane_id_source_not_real']),
|
|
142
142
|
...(workerRecords.every((row) => row.zellij_pane_id) ? [] : ['real_codex_zellij_pane_id_missing']),
|
|
143
143
|
...(workerRecords.every((row) => row.sdk_thread_id) ? [] : ['real_codex_zellij_sdk_thread_missing']),
|
|
@@ -171,6 +171,12 @@ async function inspectRealLedger(ledgerRoot) {
|
|
|
171
171
|
blockers
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
+
function isZellijCodexWorkerRecord(row) {
|
|
175
|
+
return row?.scaling_primitive === 'native_cli_process_in_zellij_worker_pane'
|
|
176
|
+
|| row?.scaling_primitive === 'native_cli_process_with_zellij_slot_renderer'
|
|
177
|
+
|| row?.pane_kind === 'worker_codex_sdk'
|
|
178
|
+
|| row?.pane_kind === 'slot_status_renderer';
|
|
179
|
+
}
|
|
174
180
|
async function emit(report) {
|
|
175
181
|
await fs.mkdir(path.join(root, '.sneakoscope', 'reports'), { recursive: true });
|
|
176
182
|
await fs.writeFile(path.join(root, '.sneakoscope', 'reports', 'agent-real-codex-in-zellij-worker-pane.json'), `${JSON.stringify(report, null, 2)}\n`);
|
|
@@ -14,10 +14,10 @@ const records = [1, 2, 3].map((index) => ({
|
|
|
14
14
|
session_id: `slot-001-gen-${index}`,
|
|
15
15
|
slot_id: 'slot-001',
|
|
16
16
|
generation_index: index,
|
|
17
|
-
pane_kind: 'worker_codex_sdk',
|
|
17
|
+
pane_kind: index === 1 ? 'slot_status_renderer' : 'worker_codex_sdk',
|
|
18
18
|
zellij_pane_id: String(100 + index),
|
|
19
19
|
zellij_pane_id_source: index % 2 ? 'zellij_worker_new_pane_stdout' : 'zellij_worker_list_panes',
|
|
20
|
-
scaling_primitive: 'native_cli_process_in_zellij_worker_pane',
|
|
20
|
+
scaling_primitive: index === 1 ? 'native_cli_process_with_zellij_slot_renderer' : 'native_cli_process_in_zellij_worker_pane',
|
|
21
21
|
sdk_thread_id: `sdk-thread-${index}`,
|
|
22
22
|
sdk_run_id: `sdk-run-${index}`,
|
|
23
23
|
stream_event_count: 4,
|
|
@@ -35,9 +35,9 @@ await fs.writeFile(path.join(tmp, 'agent-native-cli-session-swarm.json'), `${JSO
|
|
|
35
35
|
}, null, 2)}\n`);
|
|
36
36
|
await fs.writeFile(path.join(tmp, 'agent-zellij-pane-launch-ledger.jsonl'), records.map((record) => JSON.stringify({
|
|
37
37
|
schema: 'sks.agent-zellij-pane-launch.v1',
|
|
38
|
-
pane_kind:
|
|
38
|
+
pane_kind: record.pane_kind,
|
|
39
39
|
persistent_slot_lane: false,
|
|
40
|
-
scaling_primitive:
|
|
40
|
+
scaling_primitive: record.scaling_primitive,
|
|
41
41
|
slot_id: record.slot_id,
|
|
42
42
|
generation_index: record.generation_index,
|
|
43
43
|
pane_id: record.zellij_pane_id,
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
import { assertGate, emitGate, packageScripts, readText, releaseGateIds } from './lib/codex-sdk-gate-lib.js';
|
|
4
4
|
const scripts = packageScripts();
|
|
5
5
|
const releaseRealCheck = String(scripts['release:real-check'] || '');
|
|
6
|
+
const releaseRealCheckSource = readText('src/scripts/release-real-check.ts');
|
|
6
7
|
const releaseGates = releaseGateIds();
|
|
7
8
|
assertGate(releaseGates.has('codex-sdk:capability'), 'release gate DAG must include Codex SDK capability gate');
|
|
8
9
|
assertGate(releaseGates.has('codex-sdk:all-pipelines'), 'release gate DAG must include Codex SDK all-pipelines gate');
|
|
9
|
-
assertGate(releaseRealCheck.includes('codex-sdk:real-smoke'), 'release:real-check must include Codex SDK real smoke');
|
|
10
|
+
assertGate(releaseRealCheck.includes('codex-sdk:real-smoke') || releaseRealCheckSource.includes('codex-sdk:real-smoke'), 'release:real-check must include Codex SDK real smoke');
|
|
10
11
|
assertGate(readText('src/core/agents/agent-orchestrator.ts').includes('legacy_codex_exec_runtime_removed'), 'orchestrator must block legacy codex-exec requests');
|
|
11
12
|
emitGate('codex-sdk:release-review-pipeline', { release_gate_dag_contains_sdk: true });
|
|
12
13
|
//# sourceMappingURL=codex-sdk-release-review-pipeline-check.js.map
|
|
@@ -7,7 +7,7 @@ const proofSource = readText('src/core/agents/agent-slot-pane-binding-proof.ts')
|
|
|
7
7
|
const managerSource = readText('src/core/zellij/zellij-worker-pane-manager.ts');
|
|
8
8
|
assertGate(fixture.proof.zellij_pane_id === 'pane-999', 'control proof must link zellij pane id', fixture.proof);
|
|
9
9
|
assertGate(swarmSource.includes('codex_sdk_thread_started'), 'swarm must emit SDK thread event');
|
|
10
|
-
assertGate(proofSource.includes('worker_codex_sdk'), 'slot-pane proof must
|
|
11
|
-
assertGate(managerSource.includes(
|
|
10
|
+
assertGate(proofSource.includes('worker_codex_sdk') && proofSource.includes('slot_status_renderer'), 'slot-pane proof must accept worker command panes and compact slot renderer panes');
|
|
11
|
+
assertGate(managerSource.includes('slot_status_renderer') && managerSource.includes('worker_codex_sdk'), 'worker pane manager must distinguish slot renderer and worker command pane kinds');
|
|
12
12
|
emitGate('codex-sdk:zellij-pane-binding', { zellij_pane_id: fixture.proof.zellij_pane_id, sdk_thread_id: fixture.proof.sdk_thread_id });
|
|
13
13
|
//# sourceMappingURL=codex-sdk-zellij-pane-binding-check.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
6
|
+
import { checkpointWorkerWorktree } from '../core/git/git-worktree-checkpoint.js';
|
|
7
|
+
import { runGitCommand } from '../core/git/git-worktree-runner.js';
|
|
8
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-worktree-checkpoint-'));
|
|
9
|
+
await runGitCommand(root, ['init']);
|
|
10
|
+
await runGitCommand(root, ['config', 'user.email', 'sks@example.test']);
|
|
11
|
+
await runGitCommand(root, ['config', 'user.name', 'SKS Test']);
|
|
12
|
+
fs.writeFileSync(path.join(root, 'a.txt'), 'a\n');
|
|
13
|
+
await runGitCommand(root, ['add', 'a.txt']);
|
|
14
|
+
await runGitCommand(root, ['commit', '-m', 'base']);
|
|
15
|
+
fs.writeFileSync(path.join(root, 'a.txt'), 'a2\n');
|
|
16
|
+
fs.writeFileSync(path.join(root, 'b.txt'), 'b\n');
|
|
17
|
+
const report = await checkpointWorkerWorktree({ worktreePath: root, repoRoot: root, workerId: 'W1', taskId: 'T1', mode: 'auto' });
|
|
18
|
+
assertGate(report.ok && report.mode_applied === 'checkpoint-commit' && Boolean(report.commit_hash), 'Git worktree checkpoint must create checkpoint commits for multi-file changes', report);
|
|
19
|
+
emitGate('git:worktree-checkpoint', report);
|
|
20
|
+
//# sourceMappingURL=git-worktree-checkpoint-check.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
6
|
+
import { crossRebaseIdleWorktrees } from '../core/git/git-worktree-cross-rebase.js';
|
|
7
|
+
import { runGitCommand, gitOutputLine } from '../core/git/git-worktree-runner.js';
|
|
8
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-worktree-cross-rebase-'));
|
|
9
|
+
await runGitCommand(root, ['init']);
|
|
10
|
+
await runGitCommand(root, ['config', 'user.email', 'sks@example.test']);
|
|
11
|
+
await runGitCommand(root, ['config', 'user.name', 'SKS Test']);
|
|
12
|
+
fs.writeFileSync(path.join(root, 'base.txt'), 'base\n');
|
|
13
|
+
await runGitCommand(root, ['add', 'base.txt']);
|
|
14
|
+
await runGitCommand(root, ['commit', '-m', 'base']);
|
|
15
|
+
const workerPath = path.join(root, '..', `worker-${process.pid}`);
|
|
16
|
+
const dirtyWorkerPath = path.join(root, '..', `worker-dirty-${process.pid}`);
|
|
17
|
+
const runningWorkerPath = path.join(root, '..', `worker-running-${process.pid}`);
|
|
18
|
+
await runGitCommand(root, ['worktree', 'add', workerPath, 'HEAD']);
|
|
19
|
+
await runGitCommand(root, ['worktree', 'add', dirtyWorkerPath, 'HEAD']);
|
|
20
|
+
await runGitCommand(root, ['worktree', 'add', runningWorkerPath, 'HEAD']);
|
|
21
|
+
fs.writeFileSync(path.join(dirtyWorkerPath, 'dirty.txt'), 'dirty\n');
|
|
22
|
+
fs.writeFileSync(path.join(root, 'integration.txt'), 'integration\n');
|
|
23
|
+
await runGitCommand(root, ['add', 'integration.txt']);
|
|
24
|
+
await runGitCommand(root, ['commit', '-m', 'integration']);
|
|
25
|
+
const head = gitOutputLine(await runGitCommand(root, ['rev-parse', 'HEAD']));
|
|
26
|
+
const report = await crossRebaseIdleWorktrees({
|
|
27
|
+
integrationHead: head,
|
|
28
|
+
workers: [
|
|
29
|
+
{ worker_id: 'W1', worktree_path: workerPath, state: 'idle' },
|
|
30
|
+
{ worker_id: 'W-dirty', worktree_path: dirtyWorkerPath, state: 'idle' },
|
|
31
|
+
{ worker_id: 'W-running', worktree_path: runningWorkerPath, state: 'running' }
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
assertGate(report.ok && report.applied_count === 1, 'Idle worktrees must cross-rebase to the integration head', report);
|
|
35
|
+
assertGate(report.skipped_count === 2, 'Dirty and running worktrees must be skipped during cross-rebase', report);
|
|
36
|
+
assertGate(report.records.some((row) => row.worker_id === 'W-dirty' && row.status === 'skipped' && row.reason === 'dirty_worktree_skipped'), 'Dirty worktree must skip cross-rebase', report);
|
|
37
|
+
assertGate(report.records.some((row) => row.worker_id === 'W-running' && row.status === 'skipped' && row.reason === 'worker_not_idle'), 'Running worktree must skip cross-rebase', report);
|
|
38
|
+
emitGate('git:worktree-cross-rebase', report);
|
|
39
|
+
//# sourceMappingURL=git-worktree-cross-rebase-check.js.map
|
|
@@ -20,6 +20,7 @@ const diffB = await diffMod.exportGitWorktreeDiff({ mainRepoRoot: repo, worktree
|
|
|
20
20
|
const integration = await integrationMod.createGitIntegrationWorktree({ repoRoot: repo, missionId: 'M-merge' });
|
|
21
21
|
const report = await queueMod.applyGitWorktreeMergeQueue({ integrationWorktreePath: integration.worktree_path, diffs: [diffA, diffB] });
|
|
22
22
|
assertGate(report.ok === true && report.applied_count === 2, 'merge queue must apply non-conflicting worktree diffs', report);
|
|
23
|
+
assertGate((report.strategy_results || []).every((row) => row.strategy === 'diff-apply-3way'), 'merge queue must record the diff apply strategy for worktree diffs', report.strategy_results || []);
|
|
23
24
|
assertGate(fs.readFileSync(path.join(integration.worktree_path, 'a.txt'), 'utf8').includes('from-a'), 'integration worktree must include first diff');
|
|
24
25
|
assertGate(fs.readFileSync(path.join(integration.worktree_path, 'b.txt'), 'utf8').includes('from-b'), 'integration worktree must include second diff');
|
|
25
26
|
assertGate(fs.readFileSync(path.join(repo, 'a.txt'), 'utf8') === 'alpha\n', 'main worktree must remain unchanged before integration commit');
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
|
|
4
|
+
import { gptFinalRequiredForPipeline } from '../core/pipeline/gpt-final-required.js';
|
|
5
|
+
import { finalizePipelineResult } from '../core/pipeline/finalize-pipeline-result.js';
|
|
6
|
+
import { selectFinalGptPatchSource } from '../core/pipeline/final-gpt-patch-stage.js';
|
|
7
|
+
const worktreeEnvelope = {
|
|
8
|
+
schema: 'sks.agent-patch-envelope.v1',
|
|
9
|
+
source: 'git-worktree-diff',
|
|
10
|
+
git_worktree: {
|
|
11
|
+
worktree_path: '/tmp/sks-worker',
|
|
12
|
+
changed_files: ['src/core/example.ts']
|
|
13
|
+
},
|
|
14
|
+
operations: [{ op: 'git_apply_patch', path: '.', diff: 'diff --git a/src/core/example.ts b/src/core/example.ts\n' }]
|
|
15
|
+
};
|
|
16
|
+
const requirement = gptFinalRequiredForPipeline({
|
|
17
|
+
localParticipated: false,
|
|
18
|
+
candidateResults: [{ backend: 'codex-sdk', patch_envelopes: [worktreeEnvelope] }],
|
|
19
|
+
candidatePatchEnvelopes: [worktreeEnvelope]
|
|
20
|
+
});
|
|
21
|
+
const blocked = await finalizePipelineResult({
|
|
22
|
+
route: '$Naruto',
|
|
23
|
+
missionId: 'M-worktree-gpt-final-policy',
|
|
24
|
+
localParticipated: false,
|
|
25
|
+
candidateResults: [{ backend: 'codex-sdk', patch_envelopes: [worktreeEnvelope] }],
|
|
26
|
+
candidatePatchEnvelopes: [worktreeEnvelope],
|
|
27
|
+
verificationResults: [],
|
|
28
|
+
sideEffectReport: {},
|
|
29
|
+
mutationLedger: {},
|
|
30
|
+
rollbackPlan: {},
|
|
31
|
+
applyPatches: true,
|
|
32
|
+
forceGptFinalUnavailable: true
|
|
33
|
+
});
|
|
34
|
+
const modified = selectFinalGptPatchSource({
|
|
35
|
+
result: {
|
|
36
|
+
status: 'modified',
|
|
37
|
+
modified_patch_envelopes: [{ ...worktreeEnvelope, source: 'model_authored', operations: [{ op: 'replace', path: 'src/core/example.ts', search: 'old', replace: 'new' }] }]
|
|
38
|
+
}
|
|
39
|
+
}, [worktreeEnvelope]);
|
|
40
|
+
const rejected = selectFinalGptPatchSource({ result: { status: 'rejected' } }, [worktreeEnvelope]);
|
|
41
|
+
const ok = requirement.gpt_final_required === true
|
|
42
|
+
&& requirement.worktree_participated === true
|
|
43
|
+
&& blocked.ok === false
|
|
44
|
+
&& blocked.apply_allowed === false
|
|
45
|
+
&& blocked.blockers.includes('gpt_final_arbiter_required_not_passed')
|
|
46
|
+
&& modified.ok === true
|
|
47
|
+
&& modified.final_patch_source === 'gpt_final_arbiter'
|
|
48
|
+
&& modified.patch_envelopes[0]?.source === 'model_authored'
|
|
49
|
+
&& rejected.ok === false
|
|
50
|
+
&& rejected.final_patch_source === 'blocked';
|
|
51
|
+
assertGate(ok, 'Worktree/local candidate output must require GPT Final before apply, and GPT modified/rejected decisions must control final patch source', {
|
|
52
|
+
requirement,
|
|
53
|
+
blocked,
|
|
54
|
+
modified,
|
|
55
|
+
rejected
|
|
56
|
+
});
|
|
57
|
+
emitGate('local-collab:worktree-gpt-final-apply-policy', {
|
|
58
|
+
requirement,
|
|
59
|
+
blocked_apply_allowed: blocked.apply_allowed,
|
|
60
|
+
modified_source: modified.final_patch_source,
|
|
61
|
+
rejected_source: rejected.final_patch_source
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=local-collab-worktree-gpt-final-apply-policy-check.js.map
|