sneakoscope 3.1.3 → 3.1.5

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.
Files changed (51) hide show
  1. package/README.md +1 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/cli/install-helpers.js +56 -4
  8. package/dist/commands/codex-app.js +45 -1
  9. package/dist/commands/codex-lb.js +12 -9
  10. package/dist/commands/doctor.js +44 -1
  11. package/dist/core/codex-app/codex-agent-role-sync.js +119 -0
  12. package/dist/core/codex-app/codex-agent-type-probe.js +202 -0
  13. package/dist/core/codex-app/codex-app-execution-profile.js +39 -0
  14. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -4
  15. package/dist/core/codex-app/codex-app-harness-matrix.js +127 -0
  16. package/dist/core/codex-app/codex-app-types.js +21 -0
  17. package/dist/core/codex-app/codex-hook-approval-probe.js +188 -0
  18. package/dist/core/codex-app/codex-hook-lifecycle.js +61 -0
  19. package/dist/core/codex-app/codex-init-deep.js +180 -0
  20. package/dist/core/codex-app/codex-skill-sync.js +164 -0
  21. package/dist/core/codex-app/lazycodex-analysis.js +72 -0
  22. package/dist/core/codex-app/lazycodex-interop-policy.js +60 -0
  23. package/dist/core/codex-app/lazycodex-live-analyzer.js +98 -0
  24. package/dist/core/commands/loop-command.js +11 -0
  25. package/dist/core/commands/mad-sks-command.js +113 -17
  26. package/dist/core/commands/qa-loop-command.js +3 -2
  27. package/dist/core/commands/research-command.js +2 -2
  28. package/dist/core/doctor/doctor-readiness-matrix.js +7 -0
  29. package/dist/core/doctor/doctor-zellij-repair.js +40 -0
  30. package/dist/core/feature-fixtures.js +1 -0
  31. package/dist/core/feature-registry.js +4 -1
  32. package/dist/core/fsx.js +1 -1
  33. package/dist/core/hooks-runtime.js +13 -0
  34. package/dist/core/init.js +4 -1
  35. package/dist/core/loops/loop-continuation-enforcer.js +40 -0
  36. package/dist/core/loops/loop-planner.js +29 -3
  37. package/dist/core/loops/loop-worker-runtime.js +27 -7
  38. package/dist/core/naruto/naruto-loop-worker-router.js +11 -2
  39. package/dist/core/qa-loop.js +39 -4
  40. package/dist/core/research/research-cycle-runner.js +1 -0
  41. package/dist/core/research/research-stage-runner.js +9 -2
  42. package/dist/core/research.js +35 -1
  43. package/dist/core/version.js +1 -1
  44. package/dist/core/zellij/homebrew-policy.js +44 -0
  45. package/dist/core/zellij/zellij-capability.js +32 -3
  46. package/dist/core/zellij/zellij-self-heal-types.js +45 -0
  47. package/dist/core/zellij/zellij-self-heal.js +414 -0
  48. package/dist/core/zellij/zellij-update.js +39 -6
  49. package/dist/scripts/sks-3-1-4-directive-check-lib.js +241 -0
  50. package/dist/scripts/sks-3-1-5-directive-check-lib.js +347 -0
  51. package/package.json +52 -2
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env node
2
+ // @ts-nocheck
3
+ import fs from 'node:fs';
4
+ import fsp from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { spawnSync } from 'node:child_process';
8
+ import { assertGate, emitGate, importDist, root } from './sks-1-18-gate-lib.js';
9
+ export async function runDirective314Gate(id) {
10
+ if (id.startsWith('zellij:'))
11
+ return zellijGate(id);
12
+ if (id.startsWith('doctor:zellij'))
13
+ return doctorZellijGate(id);
14
+ if (id.startsWith('mad:zellij'))
15
+ return madZellijGate(id);
16
+ if (id === 'lazycodex:analysis')
17
+ return lazycodexAnalysisGate(id);
18
+ if (id.startsWith('codex-app:') || id === 'doctor:codex-app-harness')
19
+ return codexAppGate(id);
20
+ if (id.startsWith('loop:'))
21
+ return loopGate(id);
22
+ if (id === 'lazycodex:interop-policy' || id === 'lazycodex:pattern-adoption-blackbox')
23
+ return lazycodexInteropGate(id);
24
+ throw new Error(`unknown_gate:${id}`);
25
+ }
26
+ async function zellijGate(id) {
27
+ const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
28
+ const selfHeal = await importDist('core/zellij/zellij-self-heal.js');
29
+ const policy = await importDist('core/zellij/homebrew-policy.js');
30
+ if (id === 'zellij:homebrew-policy') {
31
+ assertGate(policy.resolveHomebrewInstallPolicy({ env: {} }).allowed === false, 'Homebrew install must not be silent by default');
32
+ assertGate(policy.resolveHomebrewInstallPolicy({ installHomebrew: true }).allowed === false, 'Homebrew install flag alone must still require --yes or interactive/env approval');
33
+ assertGate(policy.resolveHomebrewInstallPolicy({ installHomebrew: true, autoApprove: true }).allowed === true, 'Homebrew install should be allowed with explicit flag+yes');
34
+ assertGate(policy.homebrewMissingDoctorMessage().includes('sks doctor --fix --install-homebrew --yes'), 'Homebrew policy must expose one-shot doctor command');
35
+ return emitGate(id, { fixtures: 3 });
36
+ }
37
+ if (id === 'zellij:update-missing-self-heal') {
38
+ const update = await importDist('core/zellij/zellij-update.js');
39
+ const result = await update.maybePromptZellijUpdateForLaunch(['--yes'], {
40
+ label: 'MAD launch',
41
+ root: rootDir,
42
+ selfHealOnMissing: true,
43
+ autoApprove: true,
44
+ env: fakeZellijEnv('missing', { brew: true })
45
+ });
46
+ assertGate(result.status === 'installed', 'missing update path must self-heal when requested', result);
47
+ return emitGate(id, { status: result.status });
48
+ }
49
+ const result = await selfHeal.repairZellijForSks({
50
+ root: rootDir,
51
+ requestedBy: 'doctor --fix',
52
+ fixRequested: true,
53
+ autoApprove: true,
54
+ installHomebrew: false,
55
+ env: fakeZellijEnv('missing', { brew: true })
56
+ });
57
+ assertGate(result.ok === true, 'zellij self-heal must succeed with fake brew present', result);
58
+ assertGate(result.strategy === 'brew-install-zellij', 'missing zellij must select brew-install-zellij', result);
59
+ assertGate(fs.existsSync(path.join(rootDir, '.sneakoscope', 'reports', 'zellij-self-heal.json')), 'self-heal artifact missing');
60
+ emitGate(id, { strategy: result.strategy });
61
+ }
62
+ async function doctorZellijGate(id) {
63
+ const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
64
+ const mod = await importDist('core/doctor/doctor-zellij-repair.js');
65
+ const env = fakeZellijEnv(id.includes('upgrade') ? 'too_old' : 'missing', { brew: !id.includes('no-homebrew') });
66
+ const previous = swapEnv(env);
67
+ try {
68
+ const result = await mod.runDoctorZellijRepair({ root: rootDir, args: ['--fix', '--yes'], doctorFix: true });
69
+ if (id.includes('no-homebrew')) {
70
+ assertGate(result.strategy === 'manual-required', 'no-homebrew doctor repair must be manual-required', result);
71
+ assertGate(String(result.command).includes('--install-homebrew'), 'manual no-homebrew path must show install-homebrew command', result);
72
+ }
73
+ else if (id.includes('upgrade')) {
74
+ assertGate(result.strategy === 'brew-upgrade-zellij', 'stale zellij must upgrade', result);
75
+ }
76
+ else {
77
+ assertGate(result.strategy === 'brew-install-zellij', 'missing zellij must install', result);
78
+ }
79
+ if (id === 'doctor:zellij-fix-output') {
80
+ const line = mod.doctorZellijRepairConsoleLine(result);
81
+ assertGate(!/optional live panes disabled/i.test(line), 'doctor repair output must not use optional/blocking wording', { line });
82
+ }
83
+ emitGate(id, { strategy: result.strategy });
84
+ }
85
+ finally {
86
+ restoreEnv(previous);
87
+ }
88
+ }
89
+ async function madZellijGate(id) {
90
+ const source = fs.readFileSync(path.join(root, 'src/core/commands/mad-sks-command.ts'), 'utf8');
91
+ assertGate(source.includes("requestedBy: 'sks --mad'"), 'MAD must request zellij self-heal as sks --mad');
92
+ assertGate(source.includes('--headless') && source.includes('live_panes: false'), 'MAD must support headless live_panes=false');
93
+ assertGate(!/optional live panes disabled/.test(source), 'MAD source must not print optional live panes disabled');
94
+ if (id === 'mad:zellij-no-contradictory-output') {
95
+ const update = fs.readFileSync(path.join(root, 'src/core/zellij/zellij-update.ts'), 'utf8');
96
+ assertGate(!/Zellij not found \(optional live panes disabled\)/.test(update), 'Zellij missing output must not be contradictory optional wording');
97
+ }
98
+ emitGate(id, { source_checked: true });
99
+ }
100
+ async function lazycodexAnalysisGate(id) {
101
+ const mod = await importDist('core/codex-app/lazycodex-analysis.js');
102
+ const report = await mod.writeLazyCodexPatternAnalysis(root);
103
+ assertGate(report.patterns.length >= 14, 'LazyCodex analysis must include required patterns', report);
104
+ const docs = mod.renderLazyCodexAnalysisMarkdown(report);
105
+ await fsp.writeFile(path.join(root, 'docs', 'lazycodex-analysis.md'), `${docs}\n`, 'utf8');
106
+ emitGate(id, { patterns: report.patterns.length });
107
+ }
108
+ async function codexAppGate(id) {
109
+ const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
110
+ const previous = swapEnv({
111
+ SKS_CODEX_0138_FAKE: '1',
112
+ SKS_CODEX_0139_FAKE: '1',
113
+ SKS_CODEX_PLUGIN_JSON_FAKE: '1',
114
+ SKS_CODEX_AGENT_TYPE_SUPPORTED: id.includes('blackbox') ? '1' : ''
115
+ });
116
+ try {
117
+ if (id === 'codex-app:harness-matrix' || id === 'doctor:codex-app-harness' || id === 'codex-app:harness-blackbox') {
118
+ const mod = await importDist('core/codex-app/codex-app-harness-matrix.js');
119
+ const matrix = await mod.buildCodexAppHarnessMatrix({ root: rootDir });
120
+ assertGate(matrix.schema === 'sks.codex-app-harness-matrix.v1', 'harness matrix schema mismatch', matrix);
121
+ assertGate(matrix.app_features.plugin_json === true, 'fixture should expose plugin_json', matrix);
122
+ if (id === 'doctor:codex-app-harness') {
123
+ const doctor = fs.readFileSync(path.join(root, 'src/commands/doctor.ts'), 'utf8');
124
+ assertGate(doctor.includes('Codex App Harness:'), 'doctor output must include Codex App Harness section');
125
+ assertGate(doctor.includes('codex_app_harness_matrix'), 'doctor JSON must include codex_app_harness_matrix');
126
+ }
127
+ return emitGate(id, { ok: matrix.ok, warnings: matrix.warnings.length });
128
+ }
129
+ if (id === 'codex-app:skill-sync' || id === 'codex-app:skill-agent-blackbox') {
130
+ const mod = await importDist('core/codex-app/codex-skill-sync.js');
131
+ const skillsRoot = path.join(rootDir, 'skills');
132
+ await fsp.mkdir(path.join(skillsRoot, 'ulw-loop'), { recursive: true });
133
+ const report = await mod.syncCodexSksSkills({ root: rootDir, skillsRoot, apply: true });
134
+ assertGate(report.interop.clobbered_lazycodex === false && report.lazycodex_reserved_present.includes('ulw-loop'), 'skill sync must preserve LazyCodex skills', report);
135
+ return emitGate(id, { desired: report.desired_skills.length });
136
+ }
137
+ if (id === 'codex-app:agent-role-sync') {
138
+ const mod = await importDist('core/codex-app/codex-agent-role-sync.js');
139
+ const report = await mod.syncCodexAgentRoles({ root: rootDir, codexHome: path.join(rootDir, 'codex-home'), apply: true, agentTypeSupported: true });
140
+ assertGate(report.fallback === 'agent_type', 'agent role sync should use agent_type when supported', report);
141
+ return emitGate(id, { roles: report.directive_roles.length });
142
+ }
143
+ if (id === 'codex-app:init-deep') {
144
+ const mod = await importDist('core/codex-app/codex-init-deep.js');
145
+ await fsp.mkdir(path.join(rootDir, 'src/core/zellij'), { recursive: true });
146
+ await fsp.writeFile(path.join(rootDir, 'src/core/zellij/a.ts'), 'export {}\n');
147
+ const report = await mod.runCodexInitDeep({ root: rootDir, apply: true });
148
+ assertGate(report.root_agents_preserved === true, 'init-deep must preserve user AGENTS.md', report);
149
+ return emitGate(id, { guidance: report.directory_guidance.length });
150
+ }
151
+ if (id === 'codex-app:hook-lifecycle') {
152
+ const mod = await importDist('core/codex-app/codex-hook-lifecycle.js');
153
+ const report = await mod.buildCodexHookLifecycle({ root: rootDir });
154
+ assertGate(report.approval_state === 'unknown', 'hook lifecycle must report unknown approval when not detectable', report);
155
+ return emitGate(id, { lifecycle: Object.keys(report.lifecycle).length });
156
+ }
157
+ if (id === 'codex-app:execution-profile') {
158
+ const mod = await importDist('core/codex-app/codex-app-execution-profile.js');
159
+ const profile = await mod.resolveCodexAppExecutionProfile({ root: rootDir });
160
+ assertGate(['codex-app-native', 'codex-cli-headless', 'sks-loop-headless', 'degraded-no-app'].includes(profile.mode), 'execution profile mode invalid', profile);
161
+ return emitGate(id, { mode: profile.mode });
162
+ }
163
+ }
164
+ finally {
165
+ restoreEnv(previous);
166
+ }
167
+ }
168
+ async function loopGate(id) {
169
+ const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
170
+ if (id === 'loop:planner-project-memory') {
171
+ const init = await importDist('core/codex-app/codex-init-deep.js');
172
+ const planner = await importDist('core/loops/loop-planner.js');
173
+ await fsp.mkdir(path.join(rootDir, 'src/core/loops'), { recursive: true });
174
+ await fsp.writeFile(path.join(rootDir, 'src/core/loops/a.ts'), 'export {}\n');
175
+ await init.runCodexInitDeep({ root: rootDir, apply: true });
176
+ const plan = await planner.planLoopsFromRequest({ root: rootDir, missionId: 'M-loop-memory', request: 'update loop planner project memory', sourceCommand: 'loop' });
177
+ assertGate(plan.project_memory?.injected === true, 'loop planner must consume init-deep memory hints', plan);
178
+ return emitGate(id, { injected: true });
179
+ }
180
+ const planDir = path.join(rootDir, '.sneakoscope', 'missions', 'M-loop-cont', 'loops');
181
+ await fsp.mkdir(planDir, { recursive: true });
182
+ await fsp.writeFile(path.join(rootDir, '.sneakoscope', 'missions', 'M-loop-cont', 'loops', 'loop-plan.json'), JSON.stringify({ graph: { nodes: [{ loop_id: 'loop-a' }] } }));
183
+ const mod = await importDist('core/loops/loop-continuation-enforcer.js');
184
+ const report = await mod.evaluateLoopContinuation({ root: rootDir, missionId: 'M-loop-cont' });
185
+ assertGate(report.should_continue === true, 'loop continuation should request resume when proof missing', report);
186
+ emitGate(id, { should_continue: report.should_continue });
187
+ }
188
+ async function lazycodexInteropGate(id) {
189
+ const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
190
+ const previous = swapEnv({ SKS_CODEX_PLUGIN_JSON_FAKE: '1' });
191
+ try {
192
+ const mod = await importDist('core/codex-app/lazycodex-interop-policy.js');
193
+ const skillsRoot = path.join(rootDir, '.codex', 'skills');
194
+ await fsp.mkdir(path.join(skillsRoot, 'start-work'), { recursive: true });
195
+ const report = await mod.buildLazyCodexInteropPolicy({ root: rootDir, codexHome: path.join(rootDir, '.codex') });
196
+ assertGate(report.policy.clobber_lazycodex_skills === false, 'interop policy must not clobber LazyCodex skills', report);
197
+ emitGate(id, { detected: report.lazycodex_detected, collisions: report.detection.collisions.length });
198
+ }
199
+ finally {
200
+ restoreEnv(previous);
201
+ }
202
+ }
203
+ function fakeZellijEnv(status, opts = {}) {
204
+ return {
205
+ ...process.env,
206
+ SKS_ZELLIJ_CAPABILITY_FAKE_STATUS: status,
207
+ SKS_ZELLIJ_CAPABILITY_FAKE_VERSION: status === 'too_old' ? '0.40.0' : '0.44.0',
208
+ SKS_ZELLIJ_SELF_HEAL_BEFORE_STATUS: status,
209
+ SKS_ZELLIJ_SELF_HEAL_BEFORE_VERSION: status === 'too_old' ? '0.40.0' : '',
210
+ SKS_ZELLIJ_SELF_HEAL_AFTER_STATUS: 'ok',
211
+ SKS_ZELLIJ_SELF_HEAL_AFTER_VERSION: '0.44.3',
212
+ SKS_ZELLIJ_LATEST_VERSION: '0.44.3',
213
+ SKS_ZELLIJ_SELF_HEAL_FAKE_RUN: '1',
214
+ SKS_ZELLIJ_SELF_HEAL_BREW_PRESENT: opts.brew ? '1' : '0'
215
+ };
216
+ }
217
+ async function tempRoot(prefix) {
218
+ const dir = await fsp.mkdtemp(path.join(os.tmpdir(), prefix));
219
+ await fsp.mkdir(path.join(dir, '.sneakoscope', 'reports'), { recursive: true });
220
+ return dir;
221
+ }
222
+ function swapEnv(next) {
223
+ const previous = {};
224
+ for (const [key, value] of Object.entries(next)) {
225
+ previous[key] = process.env[key];
226
+ if (value === '')
227
+ delete process.env[key];
228
+ else
229
+ process.env[key] = value;
230
+ }
231
+ return previous;
232
+ }
233
+ function restoreEnv(previous) {
234
+ for (const [key, value] of Object.entries(previous)) {
235
+ if (value === undefined)
236
+ delete process.env[key];
237
+ else
238
+ process.env[key] = value;
239
+ }
240
+ }
241
+ //# sourceMappingURL=sks-3-1-4-directive-check-lib.js.map
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env node
2
+ // @ts-nocheck
3
+ import fs from 'node:fs';
4
+ import fsp from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { assertGate, emitGate, importDist, root } from './sks-1-18-gate-lib.js';
8
+ const CORE_NO_TS_NOCHECK_DIRS = [
9
+ 'src/core/zellij',
10
+ 'src/core/doctor',
11
+ 'src/core/codex-app',
12
+ 'src/core/loops',
13
+ 'src/core/naruto'
14
+ ];
15
+ const TARGET_TYPED_FILES = [
16
+ 'src/core/zellij/zellij-self-heal.ts',
17
+ 'src/core/zellij/homebrew-policy.ts',
18
+ 'src/core/doctor/doctor-zellij-repair.ts',
19
+ 'src/core/codex-app/codex-app-harness-matrix.ts',
20
+ 'src/core/codex-app/lazycodex-analysis.ts',
21
+ 'src/core/codex-app/codex-skill-sync.ts',
22
+ 'src/core/codex-app/codex-agent-role-sync.ts',
23
+ 'src/core/codex-app/codex-init-deep.ts',
24
+ 'src/core/codex-app/codex-hook-lifecycle.ts',
25
+ 'src/core/codex-app/codex-app-execution-profile.ts',
26
+ 'src/core/codex-app/lazycodex-interop-policy.ts',
27
+ 'src/core/loops/loop-continuation-enforcer.ts'
28
+ ];
29
+ export async function runDirective315Gate(id) {
30
+ if (id === 'lint:no-ts-nocheck-core')
31
+ return noTsNoCheckCore(id);
32
+ if (id === 'codex-app:type-safety')
33
+ return codexAppTypeSafety(id);
34
+ if (id === 'type-surface:codex-app')
35
+ return typeSurfaceCodexApp(id);
36
+ if (id.startsWith('zellij:'))
37
+ return zellijGate(id);
38
+ if (id.startsWith('codex-app:hook-approval'))
39
+ return hookApprovalGate(id);
40
+ if (id.startsWith('codex-app:agent-type'))
41
+ return agentTypeGate(id);
42
+ if (id.startsWith('lazycodex:'))
43
+ return lazycodexGate(id);
44
+ if (id.includes('init-deep') || id.includes('planner-project-memory-deep'))
45
+ return initDeepGate(id);
46
+ if (id.includes('execution-profile-routing'))
47
+ return executionProfileRoutingGate(id);
48
+ if (id === 'codex-app:skill-rich-content')
49
+ return richContentGate(id);
50
+ if (id === 'codex-app:agent-role-rich-content')
51
+ return richContentGate(id);
52
+ throw new Error(`unknown_gate:${id}`);
53
+ }
54
+ function noTsNoCheckCore(id) {
55
+ const offenders = [];
56
+ for (const dir of CORE_NO_TS_NOCHECK_DIRS) {
57
+ for (const file of walkTs(path.join(root, dir))) {
58
+ const text = fs.readFileSync(file, 'utf8');
59
+ if (/^\s*\/\/\s*@ts-nocheck\b/m.test(text))
60
+ offenders.push(path.relative(root, file));
61
+ }
62
+ }
63
+ assertGate(offenders.length === 0, 'core @ts-nocheck offenders found', { offenders });
64
+ emitGate(id, { scanned_dirs: CORE_NO_TS_NOCHECK_DIRS.length });
65
+ }
66
+ function codexAppTypeSafety(id) {
67
+ const required = [
68
+ 'src/core/codex-app/codex-app-types.ts',
69
+ 'src/core/zellij/zellij-self-heal-types.ts',
70
+ 'src/core/codex-app/codex-hook-approval-probe.ts',
71
+ 'src/core/codex-app/codex-agent-type-probe.ts',
72
+ 'src/core/codex-app/lazycodex-live-analyzer.ts'
73
+ ];
74
+ for (const file of [...required, ...TARGET_TYPED_FILES])
75
+ assertGate(fs.existsSync(path.join(root, file)), `missing ${file}`);
76
+ for (const file of TARGET_TYPED_FILES) {
77
+ const text = fs.readFileSync(path.join(root, file), 'utf8');
78
+ assertGate(!/^\s*\/\/\s*@ts-nocheck\b/m.test(text), `target file still has @ts-nocheck: ${file}`);
79
+ }
80
+ const matrix = fs.readFileSync(path.join(root, 'src/core/codex-app/codex-app-harness-matrix.ts'), 'utf8');
81
+ assertGate(!/hookApprovalKnown\s*=\s*false/.test(matrix), 'harness matrix must not hardcode hook approval unknown');
82
+ assertGate(!/SKS_CODEX_AGENT_TYPE_SUPPORTED\s*===\s*['"]1['"]/.test(matrix), 'harness matrix must not use env-only agent_type support');
83
+ emitGate(id, { target_files: TARGET_TYPED_FILES.length });
84
+ }
85
+ async function typeSurfaceCodexApp(id) {
86
+ const types = await importDist('core/codex-app/codex-app-types.js');
87
+ const zellijTypes = await importDist('core/zellij/zellij-self-heal-types.js');
88
+ const agentProbe = await importDist('core/codex-app/codex-agent-type-probe.js');
89
+ const sampleMatrix = {
90
+ schema: 'sks.codex-app-harness-matrix.v1',
91
+ generated_at: new Date().toISOString(),
92
+ ok: true,
93
+ codex_cli: { available: true, version: 'codex test' },
94
+ app_features: {
95
+ plugin_json: true,
96
+ marketplace_add: true,
97
+ marketplace_upgrade: true,
98
+ startup_review_detectable: true,
99
+ hook_approval_state_detectable: true,
100
+ hook_approval_state: 'approved',
101
+ skill_picker_ready: true,
102
+ agent_type_supported: true,
103
+ mcp_inventory_ready: true,
104
+ app_handoff_ready: true,
105
+ image_path_exposure_ready: true
106
+ },
107
+ sks_integrations: {
108
+ dollar_skills_synced: true,
109
+ agent_roles_synced: true,
110
+ hooks_synced: true,
111
+ init_deep_available: true,
112
+ loop_mesh_app_profile_available: true
113
+ },
114
+ probes: {},
115
+ blockers: [],
116
+ warnings: []
117
+ };
118
+ assertGate(types.isCodexAppHarnessMatrix(sampleMatrix) === true, 'CodexAppHarnessMatrix guard rejected valid sample');
119
+ const normalized = zellijTypes.normalizeZellijSelfHealResult({ schema: 'sks.zellij-self-heal.v1', ok: true, requested_by: 'setup', strategy: 'none-current', before: {}, after: {}, blockers: [], warnings: [] });
120
+ assertGate(normalized.dry_run === false && Array.isArray(normalized.planned_mutations), 'zellij self-heal normalizer must backfill new fields', normalized);
121
+ const payload = agentProbe.agentRolePayloadFor('sks-checker', { supported: true, schema: 'sks.codex-agent-type-probe.v1' });
122
+ assertGate(payload.strategy === 'agent_type' && payload.agent_type === 'sks-checker', 'agent role payload must select agent_type when supported', payload);
123
+ emitGate(id, { guards: 3 });
124
+ }
125
+ async function zellijGate(id) {
126
+ const rootDir = await tempRoot(id);
127
+ const selfHeal = await importDist('core/zellij/zellij-self-heal.js');
128
+ if (id === 'zellij:self-heal-status-contract') {
129
+ const update = await importDist('core/zellij/zellij-update.js');
130
+ const result = await update.maybePromptZellijUpdateForLaunch(['--yes'], {
131
+ label: 'MAD launch',
132
+ root: rootDir,
133
+ selfHealOnMissing: true,
134
+ allowHeadlessFallback: true,
135
+ env: fakeZellijEnv('missing', { brew: false })
136
+ });
137
+ assertGate(result.status === 'headless_fallback', 'missing zellij with headless fallback must return headless_fallback', result);
138
+ return emitGate(id, { status: result.status });
139
+ }
140
+ const result = await selfHeal.repairZellijForSks({
141
+ root: rootDir,
142
+ requestedBy: 'doctor --fix',
143
+ fixRequested: true,
144
+ autoApprove: true,
145
+ dryRun: id.includes('dry-run') || id.includes('typed-blackbox'),
146
+ installHomebrew: false,
147
+ env: fakeZellijEnv('missing', { brew: true })
148
+ });
149
+ assertGate(result.dry_run === true, 'dry-run zellij result must set dry_run', result);
150
+ assertGate(result.planned_mutations.length >= 1, 'dry-run zellij result must include planned mutations', result);
151
+ assertGate(result.mutation_guard_artifact?.endsWith('#planned'), 'dry-run zellij mutation artifact must be planned-only', result);
152
+ emitGate(id, { planned: result.planned_mutations.length });
153
+ }
154
+ async function hookApprovalGate(id) {
155
+ const rootDir = await tempRoot(id);
156
+ const previous = swapEnv({ SKS_CODEX_HOOK_APPROVAL_FIXTURE: id.includes('blackbox') ? 'modified' : 'approved' });
157
+ try {
158
+ const probe = await importDist('core/codex-app/codex-hook-approval-probe.js');
159
+ const report = await probe.probeCodexHookApprovalState(rootDir);
160
+ if (id.includes('blackbox')) {
161
+ assertGate(report.approval_state === 'modified_requires_reapproval' && report.ok === false, 'modified hook approval must require reapproval', report);
162
+ }
163
+ else {
164
+ assertGate(report.approval_state === 'approved' && report.detectable === true, 'fixture approved hook state must be detectable', report);
165
+ }
166
+ if (id === 'codex-app:hook-approval-matrix') {
167
+ const matrixMod = await importDist('core/codex-app/codex-app-harness-matrix.js');
168
+ const matrix = await matrixMod.buildCodexAppHarnessMatrix({ root: rootDir });
169
+ assertGate(matrix.app_features.hook_approval_state === report.approval_state, 'matrix must embed hook approval probe state', matrix);
170
+ assertGate(matrix.probes.hook_approval.schema === 'sks.codex-hook-approval-probe.v1', 'matrix must embed hook approval probe');
171
+ }
172
+ emitGate(id, { state: report.approval_state });
173
+ }
174
+ finally {
175
+ restoreEnv(previous);
176
+ }
177
+ }
178
+ async function agentTypeGate(id) {
179
+ const rootDir = await tempRoot(id);
180
+ const schema = JSON.stringify([{ name: 'spawn_agent', parameters: { properties: { agent_type: { type: 'string' } } } }]);
181
+ const previous = swapEnv({ SKS_CODEX_TOOL_SCHEMA_JSON: schema });
182
+ try {
183
+ const probeMod = await importDist('core/codex-app/codex-agent-type-probe.js');
184
+ const probe = await probeMod.probeCodexAgentTypeSupport(rootDir);
185
+ assertGate(probe.supported === true && probe.source === 'codex-tool-schema', 'agent_type probe must detect schema support', probe);
186
+ if (id.includes('routing') || id.includes('blackbox')) {
187
+ const roleMod = await importDist('core/codex-app/codex-agent-role-sync.js');
188
+ const report = await roleMod.syncCodexAgentRoles({ root: rootDir, codexHome: path.join(rootDir, 'codex-home'), apply: true });
189
+ assertGate(report.fallback === 'agent_type', 'agent role sync must route to agent_type when probe supports it', report);
190
+ }
191
+ emitGate(id, { supported: probe.supported });
192
+ }
193
+ finally {
194
+ restoreEnv(previous);
195
+ }
196
+ }
197
+ async function lazycodexGate(id) {
198
+ const rootDir = await tempRoot(id);
199
+ const sourceDir = path.join(rootDir, 'lazycodex-source');
200
+ await fsp.mkdir(sourceDir, { recursive: true });
201
+ await fsp.writeFile(path.join(sourceDir, 'README.md'), [
202
+ '# LazyCodex fixture',
203
+ 'npx lazycodex-ai install aliases npx --yes',
204
+ 'codex plugin marketplace add omo@sisyphuslabs',
205
+ 'hook approval requires startup review and trust.',
206
+ '$ulw-plan and $ulw-loop continue work.',
207
+ '$init-deep writes hierarchical AGENTS.md.',
208
+ 'spawn_agent supports agent_type with message-role fallback.'
209
+ ].join('\n'));
210
+ const liveMod = await importDist('core/codex-app/lazycodex-live-analyzer.js');
211
+ const live = await liveMod.analyzeLazyCodexLiveSource({ root: rootDir, sourceDir, writeReport: id.includes('blackbox') });
212
+ assertGate(live.evidence.length >= 5, 'live LazyCodex analysis must collect hashed evidence', live);
213
+ if (id === 'lazycodex:analysis-with-evidence') {
214
+ const analysisMod = await importDist('core/codex-app/lazycodex-analysis.js');
215
+ const report = analysisMod.buildLazyCodexPatternAnalysis(live);
216
+ assertGate(report.patterns.some((row) => row.confidence === 'high' && row.live_evidence.length), 'static analysis must merge live evidence', report);
217
+ }
218
+ if (id.includes('blackbox')) {
219
+ assertGate(fs.existsSync(path.join(rootDir, '.sneakoscope', 'reports', 'lazycodex-live-analysis.json')), 'live analysis report missing');
220
+ }
221
+ emitGate(id, { evidence: live.evidence.length });
222
+ }
223
+ async function initDeepGate(id) {
224
+ const rootDir = await tempRoot(id);
225
+ await fsp.mkdir(path.join(rootDir, 'src/core/zellij'), { recursive: true });
226
+ for (let i = 0; i < 18; i += 1)
227
+ await fsp.writeFile(path.join(rootDir, 'src/core/zellij', `f${i}.ts`), 'export {}\n');
228
+ await fsp.writeFile(path.join(rootDir, 'src/core/zellij', 'AGENTS.md'), '# User local guidance\nKeep me.\n');
229
+ const init = await importDist('core/codex-app/codex-init-deep.js');
230
+ const report = await init.runCodexInitDeep({ root: rootDir, apply: true, directoryLocal: true });
231
+ const agents = fs.readFileSync(path.join(rootDir, 'src/core/zellij', 'AGENTS.md'), 'utf8');
232
+ assertGate(/BEGIN SKS INIT-DEEP MANAGED SECTION/.test(agents), 'directory AGENTS.md managed block missing', { agents });
233
+ assertGate(agents.includes('Keep me.'), 'directory AGENTS.md must preserve user content', { agents });
234
+ assertGate(report.directory_local_agents.backup_paths.length >= 1, 'directory AGENTS.md backup missing', report);
235
+ if (id === 'loop:planner-project-memory-deep') {
236
+ const planner = await importDist('core/loops/loop-planner.js');
237
+ const plan = await planner.planLoopsFromRequest({ root: rootDir, missionId: 'M-memory-deep', request: 'change zellij self heal and loop planner', sourceCommand: 'loop' });
238
+ assertGate(plan.graph.nodes.some((node) => Array.isArray(node.memory_hints) && node.memory_hints.length), 'loop nodes must consume deep memory hints', plan);
239
+ }
240
+ emitGate(id, { managed_agents: report.directory_local_agents.created.length + report.directory_local_agents.updated.length });
241
+ }
242
+ async function executionProfileRoutingGate(id) {
243
+ const rootDir = await tempRoot(id);
244
+ const previous = swapEnv({ SKS_CODEX_HOOK_APPROVAL_FIXTURE: 'approved', SKS_CODEX_AGENT_TYPE_FIXTURE: 'supported' });
245
+ try {
246
+ const profileMod = await importDist('core/codex-app/codex-app-execution-profile.js');
247
+ const profile = await profileMod.resolveCodexAppExecutionProfile({ root: rootDir });
248
+ assertGate(profile.agent_role_strategy === 'agent_type', 'execution profile must consume agent_type probe fixture', profile);
249
+ if (id === 'qa-loop:execution-profile-routing' || id === 'pipeline:execution-profile-routing-blackbox') {
250
+ const qa = await importDist('core/qa-loop.js');
251
+ const dir = path.join(rootDir, '.sneakoscope', 'missions', 'M-qa');
252
+ await fsp.mkdir(dir, { recursive: true });
253
+ await qa.writeQaLoopArtifacts(dir, { id: 'M-qa', prompt: 'QA no UI' }, { sealed_hash: 'sealed', answers: { QA_SCOPE: 'api_e2e_only', TARGET_ENVIRONMENT: 'local_dev_server', DESTRUCTIVE_DEPLOYED_TESTS_ALLOWED: 'never' } });
254
+ const gate = JSON.parse(fs.readFileSync(path.join(dir, 'qa-gate.json'), 'utf8'));
255
+ assertGate(gate.codex_app_execution_profile?.agent_role_strategy === 'agent_type', 'QA gate must consume execution profile', gate);
256
+ }
257
+ if (id === 'research:execution-profile-routing' || id === 'pipeline:execution-profile-routing-blackbox') {
258
+ const research = await importDist('core/research.js');
259
+ const dir = path.join(rootDir, '.sneakoscope', 'missions', 'M-research');
260
+ await fsp.mkdir(dir, { recursive: true });
261
+ const plan = await research.writeResearchPlan(dir, 'research execution profile routing', { root: rootDir, missionId: 'M-research' });
262
+ assertGate(plan.codex_app_execution_profile?.agent_role_strategy === 'agent_type', 'Research plan must consume execution profile', plan);
263
+ assertGate(plan.web_research_policy.source_tool_routing, 'Research plan must include source tool routing', plan);
264
+ }
265
+ if (id === 'loop:execution-profile-routing' || id === 'pipeline:execution-profile-routing-blackbox') {
266
+ const source = fs.readFileSync(path.join(root, 'src/core/loops/loop-worker-runtime.ts'), 'utf8');
267
+ assertGate(source.includes('codex_app_execution_profile') && source.includes('SKS_CODEX_APP_EXECUTION_PROFILE'), 'Loop worker runtime must persist execution profile routing');
268
+ }
269
+ emitGate(id, { mode: profile.mode, strategy: profile.agent_role_strategy });
270
+ }
271
+ finally {
272
+ restoreEnv(previous);
273
+ }
274
+ }
275
+ async function richContentGate(id) {
276
+ const rootDir = await tempRoot(id);
277
+ if (id === 'codex-app:skill-rich-content') {
278
+ const mod = await importDist('core/codex-app/codex-skill-sync.js');
279
+ const skillsRoot = path.join(rootDir, 'skills');
280
+ const report = await mod.syncCodexSksSkills({ root: rootDir, skillsRoot, apply: true });
281
+ const skill = fs.readFileSync(path.join(skillsRoot, 'loop', 'SKILL.md'), 'utf8');
282
+ assertGate(/Purpose:|Evidence:|Fallback:/.test(skill), 'managed skill must include rich route content', { skill, report });
283
+ return emitGate(id, { skills: report.created.length });
284
+ }
285
+ const previous = swapEnv({ SKS_CODEX_AGENT_TYPE_FIXTURE: 'supported' });
286
+ try {
287
+ const mod = await importDist('core/codex-app/codex-agent-role-sync.js');
288
+ const codexHome = path.join(rootDir, 'codex-home');
289
+ const report = await mod.syncCodexAgentRoles({ root: rootDir, codexHome, apply: true });
290
+ const role = fs.readFileSync(path.join(codexHome, 'agents', 'sks-checker.toml'), 'utf8');
291
+ assertGate(role.includes('SKS managed 3.1.5 directive role') && role.includes('Execution role strategy'), 'managed agent role must include rich 3.1.5 content', { role, report });
292
+ emitGate(id, { roles: report.created.length });
293
+ }
294
+ finally {
295
+ restoreEnv(previous);
296
+ }
297
+ }
298
+ function* walkTs(dir) {
299
+ if (!fs.existsSync(dir))
300
+ return;
301
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
302
+ const full = path.join(dir, entry.name);
303
+ if (entry.isDirectory())
304
+ yield* walkTs(full);
305
+ else if (entry.isFile() && full.endsWith('.ts'))
306
+ yield full;
307
+ }
308
+ }
309
+ async function tempRoot(id) {
310
+ const dir = await fsp.mkdtemp(path.join(os.tmpdir(), `sks-315-${id.replace(/[^a-z0-9]+/gi, '-')}-`));
311
+ await fsp.mkdir(path.join(dir, '.sneakoscope', 'reports'), { recursive: true });
312
+ return dir;
313
+ }
314
+ function fakeZellijEnv(status, opts = {}) {
315
+ return {
316
+ ...process.env,
317
+ SKS_ZELLIJ_CAPABILITY_FAKE_STATUS: status,
318
+ SKS_ZELLIJ_CAPABILITY_FAKE_VERSION: status === 'too_old' ? '0.40.0' : '0.44.0',
319
+ SKS_ZELLIJ_SELF_HEAL_BEFORE_STATUS: status,
320
+ SKS_ZELLIJ_SELF_HEAL_BEFORE_VERSION: status === 'too_old' ? '0.40.0' : '',
321
+ SKS_ZELLIJ_SELF_HEAL_AFTER_STATUS: 'ok',
322
+ SKS_ZELLIJ_SELF_HEAL_AFTER_VERSION: '0.44.3',
323
+ SKS_ZELLIJ_LATEST_VERSION: '0.44.3',
324
+ SKS_ZELLIJ_SELF_HEAL_FAKE_RUN: '1',
325
+ SKS_ZELLIJ_SELF_HEAL_BREW_PRESENT: opts.brew ? '1' : '0'
326
+ };
327
+ }
328
+ function swapEnv(next) {
329
+ const previous = {};
330
+ for (const [key, value] of Object.entries(next)) {
331
+ previous[key] = process.env[key];
332
+ if (value === '')
333
+ delete process.env[key];
334
+ else
335
+ process.env[key] = value;
336
+ }
337
+ return previous;
338
+ }
339
+ function restoreEnv(previous) {
340
+ for (const [key, value] of Object.entries(previous)) {
341
+ if (value === undefined)
342
+ delete process.env[key];
343
+ else
344
+ process.env[key] = value;
345
+ }
346
+ }
347
+ //# sourceMappingURL=sks-3-1-5-directive-check-lib.js.map