sneakoscope 2.0.12 → 2.0.13
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 -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/build-manifest.json +24 -8
- package/dist/core/codex-control/codex-sdk-adapter.js +10 -0
- package/dist/core/codex-control/codex-task-runner.js +4 -2
- package/dist/core/commands/research-command.js +43 -4
- package/dist/core/fsx.js +1 -1
- package/dist/core/research/claim-evidence-matrix.js +160 -0
- package/dist/core/research/experiment-plan.js +53 -0
- package/dist/core/research/falsification.js +18 -0
- package/dist/core/research/implementation-blueprint-markdown.js +31 -0
- package/dist/core/research/implementation-blueprint.js +66 -0
- package/dist/core/research/replication-pack.js +50 -0
- package/dist/core/research/research-cycle-runner.js +25 -0
- package/dist/core/research/research-final-reviewer.js +58 -0
- package/dist/core/research/research-handoff.js +51 -0
- package/dist/core/research/research-prompt-contract.js +24 -0
- package/dist/core/research/research-quality-contract.js +61 -0
- package/dist/core/research/research-report-quality.js +67 -0
- package/dist/core/research/research-stage-runner.js +16 -0
- package/dist/core/research/research-work-graph.js +75 -0
- package/dist/core/research/source-quality-report.js +94 -0
- package/dist/core/research.js +344 -44
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-column-anchor.js +5 -3
- package/dist/core/zellij/zellij-slot-pane-renderer.js +259 -16
- package/dist/scripts/codex-sdk-research-pipeline-check.js +7 -0
- package/dist/scripts/packlist-performance-check.js +1 -1
- package/dist/scripts/research-quality-gate-check.js +86 -0
- package/dist/scripts/zellij-slot-column-anchor-check.js +26 -5
- package/dist/scripts/zellij-slot-pane-renderer-check.js +73 -5
- package/package.json +13 -1
- package/schemas/research/claim-evidence-matrix.schema.json +37 -0
- package/schemas/research/experiment-plan.schema.json +17 -0
- package/schemas/research/implementation-blueprint.schema.json +30 -0
- package/schemas/research/replication-pack.schema.json +17 -0
- package/schemas/research/research-final-review.schema.json +16 -0
- package/schemas/research/research-quality-contract.schema.json +37 -0
- package/schemas/research/source-quality-report.schema.json +18 -0
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '2.0.
|
|
1
|
+
export const PACKAGE_VERSION = '2.0.13';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -8,14 +8,16 @@ export function renderZellijSlotColumnAnchor(input = {}) {
|
|
|
8
8
|
const header = `SLOTS active ${active}/${visible} · headless ${headless} · q ${queue}`;
|
|
9
9
|
const workers = Array.isArray(input.workerRows) ? input.workerRows : [];
|
|
10
10
|
if (!workers.length)
|
|
11
|
-
return header
|
|
11
|
+
return `${header}\nvisible slot panes stack below this anchor`;
|
|
12
12
|
const maxRows = Math.max(1, nonNegativeInt(input.maxWorkerRows, input.mode === 'full-debug' ? 24 : 12));
|
|
13
|
-
const
|
|
13
|
+
const overflowRows = workers.filter((row) => row.placement === 'headless').slice(0, maxRows);
|
|
14
|
+
const visibleRows = overflowRows.length ? overflowRows : workers.filter((row) => row.placement !== 'zellij-pane').slice(0, maxRows);
|
|
14
15
|
const hidden = Math.max(0, workers.length - visibleRows.length);
|
|
15
16
|
return [
|
|
16
17
|
header,
|
|
18
|
+
`visible slot panes stack below this anchor`,
|
|
17
19
|
...visibleRows.map((row, index) => renderWorkerRow(row, index + 1)),
|
|
18
|
-
...(hidden ? [`+${hidden}
|
|
20
|
+
...(hidden && visibleRows.length ? [`+${hidden} worker${hidden === 1 ? '' : 's'} in dedicated panes or overflow`] : [])
|
|
19
21
|
].join('\n');
|
|
20
22
|
}
|
|
21
23
|
export async function renderZellijSlotColumnAnchorFromArtifacts(input) {
|
|
@@ -2,40 +2,121 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
export function renderZellijSlotPane(input) {
|
|
4
4
|
const mode = input.mode || 'compact-slots';
|
|
5
|
-
const maxLines = mode === 'compact-slots' ?
|
|
6
|
-
const task = trimInline(input.
|
|
5
|
+
const maxLines = mode === 'compact-slots' ? 17 : mode === 'dashboard-plus-slots' ? 20 : 32;
|
|
6
|
+
const task = trimInline(input.currentTask || input.currentFile || 'waiting for worker intake', 78);
|
|
7
7
|
const heartbeat = input.heartbeatAgeMs == null
|
|
8
8
|
? 'unknown'
|
|
9
9
|
: input.heartbeatAgeMs < 1000
|
|
10
10
|
? 'now'
|
|
11
11
|
: `${Math.max(1, Math.round(input.heartbeatAgeMs / 1000))}s ago`;
|
|
12
|
+
const files = firstNonEmptyList(input.changedFiles, input.patchFiles, input.plannedFiles, input.currentFile ? [input.currentFile] : []);
|
|
13
|
+
const events = (input.eventLines || []).filter(Boolean).slice(-3);
|
|
14
|
+
const stdout = (input.stdoutTail || []).filter(Boolean).slice(-2);
|
|
15
|
+
const stderr = (input.stderrTail || []).filter(Boolean).slice(-1);
|
|
12
16
|
const rows = [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
`
|
|
16
|
-
`
|
|
17
|
-
`
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
`slot: ${input.slotId} / gen-${Math.max(1, Math.floor(Number(input.generationIndex) || 1))} / ${trimInline(input.status || 'running', 18)}`,
|
|
18
|
+
`role: ${trimInline(input.role || 'worker', 18)} backend: ${trimInline(input.backend || 'codex-sdk', 20)} worktree: ${trimInline(input.worktreeId || '-', 18)}`,
|
|
19
|
+
`runtime: fast ${formatFastMode(input.fastMode, input.serviceTier)} tier: ${trimInline(input.serviceTier || 'unknown', 12)} provider: ${trimInline(input.provider || 'unknown', 18)}`,
|
|
20
|
+
`model: ${trimInline(input.model || 'unknown', 28)} reasoning: ${trimInline(input.reasoningEffort || 'unknown', 16)}${input.authMode ? ` auth: ${trimInline(input.authMode, 14)}` : ''}`,
|
|
21
|
+
input.sessionId ? `session: ${trimInline(input.sessionId, 62)}` : null,
|
|
22
|
+
`heartbeat: ${heartbeat}${input.heartbeatEvent ? ` event: ${trimInline(input.heartbeatEvent, 40)}` : ''}`,
|
|
23
|
+
`doing: ${task}`,
|
|
24
|
+
`files: ${trimInline(files.length ? files.join(', ') : 'no changed file yet', 78)}`,
|
|
25
|
+
`patch: ${trimInline(input.patchStatus || 'queued', 24)} verify: ${trimInline(input.verifyStatus || 'queued', 24)}`,
|
|
26
|
+
...events.map((event) => `event: ${trimInline(event, 78)}`),
|
|
27
|
+
...stdout.map((line) => `out: ${trimInline(line, 79)}`),
|
|
28
|
+
...stderr.map((line) => `err: ${trimInline(line, 79)}`)
|
|
29
|
+
].filter((row) => Boolean(row));
|
|
30
|
+
return frameSlotPane(`LIVE SLOT ${input.slotId}`, rows.slice(0, Math.max(1, maxLines - 2)));
|
|
20
31
|
}
|
|
21
32
|
export async function renderZellijSlotPaneFromArtifacts(input) {
|
|
22
33
|
const artifactDir = path.resolve(input.artifactDir);
|
|
23
34
|
const result = await readJson(path.join(artifactDir, 'worker-result.json'));
|
|
35
|
+
const intake = await readJson(path.join(artifactDir, 'worker-intake.json'));
|
|
36
|
+
const backendReport = await readJson(path.join(artifactDir, 'worker-backend-router-report.json'));
|
|
37
|
+
const fastReport = await readJson(path.join(artifactDir, 'worker-fast-mode.json'));
|
|
38
|
+
const paneReport = await readJson(path.join(artifactDir, 'zellij-worker-pane.json'));
|
|
39
|
+
const codexProof = await readJson(path.join(artifactDir, 'codex-control-proof.json'));
|
|
40
|
+
const localProof = await readJson(path.join(artifactDir, 'local-llm-proof.json'));
|
|
41
|
+
const patch = await firstJson([
|
|
42
|
+
path.join(artifactDir, 'worker-patch-envelope.json'),
|
|
43
|
+
path.join(artifactDir, 'codex-sdk-patch-envelope.json'),
|
|
44
|
+
path.join(artifactDir, 'python-codex-sdk-patch-envelope.json'),
|
|
45
|
+
path.join(artifactDir, 'local-llm-patch-envelope.json')
|
|
46
|
+
]);
|
|
24
47
|
const heartbeatPath = path.join(artifactDir, 'worker-heartbeat.jsonl');
|
|
25
48
|
const heartbeatMtime = await statMtimeMs(heartbeatPath);
|
|
49
|
+
const heartbeatRows = await readJsonlTail(heartbeatPath, 2);
|
|
50
|
+
const eventRows = await readJsonlTails([
|
|
51
|
+
path.join(artifactDir, 'codex-sdk-events.jsonl'),
|
|
52
|
+
path.join(artifactDir, 'python-codex-sdk-events.jsonl'),
|
|
53
|
+
path.join(artifactDir, 'local-llm-events.jsonl'),
|
|
54
|
+
path.join(artifactDir, 'zellij-worker-pane-events.jsonl')
|
|
55
|
+
], 6);
|
|
56
|
+
const patchFiles = patchPaths(patch || result);
|
|
57
|
+
const changedFiles = normalizeList(result?.changed_files);
|
|
58
|
+
const plannedFiles = normalizeList([
|
|
59
|
+
...(Array.isArray(intake?.slice?.write_paths) ? intake.slice.write_paths : []),
|
|
60
|
+
...(Array.isArray(intake?.slice?.readonly_paths) ? intake.slice.readonly_paths : []),
|
|
61
|
+
...(Array.isArray(intake?.input_files) ? intake.input_files : [])
|
|
62
|
+
]);
|
|
26
63
|
const now = Date.now();
|
|
27
64
|
return renderZellijSlotPane({
|
|
28
65
|
slotId: input.slotId,
|
|
29
66
|
generationIndex: input.generationIndex,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
67
|
+
sessionId: result?.session_id || intake?.agent?.session_id || backendReport?.session_id || null,
|
|
68
|
+
role: input.role || result?.persona_id || intake?.agent?.naruto_role || intake?.agent?.role || intake?.agent?.persona_id || result?.agent_id || null,
|
|
69
|
+
backend: input.backend || result?.backend || backendReport?.selected_backend || intake?.backend || null,
|
|
70
|
+
status: result?.status || statusFromEvents(eventRows) || (heartbeatMtime ? 'running' : 'launching'),
|
|
71
|
+
fastMode: firstDefined(fastReport?.fast_mode, backendReport?.fast_mode, result?.fast_mode, intake?.fast_mode),
|
|
72
|
+
serviceTier: firstText([
|
|
73
|
+
fastReport?.service_tier,
|
|
74
|
+
paneReport?.service_tier,
|
|
75
|
+
backendReport?.service_tier,
|
|
76
|
+
codexProof?.config?.service_tier,
|
|
77
|
+
result?.service_tier,
|
|
78
|
+
intake?.service_tier
|
|
79
|
+
]),
|
|
80
|
+
provider: firstText([
|
|
81
|
+
paneReport?.provider_context?.provider,
|
|
82
|
+
paneReport?.provider,
|
|
83
|
+
codexProof?.config?.model_provider,
|
|
84
|
+
localProof?.provider
|
|
85
|
+
]),
|
|
86
|
+
authMode: firstText([
|
|
87
|
+
paneReport?.provider_context?.auth_mode,
|
|
88
|
+
codexProof?.config?.model_provider ? 'api_key' : null
|
|
89
|
+
]),
|
|
90
|
+
model: firstText([
|
|
91
|
+
codexProof?.config?.model,
|
|
92
|
+
localProof?.model,
|
|
93
|
+
intake?.ollama_model,
|
|
94
|
+
intake?.local_model_model
|
|
95
|
+
]),
|
|
96
|
+
reasoningEffort: firstText([
|
|
97
|
+
codexProof?.config?.model_reasoning_effort,
|
|
98
|
+
intake?.agent?.model_reasoning_effort,
|
|
99
|
+
intake?.agent?.reasoning_effort
|
|
100
|
+
]),
|
|
101
|
+
currentTask: firstText([
|
|
102
|
+
result?.summary,
|
|
103
|
+
intake?.slice?.description,
|
|
104
|
+
intake?.slice?.title,
|
|
105
|
+
intake?.slice?.id,
|
|
106
|
+
lastEventLine(eventRows)
|
|
107
|
+
]),
|
|
108
|
+
currentFile: changedFiles[0] || patchFiles[0] || plannedFiles[0] || null,
|
|
109
|
+
changedFiles,
|
|
110
|
+
plannedFiles,
|
|
111
|
+
patchFiles,
|
|
112
|
+
patchStatus: patchStatus(result, patch, patchFiles),
|
|
36
113
|
verifyStatus: result?.verification?.status || 'queued',
|
|
37
114
|
heartbeatAgeMs: heartbeatMtime ? now - heartbeatMtime : null,
|
|
38
|
-
|
|
115
|
+
heartbeatEvent: heartbeatRows.length ? formatArtifactEvent(heartbeatRows[heartbeatRows.length - 1]) : null,
|
|
116
|
+
worktreeId: result?.worktree?.id || intake?.worktree?.id || null,
|
|
117
|
+
eventLines: eventRows.map(formatArtifactEvent).filter(Boolean),
|
|
118
|
+
stdoutTail: await readTextTailLines(path.join(artifactDir, 'worker.stdout.log'), 2),
|
|
119
|
+
stderrTail: await readTextTailLines(path.join(artifactDir, 'worker.stderr.log'), 1),
|
|
39
120
|
mode: input.mode || 'compact-slots'
|
|
40
121
|
});
|
|
41
122
|
}
|
|
@@ -62,6 +143,14 @@ async function readJson(file) {
|
|
|
62
143
|
return null;
|
|
63
144
|
}
|
|
64
145
|
}
|
|
146
|
+
async function firstJson(files) {
|
|
147
|
+
for (const file of files) {
|
|
148
|
+
const value = await readJson(file);
|
|
149
|
+
if (value)
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
65
154
|
async function statMtimeMs(file) {
|
|
66
155
|
try {
|
|
67
156
|
return (await fs.promises.stat(file)).mtimeMs;
|
|
@@ -70,12 +159,166 @@ async function statMtimeMs(file) {
|
|
|
70
159
|
return null;
|
|
71
160
|
}
|
|
72
161
|
}
|
|
162
|
+
async function readJsonlTails(files, max) {
|
|
163
|
+
const rows = [];
|
|
164
|
+
for (const file of files)
|
|
165
|
+
rows.push(...await readJsonlTail(file, max));
|
|
166
|
+
return rows
|
|
167
|
+
.sort((a, b) => timestampMs(a) - timestampMs(b))
|
|
168
|
+
.slice(-max);
|
|
169
|
+
}
|
|
170
|
+
async function readJsonlTail(file, max) {
|
|
171
|
+
try {
|
|
172
|
+
const lines = (await fs.promises.readFile(file, 'utf8')).split(/\r?\n/).filter((line) => line.trim());
|
|
173
|
+
return lines.slice(-Math.max(1, max)).map((line) => {
|
|
174
|
+
try {
|
|
175
|
+
return JSON.parse(line);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return { message: line };
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function readTextTailLines(file, max) {
|
|
187
|
+
try {
|
|
188
|
+
const lines = (await fs.promises.readFile(file, 'utf8')).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
189
|
+
return lines.slice(-Math.max(1, max));
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function patchStatus(result, patch, files) {
|
|
196
|
+
const resultCount = Array.isArray(result?.patch_envelopes) ? result.patch_envelopes.length : 0;
|
|
197
|
+
const patchCount = Number(patch?.envelope_count || (Array.isArray(patch?.envelopes) ? patch.envelopes.length : 0));
|
|
198
|
+
const count = Math.max(resultCount, Number.isFinite(patchCount) ? patchCount : 0);
|
|
199
|
+
if (count > 0)
|
|
200
|
+
return `candidate (${count})`;
|
|
201
|
+
if (files.length)
|
|
202
|
+
return 'candidate';
|
|
203
|
+
return 'queued';
|
|
204
|
+
}
|
|
205
|
+
function patchPaths(value) {
|
|
206
|
+
const envelopes = [
|
|
207
|
+
...(Array.isArray(value?.envelopes) ? value.envelopes : []),
|
|
208
|
+
...(Array.isArray(value?.patch_envelopes) ? value.patch_envelopes : [])
|
|
209
|
+
];
|
|
210
|
+
return normalizeList(envelopes.flatMap((envelope) => {
|
|
211
|
+
const paths = [
|
|
212
|
+
...(Array.isArray(envelope?.operations) ? envelope.operations.map((operation) => operation?.path) : []),
|
|
213
|
+
...(Array.isArray(envelope?.allowed_paths) ? envelope.allowed_paths : [])
|
|
214
|
+
];
|
|
215
|
+
return paths;
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
function statusFromEvents(rows) {
|
|
219
|
+
const last = rows[rows.length - 1];
|
|
220
|
+
const status = String(last?.lane_status || last?.status || '').trim();
|
|
221
|
+
if (status)
|
|
222
|
+
return status;
|
|
223
|
+
const type = String(last?.event_type || last?.type || '').trim();
|
|
224
|
+
if (/failed|blocked/i.test(type))
|
|
225
|
+
return 'blocked';
|
|
226
|
+
if (/completed|finished|closed/i.test(type))
|
|
227
|
+
return 'done';
|
|
228
|
+
if (type)
|
|
229
|
+
return 'running';
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
function lastEventLine(rows) {
|
|
233
|
+
for (const row of [...rows].reverse()) {
|
|
234
|
+
const text = formatArtifactEvent(row);
|
|
235
|
+
if (text)
|
|
236
|
+
return text;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
function formatArtifactEvent(row) {
|
|
241
|
+
if (!row)
|
|
242
|
+
return '';
|
|
243
|
+
const status = trimInline(row.lane_status || row.status || row.event || row.event_type || row.type || row.sdk_event_type || 'event', 18);
|
|
244
|
+
const detail = firstText([
|
|
245
|
+
row.current_tool && row.current_file ? `tool ${row.current_tool} file ${row.current_file}` : null,
|
|
246
|
+
row.current_file ? `file ${row.current_file}` : null,
|
|
247
|
+
row.current_tool ? `tool ${row.current_tool}` : null,
|
|
248
|
+
row.message_tail,
|
|
249
|
+
row.blocker ? `blocker ${row.blocker}` : null,
|
|
250
|
+
row.request_id,
|
|
251
|
+
row.pane_id ? `pane ${row.pane_id}` : null,
|
|
252
|
+
row.message,
|
|
253
|
+
row.reason
|
|
254
|
+
]);
|
|
255
|
+
return trimInline(detail ? `${status}: ${detail}` : status, 96);
|
|
256
|
+
}
|
|
257
|
+
function timestampMs(row) {
|
|
258
|
+
const raw = row?.ts || row?.generated_at || row?.updated_at || row?.created_at;
|
|
259
|
+
const parsed = raw ? Date.parse(String(raw)) : NaN;
|
|
260
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
261
|
+
}
|
|
262
|
+
function firstText(values) {
|
|
263
|
+
for (const value of values) {
|
|
264
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
265
|
+
if (text)
|
|
266
|
+
return text;
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
function firstDefined(...values) {
|
|
271
|
+
for (const value of values) {
|
|
272
|
+
if (value === true || value === false)
|
|
273
|
+
return value;
|
|
274
|
+
if (typeof value === 'string' && value.trim())
|
|
275
|
+
return value;
|
|
276
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
277
|
+
return String(value);
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
function firstNonEmptyList(...values) {
|
|
282
|
+
for (const value of values) {
|
|
283
|
+
const normalized = normalizeList(value || []);
|
|
284
|
+
if (normalized.length)
|
|
285
|
+
return normalized;
|
|
286
|
+
}
|
|
287
|
+
return [];
|
|
288
|
+
}
|
|
289
|
+
function normalizeList(values) {
|
|
290
|
+
return [...new Set((Array.isArray(values) ? values : [values]).map((value) => String(value || '').trim()).filter(Boolean))];
|
|
291
|
+
}
|
|
73
292
|
function trimInline(value, max) {
|
|
74
293
|
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
75
294
|
if (text.length <= max)
|
|
76
295
|
return text;
|
|
77
296
|
return text.slice(0, Math.max(1, max - 3)) + '...';
|
|
78
297
|
}
|
|
298
|
+
function formatFastMode(value, serviceTier) {
|
|
299
|
+
const text = String(value ?? '').trim().toLowerCase();
|
|
300
|
+
if (value === true || text === 'true' || text === '1' || text === 'on' || text === 'fast')
|
|
301
|
+
return 'on';
|
|
302
|
+
if (value === false || text === 'false' || text === '0' || text === 'off' || text === 'standard')
|
|
303
|
+
return 'off';
|
|
304
|
+
const tier = String(serviceTier || '').trim().toLowerCase();
|
|
305
|
+
if (tier === 'fast' || tier === 'priority')
|
|
306
|
+
return 'on';
|
|
307
|
+
if (tier === 'standard' || tier === 'default')
|
|
308
|
+
return 'off';
|
|
309
|
+
return 'unknown';
|
|
310
|
+
}
|
|
311
|
+
function frameSlotPane(title, rows) {
|
|
312
|
+
const width = Math.min(96, Math.max(44, title.length + 6, ...rows.map((row) => row.length + 4)));
|
|
313
|
+
const line = '+' + '-'.repeat(width - 2) + '+';
|
|
314
|
+
const label = ` ${trimInline(title, width - 4)} `;
|
|
315
|
+
const titleLine = '|' + label.padEnd(width - 2, ' ') + '|';
|
|
316
|
+
const body = rows.map((row) => {
|
|
317
|
+
const text = ` ${trimInline(row, width - 4)} `;
|
|
318
|
+
return '|' + text.padEnd(width - 2, ' ') + '|';
|
|
319
|
+
});
|
|
320
|
+
return [line, titleLine, line, ...body, line].join('\n');
|
|
321
|
+
}
|
|
79
322
|
function shellQuote(value) {
|
|
80
323
|
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
81
324
|
}
|
|
@@ -4,5 +4,12 @@ import { assertGate, emitGate, readText } from './lib/codex-sdk-gate-lib.js';
|
|
|
4
4
|
const source = readText('src/core/commands/research-command.ts');
|
|
5
5
|
assertGate(source.includes("backend: mock ? 'fake' : 'codex-sdk'"), 'Research pipeline must default native agents to codex-sdk');
|
|
6
6
|
assertGate(source.includes("flag(args, '--autoresearch') ? '$AutoResearch' : '$Research'"), 'Research/AutoResearch route selection missing');
|
|
7
|
+
assertGate(source.includes('narutoWorkGraph: researchWorkGraph'), 'Research pipeline must pass the stage-aware Naruto work graph');
|
|
8
|
+
assertGate(source.includes('readonly: true'), 'Research pipeline must force read-only native orchestration');
|
|
9
|
+
assertGate(source.includes('quality_metrics'), 'Research pipeline JSON output must include quality metrics');
|
|
10
|
+
const researchCore = readText('src/core/research.ts');
|
|
11
|
+
assertGate(researchCore.includes('readResearchQualityContract'), 'Research gate must read research-quality-contract.json');
|
|
12
|
+
assertGate(researchCore.includes('claim_evidence_matrix_missing'), 'Research gate must require claim-evidence-matrix.json');
|
|
13
|
+
assertGate(researchCore.includes('research_final_review_not_approved'), 'Research gate must require final reviewer approval');
|
|
7
14
|
emitGate('codex-sdk:research-pipeline', { route: '$Research' });
|
|
8
15
|
//# sourceMappingURL=codex-sdk-research-pipeline-check.js.map
|
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
7
|
import { assertGate, emitGate, root } from './sks-1-18-gate-lib.js';
|
|
8
|
-
const MAX_FILES = Number(process.env.SKS_MAX_PACK_FILES ||
|
|
8
|
+
const MAX_FILES = Number(process.env.SKS_MAX_PACK_FILES || 1250);
|
|
9
9
|
const MAX_UNPACKED = Number(process.env.SKS_MAX_UNPACKED_BYTES || 6 * 1024 * 1024);
|
|
10
10
|
const MAX_PACKED = Number(process.env.SKS_MAX_PACK_BYTES || 1536 * 1024);
|
|
11
11
|
function runNpmPack() {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { assertGate, emitGate, readText } from './lib/codex-sdk-gate-lib.js';
|
|
4
|
+
const mode = process.argv[2] || 'all';
|
|
5
|
+
const checks = {
|
|
6
|
+
'quality-contract': () => {
|
|
7
|
+
assertFileIncludes('src/core/research/research-quality-contract.ts', [
|
|
8
|
+
'min_sources_total: 12',
|
|
9
|
+
'min_source_layers_covered: 5',
|
|
10
|
+
'min_counterevidence_sources: 2',
|
|
11
|
+
'min_trianguled_claims: 6',
|
|
12
|
+
'min_key_claims: 8',
|
|
13
|
+
'min_report_words: 2200'
|
|
14
|
+
]);
|
|
15
|
+
assertFileIncludes('src/core/research.ts', ['quality_contract', 'writeResearchQualityContract', 'research_report_too_short']);
|
|
16
|
+
},
|
|
17
|
+
'claim-matrix': () => {
|
|
18
|
+
assertFileIncludes('src/core/research/claim-evidence-matrix.ts', ['CLAIM_EVIDENCE_MATRIX_ARTIFACT', 'validateClaimEvidenceMatrix', 'buildClaimEvidenceMatrixFromLedgers']);
|
|
19
|
+
assertFileIncludes('src/core/research.ts', ['claim_evidence_matrix_missing', 'key_claims_below_contract', 'triangulated_claims_below_contract']);
|
|
20
|
+
},
|
|
21
|
+
'source-quality-report': () => {
|
|
22
|
+
assertFileIncludes('src/core/research/source-quality-report.ts', ['SOURCE_QUALITY_REPORT_ARTIFACT', 'buildSourceQualityReport', 'claim_ids']);
|
|
23
|
+
assertFileIncludes('src/core/research.ts', ['source_quality_report_missing', 'writeSourceQualityReport']);
|
|
24
|
+
},
|
|
25
|
+
'implementation-blueprint': () => {
|
|
26
|
+
assertFileIncludes('src/core/research/implementation-blueprint.ts', ['IMPLEMENTATION_BLUEPRINT_ARTIFACT', 'validateImplementationBlueprint']);
|
|
27
|
+
assertFileIncludes('src/core/research.ts', ['implementation_blueprint_missing', 'renderImplementationBlueprintMarkdown']);
|
|
28
|
+
},
|
|
29
|
+
'experiment-plan': () => {
|
|
30
|
+
assertFileIncludes('src/core/research/experiment-plan.ts', ['EXPERIMENT_PLAN_JSON_ARTIFACT', 'min_experiment_steps', 'validateExperimentPlan']);
|
|
31
|
+
assertFileIncludes('src/core/research/experiment-plan.ts', ['experiment_plan_missing', 'experiment_plan_too_thin']);
|
|
32
|
+
assertFileIncludes('src/core/research.ts', ['experiment_plan_missing', 'validateExperimentPlan']);
|
|
33
|
+
},
|
|
34
|
+
'replication-pack': () => {
|
|
35
|
+
assertFileIncludes('src/core/research/replication-pack.ts', ['REPLICATION_PACK_ARTIFACT', 'validateReplicationPack']);
|
|
36
|
+
assertFileIncludes('src/core/research.ts', ['replication_pack_missing']);
|
|
37
|
+
},
|
|
38
|
+
'final-reviewer': () => {
|
|
39
|
+
assertFileIncludes('src/core/research/research-final-reviewer.ts', ['RESEARCH_FINAL_REVIEW_ARTIFACT', 'approved', 'runResearchFinalReviewer']);
|
|
40
|
+
assertFileIncludes('src/core/research.ts', ['research_final_review_not_approved']);
|
|
41
|
+
},
|
|
42
|
+
'work-graph': () => {
|
|
43
|
+
assertFileIncludes('src/core/research/research-work-graph.ts', ['RESEARCH_WORK_GRAPH_ARTIFACT', 'buildResearchWorkGraph', 'sks.naruto-work-graph.v1']);
|
|
44
|
+
assertFileIncludes('src/core/commands/research-command.ts', ['narutoWorkGraph: researchWorkGraph', 'readonly: true', 'runResearchCycle']);
|
|
45
|
+
},
|
|
46
|
+
'prompt-contract': () => {
|
|
47
|
+
assertFileIncludes('src/core/research/research-prompt-contract.ts', ['researchPromptContractText', 'validateResearchPromptContract']);
|
|
48
|
+
assertFileIncludes('src/core/research.ts', ['QUALITY CONTRACT:', 'researchPromptContractText()']);
|
|
49
|
+
},
|
|
50
|
+
'gate-thresholds': () => {
|
|
51
|
+
assertFileIncludes('src/core/research.ts', [
|
|
52
|
+
'source_entries_below_research_quality_contract',
|
|
53
|
+
'source_layer_coverage_below_contract',
|
|
54
|
+
'counterevidence_below_contract',
|
|
55
|
+
'required_artifact_missing'
|
|
56
|
+
]);
|
|
57
|
+
assertFileIncludes('src/core/research/falsification.ts', ['falsification_cases_below_contract']);
|
|
58
|
+
},
|
|
59
|
+
'schemas': () => {
|
|
60
|
+
for (const file of [
|
|
61
|
+
'schemas/research/research-quality-contract.schema.json',
|
|
62
|
+
'schemas/research/claim-evidence-matrix.schema.json',
|
|
63
|
+
'schemas/research/source-quality-report.schema.json',
|
|
64
|
+
'schemas/research/implementation-blueprint.schema.json',
|
|
65
|
+
'schemas/research/experiment-plan.schema.json',
|
|
66
|
+
'schemas/research/replication-pack.schema.json',
|
|
67
|
+
'schemas/research/research-final-review.schema.json'
|
|
68
|
+
])
|
|
69
|
+
assertGate(readText(file).includes('"$schema"'), `${file} missing JSON Schema header`);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
if (mode === 'all') {
|
|
73
|
+
for (const check of Object.values(checks))
|
|
74
|
+
check();
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
assertGate(Boolean(checks[mode]), `unknown research quality check: ${mode}`);
|
|
78
|
+
checks[mode]();
|
|
79
|
+
}
|
|
80
|
+
emitGate(`research:${mode}`, { mode });
|
|
81
|
+
function assertFileIncludes(file, tokens) {
|
|
82
|
+
const text = readText(file);
|
|
83
|
+
for (const token of tokens)
|
|
84
|
+
assertGate(text.includes(token), `${file} missing token ${token}`);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=research-quality-gate-check.js.map
|
|
@@ -26,6 +26,24 @@ const roster = renderZellijSlotColumnAnchor({
|
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
});
|
|
29
|
+
const overflow = renderZellijSlotColumnAnchor({
|
|
30
|
+
activeWorkers: 6,
|
|
31
|
+
visiblePaneCap: 5,
|
|
32
|
+
headlessWorkers: 1,
|
|
33
|
+
queueDepth: 0,
|
|
34
|
+
mode: 'compact-slots',
|
|
35
|
+
workerRows: [
|
|
36
|
+
{
|
|
37
|
+
slotId: 'slot-009',
|
|
38
|
+
generationIndex: 1,
|
|
39
|
+
placement: 'headless',
|
|
40
|
+
status: 'running',
|
|
41
|
+
backend: 'local-llm',
|
|
42
|
+
task: 'Waiting for a visible slot pane',
|
|
43
|
+
heartbeatAgeMs: 1200
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
});
|
|
29
47
|
const command = buildZellijSlotColumnAnchorCommand({
|
|
30
48
|
nodePath: '/usr/bin/node',
|
|
31
49
|
cliPath: '/repo/dist/bin/sks.js',
|
|
@@ -34,12 +52,15 @@ const command = buildZellijSlotColumnAnchorCommand({
|
|
|
34
52
|
artifactRoot: '/repo/.sneakoscope/missions/M-check/agents',
|
|
35
53
|
watch: true
|
|
36
54
|
});
|
|
37
|
-
const ok = rendered
|
|
55
|
+
const ok = rendered.includes('SLOTS active 3/8 · headless 12 · q 44')
|
|
56
|
+
&& rendered.includes('visible slot panes stack below this anchor')
|
|
38
57
|
&& roster.includes('SLOTS active 1/5 · headless 0 · q 0')
|
|
39
|
-
&& roster.includes('slot
|
|
40
|
-
&& roster.includes('
|
|
58
|
+
&& roster.includes('visible slot panes stack below this anchor')
|
|
59
|
+
&& !roster.includes('slot-004 g1 running codex-sdk')
|
|
60
|
+
&& overflow.includes('slot-009 g1 running local-llm')
|
|
61
|
+
&& overflow.includes('Waiting for a visible slot pane')
|
|
41
62
|
&& command.includes('zellij-slot-column-anchor')
|
|
42
63
|
&& command.includes('--watch');
|
|
43
|
-
assertGate(ok, 'Zellij slot-column anchor must
|
|
44
|
-
emitGate('zellij:slot-column-anchor', { rendered, roster, command });
|
|
64
|
+
assertGate(ok, 'Zellij slot-column anchor must stay an anchor while slot workers render in dedicated stacked panes', { rendered, roster, overflow, command });
|
|
65
|
+
emitGate('zellij:slot-column-anchor', { rendered, roster, overflow, command });
|
|
45
66
|
//# sourceMappingURL=zellij-slot-column-anchor-check.js.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
3
6
|
import { assertGate, emitGate, importDist } from './sks-1-18-gate-lib.js';
|
|
4
7
|
const renderer = await importDist('core/zellij/zellij-slot-pane-renderer.js');
|
|
5
8
|
const text = renderer.renderZellijSlotPane({
|
|
@@ -8,14 +11,72 @@ const text = renderer.renderZellijSlotPane({
|
|
|
8
11
|
role: 'implementer',
|
|
9
12
|
backend: 'local-llm',
|
|
10
13
|
status: 'coding',
|
|
14
|
+
fastMode: true,
|
|
15
|
+
serviceTier: 'fast',
|
|
16
|
+
provider: 'codex-lb',
|
|
17
|
+
authMode: 'codex_lb_key',
|
|
18
|
+
model: 'gpt-5.5',
|
|
19
|
+
reasoningEffort: 'medium',
|
|
11
20
|
currentFile: 'src/core/foo.ts',
|
|
21
|
+
currentTask: 'Editing Zellij slot pane renderer',
|
|
22
|
+
changedFiles: ['src/core/foo.ts', 'src/core/bar.ts'],
|
|
12
23
|
patchStatus: 'candidate',
|
|
13
24
|
verifyStatus: 'queued',
|
|
14
25
|
heartbeatAgeMs: 2000,
|
|
15
26
|
worktreeId: 'WT-0007',
|
|
27
|
+
eventLines: ['running: tool apply_patch', 'running: file src/core/foo.ts'],
|
|
28
|
+
stdoutTail: ['renderer updated live pane output'],
|
|
16
29
|
mode: 'compact-slots'
|
|
17
30
|
});
|
|
18
31
|
const lines = text.split(/\n/);
|
|
32
|
+
const artifactDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-slot-pane-renderer-'));
|
|
33
|
+
await fs.writeFile(path.join(artifactDir, 'worker-intake.json'), JSON.stringify({
|
|
34
|
+
agent: { session_id: 'session-slot-003', role: 'implementer' },
|
|
35
|
+
backend: 'codex-sdk',
|
|
36
|
+
fast_mode: true,
|
|
37
|
+
service_tier: 'fast',
|
|
38
|
+
slice: {
|
|
39
|
+
title: 'Hydrate live slot pane from artifacts',
|
|
40
|
+
write_paths: ['src/core/zellij/zellij-slot-pane-renderer.ts']
|
|
41
|
+
},
|
|
42
|
+
worktree: { id: 'WT-0007' }
|
|
43
|
+
}, null, 2));
|
|
44
|
+
await fs.writeFile(path.join(artifactDir, 'worker-fast-mode.json'), JSON.stringify({
|
|
45
|
+
fast_mode: true,
|
|
46
|
+
service_tier: 'fast'
|
|
47
|
+
}, null, 2));
|
|
48
|
+
await fs.writeFile(path.join(artifactDir, 'zellij-worker-pane.json'), JSON.stringify({
|
|
49
|
+
provider: 'codex-lb',
|
|
50
|
+
service_tier: 'fast',
|
|
51
|
+
provider_context: {
|
|
52
|
+
provider: 'codex-lb',
|
|
53
|
+
auth_mode: 'codex_lb_key'
|
|
54
|
+
}
|
|
55
|
+
}, null, 2));
|
|
56
|
+
await fs.writeFile(path.join(artifactDir, 'codex-control-proof.json'), JSON.stringify({
|
|
57
|
+
config: {
|
|
58
|
+
model: 'gpt-5.5',
|
|
59
|
+
model_provider: 'codex-lb',
|
|
60
|
+
service_tier: 'fast',
|
|
61
|
+
model_reasoning_effort: 'medium'
|
|
62
|
+
}
|
|
63
|
+
}, null, 2));
|
|
64
|
+
await fs.writeFile(path.join(artifactDir, 'worker-heartbeat.jsonl'), `${JSON.stringify({ event: 'started', status: 'running' })}\n`);
|
|
65
|
+
await fs.writeFile(path.join(artifactDir, 'codex-sdk-events.jsonl'), `${JSON.stringify({
|
|
66
|
+
sdk_event_type: 'item.completed',
|
|
67
|
+
lane_status: 'running',
|
|
68
|
+
current_tool: 'apply_patch',
|
|
69
|
+
current_file: 'src/core/zellij/zellij-slot-pane-renderer.ts',
|
|
70
|
+
message_tail: null
|
|
71
|
+
})}\n`);
|
|
72
|
+
await fs.writeFile(path.join(artifactDir, 'worker.stdout.log'), 'renderer stdout tail\n');
|
|
73
|
+
const hydrated = await renderer.renderZellijSlotPaneFromArtifacts({
|
|
74
|
+
artifactDir,
|
|
75
|
+
slotId: 'slot-003',
|
|
76
|
+
generationIndex: 2,
|
|
77
|
+
mode: 'compact-slots'
|
|
78
|
+
});
|
|
79
|
+
await fs.rm(artifactDir, { recursive: true, force: true });
|
|
19
80
|
const command = renderer.buildZellijSlotPaneCommand({
|
|
20
81
|
cliPath: '/repo/dist/bin/sks.js',
|
|
21
82
|
missionId: 'M-test',
|
|
@@ -27,12 +88,19 @@ const command = renderer.buildZellijSlotPaneCommand({
|
|
|
27
88
|
const report = {
|
|
28
89
|
schema: 'sks.zellij-slot-pane-renderer-check.v1',
|
|
29
90
|
line_count: lines.length,
|
|
30
|
-
max_compact_lines:
|
|
31
|
-
contains_slot: /slot-003 gen-2/.test(text),
|
|
32
|
-
contains_status: /
|
|
91
|
+
max_compact_lines: 17,
|
|
92
|
+
contains_slot: /LIVE SLOT slot-003/.test(text) && /slot: slot-003 \/ gen-2/.test(text),
|
|
93
|
+
contains_status: /coding/.test(text),
|
|
94
|
+
contains_runtime: /runtime: fast on/.test(text) && /model: gpt-5\.5/.test(text) && /provider: codex-lb/.test(text),
|
|
95
|
+
contains_files: /src\/core\/foo\.ts/.test(text) && /src\/core\/bar\.ts/.test(text),
|
|
96
|
+
contains_live_event: /event: running:/.test(text),
|
|
97
|
+
artifact_hydrates_runtime: /runtime: fast on/.test(hydrated) && /model: gpt-5\.5/.test(hydrated) && /reasoning: medium/.test(hydrated) && /auth: codex_lb_key/.test(hydrated),
|
|
98
|
+
artifact_hydrates_live_event: /tool apply_patch/.test(hydrated) && /renderer stdout tail/.test(hydrated),
|
|
99
|
+
artifact_hydrates_planned_file: /zellij-slot-pane-renderer\.ts/.test(hydrated),
|
|
33
100
|
command_uses_slot_pane: command.includes('zellij-slot-pane') && command.includes('--watch'),
|
|
34
|
-
snapshot: text
|
|
101
|
+
snapshot: text,
|
|
102
|
+
hydrated_snapshot: hydrated
|
|
35
103
|
};
|
|
36
|
-
assertGate(lines.length <=
|
|
104
|
+
assertGate(lines.length <= 17 && report.contains_slot && report.contains_status && report.contains_runtime && report.contains_files && report.contains_live_event && report.artifact_hydrates_runtime && report.artifact_hydrates_live_event && report.artifact_hydrates_planned_file && report.command_uses_slot_pane, 'compact slot pane renderer must render one live work pane per slot', report);
|
|
37
105
|
emitGate('zellij:compact-slot-renderer', report);
|
|
38
106
|
//# sourceMappingURL=zellij-slot-pane-renderer-check.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.13",
|
|
5
5
|
"description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -394,6 +394,18 @@
|
|
|
394
394
|
"agent:proof-graph": "node ./dist/scripts/agent-native-release-gate.js agent-proof-graph",
|
|
395
395
|
"team:native-agent-backend": "node ./dist/scripts/agent-native-release-gate.js team-native-agent-backend",
|
|
396
396
|
"research:native-agent-backend": "node ./dist/scripts/agent-native-release-gate.js research-native-agent-backend",
|
|
397
|
+
"research:quality-contract": "node ./dist/scripts/research-quality-gate-check.js quality-contract",
|
|
398
|
+
"research:claim-matrix": "node ./dist/scripts/research-quality-gate-check.js claim-matrix",
|
|
399
|
+
"research:source-quality-report": "node ./dist/scripts/research-quality-gate-check.js source-quality-report",
|
|
400
|
+
"research:implementation-blueprint": "node ./dist/scripts/research-quality-gate-check.js implementation-blueprint",
|
|
401
|
+
"research:experiment-plan": "node ./dist/scripts/research-quality-gate-check.js experiment-plan",
|
|
402
|
+
"research:replication-pack": "node ./dist/scripts/research-quality-gate-check.js replication-pack",
|
|
403
|
+
"research:final-reviewer": "node ./dist/scripts/research-quality-gate-check.js final-reviewer",
|
|
404
|
+
"research:work-graph": "node ./dist/scripts/research-quality-gate-check.js work-graph",
|
|
405
|
+
"research:prompt-contract": "node ./dist/scripts/research-quality-gate-check.js prompt-contract",
|
|
406
|
+
"research:gate-thresholds": "node ./dist/scripts/research-quality-gate-check.js gate-thresholds",
|
|
407
|
+
"research:schemas": "node ./dist/scripts/research-quality-gate-check.js schemas",
|
|
408
|
+
"research:quality-gates": "node ./dist/scripts/research-quality-gate-check.js all",
|
|
397
409
|
"qa:native-agent-backend": "node ./dist/scripts/agent-native-release-gate.js qa-native-agent-backend",
|
|
398
410
|
"agent:non-recursive-pipeline-report": "node ./dist/scripts/non-recursive-pipeline-check.js --json",
|
|
399
411
|
"agent:legacy-multiagent-removed": "node ./dist/scripts/legacy-multiagent-removal-check.js",
|