@jinn-network/client 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +67 -1
  2. package/dist/adapters/mech/adapter.d.ts +19 -1
  3. package/dist/adapters/mech/adapter.js +130 -14
  4. package/dist/adapters/mech/adapter.js.map +1 -1
  5. package/dist/adapters/mech/contracts.d.ts +22 -1
  6. package/dist/adapters/mech/contracts.js +34 -24
  7. package/dist/adapters/mech/contracts.js.map +1 -1
  8. package/dist/adapters/mech/safe.d.ts +1 -1
  9. package/dist/adapters/mech/safe.js +5 -3
  10. package/dist/adapters/mech/safe.js.map +1 -1
  11. package/dist/adapters/mech/types.d.ts +6 -1
  12. package/dist/adapters/mech/types.js.map +1 -1
  13. package/dist/agent/operator-claude.js +8 -0
  14. package/dist/agent/operator-claude.js.map +1 -1
  15. package/dist/api/activity-events-endpoint.d.ts +14 -0
  16. package/dist/api/activity-events-endpoint.js +59 -0
  17. package/dist/api/activity-events-endpoint.js.map +1 -0
  18. package/dist/api/bootstrap-endpoint.d.ts +1 -2
  19. package/dist/api/bootstrap-endpoint.js +42 -24
  20. package/dist/api/bootstrap-endpoint.js.map +1 -1
  21. package/dist/api/codex-doctor-endpoint.d.ts +22 -5
  22. package/dist/api/codex-doctor-endpoint.js +136 -17
  23. package/dist/api/codex-doctor-endpoint.js.map +1 -1
  24. package/dist/api/debug-report-endpoint.d.ts +27 -0
  25. package/dist/api/debug-report-endpoint.js +157 -0
  26. package/dist/api/debug-report-endpoint.js.map +1 -0
  27. package/dist/api/gather-status.d.ts +33 -0
  28. package/dist/api/gather-status.js +211 -26
  29. package/dist/api/gather-status.js.map +1 -1
  30. package/dist/api/hermes-doctor-endpoint.d.ts +15 -7
  31. package/dist/api/hermes-doctor-endpoint.js +56 -19
  32. package/dist/api/hermes-doctor-endpoint.js.map +1 -1
  33. package/dist/api/launcher-status.d.ts +4 -2
  34. package/dist/api/launcher-status.js +11 -10
  35. package/dist/api/launcher-status.js.map +1 -1
  36. package/dist/api/launcher-tasks.d.ts +1 -1
  37. package/dist/api/launcher-tasks.js +12 -8
  38. package/dist/api/launcher-tasks.js.map +1 -1
  39. package/dist/api/operator-artifacts-endpoint.js +73 -6
  40. package/dist/api/operator-artifacts-endpoint.js.map +1 -1
  41. package/dist/api/portfolio-v0-build.d.ts +7 -1
  42. package/dist/api/portfolio-v0-build.js +6 -2
  43. package/dist/api/portfolio-v0-build.js.map +1 -1
  44. package/dist/api/prediction-v1-build.d.ts +6 -0
  45. package/dist/api/prediction-v1-build.js +3 -1
  46. package/dist/api/prediction-v1-build.js.map +1 -1
  47. package/dist/api/server.d.ts +17 -0
  48. package/dist/api/server.js +40 -1
  49. package/dist/api/server.js.map +1 -1
  50. package/dist/api/setup-endpoints.d.ts +0 -9
  51. package/dist/api/setup-endpoints.js +11 -153
  52. package/dist/api/setup-endpoints.js.map +1 -1
  53. package/dist/api/solvernets-endpoints.js +30 -63
  54. package/dist/api/solvernets-endpoints.js.map +1 -1
  55. package/dist/api/status-build.d.ts +115 -2
  56. package/dist/api/status-build.js +47 -11
  57. package/dist/api/status-build.js.map +1 -1
  58. package/dist/api/status-harness-rollup.d.ts +35 -0
  59. package/dist/api/status-harness-rollup.js +45 -0
  60. package/dist/api/status-harness-rollup.js.map +1 -0
  61. package/dist/api/task-runs-build.d.ts +8 -0
  62. package/dist/api/task-runs-build.js +5 -1
  63. package/dist/api/task-runs-build.js.map +1 -1
  64. package/dist/build-info.json +4 -4
  65. package/dist/build-meta.json +1 -1
  66. package/dist/captures/live-publisher.js +24 -4
  67. package/dist/captures/live-publisher.js.map +1 -1
  68. package/dist/captures/publish.d.ts +1 -1
  69. package/dist/chain-read-errors.d.ts +12 -0
  70. package/dist/chain-read-errors.js +26 -1
  71. package/dist/chain-read-errors.js.map +1 -1
  72. package/dist/cli/commands/codedigest-revert-check.d.ts +33 -0
  73. package/dist/cli/commands/codedigest-revert-check.js +249 -0
  74. package/dist/cli/commands/codedigest-revert-check.js.map +1 -0
  75. package/dist/cli/commands/solver-nets.d.ts +1 -0
  76. package/dist/cli/commands/solver-nets.js +177 -22
  77. package/dist/cli/commands/solver-nets.js.map +1 -1
  78. package/dist/cli/commands/solver-plugins-block.d.ts +33 -0
  79. package/dist/cli/commands/solver-plugins-block.js +118 -0
  80. package/dist/cli/commands/solver-plugins-block.js.map +1 -0
  81. package/dist/cli/commands/solver-plugins-feedback.d.ts +72 -0
  82. package/dist/cli/commands/solver-plugins-feedback.js +262 -0
  83. package/dist/cli/commands/solver-plugins-feedback.js.map +1 -0
  84. package/dist/cli/commands/solver-plugins-read.d.ts +54 -0
  85. package/dist/cli/commands/solver-plugins-read.js +259 -0
  86. package/dist/cli/commands/solver-plugins-read.js.map +1 -0
  87. package/dist/cli/commands/solver-plugins.d.ts +35 -0
  88. package/dist/cli/commands/solver-plugins.js +399 -2
  89. package/dist/cli/commands/solver-plugins.js.map +1 -1
  90. package/dist/cli/commands/tasks.js +15 -2
  91. package/dist/cli/commands/tasks.js.map +1 -1
  92. package/dist/cli/index.js +2 -0
  93. package/dist/cli/index.js.map +1 -1
  94. package/dist/cli/task-native-readiness.d.ts +7 -0
  95. package/dist/cli/task-native-readiness.js +7 -5
  96. package/dist/cli/task-native-readiness.js.map +1 -1
  97. package/dist/config.d.ts +183 -232
  98. package/dist/config.js +232 -107
  99. package/dist/config.js.map +1 -1
  100. package/dist/daemon/ai-units-gate.d.ts +54 -0
  101. package/dist/daemon/ai-units-gate.js +82 -0
  102. package/dist/daemon/ai-units-gate.js.map +1 -0
  103. package/dist/daemon/creator.js +13 -0
  104. package/dist/daemon/creator.js.map +1 -1
  105. package/dist/daemon/daemon.d.ts +10 -0
  106. package/dist/daemon/daemon.js +203 -30
  107. package/dist/daemon/daemon.js.map +1 -1
  108. package/dist/daemon/gate-logger.d.ts +9 -0
  109. package/dist/daemon/gate-logger.js +2 -0
  110. package/dist/daemon/gate-logger.js.map +1 -0
  111. package/dist/daemon/jinn-claim-loop.js +22 -4
  112. package/dist/daemon/jinn-claim-loop.js.map +1 -1
  113. package/dist/daemon/readiness-gate.d.ts +1 -4
  114. package/dist/daemon/readiness-gate.js.map +1 -1
  115. package/dist/daemon/spend-cap-gate.d.ts +40 -0
  116. package/dist/daemon/spend-cap-gate.js +46 -0
  117. package/dist/daemon/spend-cap-gate.js.map +1 -0
  118. package/dist/dashboard/assets/index-CzKxvMcU.css +32 -0
  119. package/dist/dashboard/assets/index-yVemxHot.js +351 -0
  120. package/dist/dashboard/index.html +2 -2
  121. package/dist/discovery/http.js +328 -1
  122. package/dist/discovery/http.js.map +1 -1
  123. package/dist/discovery/onchain.js +42 -4
  124. package/dist/discovery/onchain.js.map +1 -1
  125. package/dist/discovery/types.d.ts +129 -0
  126. package/dist/discovery/types.js.map +1 -1
  127. package/dist/discovery/with-fallback.js +27 -0
  128. package/dist/discovery/with-fallback.js.map +1 -1
  129. package/dist/earning/bootstrap.d.ts +8 -3
  130. package/dist/earning/bootstrap.js +36 -13
  131. package/dist/earning/bootstrap.js.map +1 -1
  132. package/dist/earning/safe-adapter.js +23 -11
  133. package/dist/earning/safe-adapter.js.map +1 -1
  134. package/dist/earning/types.d.ts +6 -6
  135. package/dist/earning/viem-clients.d.ts +11 -4
  136. package/dist/earning/viem-clients.js +14 -5
  137. package/dist/earning/viem-clients.js.map +1 -1
  138. package/dist/erc8004/identity.d.ts +19 -3
  139. package/dist/erc8004/identity.js +38 -11
  140. package/dist/erc8004/identity.js.map +1 -1
  141. package/dist/erc8004/index.d.ts +1 -1
  142. package/dist/erc8004/index.js.map +1 -1
  143. package/dist/events/types.d.ts +2 -2
  144. package/dist/harnesses/cost-estimates.d.ts +10 -31
  145. package/dist/harnesses/cost-estimates.js +11 -43
  146. package/dist/harnesses/cost-estimates.js.map +1 -1
  147. package/dist/harnesses/engine/engine.d.ts +28 -4
  148. package/dist/harnesses/engine/engine.js +103 -17
  149. package/dist/harnesses/engine/engine.js.map +1 -1
  150. package/dist/harnesses/engine/persistence.d.ts +21 -4
  151. package/dist/harnesses/engine/persistence.js +43 -6
  152. package/dist/harnesses/engine/persistence.js.map +1 -1
  153. package/dist/harnesses/engine/state.d.ts +9 -0
  154. package/dist/harnesses/engine/state.js +23 -10
  155. package/dist/harnesses/engine/state.js.map +1 -1
  156. package/dist/harnesses/impls/hermes-agent/bootstrap.js +4 -2
  157. package/dist/harnesses/impls/hermes-agent/bootstrap.js.map +1 -1
  158. package/dist/harnesses/impls/hermes-agent/config-builder.d.ts +1 -1
  159. package/dist/harnesses/impls/hermes-agent/config-builder.js +4 -2
  160. package/dist/harnesses/impls/hermes-agent/config-builder.js.map +1 -1
  161. package/dist/harnesses/impls/hermes-agent/harness.d.ts +14 -0
  162. package/dist/harnesses/impls/hermes-agent/harness.js +16 -2
  163. package/dist/harnesses/impls/hermes-agent/harness.js.map +1 -1
  164. package/dist/harnesses/impls/hermes-agent/prompt.d.ts +6 -6
  165. package/dist/harnesses/impls/hermes-agent/prompt.js +6 -6
  166. package/dist/harnesses/impls/learner/adapters/claude-code.d.ts +17 -0
  167. package/dist/harnesses/impls/learner/adapters/claude-code.js +113 -14
  168. package/dist/harnesses/impls/learner/adapters/claude-code.js.map +1 -1
  169. package/dist/harnesses/impls/learner/adapters/codex-code.d.ts +9 -0
  170. package/dist/harnesses/impls/learner/adapters/codex-code.js +30 -8
  171. package/dist/harnesses/impls/learner/adapters/codex-code.js.map +1 -1
  172. package/dist/harnesses/impls/learner/harness.d.ts +24 -0
  173. package/dist/harnesses/impls/learner/harness.js +27 -3
  174. package/dist/harnesses/impls/learner/harness.js.map +1 -1
  175. package/dist/harnesses/impls/learner/harvest.d.ts +1 -1
  176. package/dist/harnesses/impls/learner/harvest.js +23 -5
  177. package/dist/harnesses/impls/learner/harvest.js.map +1 -1
  178. package/dist/harnesses/impls/learner/restoration-patch.d.ts +2 -2
  179. package/dist/harnesses/impls/learner/restoration-patch.js +25 -6
  180. package/dist/harnesses/impls/learner/restoration-patch.js.map +1 -1
  181. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js +21 -1
  182. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js.map +1 -1
  183. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.d.ts +74 -5
  184. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js +103 -32
  185. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js.map +1 -1
  186. package/dist/harnesses/readiness-registry.d.ts +7 -0
  187. package/dist/harnesses/readiness-registry.js +9 -0
  188. package/dist/harnesses/readiness-registry.js.map +1 -1
  189. package/dist/learner/revert-decision.d.ts +59 -0
  190. package/dist/learner/revert-decision.js +53 -0
  191. package/dist/learner/revert-decision.js.map +1 -0
  192. package/dist/learner/revert-stats.d.ts +24 -0
  193. package/dist/learner/revert-stats.js +44 -0
  194. package/dist/learner/revert-stats.js.map +1 -0
  195. package/dist/main.js +177 -104
  196. package/dist/main.js.map +1 -1
  197. package/dist/mcp/get-codedigest-reward.d.ts +13 -0
  198. package/dist/mcp/get-codedigest-reward.js +23 -0
  199. package/dist/mcp/get-codedigest-reward.js.map +1 -0
  200. package/dist/mcp/server.js +23 -0
  201. package/dist/mcp/server.js.map +1 -1
  202. package/dist/observability/debug-report-assemble.d.ts +43 -0
  203. package/dist/observability/debug-report-assemble.js +80 -0
  204. package/dist/observability/debug-report-assemble.js.map +1 -0
  205. package/dist/observability/emit-event.d.ts +9 -2
  206. package/dist/observability/emit-event.js +36 -2
  207. package/dist/observability/emit-event.js.map +1 -1
  208. package/dist/observability/file-logger.d.ts +69 -0
  209. package/dist/observability/file-logger.js +177 -0
  210. package/dist/observability/file-logger.js.map +1 -0
  211. package/dist/observability/redact-secrets.d.ts +65 -0
  212. package/dist/observability/redact-secrets.js +300 -0
  213. package/dist/observability/redact-secrets.js.map +1 -0
  214. package/dist/observability/tar.d.ts +30 -0
  215. package/dist/observability/tar.js +102 -0
  216. package/dist/observability/tar.js.map +1 -0
  217. package/dist/plugins/learner/skills/learn/consolidator-prompt.md +18 -1
  218. package/dist/plugins/learner/skills/learn/promoter-prompt.md +72 -1
  219. package/dist/preflight/pidfile-liveness.d.ts +44 -0
  220. package/dist/preflight/pidfile-liveness.js +103 -0
  221. package/dist/preflight/pidfile-liveness.js.map +1 -0
  222. package/dist/preflight/rpc-network.d.ts +40 -0
  223. package/dist/preflight/rpc-network.js +67 -1
  224. package/dist/preflight/rpc-network.js.map +1 -1
  225. package/dist/rpc/transport.d.ts +109 -0
  226. package/dist/rpc/transport.js +220 -0
  227. package/dist/rpc/transport.js.map +1 -0
  228. package/dist/scripts/donation-consumption-acceptance.js +7 -28
  229. package/dist/scripts/donation-consumption-acceptance.js.map +1 -1
  230. package/dist/scripts/swe-rebench-v2-pytest-missing.json +16 -0
  231. package/dist/solver-nets/prediction-operator-ux.d.ts +1 -2
  232. package/dist/solver-nets/prediction-operator-ux.js +56 -53
  233. package/dist/solver-nets/prediction-operator-ux.js.map +1 -1
  234. package/dist/solver-nets/registry.d.ts +19 -1
  235. package/dist/solver-nets/registry.js +37 -24
  236. package/dist/solver-nets/registry.js.map +1 -1
  237. package/dist/solver-types/_swe-rebench-v2-pool.d.ts +9 -2
  238. package/dist/solver-types/_swe-rebench-v2-pool.js +15 -20
  239. package/dist/solver-types/_swe-rebench-v2-pool.js.map +1 -1
  240. package/dist/solver-types/_swe-rebench-v2-state.d.ts +15 -0
  241. package/dist/solver-types/_swe-rebench-v2-state.js +19 -0
  242. package/dist/solver-types/_swe-rebench-v2-state.js.map +1 -1
  243. package/dist/solver-types/_swe-rebench-v2-validated-pool.d.ts +116 -2
  244. package/dist/solver-types/_swe-rebench-v2-validated-pool.js +296 -21
  245. package/dist/solver-types/_swe-rebench-v2-validated-pool.js.map +1 -1
  246. package/dist/solver-types/swe-rebench-v2-auto.d.ts +20 -11
  247. package/dist/solver-types/swe-rebench-v2-auto.js +64 -19
  248. package/dist/solver-types/swe-rebench-v2-auto.js.map +1 -1
  249. package/dist/solver-types/swe-rebench-v2.d.ts +8 -2
  250. package/dist/solver-types/swe-rebench-v2.js +127 -11
  251. package/dist/solver-types/swe-rebench-v2.js.map +1 -1
  252. package/dist/solvernets/daemon-init.d.ts +1 -1
  253. package/dist/solvernets/daemon-init.js +19 -4
  254. package/dist/solvernets/daemon-init.js.map +1 -1
  255. package/dist/solvernets/launched-record-dispatcher.d.ts +4 -0
  256. package/dist/solvernets/launched-record-dispatcher.js +10 -4
  257. package/dist/solvernets/launched-record-dispatcher.js.map +1 -1
  258. package/dist/solvernets/registry-client-erc8004.js +11 -0
  259. package/dist/solvernets/registry-client-erc8004.js.map +1 -1
  260. package/dist/solvernets/store.d.ts +2 -2
  261. package/dist/spend/ai-units-config.d.ts +39 -0
  262. package/dist/spend/ai-units-config.js +28 -0
  263. package/dist/spend/ai-units-config.js.map +1 -0
  264. package/dist/spend/ai-units.d.ts +89 -0
  265. package/dist/spend/ai-units.js +156 -0
  266. package/dist/spend/ai-units.js.map +1 -0
  267. package/dist/spend/cost-surface-status.d.ts +12 -0
  268. package/dist/spend/cost-surface-status.js +24 -0
  269. package/dist/spend/cost-surface-status.js.map +1 -0
  270. package/dist/spend/credential.d.ts +39 -0
  271. package/dist/spend/credential.js +71 -0
  272. package/dist/spend/credential.js.map +1 -0
  273. package/dist/spend/daemon-config.d.ts +13 -0
  274. package/dist/spend/daemon-config.js +24 -0
  275. package/dist/spend/daemon-config.js.map +1 -0
  276. package/dist/spend/pricing.d.ts +16 -0
  277. package/dist/spend/pricing.js +26 -0
  278. package/dist/spend/pricing.js.map +1 -0
  279. package/dist/spend/record.d.ts +13 -0
  280. package/dist/spend/record.js +36 -0
  281. package/dist/spend/record.js.map +1 -0
  282. package/dist/spend/usage.d.ts +27 -0
  283. package/dist/spend/usage.js +113 -0
  284. package/dist/spend/usage.js.map +1 -0
  285. package/dist/store/store.d.ts +101 -0
  286. package/dist/store/store.js +304 -4
  287. package/dist/store/store.js.map +1 -1
  288. package/dist/trajectory/transcript-parsers/codex-session.d.ts +12 -6
  289. package/dist/trajectory/transcript-parsers/codex-session.js +114 -13
  290. package/dist/trajectory/transcript-parsers/codex-session.js.map +1 -1
  291. package/dist/trajectory/transcript-parsers/types.d.ts +8 -8
  292. package/dist/trajectory/transcript-session-dirs.d.ts +18 -0
  293. package/dist/trajectory/transcript-session-dirs.js +85 -0
  294. package/dist/trajectory/transcript-session-dirs.js.map +1 -0
  295. package/dist/trajectory/transcript-watcher.d.ts +20 -1
  296. package/dist/trajectory/transcript-watcher.js +108 -32
  297. package/dist/trajectory/transcript-watcher.js.map +1 -1
  298. package/dist/tx-retry.d.ts +25 -0
  299. package/dist/tx-retry.js +95 -7
  300. package/dist/tx-retry.js.map +1 -1
  301. package/dist/types/payloads/portfolio-v0.d.ts +3 -3
  302. package/dist/types/payloads/prediction-apy-v0.d.ts +3 -3
  303. package/dist/types/payloads/prediction-v0.d.ts +12 -12
  304. package/package.json +11 -3
  305. package/plugins/learner/skills/learn/consolidator-prompt.md +18 -1
  306. package/plugins/learner/skills/learn/promoter-prompt.md +72 -1
  307. package/plugins/swe-rebench-v2-diffmin/README.md +10 -9
  308. package/plugins/swe-rebench-v2-diffmin/jinn.plugin.json +1 -1
  309. package/plugins/swe-rebench-v2-diffmin/skills/diffmin/SKILL.md +15 -10
  310. package/plugins/swe-rebench-v2-diffmin/skills/test-map/SKILL.md +10 -12
  311. package/plugins/swe-rebench-v2-runtime/.claude-plugin/plugin.json +1 -1
  312. package/plugins/swe-rebench-v2-runtime/.codex-plugin/plugin.json +3 -3
  313. package/plugins/swe-rebench-v2-runtime/README.md +6 -6
  314. package/plugins/swe-rebench-v2-runtime/jinn.plugin.json +2 -3
  315. package/plugins/swe-rebench-v2-runtime/skills/task/SKILL.md +81 -0
  316. package/dist/dashboard/assets/index-BUlE8F3Y.js +0 -330
  317. package/dist/dashboard/assets/index-blqc7eqq.css +0 -32
  318. package/plugins/swe-rebench-v2-runtime/skills/orient/SKILL.md +0 -29
  319. package/plugins/swe-rebench-v2-runtime/skills/plan/SKILL.md +0 -53
@@ -0,0 +1,249 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { mkdtempSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { emitEnvelope } from '../../errors/envelope.js';
7
+ import { loadConfig } from '../../config.js';
8
+ import { createDiscoveryAPI } from '../../discovery/factory.js';
9
+ import { DiscoveryUnavailableError } from '../../discovery/types.js';
10
+ import { hashImplStateDir } from '../../harnesses/freeze.js';
11
+ import { decideRevert, resolveRevertPolicy, } from '../../learner/revert-decision.js';
12
+ const PRODUCTION_DEPS = {
13
+ buildDiscovery: (configPath) => {
14
+ const config = loadConfig(configPath);
15
+ const chainId = config.network === 'testnet' ? 84532 : 8453;
16
+ // Mirror the daemon's floor wiring (solver-plugins.ts discoveryApiFactory):
17
+ // only rpcUrl + chainId are needed for the http primary; the floor is never
18
+ // routed for getCodeDigestRewards (withFallback propagates the error).
19
+ return createDiscoveryAPI(config.discovery ?? { mode: 'onchain' }, {
20
+ rpcUrl: config.rpcUrl,
21
+ chainId,
22
+ });
23
+ },
24
+ };
25
+ /**
26
+ * A commit sha as it appears in the operator's local `improvePromotionsDir`
27
+ * records: 7-64 hex chars (abbreviated through full git object ids). Rejecting
28
+ * anything else BEFORE handing the value to `git archive` closes the
29
+ * argument-injection class where a leading-dash value (`--output=...`,
30
+ * `--remote=...`) is parsed as a git option instead of a tree-ish (#764 M1).
31
+ */
32
+ const COMMIT_SHA_RE = /^[0-9a-fA-F]{7,64}$/;
33
+ export function isValidCommitSha(sha) {
34
+ return COMMIT_SHA_RE.test(sha);
35
+ }
36
+ /**
37
+ * Hash a commit's tree the way production stamps codeDigest: export the tree
38
+ * with `git archive` (no `.git`) and re-hash with the same deterministic hasher
39
+ * `hashImplStateDir` uses, ignoring `.git`. Returns the `sha256:`-prefixed
40
+ * digest the indexer stores. Keeping this in TS means the Consolidator prompt
41
+ * never reimplements the hasher (the single largest correctness risk, #764).
42
+ */
43
+ export async function codeDigestForCommit(implStateDir, sha) {
44
+ if (!isValidCommitSha(sha)) {
45
+ throw new Error(`commit sha must be 7-64 hex chars (got "${sha}")`);
46
+ }
47
+ const exportDir = mkdtempSync(join(tmpdir(), 'cd-export-'));
48
+ try {
49
+ // `--` is an end-of-options separator: even though `sha` is shape-validated
50
+ // above, this is defense-in-depth so `git archive` can never parse a
51
+ // leading-dash value as an option (#764 M1, git argument-injection class).
52
+ const tar = execFileSync('git', ['archive', '--', sha], { cwd: implStateDir, maxBuffer: 1 << 28 });
53
+ execFileSync('tar', ['-x', '-C', exportDir], { input: tar });
54
+ const hex = await hashImplStateDir(exportDir, { ignoreRelPaths: ['.git'] });
55
+ return `sha256:${hex}`;
56
+ }
57
+ finally {
58
+ rmSync(exportDir, { recursive: true, force: true });
59
+ }
60
+ }
61
+ /**
62
+ * Validate the optional numeric CLI flags (#764 M1). Each is only checked when
63
+ * the operator actually provided it (omitted → policy default applies). Returns
64
+ * the first failure, or `undefined` when all provided flags are in range.
65
+ * - min-samples: finite integer >= 1
66
+ * - window: finite integer >= 1
67
+ * - alpha: finite number in the open interval (0, 1)
68
+ */
69
+ export function validateNumericFlags(flags) {
70
+ const isPositiveInt = (n) => Number.isFinite(n) && Number.isInteger(n) && n >= 1;
71
+ if (flags.minSamples !== undefined) {
72
+ const n = Number(flags.minSamples);
73
+ if (!isPositiveInt(n)) {
74
+ return { field: 'min-samples', message: `--min-samples must be a finite integer >= 1 (got "${flags.minSamples}")` };
75
+ }
76
+ }
77
+ if (flags.window !== undefined) {
78
+ const n = Number(flags.window);
79
+ if (!isPositiveInt(n)) {
80
+ return { field: 'window', message: `--window must be a finite integer >= 1 (got "${flags.window}")` };
81
+ }
82
+ }
83
+ if (flags.alpha !== undefined) {
84
+ const n = Number(flags.alpha);
85
+ if (!Number.isFinite(n) || n <= 0 || n >= 1) {
86
+ return { field: 'alpha', message: `--alpha must be a finite number in (0, 1) (got "${flags.alpha}")` };
87
+ }
88
+ }
89
+ return undefined;
90
+ }
91
+ function toAggregate(codeDigest, rows) {
92
+ const found = rows.find((r) => r.codeDigest === codeDigest);
93
+ if (!found)
94
+ return { codeDigest, attempts: 0, passes: 0, passRate: 0 };
95
+ return { codeDigest, attempts: found.attempts, passes: found.passes, passRate: found.passRate };
96
+ }
97
+ export function createCodedigestRevertCheckCommand(deps = PRODUCTION_DEPS) {
98
+ return {
99
+ name: 'codedigest-revert-check',
100
+ summary: 'Decide whether an Improve commit regressed the pass rate (per-codeDigest, #764)',
101
+ helpText: `Usage:
102
+ jinn codedigest-revert-check --code-digest <sha256:...> --parent-code-digest <sha256:...> [options]
103
+ jinn codedigest-revert-check --impl-state-dir <path> --commit <sha> --parent <sha> [options]
104
+
105
+ Options:
106
+ --operator 0x.. Restrict aggregates to attempts this operator Safe claimed
107
+ --solvernet <cid> Restrict to a SolverNet manifest CID
108
+ --min-samples N Minimum indexed attempts per arm (default 30)
109
+ --alpha A Two-sided significance threshold (default 0.05)
110
+ --window N Recent-attempts window per codeDigest (default 200)
111
+ --json Emit JSON (default)
112
+
113
+ Either pass two codeDigests directly, OR pass --impl-state-dir + --commit + --parent
114
+ to have the command export each commit's tree (git archive, no .git) and hash it
115
+ the way production stamps codeDigest — so the caller never reimplements the hasher.
116
+
117
+ Emits a JSON decision: { withCommit, atParent, delta, pValue, significant, recommendRevert, reason }.
118
+ On indexer outage emits recommendRevert=false reason=discovery_unavailable (the Consolidator then skips reverts).`,
119
+ async run(ctx) {
120
+ let parsed;
121
+ try {
122
+ parsed = parseArgs({
123
+ args: ctx.argv,
124
+ options: {
125
+ 'code-digest': { type: 'string' },
126
+ 'parent-code-digest': { type: 'string' },
127
+ 'impl-state-dir': { type: 'string' },
128
+ commit: { type: 'string' },
129
+ parent: { type: 'string' },
130
+ operator: { type: 'string' },
131
+ solvernet: { type: 'string' },
132
+ 'min-samples': { type: 'string' },
133
+ alpha: { type: 'string' },
134
+ window: { type: 'string' },
135
+ config: { type: 'string' },
136
+ json: { type: 'boolean', default: true },
137
+ },
138
+ allowPositionals: false,
139
+ });
140
+ }
141
+ catch (err) {
142
+ emitEnvelope({
143
+ code: 'invalid_invocation',
144
+ message: err instanceof Error ? err.message : String(err),
145
+ exampleCli: 'jinn codedigest-revert-check --code-digest sha256:.. --parent-code-digest sha256:..',
146
+ details: { field: 'flags' },
147
+ }, { writer: ctx.writer, exit: ctx.exit });
148
+ return;
149
+ }
150
+ // Resolve the two codeDigests — either supplied directly or hashed from
151
+ // an impl-state-dir git history.
152
+ let child = parsed.values['code-digest'];
153
+ let parent = parsed.values['parent-code-digest'];
154
+ const implStateDir = parsed.values['impl-state-dir'];
155
+ const commitSha = parsed.values.commit;
156
+ const parentSha = parsed.values.parent;
157
+ if ((!child || !parent) && implStateDir && commitSha && parentSha) {
158
+ // Validate sha shape before any git invocation. A leading-dash value
159
+ // would be parsed by `git archive` as an option, not a tree-ish
160
+ // (#764 M1 argument-injection). Reject with the command's existing
161
+ // invalid_invocation envelope (exit 11) rather than letting it flow
162
+ // into git.
163
+ const badSha = !isValidCommitSha(commitSha)
164
+ ? { field: 'commit', value: commitSha }
165
+ : !isValidCommitSha(parentSha)
166
+ ? { field: 'parent', value: parentSha }
167
+ : undefined;
168
+ if (badSha) {
169
+ emitEnvelope({
170
+ code: 'invalid_invocation',
171
+ message: `--${badSha.field} sha must be 7-64 hex chars (got "${badSha.value}")`,
172
+ exampleCli: 'jinn codedigest-revert-check --impl-state-dir <path> --commit <sha> --parent <sha>',
173
+ details: { field: badSha.field },
174
+ }, { writer: ctx.writer, exit: ctx.exit });
175
+ return;
176
+ }
177
+ try {
178
+ [child, parent] = await Promise.all([
179
+ codeDigestForCommit(implStateDir, commitSha),
180
+ codeDigestForCommit(implStateDir, parentSha),
181
+ ]);
182
+ }
183
+ catch (err) {
184
+ emitEnvelope({
185
+ code: 'invalid_invocation',
186
+ message: `failed to hash commit trees: ${err instanceof Error ? err.message : String(err)}`,
187
+ exampleCli: 'jinn codedigest-revert-check --impl-state-dir <path> --commit <sha> --parent <sha>',
188
+ details: { field: 'impl-state-dir' },
189
+ }, { writer: ctx.writer, exit: ctx.exit });
190
+ return;
191
+ }
192
+ }
193
+ if (!child || !parent) {
194
+ emitEnvelope({
195
+ code: 'invalid_invocation',
196
+ message: 'provide either --code-digest + --parent-code-digest, or --impl-state-dir + --commit + --parent',
197
+ exampleCli: 'jinn codedigest-revert-check --code-digest sha256:.. --parent-code-digest sha256:..',
198
+ details: { field: 'flags' },
199
+ }, { writer: ctx.writer, exit: ctx.exit });
200
+ return;
201
+ }
202
+ // Validate provided numeric flags. `Number('abc')` is NaN, `--alpha 0`
203
+ // silently disables every revert, `--window 0` is nonsensical — reject
204
+ // out-of-range input with the command's existing invalid_invocation
205
+ // envelope (exit 11) rather than letting a bad value flow into the policy.
206
+ const numErr = validateNumericFlags({
207
+ minSamples: parsed.values['min-samples'],
208
+ window: parsed.values.window,
209
+ alpha: parsed.values.alpha,
210
+ });
211
+ if (numErr) {
212
+ emitEnvelope({
213
+ code: 'invalid_invocation',
214
+ message: numErr.message,
215
+ exampleCli: 'jinn codedigest-revert-check --code-digest sha256:.. --parent-code-digest sha256:.. --alpha 0.05 --window 200 --min-samples 30',
216
+ details: { field: numErr.field },
217
+ }, { writer: ctx.writer, exit: ctx.exit });
218
+ return;
219
+ }
220
+ const policy = resolveRevertPolicy({
221
+ ...(parsed.values['min-samples'] ? { minSamplesPerArm: Number(parsed.values['min-samples']) } : {}),
222
+ ...(parsed.values.alpha ? { alpha: Number(parsed.values.alpha) } : {}),
223
+ ...(parsed.values.window ? { recentAttemptsWindow: Number(parsed.values.window) } : {}),
224
+ });
225
+ const discovery = deps.buildDiscovery(parsed.values.config);
226
+ let rows;
227
+ try {
228
+ rows = await discovery.getCodeDigestRewards({
229
+ codeDigests: [child, parent],
230
+ window: policy.recentAttemptsWindow,
231
+ ...(parsed.values.operator ? { operator: parsed.values.operator } : {}),
232
+ ...(parsed.values.solvernet ? { solverNetManifestCid: parsed.values.solvernet } : {}),
233
+ });
234
+ }
235
+ catch (err) {
236
+ if (err instanceof DiscoveryUnavailableError) {
237
+ ctx.writer.write(JSON.stringify({ recommendRevert: false, reason: 'discovery_unavailable', message: err.message }) + '\n');
238
+ return;
239
+ }
240
+ throw err;
241
+ }
242
+ const decision = decideRevert({ withCommit: toAggregate(child, rows), atParent: toAggregate(parent, rows) }, policy);
243
+ ctx.writer.write(JSON.stringify(decision) + '\n');
244
+ },
245
+ };
246
+ }
247
+ const command = createCodedigestRevertCheckCommand();
248
+ export default command;
249
+ //# sourceMappingURL=codedigest-revert-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codedigest-revert-check.js","sourceRoot":"","sources":["../../../src/cli/commands/codedigest-revert-check.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ export declare function resolveValidatePoolInstanceIds(flags: {
4
4
  instancesFile?: string;
5
5
  seedPositive?: boolean;
6
6
  knownBad?: boolean;
7
+ knownPytestMissing?: boolean;
7
8
  }): string[];
8
9
  export declare function describeSweRebenchV2PoolFreshness(opts: {
9
10
  stateDir: string;
@@ -8,9 +8,9 @@ import { emitResult } from '../output.js';
8
8
  import { loadConfig } from '../../config.js';
9
9
  import { buildHarnesses } from '../../harnesses/impls/index.js';
10
10
  import { canonicalHarnessName, CLAUDE_CODE_HARNESS, harnessNameMatches } from '../../harnesses/names.js';
11
- import { loadSolverNets } from '../../solver-nets/registry.js';
11
+ import { findJoinedByName, loadSolverNets, solverTypeFromJoinedContract, } from '../../solver-nets/registry.js';
12
12
  import { buildPredictionOperatorStatus, runPredictionSample, } from '../../solver-nets/prediction-operator-ux.js';
13
- import { EVAL_SEMANTICS_VERSION } from '../../solver-types/_swe-rebench-v2-validated-pool.js';
13
+ import { EVAL_SEMANTICS_VERSION, summarizeValidatedPool, } from '../../solver-types/_swe-rebench-v2-validated-pool.js';
14
14
  import { defaultStateDir as sweRebenchV2DefaultStateDir } from '../../solver-types/swe-rebench-v2.js';
15
15
  const DEFAULT_CONFIG_PATH = join(homedir(), '.jinn-client', 'config.json');
16
16
  // ---------------------------------------------------------------------------
@@ -60,6 +60,9 @@ export function resolveValidatePoolInstanceIds(flags) {
60
60
  if (flags.knownBad) {
61
61
  collected.push(...readInstanceIdFile('swe-rebench-v2-known-bad.json'));
62
62
  }
63
+ if (flags.knownPytestMissing) {
64
+ collected.push(...readInstanceIdFile('swe-rebench-v2-pytest-missing.json'));
65
+ }
63
66
  return Array.from(new Set(collected));
64
67
  }
65
68
  // ---------------------------------------------------------------------------
@@ -135,6 +138,61 @@ function predictionDefault() {
135
138
  taskGenerator: { enabled: true },
136
139
  };
137
140
  }
141
+ /**
142
+ * Project a joined-config entry into the legacy `SolverNetConfig` display
143
+ * shape used by the show/doctor/sample renderers. Mid-migration entries may
144
+ * lack `contract`, in which case `solverType` is reported as `'(unknown)'`.
145
+ */
146
+ function solverNetFromJoined(net) {
147
+ return {
148
+ enabled: true,
149
+ solverType: solverTypeFromJoinedContract(net) ?? '(unknown)',
150
+ ...(net.harness ? { harness: net.harness } : {}),
151
+ plugins: Array.isArray(net.plugins) ? net.plugins : [],
152
+ // Generator ownership is launched-record-driven (issue #421); a joined
153
+ // entry never carries a taskGenerator block of its own.
154
+ taskGenerator: { enabled: false },
155
+ };
156
+ }
157
+ /**
158
+ * Resolve the SolverNet a read-only subverb (show/doctor/sample) should
159
+ * operate against. Per issue #421 F4:
160
+ *
161
+ * 1. Prefer the still-on-disk legacy file shape (`cfg.solverNets[name]`)
162
+ * when present, so display-time sanitization (e.g. promoting
163
+ * `canonicalPlugin.source` into `plugins[]`) keeps working on configs
164
+ * the operator hasn't rewritten yet. The on-disk file is untouched by
165
+ * the in-memory migration; this branch surfaces what's literally on
166
+ * disk.
167
+ * 2. Otherwise consult `loadConfig().joinedSolverNets` — the canonical
168
+ * view for an operator who has rejoined via the SPA (where there is
169
+ * no legacy on-disk block to fall back to).
170
+ * 3. Otherwise surface `undefined` so the caller can emit
171
+ * `Unknown SolverNet: <name>`. The synthetic `predictionDefault()`
172
+ * shim is gone — it was masking the rejoined-via-SPA case.
173
+ */
174
+ function resolveReadOnlySolverNet(configPath, cfg, name) {
175
+ // 1. Legacy on-disk file shape (untouched by in-memory migration). Keeps
176
+ // canonicalPlugin promotion working for operators who haven't re-saved
177
+ // their config yet.
178
+ const legacy = cfg.solverNets?.[name];
179
+ if (legacy)
180
+ return legacy;
181
+ // 2. Joined view — the post-SPA-join authoritative shape.
182
+ try {
183
+ const loaded = loadConfig(configPath);
184
+ const joined = findJoinedByName(loaded.joinedSolverNets, name);
185
+ if (joined)
186
+ return solverNetFromJoined(joined);
187
+ }
188
+ catch {
189
+ // If loadConfig fails (malformed schema, missing file when configPath
190
+ // was explicit, etc.), surface `undefined` so the caller reports the
191
+ // unresolved name rather than throwing through to the operator.
192
+ }
193
+ // 3. Nothing matched.
194
+ return undefined;
195
+ }
138
196
  function sourceOf(entry) {
139
197
  return typeof entry === 'string' ? entry : entry.source;
140
198
  }
@@ -286,6 +344,7 @@ const command = {
286
344
  jinn solver-nets validate-pool swe-rebench-v2 [--limit <n>] [--force]
287
345
  [--instance-id <id>]... [--instances-file <path>]
288
346
  [--seed-positive] [--known-bad]
347
+ [--known-pytest-missing]
289
348
  Run the gold patch of each pool instance through the eval harness
290
349
  and cache which instances are scorable; the generator then posts
291
350
  only those. Requires Docker + \`jinn harnesses enable
@@ -296,8 +355,17 @@ const command = {
296
355
  --known-bad scopes the run to instances in
297
356
  client/scripts/swe-rebench-v2-known-bad.json (gold eval runs;
298
357
  expected to record scorable:false).
358
+ --known-pytest-missing scopes the run to instances in
359
+ client/scripts/swe-rebench-v2-pytest-missing.json (Stage-1 #493
360
+ sample of ungradeable:pytest_missing instances; under v4
361
+ EVAL_SEMANTICS_VERSION + the pytest-install guard these should
362
+ re-validate as scorable:true).
299
363
  Repeatable --instance-id and --instances-file scope the run to
300
364
  a specific subset.
365
+ jinn solver-nets validate-pool-report swe-rebench-v2 [--human|--json]
366
+ Read-only report of the local validated-pool.json: total entries,
367
+ scorable/unscorable counts, histogram by normalised reason, and
368
+ the highest-yield unscorable blocker. Does not run any eval.
301
369
 
302
370
  Output flags:
303
371
  --human Render readable terminal output instead of JSON (supported by
@@ -324,28 +392,24 @@ Output flags:
324
392
  'instances-file': { type: 'string' },
325
393
  'seed-positive': { type: 'boolean' },
326
394
  'known-bad': { type: 'boolean' },
395
+ 'known-pytest-missing': { type: 'boolean' },
327
396
  },
328
397
  });
329
398
  const human = Boolean(parsed.values['human']);
330
399
  const json = Boolean(parsed.values['json']);
331
400
  const [name, arg2] = parsed.positionals;
332
401
  if (!subverb || subverb === 'list') {
402
+ // Issue #421: the legacy `solverNets` block has been retired. `loadConfig`
403
+ // migrates any on-disk legacy entries into joinedSolverNets with
404
+ // synthetic `legacy:<short-name>` keys, so the joined-only iteration
405
+ // here surfaces both modern and migrated entries.
333
406
  const loaded = loadConfig(configPath);
334
- const legacy = Object.entries(loaded.solverNets).map(([netName, net]) => ({
335
- name: netName,
336
- source: 'legacy',
337
- enabled: net.enabled,
338
- solverType: net.solverType,
339
- harness: net.harness,
340
- pluginCount: net.plugins.length,
341
- taskGeneratorEnabled: net.taskGenerator.enabled,
342
- }));
343
407
  const joined = Object.entries(loaded.joinedSolverNets ?? {}).map(([cid, net]) => ({
344
408
  name: net.name ?? cid,
345
409
  source: 'joined',
346
410
  manifestCid: cid,
347
411
  enabled: true,
348
- solverType: 'prediction.v1',
412
+ solverType: net.contract ? `${net.contract.id}.${net.contract.version}` : '(unknown)',
349
413
  harness: net.harness,
350
414
  pluginCount: (net.plugins ?? []).length,
351
415
  taskGeneratorEnabled: false,
@@ -353,21 +417,89 @@ Output flags:
353
417
  const value = {
354
418
  verb: 'solver-nets list',
355
419
  configPath,
356
- solverNets: [...legacy, ...joined],
420
+ solverNets: joined,
357
421
  };
358
422
  emit(ctx, value, human, json, (v) => {
359
423
  const list = v;
360
424
  if (list.solverNets.length === 0)
361
425
  return 'No SolverNets configured.';
362
426
  return list.solverNets
363
- .map((n) => {
364
- const base = `${n.name} ${n.enabled ? 'enabled' : 'disabled'} ${n.solverType} harness=${n.harness ?? '(default)'} plugins=${n.pluginCount} generator=${n.taskGeneratorEnabled ? 'on' : 'off'} source=${n.source}`;
365
- return 'manifestCid' in n ? `${base} cid=${n.manifestCid}` : base;
366
- })
427
+ .map((n) => `${n.name} ${n.enabled ? 'enabled' : 'disabled'} ${n.solverType} ` +
428
+ `harness=${n.harness ?? '(default)'} plugins=${n.pluginCount} ` +
429
+ `generator=${n.taskGeneratorEnabled ? 'on' : 'off'} source=${n.source} cid=${n.manifestCid}`)
367
430
  .join('\n');
368
431
  });
369
432
  return;
370
433
  }
434
+ if (subverb === 'validate-pool-report') {
435
+ if (name !== 'swe-rebench-v2') {
436
+ fail(ctx, 'solver-nets validate-pool-report currently supports only `swe-rebench-v2`');
437
+ return;
438
+ }
439
+ const stateDir = ctx.env['JINN_SWE_REBENCH_V2_STATE_DIR'] ?? sweRebenchV2DefaultStateDir();
440
+ const path = join(stateDir, 'validated-pool.json');
441
+ let raw;
442
+ try {
443
+ raw = JSON.parse(readFileSync(path, 'utf8'));
444
+ }
445
+ catch {
446
+ fail(ctx, `validated-pool.json is absent or unreadable at ${path}`);
447
+ return;
448
+ }
449
+ let summary;
450
+ try {
451
+ summary = summarizeValidatedPool(raw);
452
+ }
453
+ catch (err) {
454
+ fail(ctx, `validated-pool.json is malformed: ${err.message}`);
455
+ return;
456
+ }
457
+ const evalSemanticsVersion = raw.evalSemanticsVersion ?? 'unknown';
458
+ const freshness = await describeSweRebenchV2PoolFreshness({ stateDir });
459
+ const highestYieldBlocker = summary.byReason.find((b) => b.reason !== 'gold-patch-resolves')?.reason ?? null;
460
+ const value = {
461
+ verb: 'solver-nets validate-pool-report',
462
+ solverNet: 'swe-rebench-v2',
463
+ evalSemanticsVersion,
464
+ currentEvalSemanticsVersion: EVAL_SEMANTICS_VERSION,
465
+ stateDir,
466
+ ...summary,
467
+ highestYieldBlocker,
468
+ freshness,
469
+ };
470
+ emit(ctx, value, human, json, (v) => {
471
+ const s = v;
472
+ const lines = [
473
+ `SolverNet: swe-rebench-v2`,
474
+ ` stateDir: ${s.stateDir}`,
475
+ ` evalSemanticsVersion (file): ${s.evalSemanticsVersion}`,
476
+ ` evalSemanticsVersion (current): ${s.currentEvalSemanticsVersion}`,
477
+ ` total entries: ${s.totalEntries}`,
478
+ ` scorable: ${s.scorable}`,
479
+ ` unscorable: ${s.unscorable}`,
480
+ `Histogram (by normalised reason, descending):`,
481
+ ];
482
+ for (const bucket of s.byReason) {
483
+ lines.push(` ${String(bucket.count).padStart(6)} ${bucket.reason}`);
484
+ }
485
+ if (s.highestYieldBlocker) {
486
+ lines.push(`Highest-yield unscorable blocker: ${s.highestYieldBlocker}`);
487
+ }
488
+ // #806: gold-patch-not-resolved entries whose PASS_TO_PASS tests broke
489
+ // (p2p_broke > 0) are usually an environment/setup problem, not a
490
+ // non-resolving gold patch — a chase-able, recoverable capacity blocker.
491
+ const p2pBroke = s.byReason.find((b) => b.reason === 'gold-patch-not-resolved:p2p-broke');
492
+ if (p2pBroke && p2pBroke.count > 0) {
493
+ lines.push(`Candidate capacity blocker: ${p2pBroke.count} gold-patch-not-resolved entr${p2pBroke.count === 1 ? 'y' : 'ies'} broke PASS_TO_PASS (environment/setup problem, likely recoverable — investigate before treating as non-resolving patches)`);
494
+ }
495
+ if (s.freshness.status === 'stale') {
496
+ lines.push(`Freshness: stale — ${s.freshness.reason}`);
497
+ lines.push(` run: ${s.freshness.cli}`);
498
+ }
499
+ return lines.join('\n');
500
+ });
501
+ return;
502
+ }
371
503
  if (subverb === 'validate-pool') {
372
504
  if (name !== 'swe-rebench-v2') {
373
505
  fail(ctx, 'solver-nets validate-pool currently supports only `swe-rebench-v2`');
@@ -401,6 +533,7 @@ Output flags:
401
533
  instancesFile: parsed.values['instances-file'],
402
534
  seedPositive: Boolean(parsed.values['seed-positive']),
403
535
  knownBad: Boolean(parsed.values['known-bad']),
536
+ knownPytestMissing: Boolean(parsed.values['known-pytest-missing']),
404
537
  });
405
538
  process.stderr.write('[validate-pool] loading the SWE-rebench v2 pool…\n');
406
539
  let poolTasks = await loadSweRebenchV2Pool();
@@ -428,11 +561,33 @@ Output flags:
428
561
  fail(ctx, `solver-nets ${subverb} requires <name>`);
429
562
  return;
430
563
  }
431
- const solverNets = ensureSolverNets(cfg);
432
- if (name === 'prediction' && !solverNets[name]) {
433
- solverNets[name] = predictionDefault();
564
+ const isReadOnlySubverb = subverb === 'show' || subverb === 'doctor' || subverb === 'sample';
565
+ if (!isReadOnlySubverb) {
566
+ // Issue #421: these subverbs still edit the legacy solverNets file shape
567
+ // for one upgrade cycle. The daemon's loadConfig migrates any on-disk
568
+ // legacy entries into joinedSolverNets on next start; the canonical join
569
+ // flow is the SPA (Operator > SolverNets).
570
+ process.stderr.write(`[solver-nets] WARNING: subverb '${subverb}' edits the legacy solverNets ` +
571
+ `file shape (issue #421). The daemon auto-migrates this on next load; ` +
572
+ `re-join via the SPA (Operator > SolverNets) to replace synthetic legacy:* ` +
573
+ `keys with real manifest CIDs.\n`);
574
+ }
575
+ // Resolve the SolverNet to operate on. Read-only subverbs prefer the
576
+ // joined view (issue #421 F4) so an operator who has rejoined via the
577
+ // SPA sees their real entry rather than a synthetic predictionDefault()
578
+ // stub. Mutation subverbs keep the legacy on-disk shape because they
579
+ // write through it for the one-cycle bridge.
580
+ let net;
581
+ if (isReadOnlySubverb) {
582
+ net = resolveReadOnlySolverNet(configPath, cfg, name);
583
+ }
584
+ else {
585
+ const solverNets = ensureSolverNets(cfg);
586
+ if (name === 'prediction' && !solverNets[name]) {
587
+ solverNets[name] = predictionDefault();
588
+ }
589
+ net = solverNets[name];
434
590
  }
435
- const net = solverNets[name];
436
591
  if (!net) {
437
592
  fail(ctx, `Unknown SolverNet: ${name}`);
438
593
  return;
@@ -445,7 +600,7 @@ Output flags:
445
600
  if (subverb === 'doctor') {
446
601
  if (net.solverType === 'prediction.v1') {
447
602
  const loaded = loadConfig(configPath);
448
- const status = await buildPredictionOperatorStatus({ config: loaded, configPath, name });
603
+ const status = await buildPredictionOperatorStatus({ config: loaded, configPath });
449
604
  emit(ctx, { verb: 'solver-nets doctor', ...status }, human, json, renderPredictionStatusHuman);
450
605
  return;
451
606
  }