sneakoscope 8.3.2 → 8.6.0

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 (122) hide show
  1. package/README.md +1 -1
  2. package/config/architecture-map-policy.v1.json +168 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/commands/bridge.js +19 -1
  6. package/dist/commands/doctor-profile.js +1 -1
  7. package/dist/commands/doctor.js +97 -16
  8. package/dist/config/skills-manifest.json +1 -1
  9. package/dist/core/align/align-route.js +8 -2
  10. package/dist/core/align/code-navigation-align.js +88 -32
  11. package/dist/core/architecture-map-pipeline.js +173 -0
  12. package/dist/core/architecture-map-review.js +180 -0
  13. package/dist/core/codex/agent-config-file-repair.js +17 -3
  14. package/dist/core/codex/codex-config-guard.js +3 -1
  15. package/dist/core/codex/codex-feature-flags.js +10 -0
  16. package/dist/core/codex/codex-project-config-policy.js +54 -30
  17. package/dist/core/codex/codex-startup-config-postcheck.js +5 -0
  18. package/dist/core/codex-app/codex-app-fast-ui-repair.js +2 -1
  19. package/dist/core/codex-app/menubar/constants.js +1 -0
  20. package/dist/core/codex-hooks/codex-hook-state-writer.js +2 -0
  21. package/dist/core/codex-lb/codex-lb-env.js +25 -3
  22. package/dist/core/codex-lb/combined-catalog/builder.js +7 -2
  23. package/dist/core/codex-lb/combined-catalog/model-selection.js +71 -0
  24. package/dist/core/codex-lb/combined-catalog/normalize.js +20 -3
  25. package/dist/core/codex-lb/desktop-bridge/security.js +11 -8
  26. package/dist/core/codex-lb/desktop-controller-v3/catalog.js +75 -4
  27. package/dist/core/codex-lb/desktop-controller-v3.js +5 -1
  28. package/dist/core/codex-lb/request-route-resolver.js +21 -18
  29. package/dist/core/commands/triwiki-atlas-command.js +372 -0
  30. package/dist/core/commands/triwiki-command.js +12 -1
  31. package/dist/core/doctor/doctor-dirty-planner.js +16 -1
  32. package/dist/core/doctor/doctor-idempotence.js +11 -0
  33. package/dist/core/doctor/doctor-repair-postcheck.js +49 -0
  34. package/dist/core/doctor/doctor-transaction.js +47 -2
  35. package/dist/core/doctor/legacy-global-hook-cleanup.js +1 -1
  36. package/dist/core/fsx.js +14 -0
  37. package/dist/core/git-hygiene/git-policy.js +4 -0
  38. package/dist/core/git-hygiene/gitignore-writer.js +2 -0
  39. package/dist/core/gx-renderer.js +2 -1
  40. package/dist/core/init.js +40 -17
  41. package/dist/core/managed-paths.js +1 -0
  42. package/dist/core/perf/release-latency-slo.js +57 -11
  43. package/dist/core/pipeline-internals/pipeline-stage-builder.js +181 -0
  44. package/dist/core/pipeline-internals/runtime-core.js +18 -172
  45. package/dist/core/pipeline-internals/runtime-gates.js +12 -1
  46. package/dist/core/release/content-digest-cache.js +70 -0
  47. package/dist/core/release/gate-manifest.js +9 -0
  48. package/dist/core/release/main-push-guard.js +4 -3
  49. package/dist/core/release/package-dist-snapshot.js +18 -2
  50. package/dist/core/release/physical-release-gates.js +26 -3
  51. package/dist/core/release/release-authorization-snapshot.js +4 -0
  52. package/dist/core/release/release-gate-cache-v2.js +27 -3
  53. package/dist/core/release/release-gate-contract.js +2 -0
  54. package/dist/core/release/release-gate-dag.js +3 -0
  55. package/dist/core/release/release-inspection-memo.js +69 -0
  56. package/dist/core/release/release-pack-content-scanner.js +5 -0
  57. package/dist/core/release/release-pack-receipt.js +28 -18
  58. package/dist/core/release/release-pack-tarball.js +10 -0
  59. package/dist/core/release/release-upgrade-baseline.js +4 -0
  60. package/dist/core/routes.js +1 -1
  61. package/dist/core/safety/ssot-authority-inventory.js +114 -0
  62. package/dist/core/safety/ssot-guard.js +2 -44
  63. package/dist/core/subagents/agent-catalog.js +4 -2
  64. package/dist/core/subagents/official-subagent-config.js +92 -15
  65. package/dist/core/subagents/official-subagent-preparation.js +1 -0
  66. package/dist/core/triwiki/context-graph/architecture/baseline.js +81 -0
  67. package/dist/core/triwiki/context-graph/architecture/capsule.js +34 -0
  68. package/dist/core/triwiki/context-graph/architecture/contracts.js +67 -0
  69. package/dist/core/triwiki/context-graph/architecture/delta.js +149 -0
  70. package/dist/core/triwiki/context-graph/architecture/finding-factory.js +28 -0
  71. package/dist/core/triwiki/context-graph/architecture/findings.js +173 -0
  72. package/dist/core/triwiki/context-graph/architecture/fingerprint.js +75 -0
  73. package/dist/core/triwiki/context-graph/architecture/index.js +15 -0
  74. package/dist/core/triwiki/context-graph/architecture/input-bundle.js +66 -0
  75. package/dist/core/triwiki/context-graph/architecture/metrics.js +137 -0
  76. package/dist/core/triwiki/context-graph/architecture/policy.js +321 -0
  77. package/dist/core/triwiki/context-graph/architecture/slice.js +100 -0
  78. package/dist/core/triwiki/context-graph/architecture/ssot-analysis.js +59 -0
  79. package/dist/core/triwiki/context-graph/architecture/topology-overlay.js +106 -0
  80. package/dist/core/triwiki/context-graph/architecture/validation.js +145 -0
  81. package/dist/core/triwiki/context-graph/architecture/voxel-context.js +39 -0
  82. package/dist/core/triwiki/context-graph/extractors/index.js +4 -0
  83. package/dist/core/triwiki/context-graph/projections/index.js +2 -0
  84. package/dist/core/triwiki/context-graph/projections/mermaid/architecture-delta.js +68 -0
  85. package/dist/core/triwiki/context-graph/projections/mermaid/ast.js +49 -0
  86. package/dist/core/triwiki/context-graph/projections/mermaid/change-impact.js +43 -0
  87. package/dist/core/triwiki/context-graph/projections/mermaid/contracts.js +5 -0
  88. package/dist/core/triwiki/context-graph/projections/mermaid/escape.js +45 -0
  89. package/dist/core/triwiki/context-graph/projections/mermaid/ids.js +73 -0
  90. package/dist/core/triwiki/context-graph/projections/mermaid/index.js +113 -0
  91. package/dist/core/triwiki/context-graph/projections/mermaid/manifest.js +37 -0
  92. package/dist/core/triwiki/context-graph/projections/mermaid/module-dependency.js +21 -0
  93. package/dist/core/triwiki/context-graph/projections/mermaid/ownership-workstream.js +21 -0
  94. package/dist/core/triwiki/context-graph/projections/mermaid/project-topology.js +13 -0
  95. package/dist/core/triwiki/context-graph/projections/mermaid/public-surface.js +17 -0
  96. package/dist/core/triwiki/context-graph/projections/mermaid/reduce.js +44 -0
  97. package/dist/core/triwiki/context-graph/projections/mermaid/risk-domains.js +23 -0
  98. package/dist/core/triwiki/context-graph/projections/mermaid/runtime-control.js +20 -0
  99. package/dist/core/triwiki/context-graph/projections/mermaid/selection-ledger.js +94 -0
  100. package/dist/core/triwiki/context-graph/projections/mermaid/serializer.js +81 -0
  101. package/dist/core/triwiki/context-graph/projections/mermaid/ssot-provenance.js +17 -0
  102. package/dist/core/triwiki/context-graph/projections/mermaid/verification-coverage.js +17 -0
  103. package/dist/core/triwiki/context-graph/projections/mermaid/view-builder-common.js +1 -0
  104. package/dist/core/triwiki/context-graph/projections/mermaid/view-builder.js +173 -0
  105. package/dist/core/triwiki/context-graph/projections/mermaid/view-specs.js +33 -0
  106. package/dist/core/triwiki/context-graph/store/architecture-map-store.js +71 -0
  107. package/dist/core/triwiki/triwiki-proof-bank-index.js +26 -1
  108. package/dist/core/triwiki/triwiki-proof-bank.js +82 -2
  109. package/dist/core/version.js +1 -1
  110. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +2 -2
  111. package/dist/native/sks-menubar/Sources/ProvidersModelExposure.swift +178 -0
  112. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +10 -1
  113. package/dist/scripts/architecture-map-check.js +299 -0
  114. package/dist/scripts/lib/ensure-dist-fresh.js +48 -1
  115. package/dist/scripts/release-metadata-check.js +2 -0
  116. package/package.json +6 -3
  117. package/release-gates.v2.json +204 -0
  118. package/schemas/architecture-baseline.v1.schema.json +47 -0
  119. package/schemas/architecture-map-manifest.v1.schema.json +40 -0
  120. package/schemas/architecture-map-policy.v1.schema.json +147 -0
  121. package/schemas/architecture-review.v1.schema.json +46 -0
  122. package/schemas/mermaid-projection.v1.schema.json +47 -0
@@ -0,0 +1,69 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { SksLruCache } from '../perf/lru-cache.js';
5
+ export const RELEASE_INSPECTION_MEMO_ENV = 'SKS_RELEASE_INSPECTION_MEMO';
6
+ const MAX_ENTRIES_PER_BUCKET = 64;
7
+ const buckets = new Map();
8
+ export function releaseInspectionMemoEnabled() {
9
+ return process.env[RELEASE_INSPECTION_MEMO_ENV] !== '0';
10
+ }
11
+ export function fileIdentity(file) {
12
+ try {
13
+ const stat = fs.lstatSync(file, { bigint: true });
14
+ if (!stat.isFile() || stat.isSymbolicLink())
15
+ return null;
16
+ return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ export function toolIdentity(command) {
23
+ const searchPath = String(process.env.PATH || '').split(path.delimiter).filter(Boolean);
24
+ for (const dir of searchPath) {
25
+ const candidate = path.join(dir, command);
26
+ let resolved;
27
+ try {
28
+ resolved = fs.realpathSync(candidate);
29
+ }
30
+ catch {
31
+ continue;
32
+ }
33
+ const identity = fileIdentity(resolved);
34
+ if (identity)
35
+ return `${candidate}>${resolved}#${identity}`;
36
+ }
37
+ return null;
38
+ }
39
+ export function inspectionKey(...parts) {
40
+ if (parts.some((part) => part === null || part === undefined))
41
+ return null;
42
+ return crypto.createHash('sha256').update(parts.map(String).join('\0')).digest('hex');
43
+ }
44
+ export function memoizeReleaseInspection(bucket, key, compute, clone = defaultClone) {
45
+ if (!key || !releaseInspectionMemoEnabled())
46
+ return compute();
47
+ let cache = buckets.get(bucket);
48
+ if (!cache) {
49
+ cache = new SksLruCache(MAX_ENTRIES_PER_BUCKET);
50
+ buckets.set(bucket, cache);
51
+ }
52
+ const hit = cache.get(key);
53
+ if (hit !== null)
54
+ return clone(hit);
55
+ const value = compute();
56
+ cache.set(key, value);
57
+ return clone(value);
58
+ }
59
+ function defaultClone(value) {
60
+ if (value === null || typeof value !== 'object')
61
+ return value;
62
+ return structuredClone(value);
63
+ }
64
+ export function cloneBuffer(value) {
65
+ return Buffer.from(value);
66
+ }
67
+ export function clearReleaseInspectionMemo() {
68
+ buckets.clear();
69
+ }
@@ -3,7 +3,12 @@ import fs from 'node:fs';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { spawnSync } from 'node:child_process';
6
+ import { fileIdentity, inspectionKey, memoizeReleaseInspection, toolIdentity } from './release-inspection-memo.js';
6
7
  export function scanTarballTextContents(input) {
8
+ const key = inspectionKey(input.temp_prefix, input.tarball, fileIdentity(input.tarball), toolIdentity('tar'), String(input.max_findings), input.include_allowlisted_count === true ? 'allowlisted' : 'plain', input.patterns.map((pattern) => `${pattern.kind}=${pattern.regex.source}/${pattern.regex.flags}`).join('\0'), String(input.allow_finding || 'no_allowlist'), String(input.finding_blocker));
9
+ return memoizeReleaseInspection('tarball-content-scan', key, () => computeTarballTextContents(input));
10
+ }
11
+ function computeTarballTextContents(input) {
7
12
  const temp = fs.mkdtempSync(path.join(os.tmpdir(), input.temp_prefix));
8
13
  const findings = [];
9
14
  const blockers = [];
@@ -6,6 +6,7 @@ import { spawnSync } from 'node:child_process';
6
6
  import { tmpdir } from '../fsx.js';
7
7
  import { DEFAULT_MAX_PACK_BYTES, DEFAULT_MAX_UNPACKED_BYTES } from './package-size-budget.js';
8
8
  import { readCurrentNpmPackGateArtifacts } from './npm-pack-proof.js';
9
+ import { inspectionKey, memoizeReleaseInspection, toolIdentity } from './release-inspection-memo.js';
9
10
  import { scanTarballTextContents } from './release-pack-content-scanner.js';
10
11
  import { RELEASE_PACK_COMPARE_SCHEMA, RELEASE_PACK_RECEIPT_SCHEMA } from './release-pack-contract.js';
11
12
  import { tarInventory, tarPackageJson, tarUnpackedBytes } from './release-pack-tarball.js';
@@ -298,12 +299,32 @@ export function inspectLocalReleaseSourceState(root) {
298
299
  else if (String(fullStatus.stdout || '').split(/\r?\n/).some((line) => line.startsWith('?? '))) {
299
300
  blockers.push('npm_pack_untracked_files_present');
300
301
  }
302
+ const inventory = memoizeReleaseInspection('npm-pack-source-inventory', inspectionKey(path.resolve(root), head, toolIdentity('git'), toolIdentity('npm'), process.env.SKS_RELEASE_NPM_CACHE || '', String(trackedStatus.status), String(trackedStatus.stdout || ''), String(fullStatus.status), String(fullStatus.stdout || '')), () => packSourceInventory(root));
303
+ const trackedFiles = inventory.tracked_files ? new Set(inventory.tracked_files) : null;
304
+ if (!trackedFiles)
305
+ blockers.push('npm_pack_tracked_file_inventory_unavailable');
306
+ const packedFiles = inventory.packed_files;
307
+ if (!packedFiles)
308
+ blockers.push('npm_pack_dry_run_file_inventory_unavailable');
309
+ const packEligibleUntracked = trackedFiles && packedFiles
310
+ ? unique(packedFiles.filter((relative) => !trackedFiles.has(relative) && !isGeneratedDistPackagePath(relative))).sort()
311
+ : [];
312
+ if (packEligibleUntracked.length > 0)
313
+ blockers.push('npm_pack_eligible_untracked_files_present');
314
+ return {
315
+ ok: blockers.length === 0,
316
+ head,
317
+ source_tree_sha256: sourceTreeSha256,
318
+ tracked_changes: trackedChanges,
319
+ pack_eligible_untracked: packEligibleUntracked,
320
+ blockers: unique(blockers)
321
+ };
322
+ }
323
+ function packSourceInventory(root) {
301
324
  const trackedFilesResult = spawnSync('git', ['ls-files', '-z'], { cwd: root, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
302
325
  const trackedFiles = trackedFilesResult.status === 0
303
- ? new Set(String(trackedFilesResult.stdout || '').split('\0').filter(Boolean).map(normalizePath))
326
+ ? unique(String(trackedFilesResult.stdout || '').split('\0').filter(Boolean).map(normalizePath))
304
327
  : null;
305
- if (!trackedFiles)
306
- blockers.push('npm_pack_tracked_file_inventory_unavailable');
307
328
  const dryRun = spawnSync('npm', ['pack', '--dry-run', '--ignore-scripts', '--json'], {
308
329
  cwd: root,
309
330
  encoding: 'utf8',
@@ -323,21 +344,7 @@ export function inspectLocalReleaseSourceState(root) {
323
344
  packedFiles = null;
324
345
  }
325
346
  }
326
- if (!packedFiles)
327
- blockers.push('npm_pack_dry_run_file_inventory_unavailable');
328
- const packEligibleUntracked = trackedFiles && packedFiles
329
- ? unique(packedFiles.filter((relative) => !trackedFiles.has(relative) && !isGeneratedDistPackagePath(relative))).sort()
330
- : [];
331
- if (packEligibleUntracked.length > 0)
332
- blockers.push('npm_pack_eligible_untracked_files_present');
333
- return {
334
- ok: blockers.length === 0,
335
- head,
336
- source_tree_sha256: sourceTreeSha256,
337
- tracked_changes: trackedChanges,
338
- pack_eligible_untracked: packEligibleUntracked,
339
- blockers: unique(blockers)
340
- };
347
+ return { tracked_files: trackedFiles, packed_files: packedFiles };
341
348
  }
342
349
  export function writeReleaseJson(file, value) {
343
350
  fs.mkdirSync(path.dirname(file), { recursive: true });
@@ -536,6 +543,9 @@ function gitHead(root) {
536
543
  return result.status === 0 ? String(result.stdout || '').trim() : '';
537
544
  }
538
545
  function gitTreeSha256(root, commit) {
546
+ return memoizeReleaseInspection('git-tree-sha256', inspectionKey(path.resolve(root), commit, toolIdentity('git')), () => computeGitTreeSha256(root, commit));
547
+ }
548
+ function computeGitTreeSha256(root, commit) {
539
549
  if (!/^[a-f0-9]{40}$/i.test(commit))
540
550
  return '';
541
551
  const verified = spawnSync('git', ['rev-parse', '--verify', `${commit}^{commit}`], { cwd: root, encoding: 'utf8' });
@@ -2,7 +2,11 @@ import fs from 'node:fs';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { spawnSync } from 'node:child_process';
5
+ import { fileIdentity, inspectionKey, memoizeReleaseInspection, toolIdentity } from './release-inspection-memo.js';
5
6
  export function tarInventory(tarball) {
7
+ return memoizeReleaseInspection('tar-inventory', inspectionKey(tarball, fileIdentity(tarball), toolIdentity('tar')), () => computeTarInventory(tarball));
8
+ }
9
+ function computeTarInventory(tarball) {
6
10
  const result = spawnSync('tar', ['-tzf', tarball], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
7
11
  if (result.status !== 0)
8
12
  return { files: [], blockers: ['tarball_inventory_command_failed'] };
@@ -28,6 +32,9 @@ export function tarInventory(tarball) {
28
32
  return { files, blockers };
29
33
  }
30
34
  export function tarPackageJson(tarball) {
35
+ return memoizeReleaseInspection('tar-package-json', inspectionKey(tarball, fileIdentity(tarball), toolIdentity('tar')), () => computeTarPackageJson(tarball));
36
+ }
37
+ function computeTarPackageJson(tarball) {
31
38
  const result = spawnSync('tar', ['-xOzf', tarball, 'package/package.json'], { encoding: 'utf8', maxBuffer: 1024 * 1024 });
32
39
  if (result.status !== 0)
33
40
  return null;
@@ -40,6 +47,9 @@ export function tarPackageJson(tarball) {
40
47
  }
41
48
  }
42
49
  export function tarUnpackedBytes(tarball) {
50
+ return memoizeReleaseInspection('tar-unpacked-bytes', inspectionKey(tarball, fileIdentity(tarball), toolIdentity('tar')), () => computeTarUnpackedBytes(tarball));
51
+ }
52
+ function computeTarUnpackedBytes(tarball) {
43
53
  const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-release-pack-unpacked-'));
44
54
  try {
45
55
  const result = spawnSync('tar', ['-xzf', tarball, '-C', temp], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
@@ -4,6 +4,10 @@ export const RELEASE_UPGRADE_BASELINE_SHA256 = '40a9e89f3a234dfcd32507ab7deeb950
4
4
  export function releaseUpgradeProofFilename(targetVersion) {
5
5
  return `upgrade-${RELEASE_UPGRADE_BASELINE_LABEL}-to-${targetVersion}.json`;
6
6
  }
7
+ export function isExpectedReleaseUpgradeLaunchctlCall(call) {
8
+ return /^(?:unsetenv (?:CODEX_LB_API_KEY|OPENROUTER_API_KEY)|print|bootout (?:com\.sneakoscope\.sks-menubar|com\.sneakoscope\.telegram-hub))$/
9
+ .test(String(call));
10
+ }
7
11
  export function releaseUpgradeExpectedVersion(stateName, targetVersion) {
8
12
  return stateName.startsWith('target_') ? targetVersion : RELEASE_UPGRADE_BASELINE_VERSION;
9
13
  }
@@ -647,7 +647,7 @@ export const COMMAND_CATALOG = [
647
647
  { name: 'check', usage: 'sks check --tier instant|affected|confidence|release|real-check [--sla 5m] [--changed-since auto] [--json]', description: 'Run build-once proof-bank checks: affected/confidence use incremental build and cached proof reuse; release keeps full clean proof for publish readiness.' },
648
648
  { name: 'task', usage: 'sks task run [--sla 5m] [--json]', description: 'Run the normal affected-scope, release-equivalent task verification path.' },
649
649
  { name: 'release', usage: 'sks release affected|full|background|stage [--json]', description: 'Run affected release proof, full release proof, or background release proof explicitly, or drive the staged npm publish up to the human approval step.' },
650
- { name: 'triwiki', usage: 'sks triwiki index|affected|proof-bank [--json]', description: 'Inspect TriWiki module cards, gate impact maps, affected graphs, and proof bank status.' },
650
+ { name: 'triwiki', usage: 'sks triwiki index|affected|proof-bank|graph-status|graph-lint|graph-query|atlas-status|atlas-lint|atlas-list|atlas-show|atlas-why [--json]', description: 'Inspect TriWiki module cards, gate impact maps, affected graphs, proof bank status, and Architecture Map views.' },
651
651
  { name: 'daemon', usage: 'sks daemon status|warm|stop [--json]', description: 'Inspect or warm the local SKS daemon cache state for build/proof reuse.' },
652
652
  { name: 'run', usage: 'sks run "task" [--visual|--research|--db] [--json]', description: 'Classify a plain-language task, materialize a mission, and route it through the SKS trust kernel.' },
653
653
  { name: 'plan', usage: 'sks plan "task" [--json]', description: 'Write a planning-only artifact under .sneakoscope/plans without editing code.' },
@@ -0,0 +1,114 @@
1
+ export const SKS_SSOT_AUTHORITY_INVENTORY = Object.freeze([
2
+ Object.freeze({
3
+ id: 'route_contract',
4
+ title: 'Route contract',
5
+ authorityKind: 'runtime_owner',
6
+ canonicalSources: Object.freeze([
7
+ 'decision-contract.json, route prompt, and pipeline-plan.json'
8
+ ]),
9
+ allowedWriters: Object.freeze(['pipeline plan writer', 'request intake']),
10
+ allowedReaders: Object.freeze(['naruto', 'workers', 'stop gate']),
11
+ derivedArtifacts: Object.freeze([
12
+ 'subagent-plan.json',
13
+ 'subagent-events.jsonl',
14
+ 'worker inboxes'
15
+ ]),
16
+ protected: true,
17
+ description: 'The sealed user objective, constraints, non-goals, and acceptance criteria define what code may be created.',
18
+ authority: 'The sealed user objective, constraints, non-goals, and acceptance criteria define what code may be created.',
19
+ rule: 'Do not implement behavior outside the sealed route contract; block with evidence if the requested path cannot be honored.'
20
+ }),
21
+ Object.freeze({
22
+ id: 'triwiki_context',
23
+ title: 'TriWiki context pack',
24
+ authorityKind: 'file',
25
+ canonicalSources: Object.freeze(['.sneakoscope/wiki/context-pack.json']),
26
+ allowedWriters: Object.freeze(['sks wiki pack', 'sks align run', 'triwiki refresh']),
27
+ allowedReaders: Object.freeze(['pipeline stages', 'naruto parent', 'honest mode']),
28
+ derivedArtifacts: Object.freeze([
29
+ 'subagent-parent-summary.json',
30
+ 'subagent-evidence.json',
31
+ 'reflection.md'
32
+ ]),
33
+ protected: true,
34
+ description: 'TriWiki is the bounded mission context SSOT and must be refreshed or packed, then validated before risky handoffs and final claims.',
35
+ authority: 'TriWiki is the bounded mission context SSOT and must be refreshed or packed, then validated before risky handoffs and final claims.',
36
+ rule: 'Use the latest coordinate+voxel overlay pack; coordinate-only legacy packs are invalid for pipeline decisions.'
37
+ }),
38
+ Object.freeze({
39
+ id: 'runtime_source',
40
+ title: 'TypeScript runtime source',
41
+ authorityKind: 'file',
42
+ canonicalSources: Object.freeze(['src/**/*.ts']),
43
+ allowedWriters: Object.freeze(['implementation workers under sealed route']),
44
+ allowedReaders: Object.freeze(['build', 'tests', 'release gates']),
45
+ derivedArtifacts: Object.freeze(['dist/**', 'dist/bin/sks.js']),
46
+ protected: true,
47
+ description: 'TypeScript source is the runtime SSOT.',
48
+ authority: 'TypeScript source is the runtime SSOT.',
49
+ rule: 'Edit source, rebuild derived output, and rely on runtime:ts-source-of-truth plus runtime:dist-parity.'
50
+ }),
51
+ Object.freeze({
52
+ id: 'generated_outputs',
53
+ title: 'Generated outputs',
54
+ authorityKind: 'generated_projection',
55
+ canonicalSources: Object.freeze([
56
+ 'source generators, build scripts, and schema definitions'
57
+ ]),
58
+ allowedWriters: Object.freeze(['generators', 'build scripts']),
59
+ allowedReaders: Object.freeze(['release', 'doctor', 'install']),
60
+ derivedArtifacts: Object.freeze([
61
+ 'release-gates.v2.json',
62
+ 'infra-harness-gates.json',
63
+ '.sneakoscope/reports/**',
64
+ 'dist/build-manifest.json'
65
+ ]),
66
+ protected: true,
67
+ description: 'Generated files are derived from their generator or schema owner.',
68
+ authority: 'Generated files are derived from their generator or schema owner.',
69
+ rule: 'Regenerate derived artifacts instead of hand-editing them as independent truth.'
70
+ }),
71
+ Object.freeze({
72
+ id: 'stack_current_docs',
73
+ title: 'Stack current docs',
74
+ authorityKind: 'file',
75
+ canonicalSources: Object.freeze(['.sneakoscope/memory/q2_facts/stack-current-docs.md']),
76
+ allowedWriters: Object.freeze(['Context7 hydration', 'docs refresh']),
77
+ allowedReaders: Object.freeze(['implementation', 'research']),
78
+ derivedArtifacts: Object.freeze(['implementation notes', 'route evidence']),
79
+ protected: false,
80
+ description: 'Current vendor or Context7 docs override model-memory defaults when stack versions or APIs change.',
81
+ authority: 'Current vendor or Context7 docs override model-memory defaults when stack versions or APIs change.',
82
+ rule: 'Fetch and record current docs before relying on external package, SDK, API, MCP, or generated-doc behavior that may have changed.'
83
+ }),
84
+ Object.freeze({
85
+ id: 'release_gate_manifest',
86
+ title: 'Release gate manifest',
87
+ authorityKind: 'manifest',
88
+ canonicalSources: Object.freeze([
89
+ 'src/core/release/gate-manifest.ts and src/scripts/release-parallel-check.ts'
90
+ ]),
91
+ allowedWriters: Object.freeze(['release maintainers via source edit']),
92
+ allowedReaders: Object.freeze(['release DAG', 'publish preflight']),
93
+ derivedArtifacts: Object.freeze([
94
+ '.sneakoscope/reports/release-gate-plan.json',
95
+ '.sneakoscope/reports/gate-policy-audit.json'
96
+ ]),
97
+ protected: true,
98
+ description: 'Release gate selection and publish-required status live in the manifest plus the release DAG.',
99
+ authority: 'Release gate selection and publish-required status live in the manifest plus the release DAG.',
100
+ rule: 'Publish-blocking guard gates must appear in the DAG, manifest, and package scripts.'
101
+ })
102
+ ]);
103
+ export function canonicalSsotAuthorityInventory() {
104
+ return SKS_SSOT_AUTHORITY_INVENTORY;
105
+ }
106
+ export function inventoryAsSsotGuardSources() {
107
+ return SKS_SSOT_AUTHORITY_INVENTORY.map((domain) => ({
108
+ id: domain.id,
109
+ source: domain.canonicalSources.join(', '),
110
+ authority: domain.authority,
111
+ derived: [...domain.derivedArtifacts],
112
+ rule: domain.rule
113
+ }));
114
+ }
@@ -1,5 +1,6 @@
1
1
  import { nowIso } from '../fsx.js';
2
2
  import { isRecord } from '../json/records.js';
3
+ import { inventoryAsSsotGuardSources } from './ssot-authority-inventory.js';
3
4
  export const SSOT_GUARD_SCHEMA = 'sks.ssot-guard.v1';
4
5
  export const SSOT_GUARD_ARTIFACT = 'ssot-guard.json';
5
6
  export function buildSsotGuard(input = {}) {
@@ -34,50 +35,7 @@ export function buildSsotGuard(input = {}) {
34
35
  };
35
36
  }
36
37
  export function canonicalSsotSources() {
37
- return [
38
- {
39
- id: 'route_contract',
40
- source: 'decision-contract.json, route prompt, and pipeline-plan.json',
41
- authority: 'The sealed user objective, constraints, non-goals, and acceptance criteria define what code may be created.',
42
- derived: ['subagent-plan.json', 'subagent-events.jsonl', 'worker inboxes'],
43
- rule: 'Do not implement behavior outside the sealed route contract; block with evidence if the requested path cannot be honored.'
44
- },
45
- {
46
- id: 'triwiki_context',
47
- source: '.sneakoscope/wiki/context-pack.json',
48
- authority: 'TriWiki is the bounded mission context SSOT and must be refreshed or packed, then validated before risky handoffs and final claims.',
49
- derived: ['subagent-parent-summary.json', 'subagent-evidence.json', 'reflection.md'],
50
- rule: 'Use the latest coordinate+voxel overlay pack; coordinate-only legacy packs are invalid for pipeline decisions.'
51
- },
52
- {
53
- id: 'runtime_source',
54
- source: 'src/**/*.ts',
55
- authority: 'TypeScript source is the runtime SSOT.',
56
- derived: ['dist/**', 'dist/bin/sks.js'],
57
- rule: 'Edit source, rebuild derived output, and rely on runtime:ts-source-of-truth plus runtime:dist-parity.'
58
- },
59
- {
60
- id: 'generated_outputs',
61
- source: 'source generators, build scripts, and schema definitions',
62
- authority: 'Generated files are derived from their generator or schema owner.',
63
- derived: ['release-gates.v2.json', 'infra-harness-gates.json', '.sneakoscope/reports/**', 'dist/build-manifest.json'],
64
- rule: 'Regenerate derived artifacts instead of hand-editing them as independent truth.'
65
- },
66
- {
67
- id: 'stack_current_docs',
68
- source: '.sneakoscope/memory/q2_facts/stack-current-docs.md',
69
- authority: 'Current vendor or Context7 docs override model-memory defaults when stack versions or APIs change.',
70
- derived: ['implementation notes', 'route evidence'],
71
- rule: 'Fetch and record current docs before relying on external package, SDK, API, MCP, or generated-doc behavior that may have changed.'
72
- },
73
- {
74
- id: 'release_gate_manifest',
75
- source: 'src/core/release/gate-manifest.ts and src/scripts/release-parallel-check.ts',
76
- authority: 'Release gate selection and publish-required status live in the manifest plus the release DAG.',
77
- derived: ['.sneakoscope/reports/release-gate-plan.json', '.sneakoscope/reports/gate-policy-audit.json'],
78
- rule: 'Publish-blocking guard gates must appear in the DAG, manifest, and package scripts.'
79
- }
80
- ];
38
+ return inventoryAsSsotGuardSources().map((row) => ({ ...row }));
81
39
  }
82
40
  export function validateSsotGuardArtifact(value) {
83
41
  const issues = [];
@@ -100,7 +100,8 @@ export function officialSubagentFanoutPolicy(input = {}) {
100
100
  ...(input.suggestedRoles === undefined ? {} : { suggestedRoles: input.suggestedRoles }),
101
101
  ...(input.independentSliceCount === undefined ? {} : { independentSliceCount: input.independentSliceCount }),
102
102
  ...(input.maxThreads === undefined ? {} : { maxThreads: input.maxThreads }),
103
- ...(input.hardware === undefined ? {} : { hardware: input.hardware })
103
+ ...(input.hardware === undefined ? {} : { hardware: input.hardware }),
104
+ ...(input.requiresWrite === undefined ? {} : { requiresWrite: input.requiresWrite })
104
105
  });
105
106
  const requested = explicit ? explicitRequested : automatic.count;
106
107
  const automaticReviewerCeiling = automatic.criticalMultiDomain
@@ -141,7 +142,8 @@ function automaticSubagentFanout(input) {
141
142
  const critical = CRITICAL_RISK_RE.test(text);
142
143
  const largeScale = LARGE_SCALE_WORK_RE.test(text);
143
144
  const criticalMultiDomain = highRisk && critical && riskDomains.length >= 3;
144
- const reviewerOnly = isReviewerOnlyFanout(input.suggestedRoles || []);
145
+ const reviewerOnly = isReviewerOnlyFanout(input.suggestedRoles || [])
146
+ && input.requiresWrite !== true;
145
147
  const massLane = decideSubagentModel({ description: text }).policy;
146
148
  const massParallel = MASS_PARALLEL_WORK_RE.test(text)
147
149
  && !NON_CHEAP_MASS_ACTION_RE.test(text)
@@ -20,17 +20,23 @@ export const LEGACY_SKS_MAX_THREAD_VALUES = Object.freeze([4, 5, 6, 12]);
20
20
  export const AGENTS_MAX_CONCURRENT_THREADS_KEY = 'max_concurrent_threads_per_session';
21
21
  export const LEGACY_AGENTS_MAX_THREADS_KEY = 'max_threads';
22
22
  export const LEGACY_AGENTS_JOB_MAX_RUNTIME_KEY = 'job_max_runtime_seconds';
23
+ export const SKS_MANAGED_CODEX_CONFIG_MARKER = '# SKS-MANAGED-CODEX-CONFIG';
23
24
  export function mergeOfficialSubagentConfig(text = '', opts = {}) {
25
+ return mergeOfficialSubagentConfigResult(text, opts).text;
26
+ }
27
+ export function mergeOfficialSubagentConfigResult(text = '', opts = {}) {
24
28
  const source = String(text || '');
25
- if (!inspectToml(source).ok)
26
- return source;
29
+ if (!inspectToml(source).ok) {
30
+ return { text: source, blockers: ['project_official_subagent_config_toml_parse_failed'] };
31
+ }
27
32
  const inherited = parsedToml(opts.inheritedText || '');
28
33
  const inheritedAgents = objectValue(inherited?.agents);
29
34
  const inheritedFeatures = objectValue(inherited?.features);
30
35
  let next = source.trimEnd();
31
36
  if (!next.trim())
32
- next = '# SKS-MANAGED-CODEX-CONFIG';
37
+ next = SKS_MANAGED_CODEX_CONFIG_MARKER;
33
38
  if (opts.sksOwned === true) {
39
+ next = stampManagedConfigMarker(next);
34
40
  next = removeExactLegacyManagedAgentBlocks(next);
35
41
  next = stripLegacyUnsupportedAgentKeys(next);
36
42
  }
@@ -48,10 +54,12 @@ export function mergeOfficialSubagentConfig(text = '', opts = {}) {
48
54
  next = mergeOfficialMultiAgentV2FeatureConfig(next, {
49
55
  sksOwned: opts.sksOwned === true,
50
56
  inheritedFeatures,
51
- maxThreads: readAgentsMaxThreads(next) || targetMaxThreads
57
+ maxThreads: Math.min(readAgentsMaxThreads(next) || targetMaxThreads, HARD_NARUTO_MAX_THREADS)
52
58
  });
53
59
  const merged = ensureTrailingNewline(next);
54
- return inspectToml(merged).ok ? merged : source;
60
+ if (inspectToml(merged).ok)
61
+ return { text: merged, blockers: [] };
62
+ return { text: source, blockers: ['official_subagent_config_merge_produced_invalid_toml'] };
55
63
  }
56
64
  export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
57
65
  const source = String(text || '');
@@ -61,18 +69,21 @@ export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
61
69
  const inheritedMaV2 = featureTomlObject(inheritedFeatures.multi_agent_v2);
62
70
  let next = source.trimEnd();
63
71
  if (!next.trim())
64
- next = '# SKS-MANAGED-CODEX-CONFIG';
65
- if (hasTomlTableKey(next, 'features', 'multi_agent_v2')) {
72
+ next = SKS_MANAGED_CODEX_CONFIG_MARKER;
73
+ if (hasUnmanageableMultiAgentV2Declaration(next))
74
+ return ensureTrailingNewline(next);
75
+ const booleanForm = readTomlTableBoolean(next, 'features', 'multi_agent_v2');
76
+ if (booleanForm !== null) {
66
77
  next = removeTomlTableKey(next, 'features', 'multi_agent_v2');
67
78
  }
68
79
  const maxThreads = boundedManagedMaxThreads(opts.maxThreads, DEFAULT_OFFICIAL_SUBAGENT_MAX_THREADS);
69
80
  const targetTotal = Math.max(1, maxThreads + 1);
70
81
  if (!hasTomlTable(next, 'features.multi_agent_v2')) {
71
- if (inheritedMaV2)
82
+ if (inheritedMaV2 && booleanForm === null)
72
83
  return ensureTrailingNewline(next);
73
84
  next = upsertTomlTable(next, 'features.multi_agent_v2', [
74
85
  '[features.multi_agent_v2]',
75
- 'enabled = true',
86
+ `enabled = ${booleanForm === null ? true : booleanForm}`,
76
87
  `max_concurrent_threads_per_session = ${targetTotal}`,
77
88
  'expose_spawn_agent_model_overrides = true'
78
89
  ].join('\n'));
@@ -81,7 +92,7 @@ export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
81
92
  if (opts.sksOwned === true) {
82
93
  next = upsertTomlTableKey(next, 'features.multi_agent_v2', 'enabled = true');
83
94
  const currentTotal = readTomlTableInteger(next, 'features.multi_agent_v2', 'max_concurrent_threads_per_session');
84
- if (currentTotal === DEFAULT_NARUTO_LEGACY_TOTAL_THREADS || currentTotal === null) {
95
+ if (currentTotal === null || LEGACY_MANAGED_V2_TOTAL_THREADS.includes(currentTotal)) {
85
96
  next = upsertTomlTableKey(next, 'features.multi_agent_v2', `max_concurrent_threads_per_session = ${targetTotal}`);
86
97
  }
87
98
  if (!hasTomlTableKey(next, 'features.multi_agent_v2', 'expose_spawn_agent_model_overrides')) {
@@ -105,6 +116,10 @@ export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
105
116
  return ensureTrailingNewline(next);
106
117
  }
107
118
  const DEFAULT_NARUTO_LEGACY_TOTAL_THREADS = 13;
119
+ const LEGACY_MANAGED_V2_TOTAL_THREADS = Object.freeze([
120
+ ...LEGACY_SKS_MAX_THREAD_VALUES.map((value) => value + 1),
121
+ DEFAULT_NARUTO_LEGACY_TOTAL_THREADS
122
+ ]);
108
123
  export async function readOfficialSubagentConfig(root, opts = {}) {
109
124
  const projectConfigPath = path.resolve(opts.projectConfigPath || path.join(root, '.codex', 'config.toml'));
110
125
  const home = opts.home || process.env.HOME || os.homedir();
@@ -173,6 +188,9 @@ export function officialSubagentConfigWarnings(text = '', inheritedText = '') {
173
188
  ? [`official_subagent_max_depth_coerced_to_one:${maxDepth.value}:${maxDepth.source}`]
174
189
  : []),
175
190
  ...capacityNormalizationWarnings(maxThreads, multiAgentV2),
191
+ ...(hasUnmanageableMultiAgentV2Declaration(text)
192
+ ? ['project_multi_agent_v2_declaration_form_unmanaged']
193
+ : []),
176
194
  ...project.legacyWarnings,
177
195
  ...inherited.legacyWarnings
178
196
  ];
@@ -315,6 +333,8 @@ export function officialSubagentConfigOwnershipProof(input = {}) {
315
333
  }
316
334
  if (hasExactManagedConfigMarker(text))
317
335
  reasons.push('managed_marker_or_hash');
336
+ if (hasManagedAgentsConfigFingerprint(text))
337
+ reasons.push('managed_agents_config_fingerprint');
318
338
  if (migrationReceiptProvesManagedMaxThreads(input.migrationReceipt)) {
319
339
  reasons.push('migration_receipt:agents.max_concurrent_threads_per_session');
320
340
  }
@@ -482,7 +502,38 @@ function featureTomlObject(value) {
482
502
  return Object.keys(row).length ? row : null;
483
503
  }
484
504
  function hasExactManagedConfigMarker(text) {
485
- return /^(?:#\s*SKS-MANAGED-CODEX-CONFIG\b|#\s*SKS managed Codex config\b|#\s*sks_managed_(?:body_)?sha256\s*=\s*["'][a-f0-9]{64}["'])/mi.test(text);
505
+ return /^\s*(?:#\s*SKS-MANAGED-CODEX-CONFIG\b|#\s*SKS managed Codex config\b|#\s*sks_managed_(?:body_)?sha256\s*=\s*["'][a-f0-9]{64}["'])/mi.test(text);
506
+ }
507
+ export function stampSksManagedCodexConfigMarker(text) {
508
+ return stampManagedConfigMarker(text);
509
+ }
510
+ function stampManagedConfigMarker(text) {
511
+ const source = String(text || '');
512
+ if (hasExactManagedConfigMarker(source))
513
+ return source;
514
+ const body = source.replace(/^\n+/, '');
515
+ return body ? `${SKS_MANAGED_CODEX_CONFIG_MARKER}\n${body}` : SKS_MANAGED_CODEX_CONFIG_MARKER;
516
+ }
517
+ const MANAGED_AGENTS_FINGERPRINT_KEYS = Object.freeze([
518
+ 'enabled',
519
+ 'max_depth',
520
+ 'interrupt_message',
521
+ 'default_subagent_model',
522
+ 'default_subagent_reasoning_effort'
523
+ ]);
524
+ export function hasManagedAgentsConfigFingerprint(text) {
525
+ const parsed = parsedToml(text);
526
+ if (!parsed)
527
+ return false;
528
+ const agents = objectValue(parsed.agents);
529
+ if (!MANAGED_AGENTS_FINGERPRINT_KEYS.every((key) => hasOwn(agents, key)))
530
+ return false;
531
+ if (readAgentsMaxThreadsFromRecord(agents) === undefined)
532
+ return false;
533
+ if (agents.max_depth !== DEFAULT_OFFICIAL_SUBAGENT_MAX_DEPTH)
534
+ return false;
535
+ const multiAgentV2 = featureTomlObject(objectValue(parsed.features).multi_agent_v2);
536
+ return multiAgentV2 !== null && hasOwn(multiAgentV2, 'expose_spawn_agent_model_overrides');
486
537
  }
487
538
  function migrationReceiptProvesManagedMaxThreads(value) {
488
539
  const root = objectValue(value);
@@ -676,11 +727,37 @@ function upsertDefaultUnlessInherited(text, inheritedAgents, key, line) {
676
727
  }
677
728
  function readTomlTableInteger(text, table, key) {
678
729
  const line = tomlTableKeyLine(text, table, key);
679
- const match = line?.match(/^\s*[^=]+\s*=\s*([0-9]+)\s*(?:#.*)?$/);
680
- if (!match?.[1])
730
+ const raw = line?.match(/^\s*[^=]+\s*=\s*([^#]+?)\s*(?:#.*)?$/)?.[1];
731
+ return parseTomlInteger(raw);
732
+ }
733
+ function parseTomlInteger(raw) {
734
+ const value = String(raw ?? '').trim().replace(/_/g, '');
735
+ const match = value.match(/^([+-]?)(?:0x([0-9a-fA-F]+)|0o([0-7]+)|0b([01]+)|([0-9]+))$/);
736
+ if (!match)
681
737
  return null;
682
- const value = Number(match[1]);
683
- return Number.isSafeInteger(value) ? value : null;
738
+ const magnitude = match[2] !== undefined
739
+ ? Number.parseInt(match[2], 16)
740
+ : match[3] !== undefined
741
+ ? Number.parseInt(match[3], 8)
742
+ : match[4] !== undefined
743
+ ? Number.parseInt(match[4], 2)
744
+ : Number(match[5]);
745
+ const parsed = (match[1] === '-' ? -1 : 1) * magnitude;
746
+ return Number.isSafeInteger(parsed) ? parsed : null;
747
+ }
748
+ function hasUnmanageableMultiAgentV2Declaration(text) {
749
+ const parsed = parsedToml(text);
750
+ if (!parsed)
751
+ return false;
752
+ if (!hasOwn(objectValue(parsed.features), 'multi_agent_v2'))
753
+ return false;
754
+ return !hasTomlTable(text, 'features.multi_agent_v2')
755
+ && readTomlTableBoolean(text, 'features', 'multi_agent_v2') === null;
756
+ }
757
+ function readTomlTableBoolean(text, table, key) {
758
+ const line = tomlTableKeyLine(text, table, key);
759
+ const match = line?.match(/^\s*[^=]+\s*=\s*(true|false)\s*(?:#.*)?$/);
760
+ return match?.[1] ? match[1] === 'true' : null;
684
761
  }
685
762
  function hasTomlTableKey(text, table, key) {
686
763
  return tomlTableKeyLine(text, table, key) !== null;
@@ -107,6 +107,7 @@ async function prepareOfficialSubagentMissionLocked(input) {
107
107
  taskProfile,
108
108
  suggestedRoles: suggestedAgents,
109
109
  goal,
110
+ requiresWrite: input.readOnly !== true,
110
111
  maxThreads: input.maxThreads ?? officialConfig.maxThreads,
111
112
  ...(input.hardware ? { hardware: input.hardware } : {}),
112
113
  ...(slices.length > 0 ? { independentSliceCount: slices.length } : {})