sneakoscope 7.3.0 → 7.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 (283) hide show
  1. package/README.md +129 -10
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +4 -1
  6. package/dist/cli/command-registry.js +2 -0
  7. package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
  8. package/dist/cli/install-helpers-codex-lb-config.js +114 -9
  9. package/dist/cli/install-helpers-install-support.js +6 -16
  10. package/dist/cli/install-helpers.js +788 -600
  11. package/dist/cli/router.js +30 -2
  12. package/dist/commands/codex-app.js +37 -1
  13. package/dist/commands/codex-lb.js +491 -300
  14. package/dist/config/skills-manifest.json +57 -71
  15. package/dist/core/align/align-route.js +127 -0
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
  17. package/dist/core/codex-app/codex-app-restart.js +83 -0
  18. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
  19. package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
  20. package/dist/core/codex-app/menubar/constants.js +5 -0
  21. package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
  22. package/dist/core/codex-app/menubar/installer.js +18 -0
  23. package/dist/core/codex-app/multi-provider-router.js +22 -10
  24. package/dist/core/codex-app/openrouter-activate.js +26 -1
  25. package/dist/core/codex-app.js +118 -26
  26. package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
  27. package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
  28. package/dist/core/codex-control/codex-task-runner.js +2 -2
  29. package/dist/core/codex-lb/capability-runner.js +204 -0
  30. package/dist/core/codex-lb/capability-types.js +1 -0
  31. package/dist/core/codex-lb/codex-lb-env.js +9 -13
  32. package/dist/core/codex-lb/codex-lb-setup.js +45 -8
  33. package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
  34. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
  35. package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
  36. package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
  37. package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
  38. package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
  39. package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
  40. package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
  41. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
  42. package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
  43. package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
  44. package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
  45. package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
  46. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
  47. package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
  48. package/dist/core/codex-lb/desktop-controller.js +1410 -0
  49. package/dist/core/codex-lb/desktop-mode.js +32 -0
  50. package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
  51. package/dist/core/codex-lb/desktop-service.js +613 -0
  52. package/dist/core/codex-lb/legacy-migration.js +575 -0
  53. package/dist/core/codex-lb/migration-receipt.js +246 -0
  54. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
  55. package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
  56. package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
  57. package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
  58. package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
  59. package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
  60. package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
  61. package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
  62. package/dist/core/codex-native/core-skill-manifest.js +22 -6
  63. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
  64. package/dist/core/commands/align-command.js +220 -0
  65. package/dist/core/commands/image-ux-review-command.js +280 -68
  66. package/dist/core/commands/naruto-command.js +257 -8
  67. package/dist/core/commands/ppt-command.js +2 -2
  68. package/dist/core/commands/telegram-command.js +402 -34
  69. package/dist/core/commands/triwiki-command.js +11 -1
  70. package/dist/core/commands/triwiki-graph-command.js +160 -0
  71. package/dist/core/commands/wiki-command.js +100 -20
  72. package/dist/core/doctor/skill-legacy-surface.js +1 -3
  73. package/dist/core/errors/message.js +3 -0
  74. package/dist/core/evidence/evidence-freshness.js +14 -0
  75. package/dist/core/feature-fixtures.js +9 -1
  76. package/dist/core/feature-registry.js +5 -3
  77. package/dist/core/fsx.js +7 -3
  78. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  79. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  80. package/dist/core/hooks-runtime/hook-context.js +15 -2
  81. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
  82. package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
  83. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  84. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  85. package/dist/core/hooks-runtime.js +33 -89
  86. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  87. package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
  88. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  89. package/dist/core/image-ux-review.js +230 -12
  90. package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
  91. package/dist/core/imagegen/imagegen-capability.js +10 -4
  92. package/dist/core/imagegen/require-imagegen.js +29 -5
  93. package/dist/core/init/skills.js +25 -13
  94. package/dist/core/init.js +1 -1
  95. package/dist/core/install/installed-package-smoke.js +157 -4
  96. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  97. package/dist/core/managed-path-safety.js +8 -10
  98. package/dist/core/mcp-config/backup.js +2 -4
  99. package/dist/core/mcp-config/config-reader.js +2 -4
  100. package/dist/core/mcp-config/scope.js +3 -5
  101. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  102. package/dist/core/naruto/context-graph-advisor.js +0 -0
  103. package/dist/core/perf/release-latency-slo.js +12 -6
  104. package/dist/core/pipeline-internals/runtime-core.js +78 -0
  105. package/dist/core/pipeline-internals/runtime-gates.js +5 -2
  106. package/dist/core/policy/latest-version-guidance.js +113 -0
  107. package/dist/core/ppt.js +8 -8
  108. package/dist/core/preflight/parallel-preflight-engine.js +1 -1
  109. package/dist/core/proof/auto-finalize.js +98 -6
  110. package/dist/core/proof/route-proof-gate.js +12 -1
  111. package/dist/core/proof/route-proof-policy.js +8 -8
  112. package/dist/core/release/gate-manifest.js +32 -1
  113. package/dist/core/release/npm-stage-contract.js +21 -0
  114. package/dist/core/release/package-size-budget.js +2 -2
  115. package/dist/core/release/release-gate-contract.js +5 -0
  116. package/dist/core/release/release-proof-io.js +1 -3
  117. package/dist/core/release/release-real-contract.js +5 -4
  118. package/dist/core/release/release-stamp-proof.js +1 -3
  119. package/dist/core/release/stage-publish.js +27 -0
  120. package/dist/core/remote/audit-idempotency.js +50 -18
  121. package/dist/core/remote/codex-public-activity.js +283 -0
  122. package/dist/core/remote/index.js +1 -0
  123. package/dist/core/remote/ssh-worker-client.js +4 -1
  124. package/dist/core/remote/worker.js +206 -27
  125. package/dist/core/research/research-adversarial-review.js +1 -3
  126. package/dist/core/research/research-claim-synthesizer.js +1 -3
  127. package/dist/core/research/research-falsification-runner.js +1 -3
  128. package/dist/core/research/research-source-evidence.js +1 -3
  129. package/dist/core/responses-stream.js +65 -19
  130. package/dist/core/routes/constants.js +1 -1
  131. package/dist/core/routes/design-policy.js +7 -2
  132. package/dist/core/routes/dollar-manifest-lite.js +2 -4
  133. package/dist/core/routes/evidence.js +1 -1
  134. package/dist/core/routes.js +30 -10
  135. package/dist/core/safety/command-contract/contracts.js +2 -0
  136. package/dist/core/safety/command-contract/types.js +1 -1
  137. package/dist/core/search/context-graph-seeds.js +205 -0
  138. package/dist/core/search/context.js +0 -0
  139. package/dist/core/search/provider.js +2 -2
  140. package/dist/core/secret-redaction.js +6 -0
  141. package/dist/core/subagents/naruto-help-contract.js +2 -1
  142. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  143. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  144. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  145. package/dist/core/subagents/official-subagent-runner.js +26 -8
  146. package/dist/core/subagents/triwiki-attention.js +135 -123
  147. package/dist/core/telegram/bot-api.js +85 -10
  148. package/dist/core/telegram/center-snapshot.js +718 -0
  149. package/dist/core/telegram/config.js +158 -31
  150. package/dist/core/telegram/hub.js +288 -33
  151. package/dist/core/telegram/index.js +2 -0
  152. package/dist/core/telegram/ingress-spool.js +419 -0
  153. package/dist/core/telegram/ledgers.js +188 -29
  154. package/dist/core/telegram/messages.js +26 -6
  155. package/dist/core/telegram/owner-lock.js +10 -7
  156. package/dist/core/telegram/runtime-projection.js +38 -3
  157. package/dist/core/telegram/runtime.js +298 -63
  158. package/dist/core/telegram/service.js +137 -8
  159. package/dist/core/telegram/setup.js +291 -73
  160. package/dist/core/text/strings.js +3 -0
  161. package/dist/core/triwiki/code-pack.js +12 -120
  162. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  163. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  164. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  165. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  166. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  167. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  168. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  169. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  170. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  171. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  172. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  173. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  174. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  175. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  176. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  177. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  178. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  179. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  180. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  181. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  182. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  183. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  184. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  185. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  186. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  187. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  188. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  189. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  190. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  191. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  192. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  193. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  194. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  195. package/dist/core/triwiki/context-graph/ids.js +0 -0
  196. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  197. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  198. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  199. package/dist/core/triwiki/context-graph/paths.js +116 -0
  200. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  201. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  202. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  203. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  204. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  205. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  206. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  207. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  208. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  209. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  210. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  211. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  212. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  213. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  214. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  215. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  216. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  217. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  218. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  219. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  220. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  221. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  222. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  223. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  224. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  225. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  226. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  227. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  228. package/dist/core/verification/context-graph-affected.js +271 -0
  229. package/dist/core/version.js +1 -1
  230. package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
  231. package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
  232. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
  233. package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
  234. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
  235. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
  236. package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
  237. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
  238. package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
  239. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
  240. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
  241. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
  242. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
  243. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
  244. package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
  245. package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
  246. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
  247. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
  248. package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
  249. package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
  250. package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
  251. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
  252. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
  253. package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
  254. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
  255. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
  256. package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
  257. package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
  258. package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
  259. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
  260. package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
  261. package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
  262. package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
  263. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
  264. package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
  265. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
  266. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
  267. package/dist/scripts/context-graph-check.js +191 -0
  268. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
  269. package/dist/scripts/gate-policy-audit-check.js +1 -1
  270. package/dist/scripts/helper-dedup-check.js +0 -2
  271. package/dist/scripts/installed-package-smoke-check.js +4 -1
  272. package/dist/scripts/latest-version-guidance-check.js +74 -0
  273. package/dist/scripts/release-metadata-check.js +21 -4
  274. package/dist/scripts/release-real-check.js +7 -0
  275. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  276. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  277. package/package.json +8 -3
  278. package/release-gates.v2.json +176 -1
  279. package/runtime-required-scripts.json +30 -0
  280. package/schemas/triwiki/context-graph.schema.json +119 -0
  281. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
  282. package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
  283. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -0,0 +1,160 @@
1
+ import { flag, readOption } from '../../cli/args.js';
2
+ import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../triwiki/context-graph/contracts.js';
3
+ import { contextGraphFreshnessPreflight } from '../hooks-runtime/context-graph-freshness-preflight.js';
4
+ import { contextGraphExtractors } from '../triwiki/context-graph/extractors/index.js';
5
+ import { runContextGraphLint } from '../triwiki/context-graph/lint/index.js';
6
+ import { contextGraphQueryProfile } from '../triwiki/context-graph/profiles.js';
7
+ import { queryContextGraph } from '../triwiki/context-graph/query/index.js';
8
+ import { contextGraphStatus } from '../triwiki/context-graph/store/graph-status.js';
9
+ import { readContextGraphMeta, readContextGraphSnapshot } from '../triwiki/context-graph/store/snapshot-store.js';
10
+ export const TRIWIKI_GRAPH_SUBCOMMANDS = ['graph-status', 'graph-lint', 'graph-query'];
11
+ export function isTriWikiGraphSubcommand(value) {
12
+ return typeof value === 'string' && TRIWIKI_GRAPH_SUBCOMMANDS.includes(value);
13
+ }
14
+ function issueLine(issue) {
15
+ const where = issue.nodeId ?? issue.edgeId ?? issue.path ?? '';
16
+ return `${issue.severity}: ${issue.code}${where ? ` ${where}` : ''} — ${issue.message}`;
17
+ }
18
+ async function graphStatusFast(root) {
19
+ const preflight = await contextGraphFreshnessPreflight(root);
20
+ return {
21
+ result: { ...preflight, mode: 'fast', ok: preflight.usable },
22
+ ok: preflight.usable,
23
+ lines: [
24
+ `Context graph: ${preflight.status} (${preflight.node_count} nodes, ${preflight.edge_count} edges, ${preflight.coverage})`,
25
+ ...(preflight.reasons.length ? [`Stale reasons: ${preflight.reasons.join(', ')}`] : []),
26
+ ...(preflight.unverified_reasons.length ? [`Not evaluated: ${preflight.unverified_reasons.join(', ')}`] : []),
27
+ ...(preflight.usable ? [] : [`Repair: ${preflight.repair_command}`])
28
+ ]
29
+ };
30
+ }
31
+ async function graphStatus(root) {
32
+ const status = await contextGraphStatus(root, { extractors: contextGraphExtractors() });
33
+ const meta = await readContextGraphMeta(root);
34
+ const result = {
35
+ schema: 'sks.triwiki-graph-status.v1',
36
+ ok: status.status === 'fresh',
37
+ status: status.status,
38
+ snapshot_hash: status.snapshotHash,
39
+ generated_at: status.generatedAt,
40
+ stale_reasons: status.reasons,
41
+ error_code: status.errorCode,
42
+ node_count: status.nodeCount,
43
+ edge_count: status.edgeCount,
44
+ lint: meta.status === 'ok' && meta.meta ? meta.meta.lint : null,
45
+ repair_command: status.repairCommand
46
+ };
47
+ const lines = [
48
+ `Context graph: ${status.status} (${status.nodeCount} nodes, ${status.edgeCount} edges)`,
49
+ ...(status.snapshotHash ? [`Snapshot: ${status.snapshotHash.slice(0, 16)}`] : []),
50
+ ...(status.reasons.length ? [`Stale reasons: ${status.reasons.join(', ')}`] : []),
51
+ ...(status.status === 'fresh' ? [] : [`Repair: ${status.repairCommand}`])
52
+ ];
53
+ return { result, ok: result.ok, lines };
54
+ }
55
+ async function graphLint(root) {
56
+ const snapshotLoad = await readContextGraphSnapshot(root);
57
+ if (snapshotLoad.status !== 'ok' || !snapshotLoad.snapshot) {
58
+ const errorCode = snapshotLoad.status === 'missing' ? 'context_graph_missing' : 'context_graph_corrupt';
59
+ return {
60
+ result: {
61
+ schema: 'sks.triwiki-graph-lint.v1',
62
+ ok: false,
63
+ error_code: errorCode,
64
+ errors: [],
65
+ warnings: [],
66
+ repair_command: CONTEXT_GRAPH_REPAIR_COMMAND
67
+ },
68
+ ok: false,
69
+ lines: [`Context graph lint: ${errorCode}`, `Repair: ${CONTEXT_GRAPH_REPAIR_COMMAND}`]
70
+ };
71
+ }
72
+ const meta = await readContextGraphMeta(root);
73
+ const lint = runContextGraphLint({
74
+ root,
75
+ snapshot: snapshotLoad.snapshot,
76
+ meta: meta.status === 'ok' ? meta.meta : null
77
+ });
78
+ const result = {
79
+ schema: 'sks.triwiki-graph-lint.v1',
80
+ ok: lint.ok,
81
+ snapshot_hash: snapshotLoad.snapshot.snapshotHash,
82
+ error_count: lint.errorCount,
83
+ warning_count: lint.warningCount,
84
+ errors: lint.errors.map(issueLine),
85
+ warnings: lint.warnings.slice(0, 40).map(issueLine),
86
+ repair_command: CONTEXT_GRAPH_REPAIR_COMMAND
87
+ };
88
+ return {
89
+ result,
90
+ ok: lint.ok,
91
+ lines: [
92
+ `Context graph lint: ${lint.ok ? 'ok' : 'blocked'} (${lint.errorCount} errors, ${lint.warningCount} warnings)`,
93
+ ...lint.errors.slice(0, 20).map((issue) => `- ${issueLine(issue)}`)
94
+ ]
95
+ };
96
+ }
97
+ async function graphQuery(root, args) {
98
+ const query = args.find((arg) => !arg.startsWith('-')) ?? '';
99
+ const profile = contextGraphQueryProfile(readOption(args, '--profile', 'implementation')).name;
100
+ const budgetRaw = readOption(args, '--token-budget', '');
101
+ const parsedBudget = budgetRaw ? Number.parseInt(String(budgetRaw), 10) : Number.NaN;
102
+ if (!query) {
103
+ return {
104
+ result: { schema: 'sks.triwiki-graph-query.v1', ok: false, errors: ['missing_query'] },
105
+ ok: false,
106
+ lines: ['Usage: sks triwiki graph-query "<query>" [--profile implementation|review|planning|answer] [--json]']
107
+ };
108
+ }
109
+ const answer = await queryContextGraph({
110
+ root,
111
+ query,
112
+ profile,
113
+ ...(Number.isFinite(parsedBudget) ? { tokenBudget: parsedBudget } : {})
114
+ });
115
+ const result = {
116
+ schema: 'sks.triwiki-graph-query.v1',
117
+ ok: answer.ok,
118
+ profile: answer.profile,
119
+ snapshot_hash: answer.snapshotHash,
120
+ snapshot_freshness: answer.snapshotFreshness,
121
+ seed_count: answer.seedCount,
122
+ visited_nodes: answer.visitedNodes,
123
+ selected: answer.selected.map((node) => ({
124
+ id: node.nodeId,
125
+ kind: node.kind,
126
+ path: node.path ?? null,
127
+ score: node.score,
128
+ trust: node.trust,
129
+ freshness: node.freshness,
130
+ token_cost: node.tokenCost,
131
+ reason_path: node.reasonPath,
132
+ provenance: node.provenance
133
+ })),
134
+ provenance_coverage: answer.provenanceCoverage,
135
+ stale_excluded: answer.staleExcluded,
136
+ invalidated_excluded: answer.invalidatedExcluded,
137
+ token_cost: answer.tokenCost,
138
+ token_budget: answer.tokenBudget,
139
+ truncated: answer.truncated,
140
+ timeout: answer.timeout,
141
+ omission_reasons: answer.omissionReasons,
142
+ warnings: answer.warnings,
143
+ errors: answer.errors,
144
+ repair_command: CONTEXT_GRAPH_REPAIR_COMMAND
145
+ };
146
+ const lines = answer.ok
147
+ ? [
148
+ `Context graph query (${answer.profile}): ${answer.selected.length} nodes, ${answer.tokenCost}/${answer.tokenBudget} tokens`,
149
+ ...answer.selected.slice(0, 12).map((node) => `- ${node.nodeId} [${node.reasonPath.join(' ')}]`)
150
+ ]
151
+ : [`Context graph query blocked: ${answer.errors.join(', ')}`, `Repair: ${CONTEXT_GRAPH_REPAIR_COMMAND}`];
152
+ return { result, ok: answer.ok, lines };
153
+ }
154
+ export async function triwikiGraphCommand(root, sub, args) {
155
+ if (sub === 'graph-status')
156
+ return flag(args, '--fast') ? graphStatusFast(root) : graphStatus(root);
157
+ if (sub === 'graph-lint')
158
+ return graphLint(root);
159
+ return graphQuery(root, args.filter((arg) => arg !== 'graph-query' && !flag([arg], '--json')));
160
+ }
@@ -20,8 +20,15 @@ import { wrongnessContextForRoute } from '../triwiki-wrongness/wrongness-retriev
20
20
  import { readCombinedWrongnessRecords } from '../triwiki-wrongness/wrongness-ledger.js';
21
21
  import { recordImageWrongnessFromValidation } from '../triwiki-wrongness/image-wrongness.js';
22
22
  import { publishSharedMemory, rebuildSharedIndexes, sharedMemorySummary, validateSharedMemory } from '../git-hygiene/shared-memory-publish.js';
23
- import { scanCodebaseIndex } from '../triwiki/code-index-scanner.js';
24
- import { buildCodePack, validateCodePack, writeCodePackAtomic } from '../triwiki/code-pack.js';
23
+ import { codePackPath, isCodePackProjectionBoundToSnapshot, validateCodePack, writeCodePackAtomic } from '../triwiki/code-pack.js';
24
+ import { repairTriWikiProofIndex } from '../triwiki/triwiki-proof-bank-index.js';
25
+ import { runContextGraphLint } from '../triwiki/context-graph/lint/index.js';
26
+ import { contextGraphStatus } from '../triwiki/context-graph/store/graph-status.js';
27
+ import { readContextGraphMeta, readContextGraphSnapshot } from '../triwiki/context-graph/store/snapshot-store.js';
28
+ import { compileContextGraph } from '../triwiki/context-graph/compiler/index.js';
29
+ import { contextGraphExtractors } from '../triwiki/context-graph/extractors/index.js';
30
+ import { buildWorkspaceCodePack } from '../triwiki/context-graph/projections/code-pack-workspace.js';
31
+ import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../triwiki/context-graph/contracts.js';
25
32
  import { inspectCodePackHeadFreshness } from '../triwiki/code-pack-head-freshness.js';
26
33
  import { sealTriWikiContextPack, validateTriWikiContextPackProvenance } from '../triwiki-provenance.js';
27
34
  import { flag, positionalArgs, readFlagValue, readOption, resolveMissionId } from './command-utils.js';
@@ -205,46 +212,119 @@ export async function wikiCommand(sub, args = []) {
205
212
  const pack = await readJson(path.resolve(target));
206
213
  const { result, trustAnchors } = wikiValidationResult(pack, root);
207
214
  const codePack = await codePackFreshness(root);
208
- process.exitCode = result.ok ? 0 : 2;
209
- if (flag(args, '--json'))
210
- return console.log(JSON.stringify({ ...result, code_pack: codePack }, null, 2));
215
+ const graph = await wikiValidateContextGraph(root);
216
+ const ok = result.ok && graph.ok;
217
+ process.exitCode = ok ? 0 : 2;
218
+ if (flag(args, '--json')) {
219
+ return console.log(JSON.stringify({ ...result, ok, code_pack: codePack, context_graph: graph }, null, 2));
220
+ }
211
221
  console.log(`Wiki coordinate index: ${result.ok ? 'ok' : 'failed'}`);
212
222
  console.log(`Anchors checked: ${result.checked}`);
213
223
  console.log(`Trust anchors: ${trustAnchors}/${result.checked}`);
214
224
  for (const issue of result.issues)
215
225
  console.log(`- ${issue.severity}: ${issue.id}${issue.anchor ? ` ${issue.anchor}` : ''}`);
216
- console.log(`Code pack: ${codePack.status}${codePack.status === 'stale' ? ' — run `sks wiki refresh --code`' : ''}`);
226
+ console.log(`Code pack: ${codePack.status}${codePack.status === 'stale' ? ` — run \`${CONTEXT_GRAPH_REPAIR_COMMAND}\`` : ''}`);
227
+ console.log(`Context graph: ${graph.status}${graph.ok ? '' : ` — run \`${CONTEXT_GRAPH_REPAIR_COMMAND}\``}`);
228
+ for (const issue of graph.issues)
229
+ console.log(`- ${issue}`);
217
230
  return;
218
231
  }
219
232
  console.error('Usage: sks wiki coords|pack|refresh|publish|rebuild-index|rebuild-summary|validate|validate-shared|wrongness|image-ingest|anchor-add|relation-add|image-validate|image-summary');
220
233
  process.exitCode = 1;
221
234
  }
235
+ async function wikiValidateContextGraph(root) {
236
+ const status = await contextGraphStatus(root, { extractors: contextGraphExtractors() });
237
+ if (status.status !== 'fresh') {
238
+ return {
239
+ ok: false,
240
+ status: status.status,
241
+ snapshot_hash: status.snapshotHash,
242
+ issues: [status.errorCode ?? 'context_graph_not_fresh', ...status.reasons]
243
+ };
244
+ }
245
+ const snapshotLoad = await readContextGraphSnapshot(root);
246
+ const metaLoad = await readContextGraphMeta(root);
247
+ if (snapshotLoad.status !== 'ok' || !snapshotLoad.snapshot || metaLoad.status !== 'ok' || !metaLoad.meta) {
248
+ return { ok: false, status: 'corrupt', snapshot_hash: status.snapshotHash, issues: ['context_graph_snapshot_meta_unreadable'] };
249
+ }
250
+ const lint = runContextGraphLint({ root, snapshot: snapshotLoad.snapshot, meta: metaLoad.meta });
251
+ const issues = lint.errors.slice(0, 20).map((issue) => `${issue.code}: ${issue.message}`);
252
+ const pack = await readJson(codePackPath(root), null).catch(() => null);
253
+ const digest = typeof pack?.index_digest === 'string' ? pack.index_digest : '';
254
+ const boundToSnapshot = Boolean(pack
255
+ && Array.isArray(pack.entries)
256
+ && isCodePackProjectionBoundToSnapshot(snapshotLoad.snapshot.snapshotHash, pack));
257
+ if (digest && !boundToSnapshot)
258
+ issues.push('code_pack_not_bound_to_current_snapshot');
259
+ return {
260
+ ok: lint.ok && (!digest || boundToSnapshot),
261
+ status: 'fresh',
262
+ snapshot_hash: snapshotLoad.snapshot.snapshotHash,
263
+ issues
264
+ };
265
+ }
222
266
  async function wikiRefreshCode(args = []) {
223
267
  const root = await sksRoot();
224
- const index = await scanCodebaseIndex(root);
225
268
  const tokenBudgetRaw = readFlagValue(args, '--token-budget', null);
226
- const tokenBudget = tokenBudgetRaw ? Number.parseInt(String(tokenBudgetRaw), 10) : undefined;
227
- const pack = tokenBudget && Number.isFinite(tokenBudget) ? buildCodePack(root, index, tokenBudget) : buildCodePack(root, index);
228
- const validation = await validateCodePack(pack, root);
229
- const written = validation.ok ? await writeCodePackAtomic(root, pack) : null;
269
+ const parsedBudget = tokenBudgetRaw ? Number.parseInt(String(tokenBudgetRaw), 10) : Number.NaN;
270
+ const tokenBudget = Number.isFinite(parsedBudget) ? parsedBudget : undefined;
271
+ const proofIndex = repairTriWikiProofIndex(root);
272
+ const compiled = await compileContextGraph({
273
+ root,
274
+ extractors: contextGraphExtractors(),
275
+ useFragmentCache: false
276
+ });
277
+ const lintErrors = compiled.issues.filter((issue) => issue.severity === 'error');
278
+ if (!compiled.ok || !compiled.snapshot) {
279
+ const blocked = {
280
+ schema: 'sks.wiki-refresh-code.v1',
281
+ ok: false,
282
+ reason: compiled.reason ?? 'context_graph_compile_failed',
283
+ blockers: compiled.blockers ?? [],
284
+ issues: lintErrors.slice(0, 20).map((issue) => `${issue.code}: ${issue.message}`),
285
+ repair_command: CONTEXT_GRAPH_REPAIR_COMMAND,
286
+ written: null
287
+ };
288
+ process.exitCode = 2;
289
+ process.stderr.write(`wiki-refresh-code: root=${root} graph=blocked reason=${blocked.reason} blockers=${JSON.stringify(blocked.blockers)}\n`);
290
+ if (flag(args, '--json'))
291
+ return console.log(JSON.stringify(blocked, null, 2));
292
+ console.log('Sneakoscope TriWiki Context Graph Refresh');
293
+ console.log(`Context graph: blocked (${blocked.reason})`);
294
+ for (const issue of blocked.issues)
295
+ console.log(`- ${issue}`);
296
+ return;
297
+ }
298
+ const projected = await buildWorkspaceCodePack(root, {
299
+ ...(tokenBudget === undefined ? {} : { tokenBudget }),
300
+ status: { status: 'fresh', snapshotHash: compiled.snapshotHash }
301
+ });
302
+ const pack = projected.pack;
303
+ const validation = pack ? await validateCodePack(pack, root) : { ok: false, issues: projected.errors };
304
+ const written = pack && validation.ok ? await writeCodePackAtomic(root, pack) : null;
230
305
  const result = {
231
306
  schema: 'sks.wiki-refresh-code.v1',
232
307
  ok: validation.ok,
233
308
  issues: validation.issues,
234
- modules: index.modules.length,
235
- truncated: index.truncated,
236
- scanned_file_count: index.scanned_file_count,
237
- entries: pack.entries.length,
238
- token_cost: pack.total_token_cost,
239
- token_budget: pack.token_budget,
309
+ snapshot_hash: compiled.snapshotHash,
310
+ nodes: compiled.snapshot.nodeCount,
311
+ edges: compiled.snapshot.edgeCount,
312
+ graph_warnings: compiled.issues.filter((issue) => issue.severity === 'warning').length,
313
+ incremental: Boolean(compiled.incremental),
314
+ proof_index: { ok: proofIndex.ok, indexed: proofIndex.indexed_count, previous_status: proofIndex.previous_status },
315
+ compile_ms: compiled.durationMs,
316
+ entries: pack ? pack.entries.length : 0,
317
+ token_cost: pack ? pack.total_token_cost : 0,
318
+ token_budget: pack ? pack.token_budget : 0,
240
319
  written: written ? written.path : null
241
320
  };
242
321
  process.exitCode = validation.ok ? 0 : 2;
243
- process.stderr.write(`wiki-refresh-code: root=${root} modules=${index.modules.length} entries=${pack.entries.length} token_cost=${pack.total_token_cost}/${pack.token_budget} issues=${JSON.stringify(validation.issues)}\n`);
322
+ process.stderr.write(`wiki-refresh-code: root=${root} nodes=${result.nodes} edges=${result.edges} entries=${result.entries} token_cost=${result.token_cost}/${result.token_budget} issues=${JSON.stringify(validation.issues)}\n`);
244
323
  if (flag(args, '--json'))
245
324
  return console.log(JSON.stringify(result, null, 2));
246
- console.log('Sneakoscope TriWiki Code Pack Refresh');
247
- console.log(`Code pack refresh: ${validation.ok ? 'ok' : 'blocked'} (${pack.entries.length} entries from ${index.modules.length} modules${index.truncated ? ', truncated' : ''})`);
325
+ console.log('Sneakoscope TriWiki Context Graph Refresh');
326
+ console.log(`Context graph: ok (${result.nodes} nodes, ${result.edges} edges, snapshot ${String(result.snapshot_hash).slice(0, 12)})`);
327
+ console.log(`Code pack projection: ${validation.ok ? 'ok' : 'blocked'} (${result.entries} entries, ${result.token_cost}/${result.token_budget} tokens)`);
248
328
  for (const issue of validation.issues)
249
329
  console.log(`- ${issue}`);
250
330
  }
@@ -6,6 +6,7 @@ import { legacyCoreSkillNames } from '../codex-native/core-skill-manifest.js';
6
6
  import { prefixKnownSksDollarReferences } from '../routes/dollar-prefix.js';
7
7
  import { inspectConfinedPath, moveConfinedPath, uniqueConfinedPath } from '../managed-path-safety.js';
8
8
  import { writeTextAtomic } from '../fsx.js';
9
+ import { messageOf as errorMessage } from '../errors/message.js';
9
10
  import { collectNestedProjectRoots } from './current-project-guidance-nested.js';
10
11
  import { containsRetiredPublicSurface } from './current-project-guidance.js';
11
12
  export const SKILL_LEGACY_SURFACE_SCHEMA = 'sks.skill-legacy-surface.v1';
@@ -346,6 +347,3 @@ async function pathExists(ownerRoot, target) {
346
347
  return false;
347
348
  }
348
349
  }
349
- function errorMessage(error) {
350
- return error instanceof Error ? error.message : String(error);
351
- }
@@ -1,3 +1,6 @@
1
1
  export function messageOf(error) {
2
2
  return error instanceof Error ? error.message : String(error);
3
3
  }
4
+ export function errorCodeOf(error) {
5
+ return error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
6
+ }
@@ -27,6 +27,8 @@ export async function lastJsonlEventTime(file) {
27
27
  for (const line of text.split(/\n/).filter(Boolean)) {
28
28
  try {
29
29
  const parsed = JSON.parse(line);
30
+ if (!eventInvalidatesProof(parsed))
31
+ continue;
30
32
  const ts = Date.parse(parsed.ts || parsed.time || parsed.created_at || '');
31
33
  if (Number.isFinite(ts) && (latest == null || ts > latest))
32
34
  latest = ts;
@@ -35,3 +37,15 @@ export async function lastJsonlEventTime(file) {
35
37
  }
36
38
  return latest;
37
39
  }
40
+ export function eventInvalidatesProof(event = {}) {
41
+ if (event?.proof_invalidating === false)
42
+ return false;
43
+ const type = String(event?.type || '').trim();
44
+ if (/^triwiki\.agents_md_project(?:ed|_failed)$/.test(type))
45
+ return false;
46
+ if (/^pipeline\.compliance_loop_guard(?:\..+)?$/.test(type))
47
+ return false;
48
+ if (/^pipeline\.honest_mode\.loopback(?:_resolved)?$/.test(type))
49
+ return false;
50
+ return true;
51
+ }
@@ -58,6 +58,9 @@ const FIXTURES = Object.freeze({
58
58
  'cli-git': fixture('execute', 'sks git policy --json', [], 'pass'),
59
59
  'cli-uninstall': fixture('execute', 'sks uninstall --dry-run --json', [], 'pass'),
60
60
  'cli-goal': fixture('execute', 'sks goal create "Fixture smoke: create a minimal Node.js CLI health-check script" --json', [], 'pass', { reason: 'The compatibility command is stateless: it prints a detailed Codex native /goal request and writes no SKS mission, artifact, loop, or fallback state.' }),
61
+ 'cli-align': fixture('execute_and_validate_artifacts', 'sks align prepare "fixture modernization contract" --json', ['align-plan.json', 'align-ledger.json', 'align-gate.json'], 'pass', {
62
+ reason: 'The CLI fixture exercises the real hermetic preparation path. It intentionally leaves align-gate.json blocked because no external modernization work is fabricated.'
63
+ }),
61
64
  'cli-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode seo --json', ['search-visibility/site-inventory.json', 'search-visibility/seo-findings.json', 'search-visibility/verification-report.json', 'seo-gate.json', 'completion-proof.json'], 'pass'),
62
65
  'cli-research': fixture('execute_and_validate_artifacts', 'sks research run latest --mock --json', ['research-gate.json', 'completion-proof.json'], 'blocked', { timeout_ms: 180000, reason: '"run" (not "status") is the command that actually writes research-gate.json/completion-proof.json, but research is a two-step prepare-then-run workflow gated by an active-route-not-closed check between steps that a single fixture command cannot express; on a hermetic run "latest" will not be a properly prepared+closed research mission.' }),
63
66
  'cli-qa-loop': fixture('execute_and_validate_artifacts', 'sks qa-loop run latest --mock --json', ['qa-gate.json', 'completion-proof.json'], 'pass', { timeout_ms: 180000 }),
@@ -126,7 +129,9 @@ const FIXTURES = Object.freeze({
126
129
  'skill-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode geo --json', ['search-visibility/site-inventory.json', 'search-visibility/geo-findings.json', 'geo-gate.json', 'completion-proof.json'], 'pass'),
127
130
  'cli-proof': fixture('execute_and_validate_artifacts', 'sks proof smoke --json', ['.sneakoscope/proof/latest.json'], 'pass'),
128
131
  'cli-trust': fixture('execute_and_validate_artifacts', 'sks trust report latest --json', ['trust-report.json'], 'pass'),
129
- 'cli-wrongness': fixture('execute_and_validate_artifacts', 'sks wrongness add --kind missing_evidence --claim "fixture wrongness" --json', ['.sneakoscope/wiki/wrongness-ledger.json'], 'pass'),
132
+ 'cli-wrongness': fixture('execute', 'sks wrongness validate project --json', [], 'pass', {
133
+ reason: 'Release fixtures must not add synthetic project-wide wrongness records. The read-only validator exercises the CLI and ledger schema without contaminating trust state for later route proofs.'
134
+ }),
130
135
  'route-naruto': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --agents 4 --max-threads 4 --json', ['subagent-plan.json', 'subagent-events.jsonl', 'subagent-evidence.json', 'naruto-summary.json', 'naruto-gate.json', 'work-order-ledger.json'], 'pass', preparationFixtureContract({ timeout_ms: 90000 })),
131
136
  'route-work': fixture('static', '$Work compatibility alias for the Naruto Codex official subagent workflow', [], 'pass', { quality: 'wiring_only', reason: 'Pure alias of $Naruto; official workflow execution is covered by route-naruto.' }),
132
137
  'route-plan': fixture('execute', 'sks plan "fixture" --json', [], 'pass'),
@@ -148,6 +153,9 @@ const FIXTURES = Object.freeze({
148
153
  quality: 'wiring_only',
149
154
  reason: 'Goal lifecycle is owned entirely by Codex native /goal. The SKS surface only builds a detailed native objective and command; it creates no mission, proof artifact, compatibility loop, or fallback goal state.'
150
155
  }),
156
+ 'route-align': fixture('execute_and_validate_artifacts', 'sks align fixture --json', ['align-plan.json', 'align-ledger.json', 'align-gate.json', 'completion-proof.json'], 'blocked', {
157
+ reason: 'The Align route fixture creates a real hermetic mission and canonical Completion Proof, then exits blocked because the six modernization workstreams were not actually executed. This verifies route wiring and proof closure without inventing official-doc work.'
158
+ }),
151
159
  'route-super-search': fixture('execute', 'sks run "$Super-Search doctor" --execute --json', [], 'pass'),
152
160
  'route-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode geo --json', ['search-visibility/site-inventory.json', 'search-visibility/geo-findings.json', 'search-visibility/verification-report.json', 'geo-gate.json', 'completion-proof.json'], 'pass'),
153
161
  'route-autoresearch': fixture('mock', '$AutoResearch fixture route', ['research-gate.json', 'completion-proof.json'], 'pass', {
@@ -506,7 +506,7 @@ const SAFE_EXECUTABLE_FIXTURE_ARGS = Object.freeze({
506
506
  'cli-proof-field': ['proof-field', 'scan', '--json', '--intent', 'fixture'],
507
507
  'cli-proof': ['proof', 'smoke', '--json'],
508
508
  'cli-trust': { setup: [['run', 'fixture', '--mock', '--json']], command: ['trust', 'report', 'latest', '--json'] },
509
- 'cli-wrongness': ['wrongness', 'add', '--kind', 'missing_evidence', '--claim', 'fixture wrongness', '--json'],
509
+ 'cli-wrongness': ['wrongness', 'validate', 'project', '--json'],
510
510
  'cli-wiki': ['wiki', 'image-ingest', 'test/fixtures/images/one-by-one.png', '--json'],
511
511
  'cli-codex': ['codex', 'compatibility', '--json'],
512
512
  'cli-codex-lb': ['codex-lb', 'metrics', '--json'],
@@ -527,6 +527,7 @@ const SAFE_EXECUTABLE_FIXTURE_ARGS = Object.freeze({
527
527
  'cli-dfix': ['dfix', 'fixture', '--json'],
528
528
  'cli-all-features': ['all-features', 'complete', '--json'],
529
529
  'cli-naruto': ['naruto', 'run', 'fixture', '--agents', '4', '--max-threads', '4', '--json'],
530
+ 'cli-align': ['align', 'prepare', 'fixture modernization contract', '--json'],
530
531
  'route-naruto': ['naruto', 'run', 'fixture', '--agents', '4', '--max-threads', '4', '--json'],
531
532
  'route-qa-loop': { setup: [['qa-loop', 'prepare', 'fixture API QA', '--json']], command: ['qa-loop', 'run', 'latest', '--mock', '--json'] },
532
533
  'route-research': { setup: [['research', 'prepare', 'fixture research topic', '--json']], command: ['research', 'run', 'latest', '--mock', '--json'] },
@@ -534,6 +535,7 @@ const SAFE_EXECUTABLE_FIXTURE_ARGS = Object.freeze({
534
535
  'route-image-ux-review': ['image-ux-review', 'fixture', '--mock', '--json'],
535
536
  'route-computer-use': ['computer-use', 'import-fixture', '--mock', '--json'],
536
537
  'route-dfix': ['dfix', 'fixture', '--json'],
538
+ 'route-align': ['align', 'fixture', '--json'],
537
539
  'route-seo-geo-optimizer': ['seo-geo-optimizer', 'fixture', '--mode', 'geo', '--json'],
538
540
  'route-fast-mode': ['fast-mode', 'status', '--json'],
539
541
  'route-wiki': ['wiki', 'image-ingest', 'test/fixtures/images/one-by-one.png', '--json'],
@@ -735,8 +737,8 @@ function imagegenWiringFeatures() {
735
737
  cli_command_names: ['image-ux-review'],
736
738
  handler_keys: ['image-ux-review', 'ux-review'],
737
739
  dollar_commands: ['$Image-UX-Review', '$UX-Review'],
738
- app_skill_aliases: ['$image-ux-review', '$ux-review'],
739
- skills: ['image-ux-review', 'ux-review', 'imagegen']
740
+ app_skill_aliases: ['$image-ux-review'],
741
+ skills: ['image-ux-review', 'imagegen']
740
742
  }
741
743
  }),
742
744
  baseFeature({
package/dist/core/fsx.js CHANGED
@@ -227,18 +227,22 @@ export async function writeReceiptRotated(p, data, opts = {}) {
227
227
  .slice(keep);
228
228
  await Promise.all(removable.map((row) => fsp.rm(row.file, { force: true }).catch(() => undefined)));
229
229
  }
230
- export async function writeBinaryAtomic(p, data) {
230
+ export async function writeBinaryAtomic(p, data, options = {}) {
231
231
  await ensureDir(path.dirname(p));
232
232
  try {
233
- if ((await fsp.readFile(p)).equals(data))
233
+ if (options.mode === undefined && (await fsp.readFile(p)).equals(data))
234
234
  return;
235
235
  }
236
236
  catch { }
237
237
  const tmp = `${p}.${process.pid}.${randomId(6)}.tmp`;
238
238
  try {
239
- const handle = await fsp.open(tmp, 'w');
239
+ const handle = options.mode === undefined
240
+ ? await fsp.open(tmp, 'w')
241
+ : await fsp.open(tmp, 'w', options.mode);
240
242
  try {
241
243
  await handle.writeFile(data);
244
+ if (options.mode !== undefined)
245
+ await handle.chmod(options.mode);
242
246
  await handle.sync().catch(() => { });
243
247
  }
244
248
  finally {
@@ -0,0 +1,82 @@
1
+ import path from 'node:path';
2
+ import { packageRoot, runProcess, tmpdir } from '../fsx.js';
3
+ export async function selftestCodexCommitHooks() {
4
+ const root = tmpdir();
5
+ const hookBin = path.join(packageRoot(), 'dist', 'bin', 'sks.js');
6
+ const env = { SKS_DISABLE_UPDATE_CHECK: '1' };
7
+ const setup = await runProcess(process.execPath, [hookBin, 'setup', '--install-scope', 'project'], { cwd: root, env, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
8
+ if (setup.code !== 0)
9
+ throw new Error(`selftest failed: commit setup ${setup.code}: ${setup.stderr}`);
10
+ const runHook = (name, payload) => runProcess(process.execPath, [hookBin, 'hook', name], { cwd: root, input: JSON.stringify({ cwd: root, ...payload }), env, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
11
+ const id = 'commit-selftest';
12
+ const hook = await runHook('user-prompt-submit', { conversation_id: id, action: 'codex_git_commit', prompt: 'Generate a git commit message for the staged diff.' });
13
+ if (hook.code !== 0)
14
+ throw new Error(`selftest failed: commit hook ${hook.code}: ${hook.stderr}`);
15
+ const hookJson = JSON.parse(hook.stdout);
16
+ if (hookJson.decision === 'block' || hookJson.hookSpecificOutput?.additionalContext || !String(hookJson.systemMessage || '').includes('git action'))
17
+ throw new Error('selftest failed: commit route bypass');
18
+ const stop = await runHook('stop', { conversation_id: id, last_assistant_message: 'Fix Codex App commit message hook bypass' });
19
+ if (stop.code !== 0)
20
+ throw new Error(`selftest failed: commit stop ${stop.code}: ${stop.stderr}`);
21
+ const stopJson = JSON.parse(stop.stdout);
22
+ if (stopJson.decision === 'block' || !String(stopJson.systemMessage || '').includes('accepted without route finalization'))
23
+ throw new Error('selftest failed: commit stop bypass');
24
+ const commitPushId = 'commit-push-selftest';
25
+ const appCommitPushHook = await runHook('user-prompt-submit', { conversation_id: commitPushId, action: 'Codex App Git Actions Commit and Push', prompt: 'Commit and push changes.' });
26
+ if (appCommitPushHook.code !== 0)
27
+ throw new Error(`selftest failed: app commit-push hook ${appCommitPushHook.code}: ${appCommitPushHook.stderr}`);
28
+ const appCommitPushJson = JSON.parse(appCommitPushHook.stdout);
29
+ if (appCommitPushJson.decision === 'block' || appCommitPushJson.hookSpecificOutput?.additionalContext || !String(appCommitPushJson.systemMessage || '').includes('git action'))
30
+ throw new Error('selftest failed: app commit-push route bypass');
31
+ const appCommitPushStop = await runHook('stop', { conversation_id: commitPushId, last_assistant_message: 'Commit and push complete.' });
32
+ if (appCommitPushStop.code !== 0)
33
+ throw new Error(`selftest failed: app commit-push stop ${appCommitPushStop.code}: ${appCommitPushStop.stderr}`);
34
+ if (JSON.parse(appCommitPushStop.stdout).decision === 'block')
35
+ throw new Error('selftest failed: app commit-push stop bypass');
36
+ const appPushId = 'app-push-selftest';
37
+ const appPushHook = await runHook('user-prompt-submit', { conversation_id: appPushId, metadata: { source: 'codex_app', action: 'Git Actions Push' }, prompt: 'Push changes.' });
38
+ if (appPushHook.code !== 0)
39
+ throw new Error(`selftest failed: app push hook ${appPushHook.code}: ${appPushHook.stderr}`);
40
+ const appPushJson = JSON.parse(appPushHook.stdout);
41
+ if (appPushJson.decision === 'block' || appPushJson.hookSpecificOutput?.additionalContext || !String(appPushJson.systemMessage || '').includes('git action'))
42
+ throw new Error('selftest failed: app push metadata route bypass');
43
+ const appPushStop = await runHook('stop', { conversation_id: appPushId, metadata: { source: 'codex_app', action: 'Git Actions Push' }, last_assistant_message: 'Done.' });
44
+ if (appPushStop.code !== 0)
45
+ throw new Error(`selftest failed: app push stop ${appPushStop.code}: ${appPushStop.stderr}`);
46
+ if (JSON.parse(appPushStop.stdout).decision === 'block')
47
+ throw new Error('selftest failed: app push metadata stop bypass');
48
+ const metadataLightId = 'metadata-light-commit-push-selftest';
49
+ const metadataLightHook = await runHook('user-prompt-submit', { conversation_id: metadataLightId, prompt: 'Commit and push changes.' });
50
+ if (metadataLightHook.code !== 0)
51
+ throw new Error(`selftest failed: metadata-light commit-push hook ${metadataLightHook.code}: ${metadataLightHook.stderr}`);
52
+ const metadataLightJson = JSON.parse(metadataLightHook.stdout);
53
+ if (metadataLightJson.decision === 'block' || metadataLightJson.hookSpecificOutput?.additionalContext || !String(metadataLightJson.systemMessage || '').includes('git action'))
54
+ throw new Error('selftest failed: metadata-light app commit-push route bypass');
55
+ const metadataLightStop = await runHook('stop', { conversation_id: metadataLightId, last_assistant_message: 'Commit and push complete.' });
56
+ if (metadataLightStop.code !== 0)
57
+ throw new Error(`selftest failed: metadata-light commit-push stop ${metadataLightStop.code}: ${metadataLightStop.stderr}`);
58
+ if (JSON.parse(metadataLightStop.stdout).decision === 'block')
59
+ throw new Error('selftest failed: metadata-light commit-push stop bypass');
60
+ const settingsHook = await runHook('user-prompt-submit', { model: 'future-codex-catalog-model', metadata: { source: 'codex_app_settings', feature: 'speed profile' } });
61
+ if (settingsHook.code !== 0)
62
+ throw new Error(`selftest failed: settings hook ${settingsHook.code}: ${settingsHook.stderr}`);
63
+ const settingsJson = JSON.parse(settingsHook.stdout);
64
+ if (settingsJson.decision === 'block' || settingsJson.hookSpecificOutput?.additionalContext || !String(settingsJson.systemMessage || '').includes('settings/profile event ignored'))
65
+ throw new Error('selftest failed: settings/profile event should not route or block');
66
+ const userHook = await runHook('user-prompt-submit', { prompt: '[커밋 메시지를 생성하지 못했습니다.] 코덱스 앱에서 이 버그 수정해줘' });
67
+ if (userHook.code !== 0)
68
+ throw new Error(`selftest failed: user commit hook ${userHook.code}: ${userHook.stderr}`);
69
+ if (!JSON.parse(userHook.stdout).hookSpecificOutput?.additionalContext?.includes('$Naruto route prepared'))
70
+ throw new Error('selftest failed: user prompt route');
71
+ const userCommitPushHook = await runHook('user-prompt-submit', { prompt: '배포하게 커밋하고 푸쉬해줘' });
72
+ if (userCommitPushHook.code !== 0)
73
+ throw new Error(`selftest failed: user commit-push hook ${userCommitPushHook.code}: ${userCommitPushHook.stderr}`);
74
+ const userCommitPushJson = JSON.parse(userCommitPushHook.stdout);
75
+ if (userCommitPushJson.decision === 'block' || userCommitPushJson.hookSpecificOutput?.additionalContext || !String(userCommitPushJson.systemMessage || '').includes('git action'))
76
+ throw new Error('selftest failed: user commit-push prompt should bypass route');
77
+ const userCommitPushStop = await runHook('stop', { last_assistant_message: 'Commit and push complete.' });
78
+ if (userCommitPushStop.code !== 0)
79
+ throw new Error(`selftest failed: user commit-push stop ${userCommitPushStop.code}: ${userCommitPushStop.stderr}`);
80
+ if (JSON.parse(userCommitPushStop.stdout).decision === 'block')
81
+ throw new Error('selftest failed: user commit-push stop bypass');
82
+ }