sneakoscope 3.1.6 → 3.1.8

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 (49) hide show
  1. package/README.md +10 -3
  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/commands/codex-app.js +20 -2
  8. package/dist/commands/codex-native.js +18 -2
  9. package/dist/commands/doctor.js +106 -12
  10. package/dist/core/codex-app/codex-agent-role-sync.js +9 -5
  11. package/dist/core/codex-app/codex-init-deep.js +6 -2
  12. package/dist/core/codex-app/codex-skill-sync.js +80 -152
  13. package/dist/core/codex-control/codex-0138-capability.js +5 -2
  14. package/dist/core/codex-native/codex-native-feature-broker.js +74 -6
  15. package/dist/core/codex-native/codex-native-pattern-analysis.js +14 -2
  16. package/dist/core/codex-native/codex-native-reference-cache.js +98 -0
  17. package/dist/core/codex-native/codex-native-reference-source.js +50 -11
  18. package/dist/core/codex-native/codex-native-repair-transaction.js +150 -0
  19. package/dist/core/codex-native/core-skill-integrity.js +89 -0
  20. package/dist/core/codex-native/core-skill-manifest.js +156 -0
  21. package/dist/core/codex-native/native-capability-postcheck.js +35 -0
  22. package/dist/core/codex-native/native-capability-repair-matrix.js +210 -0
  23. package/dist/core/codex-native/native-capability-repair.js +47 -0
  24. package/dist/core/codex-native/native-media-computer-repair.js +5 -0
  25. package/dist/core/codex-native/project-skill-dedupe.js +109 -0
  26. package/dist/core/codex-native/skill-name-canonicalizer.js +21 -0
  27. package/dist/core/codex-native/skill-registry-ledger.js +85 -0
  28. package/dist/core/codex-plugins/codex-plugin-json.js +5 -2
  29. package/dist/core/commands/basic-cli.js +15 -9
  30. package/dist/core/commands/mad-sks-command.js +16 -0
  31. package/dist/core/config/config-migration-journal.js +27 -0
  32. package/dist/core/config/managed-config-merge.js +105 -0
  33. package/dist/core/config/secret-preservation.js +169 -0
  34. package/dist/core/config/supabase-secret-preservation.js +29 -0
  35. package/dist/core/doctor/doctor-native-capability-repair.js +48 -0
  36. package/dist/core/fsx.js +1 -1
  37. package/dist/core/init.js +5 -1
  38. package/dist/core/loops/loop-planner.js +1 -1
  39. package/dist/core/loops/loop-worker-prompts.js +2 -0
  40. package/dist/core/loops/loop-worker-runtime.js +8 -1
  41. package/dist/core/version.js +1 -1
  42. package/dist/scripts/codex-native-runtime-e2e-fixture.js +75 -0
  43. package/dist/scripts/loop-worker-fixture-child.js +2 -1
  44. package/dist/scripts/sizecheck.js +8 -2
  45. package/dist/scripts/sks-3-1-5-directive-check-lib.js +1 -1
  46. package/dist/scripts/sks-3-1-6-directive-check-lib.js +2 -2
  47. package/dist/scripts/sks-3-1-7-directive-check-lib.js +58 -0
  48. package/dist/scripts/sks-3-1-8-check-lib.js +30 -0
  49. package/package.json +39 -2
package/README.md CHANGED
@@ -35,7 +35,14 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **3.1.6** removes external reference branding from user-visible release surfaces and routes Codex-native feature decisions through one broker for Loop, QA, Research, Image, MAD, and Doctor.
38
+ SKS **3.1.8** hardens update/setup/doctor safety around four operator-facing failure modes: immutable core skills, duplicate project skills, real native capability repair status, and Supabase/secret preservation.
39
+
40
+ What changed in 3.1.8:
41
+
42
+ - **Core SKS skills are content-addressed and immutable.** The eight built-in route skills now have a manifest and no-drift gates; setup/update/doctor may install missing managed copies or restore corrupted managed copies, but they do not overwrite user-authored collisions.
43
+ - **Duplicate skills are detected and repaired safely.** Canonical skill names collapse variants such as `Loop`, `loop`, and `loop/SKILL.md`; SKS-managed duplicates can be quarantined automatically, while user-authored duplicates require explicit confirmation.
44
+ - **`sks doctor --fix` reports native capability truthfully.** Image generation, image follow-up edit paths, Computer Use, Chrome/web review, app screenshots, app handoff, and image path exposure now run through a repair matrix and postcheck instead of capability assumptions.
45
+ - **Supabase keys survive setup/update/doctor.** Protected secret surfaces are fingerprinted before and after guarded operations; reports store only redacted previews and hashes, never raw values.
39
46
 
40
47
  SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
41
48
 
@@ -79,7 +86,7 @@ npm run runtime:ts-python-boundary
79
86
  npm run codex-control:all-pipelines
80
87
  ```
81
88
 
82
- Broader release checks still live behind `npm run release:check`. Detailed release history is in [CHANGELOG.md](CHANGELOG.md), and release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
89
+ Change-aware release checks live behind `npm run release:check`; publish-authorizing full DAG checks use `npm run release:check:full`. Detailed release history is in [CHANGELOG.md](CHANGELOG.md), and release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
83
90
 
84
91
  ## 🍥 Parallelism, UX, And Integrations
85
92
 
@@ -622,7 +629,7 @@ TriWiki is intentionally sparse: `sks wiki sweep` records demote, soft-forget, a
622
629
 
623
630
  ```sh
624
631
  sks codex-native status --json
625
- sks codex-native invocation-plan --route '$Loop' --capability agent-role --json
632
+ sks codex-native invocation-plan --route Loop --capability agent-role --json
626
633
  sks codex-native init-deep --apply --directory-local --json
627
634
  ```
628
635
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "3.1.6"
79
+ version = "3.1.8"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "3.1.6"
3
+ version = "3.1.8"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 3.1.6"),
7
+ Some("--version") => println!("sks-rs 3.1.8"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "3.1.6",
5
- "source_digest": "98f9f4c6a64ddd6b1d43f757e16358768f24b27cd9c3285eab0bdacce7cbf5d6",
6
- "source_file_count": 2543,
7
- "built_at_source_time": 1781450666248
4
+ "package_version": "3.1.8",
5
+ "source_digest": "94742dc3eb63f280df98aa286ac2a94a14fd140f27c89db85880e4f92c630454",
6
+ "source_file_count": 2602,
7
+ "built_at_source_time": 1781513577024
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '3.1.6';
2
+ const FAST_PACKAGE_VERSION = '3.1.8';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -9,12 +9,15 @@ import { syncCodexAgentRoles } from '../core/codex-app/codex-agent-role-sync.js'
9
9
  import { runCodexInitDeep } from '../core/codex-app/codex-init-deep.js';
10
10
  import { buildCodexHookLifecycle } from '../core/codex-app/codex-hook-lifecycle.js';
11
11
  import { resolveCodexAppExecutionProfile } from '../core/codex-app/codex-app-execution-profile.js';
12
+ import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
12
13
  export async function run(_command, args = []) {
13
14
  const action = args[0] || 'check';
14
15
  if (action === 'remote-control' || action === 'remote')
15
16
  return codexAppRemoteControlCommand(args.slice(1));
16
- if (action === 'harness-matrix')
17
- return printCodexAppResult(args, await buildCodexAppHarnessMatrix({ root: await sksRoot(), applyRepairs: flag(args, '--fix') || flag(args, '--apply') }));
17
+ if (action === 'harness-matrix') {
18
+ const root = await sksRoot();
19
+ return printCodexAppResult(args, await maybeRepairThenReadOnlyHarness(args, root));
20
+ }
18
21
  if (action === 'skill-sync')
19
22
  return printCodexAppResult(args, await syncCodexSksSkills({ root: await sksRoot(), apply: flag(args, '--apply') || flag(args, '--fix') }));
20
23
  if (action === 'agent-role-sync')
@@ -103,4 +106,19 @@ function printCodexAppResult(args = [], result) {
103
106
  if (result?.ok === false)
104
107
  process.exitCode = 1;
105
108
  }
109
+ async function maybeRepairThenReadOnlyHarness(args = [], root) {
110
+ const wantsRepair = flag(args, '--fix') || flag(args, '--apply') || flag(args, '--repair-codex-native');
111
+ if (!wantsRepair)
112
+ return buildCodexAppHarnessMatrix({ root, mode: 'read-only' });
113
+ const repair = await repairCodexNativeManagedAssets({ root, requestedBy: 'manual', yes: flag(args, '--yes') });
114
+ const matrix = await buildCodexAppHarnessMatrix({ root, mode: 'read-only' });
115
+ return {
116
+ schema: 'sks.codex-app-harness-read-repair-split.v1',
117
+ ok: repair.ok && matrix?.ok !== false,
118
+ repair,
119
+ matrix,
120
+ blockers: [...(repair.blockers || []), ...(matrix?.blockers || [])],
121
+ warnings: [...(repair.warnings || []), 'harness_probe_after_explicit_repair_transaction']
122
+ };
123
+ }
106
124
  //# sourceMappingURL=codex-app.js.map
@@ -12,14 +12,15 @@ import { resolveCodexNativeInvocationPlan } from '../core/codex-native/codex-nat
12
12
  import { buildCodexNativeInteropPolicy } from '../core/codex-native/codex-native-interop-policy.js';
13
13
  import { analyzeCodexNativeReferenceSource } from '../core/codex-native/codex-native-reference-evidence.js';
14
14
  import { writeCodexNativePatternAnalysis } from '../core/codex-native/codex-native-pattern-analysis.js';
15
+ import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
15
16
  export async function run(_command, args = []) {
16
17
  const root = await sksRoot();
17
18
  const action = String(args[0] || 'status');
18
19
  if (action === 'status' || action === 'check' || action === 'feature-broker' || action === 'feature-matrix') {
19
- return printCodexNativeResult(args, await buildCodexNativeFeatureMatrix({ root, applyRepairs: flag(args, '--fix') || flag(args, '--apply') }));
20
+ return printCodexNativeResult(args, await maybeRepairThenReadOnlyMatrix(args, root, () => buildCodexNativeFeatureMatrix({ root, mode: 'read-only' })));
20
21
  }
21
22
  if (action === 'harness-matrix' || action === 'harness-compat') {
22
- return printCodexNativeResult(args, await buildCodexAppHarnessMatrix({ root, applyRepairs: flag(args, '--fix') || flag(args, '--apply') }));
23
+ return printCodexNativeResult(args, await maybeRepairThenReadOnlyMatrix(args, root, () => buildCodexAppHarnessMatrix({ root, mode: 'read-only' })));
23
24
  }
24
25
  if (action === 'skill-sync')
25
26
  return printCodexNativeResult(args, await syncCodexSksSkills({ root, apply: flag(args, '--apply') || flag(args, '--fix') }));
@@ -46,6 +47,21 @@ export async function run(_command, args = []) {
46
47
  console.error('Usage: sks codex-native status|feature-broker|harness-compat|skill-sync|agent-role-sync|init-deep|hook-lifecycle|execution-profile|interop-policy|reference-evidence|pattern-analysis|invocation-plan [--json]');
47
48
  process.exitCode = 1;
48
49
  }
50
+ async function maybeRepairThenReadOnlyMatrix(args = [], root, matrixFn) {
51
+ const wantsRepair = flag(args, '--fix') || flag(args, '--apply') || flag(args, '--repair-codex-native');
52
+ if (!wantsRepair)
53
+ return matrixFn();
54
+ const repair = await repairCodexNativeManagedAssets({ root, requestedBy: 'manual', yes: flag(args, '--yes') });
55
+ const matrix = await matrixFn();
56
+ return {
57
+ schema: 'sks.codex-native-read-repair-split.v1',
58
+ ok: repair.ok && matrix?.ok !== false,
59
+ repair,
60
+ matrix,
61
+ blockers: [...(repair.blockers || []), ...(matrix?.blockers || [])],
62
+ warnings: [...(repair.warnings || []), 'matrix_probe_after_explicit_repair_transaction']
63
+ };
64
+ }
49
65
  function printCodexNativeResult(args = [], result) {
50
66
  if (flag(args, '--json')) {
51
67
  printJson(result);
@@ -26,6 +26,8 @@ import { writeMcpPluginInventoryArtifacts } from '../core/mcp/mcp-plugin-invento
26
26
  import { runDoctorZellijRepair, doctorZellijRepairConsoleLine } from '../core/doctor/doctor-zellij-repair.js';
27
27
  import { buildCodexAppHarnessMatrix } from '../core/codex-app/codex-app-harness-matrix.js';
28
28
  import { buildCodexNativeFeatureMatrix } from '../core/codex-native/codex-native-feature-broker.js';
29
+ import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
30
+ import { runDoctorNativeCapabilityRepair } from '../core/doctor/doctor-native-capability-repair.js';
29
31
  export async function run(_command, args = []) {
30
32
  const doctorFix = flag(args, '--fix');
31
33
  let setupRepair = null;
@@ -68,6 +70,23 @@ export async function run(_command, args = []) {
68
70
  };
69
71
  }
70
72
  const root = await projectRoot();
73
+ const doctorNativeCapabilityRepair = await runDoctorNativeCapabilityRepair({
74
+ root,
75
+ fix: doctorFix || flag(args, '--repair-native-capabilities'),
76
+ yes: flag(args, '--yes') || flag(args, '-y'),
77
+ flags: args.map((arg) => String(arg))
78
+ }).catch((err) => ({
79
+ schema: 'sks.doctor-native-capability-repair.v1',
80
+ ok: false,
81
+ root,
82
+ fix: doctorFix,
83
+ yes: flag(args, '--yes') || flag(args, '-y'),
84
+ core_skills: null,
85
+ skill_dedupe: null,
86
+ native_capabilities: null,
87
+ secret_preservation_guard: '.sneakoscope/reports/secret-preservation-guard.json',
88
+ blockers: [err?.message || String(err)]
89
+ }));
71
90
  const codexBin = readOption(args, '--codex-bin', process.env.SKS_DOCTOR_CODEX_BIN || '');
72
91
  const configProbeOpts = {
73
92
  codexProbe: flag(args, '--fix') || flag(args, '--actual-codex') || Boolean(codexBin),
@@ -198,7 +217,23 @@ export async function run(_command, args = []) {
198
217
  const mcpPluginInventory = pluginInventory?.report
199
218
  ? await writeMcpPluginInventoryArtifacts(root, { inventory: pluginInventory.report }).catch((err) => ({ error: err?.message || String(err), candidates: null }))
200
219
  : null;
201
- const codexAppHarnessMatrix = await buildCodexAppHarnessMatrix({ root, applyRepairs: doctorFix }).catch((err) => ({
220
+ const repairCodexNative = doctorFix && flag(args, '--repair-codex-native');
221
+ const codexNativeRepair = repairCodexNative
222
+ ? await repairCodexNativeManagedAssets({
223
+ root,
224
+ requestedBy: 'doctor --fix',
225
+ yes: flag(args, '--yes') || flag(args, '-y')
226
+ }).catch((err) => ({
227
+ schema: 'sks.codex-native-repair-transaction.v1',
228
+ ok: false,
229
+ generated_at: new Date().toISOString(),
230
+ requested_by: 'doctor --fix',
231
+ repaired: [],
232
+ blockers: [err?.message || String(err)],
233
+ warnings: []
234
+ }))
235
+ : null;
236
+ const codexAppHarnessMatrix = await buildCodexAppHarnessMatrix({ root, mode: 'read-only' }).catch((err) => ({
202
237
  schema: 'sks.codex-app-harness-matrix.v1',
203
238
  ok: false,
204
239
  codex_cli: { available: false, version: null },
@@ -207,7 +242,7 @@ export async function run(_command, args = []) {
207
242
  blockers: [err?.message || String(err)],
208
243
  warnings: []
209
244
  }));
210
- const codexNativeFeatureMatrix = await buildCodexNativeFeatureMatrix({ root, applyRepairs: doctorFix }).catch((err) => ({
245
+ const codexNativeFeatureMatrix = await buildCodexNativeFeatureMatrix({ root, mode: 'read-only' }).catch((err) => ({
211
246
  schema: 'sks.codex-native-feature-matrix.v1',
212
247
  ok: false,
213
248
  codex_cli: { available: Boolean(codex.bin), version: codex.version || null, bin: codex.bin || null },
@@ -288,7 +323,7 @@ export async function run(_command, args = []) {
288
323
  ready,
289
324
  sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
290
325
  package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
291
- repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair }
326
+ repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair }
292
327
  };
293
328
  if (flag(args, '--json')) {
294
329
  printJson(result);
@@ -324,8 +359,32 @@ export async function run(_command, args = []) {
324
359
  console.log(` Loop Mesh: ${runtimeReadiness.loop_mesh}`);
325
360
  console.log(` QA Visual: ${runtimeReadiness.qa_visual}`);
326
361
  console.log(` Research Sources: ${runtimeReadiness.research_sources}`);
327
- for (const action of runtimeReadiness.repair_actions)
328
- console.log(` Repair: ${action}`);
362
+ console.log(` Image Follow-up: ${runtimeReadiness.image_followup}`);
363
+ for (const note of runtimeReadiness.notes)
364
+ console.log(` ${note}`);
365
+ if (runtimeReadiness.repair_actions.length) {
366
+ console.log('Repair actions:');
367
+ for (const action of runtimeReadiness.repair_actions)
368
+ console.log(` - ${action}`);
369
+ }
370
+ const nativeCapabilityRows = Array.isArray(doctorNativeCapabilityRepair?.native_capabilities?.capabilities)
371
+ ? doctorNativeCapabilityRepair.native_capabilities.capabilities
372
+ : [];
373
+ console.log('SKS Native Capabilities:');
374
+ console.log(` image generation: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_generation', 'repair_required')}`);
375
+ console.log(` image follow-up edit: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_followup_edit', 'degraded')}`);
376
+ console.log(` computer use: ${nativeCapabilityStatus(nativeCapabilityRows, 'computer_use', 'manual_required')}`);
377
+ console.log(` Chrome/web review: ${nativeCapabilityStatus(nativeCapabilityRows, 'chrome_web_review', 'manual_required')}`);
378
+ console.log(` app screenshot: ${nativeCapabilityStatus(nativeCapabilityRows, 'codex_app_screenshot', 'degraded')}`);
379
+ console.log(` app handoff: ${nativeCapabilityStatus(nativeCapabilityRows, 'app_handoff', 'unavailable')}`);
380
+ console.log(` image path exposure: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_path_exposure', 'fallback')}`);
381
+ console.log('SKS Skills:');
382
+ console.log(` core skills: ${doctorSkillStatus(doctorNativeCapabilityRepair?.core_skills)}`);
383
+ console.log(` duplicate project skills: ${doctorDedupeStatus(doctorNativeCapabilityRepair?.skill_dedupe)}`);
384
+ console.log('Secret preservation:');
385
+ console.log(` Supabase keys: ${doctorNativeCapabilityRepair?.ok === false && String((doctorNativeCapabilityRepair?.blockers || []).join(' ')).includes('secret_preservation_failed') ? 'blocked' : 'preserved'}`);
386
+ console.log(' secret values: redacted');
387
+ console.log(` migration journal: ${doctorNativeCapabilityRepair?.secret_preservation_guard || '.sneakoscope/reports/secret-preservation-guard.json'}`);
329
388
  console.log('Codex App Harness:');
330
389
  console.log(` plugins: ${codexAppHarnessMatrix.app_features?.plugin_json ? 'ok' : 'degraded'}`);
331
390
  console.log(` hook approval: ${codexAppHarnessMatrix.app_features?.hook_approval_state_detectable ? 'ok' : 'unknown'}`);
@@ -424,14 +483,14 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
424
483
  ? 'ok'
425
484
  : matrix?.codex_cli?.available ? 'degraded' : 'blocked';
426
485
  const repairActions = [];
427
- if (zellijStatus === 'repair_required')
428
- repairActions.push('sks doctor --fix --yes');
486
+ if (zellijStatus !== 'ok') {
487
+ repairActions.push('Zellij: sks doctor --fix --yes');
488
+ repairActions.push('Homebrew + Zellij: sks doctor --fix --install-homebrew --yes');
489
+ }
429
490
  if (codexNative !== 'ok')
430
- repairActions.push('sks doctor --fix --repair-codex-native --yes');
491
+ repairActions.push('Codex Native managed assets: sks doctor --fix --repair-codex-native --yes');
431
492
  if (matrix?.features?.project_memory?.ok !== true)
432
- repairActions.push('sks codex-native init-deep --apply --directory-local');
433
- if (hookPolicy !== 'approved-only')
434
- repairActions.push('hook-derived evidence will not count until Codex hook approval is approved');
493
+ repairActions.push('Project memory: sks codex-native init-deep --apply --directory-local');
435
494
  return {
436
495
  schema: 'sks.runtime-readiness-story.v1',
437
496
  zellij: zellijStatus,
@@ -439,6 +498,7 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
439
498
  loop_mesh: agentStrategy === 'agent_type' ? 'ok' : 'fallback',
440
499
  qa_visual: defaults.qa_visual_review_strategy || 'blocked',
441
500
  research_sources: defaults.research_source_strategy || 'local-files',
501
+ image_followup: defaults.image_followup_strategy || 'blocked',
442
502
  hook_evidence_policy: hookPolicy,
443
503
  agent_role_strategy: agentStrategy,
444
504
  notes: [
@@ -446,9 +506,43 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
446
506
  ...(hookPolicy !== 'approved-only' ? ['hook-derived evidence will not count'] : []),
447
507
  ...(agentStrategy !== 'agent_type' ? ['message-role fallback active'] : [])
448
508
  ],
449
- repair_actions: repairActions
509
+ repair_actions: [...new Set(repairActions)]
450
510
  };
451
511
  }
512
+ function nativeCapabilityStatus(rows, id, fallback) {
513
+ const row = rows.find((entry) => entry?.id === id);
514
+ if (!row)
515
+ return fallback;
516
+ if (row.after === 'verified' || row.before === 'verified')
517
+ return 'verified';
518
+ if (row.repairability === 'manual-required')
519
+ return 'manual_required';
520
+ if (row.before === 'degraded' || row.after === 'degraded')
521
+ return 'degraded';
522
+ if (row.repairability === 'doctor-fix')
523
+ return row.after === 'blocked' ? 'blocked' : 'repair_required';
524
+ if (row.repairability === 'unavailable')
525
+ return 'unavailable';
526
+ return fallback;
527
+ }
528
+ function doctorSkillStatus(coreSkills) {
529
+ if (!coreSkills)
530
+ return 'drift_detected';
531
+ if (Array.isArray(coreSkills.restored) && coreSkills.restored.length)
532
+ return 'repaired';
533
+ if (Array.isArray(coreSkills.blockers) && coreSkills.blockers.length)
534
+ return 'drift_detected';
535
+ return 'current';
536
+ }
537
+ function doctorDedupeStatus(skillDedupe) {
538
+ if (!skillDedupe)
539
+ return 'manual_required';
540
+ if (Array.isArray(skillDedupe.actions) && skillDedupe.actions.some((action) => action.action === 'quarantined'))
541
+ return 'repaired';
542
+ if (Array.isArray(skillDedupe.blockers) && skillDedupe.blockers.length)
543
+ return 'manual_required';
544
+ return 'none';
545
+ }
452
546
  // Assemble the explicit Zellij readiness block for `doctor --json` from the
453
547
  // capability probe + readiness matrix. Proof statuses are availability-derived:
454
548
  // `verified` is reserved for a real environment run (SKS_REQUIRE_ZELLIJ=1 gates);
@@ -56,7 +56,7 @@ export async function syncCodexAgentRoles(input) {
56
56
  for (const role of DIRECTIVE_ROLES) {
57
57
  const file = path.join(targetDir, `${role}.toml`);
58
58
  const current = await fs.readFile(file, 'utf8').catch(() => '');
59
- if (current && !current.includes('SKS managed 3.1.4 directive role') && !current.includes('SKS managed 3.1.5 directive role') && !current.includes('SKS managed 3.1.6 directive role') && !current.includes('SKS managed 3.1.6 bounded role'))
59
+ if (current && !current.includes('SKS managed 3.1.4 directive role') && !current.includes('SKS managed 3.1.5 directive role') && !current.includes('SKS managed 3.1.6 directive role') && !current.includes('SKS managed 3.1.6 bounded role') && !current.includes('SKS managed 3.1.7 directive role'))
60
60
  continue;
61
61
  await writeTextAtomic(file, roleToml(role, rolePayloads[role]));
62
62
  created.push(file);
@@ -69,6 +69,9 @@ export async function syncCodexAgentRoles(input) {
69
69
  apply: input.apply === true,
70
70
  agent_type_supported: agentTypeProbe.supported,
71
71
  fallback: agentTypeProbe.supported ? 'agent_type' : 'message-role',
72
+ strategy: agentTypeProbe.supported ? 'agent_type' : 'message-role',
73
+ probe_artifact_path: '.sneakoscope/reports/codex-agent-type-probe.json',
74
+ clobbered_user_roles: false,
72
75
  codex_home: codexHome,
73
76
  directive_roles: DIRECTIVE_ROLES,
74
77
  role_payloads: rolePayloads,
@@ -86,21 +89,22 @@ function roleToml(role, payload) {
86
89
  : `message_role_prefix = "${escapeToml(payload?.message_role_prefix || `Role: ${role}.`)}"`;
87
90
  return [
88
91
  `name = "${role}"`,
89
- `description = "SKS managed 3.1.6 directive role: ${role}"`,
92
+ `description = "SKS managed 3.1.7 directive role: ${role}"`,
90
93
  strategyLine,
91
94
  'model_reasoning_effort = "medium"',
92
95
  role.includes('implementer') ? 'sandbox_mode = "workspace-write"' : 'sandbox_mode = "read-only"',
93
96
  'approval_policy = "never"',
94
97
  'developer_instructions = """',
95
- `You are ${role}. SKS managed 3.1.6 directive role with bounded ownership.`,
98
+ `You are ${role}. SKS managed 3.1.7 directive role with bounded ownership.`,
96
99
  'Bounded ownership: use only the assigned owner files/directories and treat memory as guidance, not permission.',
97
100
  role.includes('implementer') ? 'Maker/checker separation: implementer may patch only owner scope and cannot self-approve.' : 'Maker/checker separation: checker is read-only and must reject missing gates or missing proof artifacts.',
101
+ role.includes('implementer') ? 'Allowed sandbox: workspace-write only within assigned owner scope.' : 'Allowed sandbox: read-only; checker roles cannot mutate.',
98
102
  role.includes('release') ? 'Release verifier: verify version truth, release DAG coverage, package scripts, packlist, and changelog evidence.' : '',
99
103
  role.includes('zellij') ? 'UI/Zellij verifier: inspect readiness status, headless fallback, repair_required, pane proof, and slot telemetry without mutating unrelated UI state.' : '',
100
104
  role.includes('codex') ? 'Codex native verifier: inspect hook approval, agent_type, skill sync, plugin inventory, MCP candidates, and invocation plan artifacts.' : '',
101
- 'Side effects: no destructive shell, package publish, global config mutation, database mutation, or external service write unless the sealed route contract explicitly allows it.',
105
+ 'Side-effect restrictions: no destructive shell, package publish, global config mutation, database mutation, or external service write unless the sealed route contract explicitly allows it.',
102
106
  'Required proof artifacts: cite concrete repo paths, command outputs, and route-local JSON proof before claiming completion.',
103
- 'Final arbiter: parent integration owns final acceptance; this role supplies evidence and cannot override missing gates.',
107
+ 'Final arbiter constraints: parent integration owns final acceptance; this role supplies evidence and cannot override missing gates.',
104
108
  `Execution role strategy: ${payload?.strategy || 'message-role'}. Probe: ${payload?.probe_artifact_path || '.sneakoscope/reports/codex-agent-type-probe.json'}.`,
105
109
  '"""',
106
110
  ''
@@ -28,7 +28,7 @@ export async function runCodexInitDeep(input = {}) {
28
28
  }
29
29
  if (existing.trim()) {
30
30
  const beforeHash = hashText(existing);
31
- const backup = `${agentsPath}.sks-backup-${beforeHash.slice(0, 12)}-${Date.now()}`;
31
+ const backup = `${agentsPath}.sks-backup-${Date.now()}-${beforeHash.slice(0, 12)}`;
32
32
  await fs.copyFile(agentsPath, backup);
33
33
  directoryLocalAgents.backup_paths.push(path.relative(root, backup));
34
34
  directoryLocalAgents.backups_created += 1;
@@ -83,9 +83,10 @@ async function pruneBackups(root, agentsPath, keep) {
83
83
  return [];
84
84
  const dir = path.dirname(agentsPath);
85
85
  const base = path.basename(agentsPath);
86
+ const backupPattern = new RegExp(`^${escapeRegExp(base)}\\.sks-backup-\\d{13}-[0-9a-f]{8,12}$`);
86
87
  const rows = await fs.readdir(dir).catch(() => []);
87
88
  const backups = rows
88
- .filter((name) => name.startsWith(`${base}.sks-backup-`))
89
+ .filter((name) => backupPattern.test(name))
89
90
  .map((name) => path.join(dir, name))
90
91
  .sort();
91
92
  const remove = backups.slice(0, Math.max(0, backups.length - keep));
@@ -99,6 +100,9 @@ async function pruneBackups(root, agentsPath, keep) {
99
100
  await guardedRm(guard, file, { force: true }).catch(() => undefined);
100
101
  return remove;
101
102
  }
103
+ function escapeRegExp(value) {
104
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
105
+ }
102
106
  function hashText(text) {
103
107
  let hash = 2166136261;
104
108
  for (let index = 0; index < text.length; index += 1) {