@openrig/cli 0.4.2 → 0.4.4

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 (499) hide show
  1. package/daemon/assets/guidance/openrig-start.md +6 -0
  2. package/daemon/assets/plugins/openrig-core/hooks/scripts/activity-relay.cjs +42 -2
  3. package/daemon/assets/plugins/openrig-core/skills/mission-slice-sop/SKILL.md +115 -0
  4. package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +12 -12
  5. package/daemon/dist/adapters/codex-runtime-adapter.d.ts +46 -0
  6. package/daemon/dist/adapters/codex-runtime-adapter.d.ts.map +1 -1
  7. package/daemon/dist/adapters/codex-runtime-adapter.js +167 -3
  8. package/daemon/dist/adapters/codex-runtime-adapter.js.map +1 -1
  9. package/daemon/dist/adapters/tmux.d.ts +31 -0
  10. package/daemon/dist/adapters/tmux.d.ts.map +1 -1
  11. package/daemon/dist/adapters/tmux.js +62 -0
  12. package/daemon/dist/adapters/tmux.js.map +1 -1
  13. package/daemon/dist/build-info.d.ts +12 -0
  14. package/daemon/dist/build-info.d.ts.map +1 -0
  15. package/daemon/dist/build-info.js +11 -0
  16. package/daemon/dist/build-info.js.map +1 -0
  17. package/daemon/dist/db/migrations/045_resume_verification.d.ts +29 -0
  18. package/daemon/dist/db/migrations/045_resume_verification.d.ts.map +1 -0
  19. package/daemon/dist/db/migrations/045_resume_verification.js +34 -0
  20. package/daemon/dist/db/migrations/045_resume_verification.js.map +1 -0
  21. package/daemon/dist/db/migrations/046_seat_identity_verdicts.d.ts +41 -0
  22. package/daemon/dist/db/migrations/046_seat_identity_verdicts.d.ts.map +1 -0
  23. package/daemon/dist/db/migrations/046_seat_identity_verdicts.js +56 -0
  24. package/daemon/dist/db/migrations/046_seat_identity_verdicts.js.map +1 -0
  25. package/daemon/dist/db/migrations/047_events_node_type_index.d.ts +48 -0
  26. package/daemon/dist/db/migrations/047_events_node_type_index.d.ts.map +1 -0
  27. package/daemon/dist/db/migrations/047_events_node_type_index.js +52 -0
  28. package/daemon/dist/db/migrations/047_events_node_type_index.js.map +1 -0
  29. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts +18 -0
  30. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts.map +1 -0
  31. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js +22 -0
  32. package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js.map +1 -0
  33. package/daemon/dist/domain/activity-endpoint.d.ts +25 -0
  34. package/daemon/dist/domain/activity-endpoint.d.ts.map +1 -0
  35. package/daemon/dist/domain/activity-endpoint.js +91 -0
  36. package/daemon/dist/domain/activity-endpoint.js.map +1 -0
  37. package/daemon/dist/domain/claim-service.d.ts +36 -0
  38. package/daemon/dist/domain/claim-service.d.ts.map +1 -1
  39. package/daemon/dist/domain/claim-service.js +90 -0
  40. package/daemon/dist/domain/claim-service.js.map +1 -1
  41. package/daemon/dist/domain/claude-compaction-enforcer.d.ts +57 -0
  42. package/daemon/dist/domain/claude-compaction-enforcer.d.ts.map +1 -1
  43. package/daemon/dist/domain/claude-compaction-enforcer.js +149 -3
  44. package/daemon/dist/domain/claude-compaction-enforcer.js.map +1 -1
  45. package/daemon/dist/domain/event-loop-monitor.d.ts +100 -0
  46. package/daemon/dist/domain/event-loop-monitor.d.ts.map +1 -0
  47. package/daemon/dist/domain/event-loop-monitor.js +127 -0
  48. package/daemon/dist/domain/event-loop-monitor.js.map +1 -0
  49. package/daemon/dist/domain/feed/attention-aggregator.d.ts +31 -0
  50. package/daemon/dist/domain/feed/attention-aggregator.d.ts.map +1 -0
  51. package/daemon/dist/domain/feed/attention-aggregator.js +119 -0
  52. package/daemon/dist/domain/feed/attention-aggregator.js.map +1 -0
  53. package/daemon/dist/domain/files/file-write-service.d.ts +11 -2
  54. package/daemon/dist/domain/files/file-write-service.d.ts.map +1 -1
  55. package/daemon/dist/domain/files/file-write-service.js +77 -0
  56. package/daemon/dist/domain/files/file-write-service.js.map +1 -1
  57. package/daemon/dist/domain/gate-predicate.d.ts +29 -0
  58. package/daemon/dist/domain/gate-predicate.d.ts.map +1 -0
  59. package/daemon/dist/domain/gate-predicate.js +73 -0
  60. package/daemon/dist/domain/gate-predicate.js.map +1 -0
  61. package/daemon/dist/domain/graph-projection.d.ts +7 -1
  62. package/daemon/dist/domain/graph-projection.d.ts.map +1 -1
  63. package/daemon/dist/domain/graph-projection.js +19 -1
  64. package/daemon/dist/domain/graph-projection.js.map +1 -1
  65. package/daemon/dist/domain/hosts/fanout-contract.d.ts +33 -0
  66. package/daemon/dist/domain/hosts/fanout-contract.d.ts.map +1 -0
  67. package/daemon/dist/domain/hosts/fanout-contract.js +23 -0
  68. package/daemon/dist/domain/hosts/fanout-contract.js.map +1 -0
  69. package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts +49 -0
  70. package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts.map +1 -0
  71. package/daemon/dist/domain/hosts/hosts-registry-reader.js +165 -0
  72. package/daemon/dist/domain/hosts/hosts-registry-reader.js.map +1 -0
  73. package/daemon/dist/domain/hosts/remote-daemon-http.d.ts +35 -0
  74. package/daemon/dist/domain/hosts/remote-daemon-http.d.ts.map +1 -0
  75. package/daemon/dist/domain/hosts/remote-daemon-http.js +105 -0
  76. package/daemon/dist/domain/hosts/remote-daemon-http.js.map +1 -0
  77. package/daemon/dist/domain/human-route-enforcer.d.ts +71 -0
  78. package/daemon/dist/domain/human-route-enforcer.d.ts.map +1 -0
  79. package/daemon/dist/domain/human-route-enforcer.js +80 -0
  80. package/daemon/dist/domain/human-route-enforcer.js.map +1 -0
  81. package/daemon/dist/domain/mission-control/audit-browse.d.ts +8 -0
  82. package/daemon/dist/domain/mission-control/audit-browse.d.ts.map +1 -1
  83. package/daemon/dist/domain/mission-control/audit-browse.js +19 -0
  84. package/daemon/dist/domain/mission-control/audit-browse.js.map +1 -1
  85. package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts +1 -1
  86. package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts.map +1 -1
  87. package/daemon/dist/domain/mission-control/mission-control-action-log.js +4 -0
  88. package/daemon/dist/domain/mission-control/mission-control-action-log.js.map +1 -1
  89. package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js +1 -1
  90. package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js.map +1 -1
  91. package/daemon/dist/domain/mission-control/mission-control-read-layer.js +1 -1
  92. package/daemon/dist/domain/mission-control/mission-control-read-layer.js.map +1 -1
  93. package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts +28 -0
  94. package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts.map +1 -1
  95. package/daemon/dist/domain/mission-control/mission-control-write-contract.js +125 -0
  96. package/daemon/dist/domain/mission-control/mission-control-write-contract.js.map +1 -1
  97. package/daemon/dist/domain/node-inventory.d.ts +7 -1
  98. package/daemon/dist/domain/node-inventory.d.ts.map +1 -1
  99. package/daemon/dist/domain/node-inventory.js +84 -5
  100. package/daemon/dist/domain/node-inventory.js.map +1 -1
  101. package/daemon/dist/domain/periodic-snapshot-scheduler.d.ts +4 -0
  102. package/daemon/dist/domain/periodic-snapshot-scheduler.d.ts.map +1 -1
  103. package/daemon/dist/domain/periodic-snapshot-scheduler.js +11 -0
  104. package/daemon/dist/domain/periodic-snapshot-scheduler.js.map +1 -1
  105. package/daemon/dist/domain/policies/idle-gate-qitem.d.ts +14 -0
  106. package/daemon/dist/domain/policies/idle-gate-qitem.d.ts.map +1 -0
  107. package/daemon/dist/domain/policies/idle-gate-qitem.js +114 -0
  108. package/daemon/dist/domain/policies/idle-gate-qitem.js.map +1 -0
  109. package/daemon/dist/domain/ps-projection.d.ts +28 -1
  110. package/daemon/dist/domain/ps-projection.d.ts.map +1 -1
  111. package/daemon/dist/domain/ps-projection.js +39 -0
  112. package/daemon/dist/domain/ps-projection.js.map +1 -1
  113. package/daemon/dist/domain/queue-repository.d.ts +25 -1
  114. package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
  115. package/daemon/dist/domain/queue-repository.js +111 -7
  116. package/daemon/dist/domain/queue-repository.js.map +1 -1
  117. package/daemon/dist/domain/restore-orchestrator.d.ts +8 -0
  118. package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
  119. package/daemon/dist/domain/restore-orchestrator.js +109 -27
  120. package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
  121. package/daemon/dist/domain/restore-plan-preview.d.ts +25 -1
  122. package/daemon/dist/domain/restore-plan-preview.d.ts.map +1 -1
  123. package/daemon/dist/domain/restore-plan-preview.js +72 -4
  124. package/daemon/dist/domain/restore-plan-preview.js.map +1 -1
  125. package/daemon/dist/domain/resume-metadata-refresher.d.ts +44 -2
  126. package/daemon/dist/domain/resume-metadata-refresher.d.ts.map +1 -1
  127. package/daemon/dist/domain/resume-metadata-refresher.js +92 -6
  128. package/daemon/dist/domain/resume-metadata-refresher.js.map +1 -1
  129. package/daemon/dist/domain/resume-token-capture.d.ts +50 -0
  130. package/daemon/dist/domain/resume-token-capture.d.ts.map +1 -0
  131. package/daemon/dist/domain/resume-token-capture.js +63 -0
  132. package/daemon/dist/domain/resume-token-capture.js.map +1 -0
  133. package/daemon/dist/domain/review/brief-spine.d.ts +19 -0
  134. package/daemon/dist/domain/review/brief-spine.d.ts.map +1 -0
  135. package/daemon/dist/domain/review/brief-spine.js +86 -0
  136. package/daemon/dist/domain/review/brief-spine.js.map +1 -0
  137. package/daemon/dist/domain/review/compose.d.ts +228 -0
  138. package/daemon/dist/domain/review/compose.d.ts.map +1 -0
  139. package/daemon/dist/domain/review/compose.js +773 -0
  140. package/daemon/dist/domain/review/compose.js.map +1 -0
  141. package/daemon/dist/domain/review/freeze.d.ts +31 -0
  142. package/daemon/dist/domain/review/freeze.d.ts.map +1 -0
  143. package/daemon/dist/domain/review/freeze.js +257 -0
  144. package/daemon/dist/domain/review/freeze.js.map +1 -0
  145. package/daemon/dist/domain/review/gather.d.ts +100 -0
  146. package/daemon/dist/domain/review/gather.d.ts.map +1 -0
  147. package/daemon/dist/domain/review/gather.js +676 -0
  148. package/daemon/dist/domain/review/gather.js.map +1 -0
  149. package/daemon/dist/domain/review/types.d.ts +258 -0
  150. package/daemon/dist/domain/review/types.d.ts.map +1 -0
  151. package/daemon/dist/domain/review/types.js +35 -0
  152. package/daemon/dist/domain/review/types.js.map +1 -0
  153. package/daemon/dist/domain/rig-status-compose.d.ts +62 -0
  154. package/daemon/dist/domain/rig-status-compose.d.ts.map +1 -0
  155. package/daemon/dist/domain/rig-status-compose.js +149 -0
  156. package/daemon/dist/domain/rig-status-compose.js.map +1 -0
  157. package/daemon/dist/domain/rig-teardown.d.ts +3 -1
  158. package/daemon/dist/domain/rig-teardown.d.ts.map +1 -1
  159. package/daemon/dist/domain/rig-teardown.js +4 -19
  160. package/daemon/dist/domain/rig-teardown.js.map +1 -1
  161. package/daemon/dist/domain/route-timing-recorder.d.ts +42 -0
  162. package/daemon/dist/domain/route-timing-recorder.d.ts.map +1 -0
  163. package/daemon/dist/domain/route-timing-recorder.js +62 -0
  164. package/daemon/dist/domain/route-timing-recorder.js.map +1 -0
  165. package/daemon/dist/domain/scope/scope-approve.d.ts +64 -0
  166. package/daemon/dist/domain/scope/scope-approve.d.ts.map +1 -0
  167. package/daemon/dist/domain/scope/scope-approve.js +153 -0
  168. package/daemon/dist/domain/scope/scope-approve.js.map +1 -0
  169. package/daemon/dist/domain/scope/scope-audit.d.ts +11 -1
  170. package/daemon/dist/domain/scope/scope-audit.d.ts.map +1 -1
  171. package/daemon/dist/domain/scope/scope-audit.js +203 -2
  172. package/daemon/dist/domain/scope/scope-audit.js.map +1 -1
  173. package/daemon/dist/domain/seat-handover-service.d.ts +50 -7
  174. package/daemon/dist/domain/seat-handover-service.d.ts.map +1 -1
  175. package/daemon/dist/domain/seat-handover-service.js +273 -33
  176. package/daemon/dist/domain/seat-handover-service.js.map +1 -1
  177. package/daemon/dist/domain/seat-identity-reconciler.d.ts +59 -0
  178. package/daemon/dist/domain/seat-identity-reconciler.d.ts.map +1 -0
  179. package/daemon/dist/domain/seat-identity-reconciler.js +207 -0
  180. package/daemon/dist/domain/seat-identity-reconciler.js.map +1 -0
  181. package/daemon/dist/domain/seat-identity-store.d.ts +27 -0
  182. package/daemon/dist/domain/seat-identity-store.d.ts.map +1 -0
  183. package/daemon/dist/domain/seat-identity-store.js +106 -0
  184. package/daemon/dist/domain/seat-identity-store.js.map +1 -0
  185. package/daemon/dist/domain/seat-switch-client-service.d.ts +57 -0
  186. package/daemon/dist/domain/seat-switch-client-service.d.ts.map +1 -0
  187. package/daemon/dist/domain/seat-switch-client-service.js +168 -0
  188. package/daemon/dist/domain/seat-switch-client-service.js.map +1 -0
  189. package/daemon/dist/domain/self-attach-service.d.ts +8 -0
  190. package/daemon/dist/domain/self-attach-service.d.ts.map +1 -1
  191. package/daemon/dist/domain/self-attach-service.js +20 -7
  192. package/daemon/dist/domain/self-attach-service.js.map +1 -1
  193. package/daemon/dist/domain/session-registry.d.ts +35 -4
  194. package/daemon/dist/domain/session-registry.d.ts.map +1 -1
  195. package/daemon/dist/domain/session-registry.js +89 -10
  196. package/daemon/dist/domain/session-registry.js.map +1 -1
  197. package/daemon/dist/domain/session-transport.d.ts +8 -1
  198. package/daemon/dist/domain/session-transport.d.ts.map +1 -1
  199. package/daemon/dist/domain/session-transport.js +143 -37
  200. package/daemon/dist/domain/session-transport.js.map +1 -1
  201. package/daemon/dist/domain/startup-orchestrator.d.ts.map +1 -1
  202. package/daemon/dist/domain/startup-orchestrator.js +34 -2
  203. package/daemon/dist/domain/startup-orchestrator.js.map +1 -1
  204. package/daemon/dist/domain/startup-proof.d.ts +63 -0
  205. package/daemon/dist/domain/startup-proof.d.ts.map +1 -0
  206. package/daemon/dist/domain/startup-proof.js +165 -0
  207. package/daemon/dist/domain/startup-proof.js.map +1 -0
  208. package/daemon/dist/domain/successor-session-launcher.d.ts +103 -0
  209. package/daemon/dist/domain/successor-session-launcher.d.ts.map +1 -0
  210. package/daemon/dist/domain/successor-session-launcher.js +246 -0
  211. package/daemon/dist/domain/successor-session-launcher.js.map +1 -0
  212. package/daemon/dist/domain/topology/multi-rig-launcher.d.ts +49 -0
  213. package/daemon/dist/domain/topology/multi-rig-launcher.d.ts.map +1 -0
  214. package/daemon/dist/domain/topology/multi-rig-launcher.js +132 -0
  215. package/daemon/dist/domain/topology/multi-rig-launcher.js.map +1 -0
  216. package/daemon/dist/domain/topology/remote-up-leaf.d.ts +29 -0
  217. package/daemon/dist/domain/topology/remote-up-leaf.d.ts.map +1 -0
  218. package/daemon/dist/domain/topology/remote-up-leaf.js +60 -0
  219. package/daemon/dist/domain/topology/remote-up-leaf.js.map +1 -0
  220. package/daemon/dist/domain/topology/topology-manifest.d.ts +34 -0
  221. package/daemon/dist/domain/topology/topology-manifest.d.ts.map +1 -0
  222. package/daemon/dist/domain/topology/topology-manifest.js +195 -0
  223. package/daemon/dist/domain/topology/topology-manifest.js.map +1 -0
  224. package/daemon/dist/domain/types.d.ts +86 -0
  225. package/daemon/dist/domain/types.d.ts.map +1 -1
  226. package/daemon/dist/domain/types.js +8 -0
  227. package/daemon/dist/domain/types.js.map +1 -1
  228. package/daemon/dist/domain/up-command-router.d.ts +1 -1
  229. package/daemon/dist/domain/up-command-router.d.ts.map +1 -1
  230. package/daemon/dist/domain/up-command-router.js +34 -2
  231. package/daemon/dist/domain/up-command-router.js.map +1 -1
  232. package/daemon/dist/domain/user-settings/settings-store.d.ts +17 -0
  233. package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
  234. package/daemon/dist/domain/user-settings/settings-store.js +109 -0
  235. package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
  236. package/daemon/dist/domain/watchdog-jobs-repository.d.ts +2 -2
  237. package/daemon/dist/domain/watchdog-jobs-repository.d.ts.map +1 -1
  238. package/daemon/dist/domain/watchdog-jobs-repository.js +4 -0
  239. package/daemon/dist/domain/watchdog-jobs-repository.js.map +1 -1
  240. package/daemon/dist/domain/watchdog-policy-engine.d.ts.map +1 -1
  241. package/daemon/dist/domain/watchdog-policy-engine.js +19 -0
  242. package/daemon/dist/domain/watchdog-policy-engine.js.map +1 -1
  243. package/daemon/dist/domain/workspace/default-workspace-scaffold.d.ts.map +1 -1
  244. package/daemon/dist/domain/workspace/default-workspace-scaffold.js +51 -6
  245. package/daemon/dist/domain/workspace/default-workspace-scaffold.js.map +1 -1
  246. package/daemon/dist/domain/workspace/workspace-doctor.d.ts +11 -0
  247. package/daemon/dist/domain/workspace/workspace-doctor.d.ts.map +1 -1
  248. package/daemon/dist/domain/workspace/workspace-doctor.js +82 -0
  249. package/daemon/dist/domain/workspace/workspace-doctor.js.map +1 -1
  250. package/daemon/dist/index.d.ts.map +1 -1
  251. package/daemon/dist/index.js +18 -1
  252. package/daemon/dist/index.js.map +1 -1
  253. package/daemon/dist/routes/activity.d.ts.map +1 -1
  254. package/daemon/dist/routes/activity.js +26 -0
  255. package/daemon/dist/routes/activity.js.map +1 -1
  256. package/daemon/dist/routes/agent-images.d.ts.map +1 -1
  257. package/daemon/dist/routes/agent-images.js +139 -0
  258. package/daemon/dist/routes/agent-images.js.map +1 -1
  259. package/daemon/dist/routes/compaction.d.ts +17 -0
  260. package/daemon/dist/routes/compaction.d.ts.map +1 -0
  261. package/daemon/dist/routes/compaction.js +125 -0
  262. package/daemon/dist/routes/compaction.js.map +1 -0
  263. package/daemon/dist/routes/config.d.ts.map +1 -1
  264. package/daemon/dist/routes/config.js +15 -6
  265. package/daemon/dist/routes/config.js.map +1 -1
  266. package/daemon/dist/routes/files.d.ts.map +1 -1
  267. package/daemon/dist/routes/files.js +45 -1
  268. package/daemon/dist/routes/files.js.map +1 -1
  269. package/daemon/dist/routes/mission-control.d.ts.map +1 -1
  270. package/daemon/dist/routes/mission-control.js +67 -2
  271. package/daemon/dist/routes/mission-control.js.map +1 -1
  272. package/daemon/dist/routes/queue.d.ts +8 -2
  273. package/daemon/dist/routes/queue.d.ts.map +1 -1
  274. package/daemon/dist/routes/queue.js +44 -4
  275. package/daemon/dist/routes/queue.js.map +1 -1
  276. package/daemon/dist/routes/restore-check.d.ts +10 -0
  277. package/daemon/dist/routes/restore-check.d.ts.map +1 -1
  278. package/daemon/dist/routes/restore-check.js +46 -37
  279. package/daemon/dist/routes/restore-check.js.map +1 -1
  280. package/daemon/dist/routes/review.d.ts +3 -0
  281. package/daemon/dist/routes/review.d.ts.map +1 -0
  282. package/daemon/dist/routes/review.js +169 -0
  283. package/daemon/dist/routes/review.js.map +1 -0
  284. package/daemon/dist/routes/rigs.d.ts.map +1 -1
  285. package/daemon/dist/routes/rigs.js +70 -1
  286. package/daemon/dist/routes/rigs.js.map +1 -1
  287. package/daemon/dist/routes/scope-approve.d.ts +3 -0
  288. package/daemon/dist/routes/scope-approve.d.ts.map +1 -0
  289. package/daemon/dist/routes/scope-approve.js +55 -0
  290. package/daemon/dist/routes/scope-approve.js.map +1 -0
  291. package/daemon/dist/routes/scope-audit.d.ts.map +1 -1
  292. package/daemon/dist/routes/scope-audit.js +28 -1
  293. package/daemon/dist/routes/scope-audit.js.map +1 -1
  294. package/daemon/dist/routes/seat.d.ts.map +1 -1
  295. package/daemon/dist/routes/seat.js +53 -2
  296. package/daemon/dist/routes/seat.js.map +1 -1
  297. package/daemon/dist/routes/sessions.d.ts.map +1 -1
  298. package/daemon/dist/routes/sessions.js +44 -9
  299. package/daemon/dist/routes/sessions.js.map +1 -1
  300. package/daemon/dist/routes/slices.d.ts.map +1 -1
  301. package/daemon/dist/routes/slices.js +49 -9
  302. package/daemon/dist/routes/slices.js.map +1 -1
  303. package/daemon/dist/routes/snapshots.d.ts.map +1 -1
  304. package/daemon/dist/routes/snapshots.js +16 -1
  305. package/daemon/dist/routes/snapshots.js.map +1 -1
  306. package/daemon/dist/routes/terminal-ws.d.ts.map +1 -1
  307. package/daemon/dist/routes/terminal-ws.js +58 -21
  308. package/daemon/dist/routes/terminal-ws.js.map +1 -1
  309. package/daemon/dist/routes/transport.d.ts.map +1 -1
  310. package/daemon/dist/routes/transport.js +12 -5
  311. package/daemon/dist/routes/transport.js.map +1 -1
  312. package/daemon/dist/routes/up.d.ts.map +1 -1
  313. package/daemon/dist/routes/up.js +84 -0
  314. package/daemon/dist/routes/up.js.map +1 -1
  315. package/daemon/dist/routes/workspace.js +2 -2
  316. package/daemon/dist/server.d.ts +33 -0
  317. package/daemon/dist/server.d.ts.map +1 -1
  318. package/daemon/dist/server.js +41 -1
  319. package/daemon/dist/server.js.map +1 -1
  320. package/daemon/dist/startup.d.ts +1 -0
  321. package/daemon/dist/startup.d.ts.map +1 -1
  322. package/daemon/dist/startup.js +108 -20
  323. package/daemon/dist/startup.js.map +1 -1
  324. package/daemon/docs/reference/agent-startup-guide.md +1 -1
  325. package/daemon/docs/reference/product-factory-vps-runbook.md +132 -0
  326. package/daemon/docs/reference/sdlc-conventions.md +187 -0
  327. package/daemon/specs/agents/conveyor/builder/agent.yaml +1 -1
  328. package/daemon/specs/agents/conveyor/builder/guidance/role.md +1 -0
  329. package/daemon/specs/agents/conveyor/lead/agent.yaml +1 -1
  330. package/daemon/specs/agents/conveyor/lead/guidance/role.md +1 -0
  331. package/daemon/specs/agents/conveyor/planner/agent.yaml +1 -1
  332. package/daemon/specs/agents/conveyor/planner/guidance/role.md +1 -0
  333. package/daemon/specs/agents/conveyor/reviewer/agent.yaml +1 -1
  334. package/daemon/specs/agents/conveyor/reviewer/guidance/role.md +1 -0
  335. package/daemon/specs/agents/design/product-designer/agent.yaml +1 -1
  336. package/daemon/specs/agents/design/product-designer/guidance/role.md +1 -0
  337. package/daemon/specs/agents/development/implementer/agent.yaml +1 -1
  338. package/daemon/specs/agents/development/implementer/guidance/role.md +1 -0
  339. package/daemon/specs/agents/development/qa/agent.yaml +1 -1
  340. package/daemon/specs/agents/development/qa/guidance/role.md +1 -0
  341. package/daemon/specs/agents/orchestration/orchestrator/agent.yaml +1 -1
  342. package/daemon/specs/agents/orchestration/orchestrator/guidance/role.md +1 -0
  343. package/daemon/specs/agents/product-management/pm/agent.yaml +1 -0
  344. package/daemon/specs/agents/product-management/pm/guidance/role.md +13 -0
  345. package/daemon/specs/agents/review/independent-reviewer/agent.yaml +1 -1
  346. package/daemon/specs/agents/review/independent-reviewer/guidance/role.md +1 -0
  347. package/daemon/specs/agents/shared/agent.yaml +2 -0
  348. package/daemon/specs/agents/shared/skills/core/mission-slice-sop/SKILL.md +115 -0
  349. package/daemon/specs/agents/shared/skills/core/openrig-operator/SKILL.md +4 -4
  350. package/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md +69 -23
  351. package/daemon/specs/agents/shared/skills/pods/orchestration-team/SKILL.md +3 -3
  352. package/daemon/specs/rigs/focused/pm-team/CULTURE.md +4 -0
  353. package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +1 -1
  354. package/daemon/specs/rigs/launch/kernel/agents/operator/agent/startup/context.md +1 -1
  355. package/daemon/specs/rigs/preview/product-team/CULTURE.md +4 -0
  356. package/dist/build-info.d.ts +8 -0
  357. package/dist/build-info.d.ts.map +1 -0
  358. package/dist/build-info.js +11 -0
  359. package/dist/build-info.js.map +1 -0
  360. package/dist/commands/compact.d.ts +15 -0
  361. package/dist/commands/compact.d.ts.map +1 -0
  362. package/dist/commands/compact.js +62 -0
  363. package/dist/commands/compact.js.map +1 -0
  364. package/dist/commands/config-init-workspace.d.ts.map +1 -1
  365. package/dist/commands/config-init-workspace.js +20 -4
  366. package/dist/commands/config-init-workspace.js.map +1 -1
  367. package/dist/commands/daemon.d.ts.map +1 -1
  368. package/dist/commands/daemon.js +18 -2
  369. package/dist/commands/daemon.js.map +1 -1
  370. package/dist/commands/doctor.js +2 -2
  371. package/dist/commands/doctor.js.map +1 -1
  372. package/dist/commands/expand.js +1 -1
  373. package/dist/commands/expand.js.map +1 -1
  374. package/dist/commands/file.d.ts +7 -0
  375. package/dist/commands/file.d.ts.map +1 -0
  376. package/dist/commands/file.js +77 -0
  377. package/dist/commands/file.js.map +1 -0
  378. package/dist/commands/fork.d.ts +4 -0
  379. package/dist/commands/fork.d.ts.map +1 -0
  380. package/dist/commands/fork.js +101 -0
  381. package/dist/commands/fork.js.map +1 -0
  382. package/dist/commands/host.d.ts +34 -0
  383. package/dist/commands/host.d.ts.map +1 -0
  384. package/dist/commands/host.js +448 -0
  385. package/dist/commands/host.js.map +1 -0
  386. package/dist/commands/launch.d.ts.map +1 -1
  387. package/dist/commands/launch.js +34 -4
  388. package/dist/commands/launch.js.map +1 -1
  389. package/dist/commands/proof.d.ts +54 -0
  390. package/dist/commands/proof.d.ts.map +1 -0
  391. package/dist/commands/proof.js +311 -0
  392. package/dist/commands/proof.js.map +1 -0
  393. package/dist/commands/ps.d.ts +41 -7
  394. package/dist/commands/ps.d.ts.map +1 -1
  395. package/dist/commands/ps.js +357 -135
  396. package/dist/commands/ps.js.map +1 -1
  397. package/dist/commands/queue.d.ts +6 -0
  398. package/dist/commands/queue.d.ts.map +1 -1
  399. package/dist/commands/queue.js +154 -4
  400. package/dist/commands/queue.js.map +1 -1
  401. package/dist/commands/reconcile-session.d.ts.map +1 -1
  402. package/dist/commands/reconcile-session.js +2 -2
  403. package/dist/commands/reconcile-session.js.map +1 -1
  404. package/dist/commands/restore-check.d.ts.map +1 -1
  405. package/dist/commands/restore-check.js +93 -0
  406. package/dist/commands/restore-check.js.map +1 -1
  407. package/dist/commands/scope.d.ts.map +1 -1
  408. package/dist/commands/scope.js +191 -4
  409. package/dist/commands/scope.js.map +1 -1
  410. package/dist/commands/seat.d.ts +17 -0
  411. package/dist/commands/seat.d.ts.map +1 -1
  412. package/dist/commands/seat.js +145 -32
  413. package/dist/commands/seat.js.map +1 -1
  414. package/dist/commands/send.d.ts.map +1 -1
  415. package/dist/commands/send.js +161 -11
  416. package/dist/commands/send.js.map +1 -1
  417. package/dist/commands/start.d.ts +5 -0
  418. package/dist/commands/start.d.ts.map +1 -1
  419. package/dist/commands/start.js.map +1 -1
  420. package/dist/commands/startup-proof.d.ts +24 -0
  421. package/dist/commands/startup-proof.d.ts.map +1 -0
  422. package/dist/commands/startup-proof.js +100 -0
  423. package/dist/commands/startup-proof.js.map +1 -0
  424. package/dist/commands/unclaim.js +2 -2
  425. package/dist/commands/unclaim.js.map +1 -1
  426. package/dist/commands/up.d.ts +2 -0
  427. package/dist/commands/up.d.ts.map +1 -1
  428. package/dist/commands/up.js +79 -2
  429. package/dist/commands/up.js.map +1 -1
  430. package/dist/commands/watchdog.js +2 -2
  431. package/dist/commands/watchdog.js.map +1 -1
  432. package/dist/commands/workspace.js +6 -6
  433. package/dist/commands/workspace.js.map +1 -1
  434. package/dist/config-store.d.ts +4 -0
  435. package/dist/config-store.d.ts.map +1 -1
  436. package/dist/config-store.js +68 -0
  437. package/dist/config-store.js.map +1 -1
  438. package/dist/cross-host-executor.d.ts +1 -0
  439. package/dist/cross-host-executor.d.ts.map +1 -1
  440. package/dist/cross-host-executor.js +4 -1
  441. package/dist/cross-host-executor.js.map +1 -1
  442. package/dist/daemon-lifecycle.d.ts +23 -0
  443. package/dist/daemon-lifecycle.d.ts.map +1 -1
  444. package/dist/daemon-lifecycle.js +54 -6
  445. package/dist/daemon-lifecycle.js.map +1 -1
  446. package/dist/host-registry.d.ts +9 -0
  447. package/dist/host-registry.d.ts.map +1 -1
  448. package/dist/host-registry.js +32 -2
  449. package/dist/host-registry.js.map +1 -1
  450. package/dist/index.d.ts +5 -0
  451. package/dist/index.d.ts.map +1 -1
  452. package/dist/index.js +15 -1
  453. package/dist/index.js.map +1 -1
  454. package/dist/lib/codex-auth.d.ts +24 -0
  455. package/dist/lib/codex-auth.d.ts.map +1 -1
  456. package/dist/lib/codex-auth.js +89 -17
  457. package/dist/lib/codex-auth.js.map +1 -1
  458. package/dist/lib/file-transfer.d.ts +89 -0
  459. package/dist/lib/file-transfer.d.ts.map +1 -0
  460. package/dist/lib/file-transfer.js +300 -0
  461. package/dist/lib/file-transfer.js.map +1 -0
  462. package/dist/lib/hosts/fanout-contract.d.ts +33 -0
  463. package/dist/lib/hosts/fanout-contract.d.ts.map +1 -0
  464. package/dist/lib/hosts/fanout-contract.js +23 -0
  465. package/dist/lib/hosts/fanout-contract.js.map +1 -0
  466. package/dist/lib/scope/scope-audit.d.ts +11 -1
  467. package/dist/lib/scope/scope-audit.d.ts.map +1 -1
  468. package/dist/lib/scope/scope-audit.js +203 -2
  469. package/dist/lib/scope/scope-audit.js.map +1 -1
  470. package/dist/lib/scope/templates.d.ts +1 -0
  471. package/dist/lib/scope/templates.d.ts.map +1 -1
  472. package/dist/lib/scope/templates.js +9 -0
  473. package/dist/lib/scope/templates.js.map +1 -1
  474. package/dist/lib/scope-templates/backlog-deprecation.md +16 -0
  475. package/dist/lib/scope-templates/backlog-tech-debt.md +16 -0
  476. package/dist/lib/scope-templates/bug-fix.md +16 -0
  477. package/dist/lib/scope-templates/implementation-prd.md +33 -0
  478. package/dist/lib/scope-templates/mission-placeholder.md +41 -0
  479. package/dist/lib/scope-templates/mission-progress.md +2 -0
  480. package/dist/lib/scope-templates/mission-release.md +41 -0
  481. package/dist/lib/scope-templates/placeholder.md +12 -4
  482. package/dist/lib/scope-templates/proof.md +6 -0
  483. package/dist/lib/scope-templates/release-feature.md +14 -6
  484. package/dist/lib/scope-templates/research.md +16 -0
  485. package/dist/lib/scope-templates/slice-progress.md +2 -0
  486. package/dist/openrig-compat.d.ts +19 -0
  487. package/dist/openrig-compat.d.ts.map +1 -1
  488. package/dist/openrig-compat.js +31 -1
  489. package/dist/openrig-compat.js.map +1 -1
  490. package/dist/version.d.ts +5 -0
  491. package/dist/version.d.ts.map +1 -1
  492. package/dist/version.js +11 -1
  493. package/dist/version.js.map +1 -1
  494. package/package.json +1 -1
  495. package/ui/dist/assets/index-D69ZhNIr.js +598 -0
  496. package/ui/dist/assets/index-DJYun-8d.css +32 -0
  497. package/ui/dist/index.html +23 -2
  498. package/ui/dist/assets/index-CnqRX7A3.css +0 -32
  499. package/ui/dist/assets/index-CtL9jXFt.js +0 -582
@@ -55,6 +55,7 @@ const NUMERIC_OPERATORS = [">=", "<=", ">", "<", "="];
55
55
  // rig-level only; `rigName` is the alias and exists at both levels (per the
56
56
  // closed `rig ps trust and scale-safety` slice at openrig 0a9fb43).
57
57
  const ALLOWED_RIG_FIELDS = new Set([
58
+ "attentionCount",
58
59
  "rigId",
59
60
  "name",
60
61
  "rigName",
@@ -79,6 +80,7 @@ const ALLOWED_NODE_FIELDS = new Set([
79
80
  "sessionStatus",
80
81
  "startupStatus",
81
82
  "restoreOutcome",
83
+ "oriented",
82
84
  "lifecycleState",
83
85
  "tmuxAttachCommand",
84
86
  "resumeCommand",
@@ -195,6 +197,45 @@ function parseFields(input, allowed, level) {
195
197
  }
196
198
  return fields;
197
199
  }
200
+ function parsePsControls(opts) {
201
+ let effectiveFilter = opts.filter;
202
+ if (opts.active) {
203
+ if (effectiveFilter) {
204
+ return {
205
+ error: `--active and --filter cannot be combined. ` +
206
+ `--active is sugar for --filter agentActivity.state=running. ` +
207
+ `Pick one form, or compose by upgrading to --filter directly.`,
208
+ };
209
+ }
210
+ effectiveFilter = "agentActivity.state=running";
211
+ }
212
+ let parsedFilter = null;
213
+ if (effectiveFilter) {
214
+ const result = parseFilter(effectiveFilter);
215
+ if ("error" in result)
216
+ return result;
217
+ parsedFilter = result;
218
+ }
219
+ const limit = opts.limit !== undefined ? Number(opts.limit) : null;
220
+ if (limit !== null && (!Number.isInteger(limit) || limit < 0)) {
221
+ return { error: `--limit must be a non-negative integer; got '${opts.limit}'` };
222
+ }
223
+ let fields = null;
224
+ if (opts.fields !== undefined) {
225
+ const fieldsLevel = opts.nodes ? "nodes" : "rig";
226
+ const fieldsAllowed = fieldsLevel === "nodes" ? ALLOWED_NODE_FIELDS : ALLOWED_RIG_FIELDS;
227
+ const fieldsResult = parseFields(opts.fields, fieldsAllowed, fieldsLevel);
228
+ if ("error" in fieldsResult)
229
+ return fieldsResult;
230
+ fields = fieldsResult;
231
+ }
232
+ return {
233
+ parsedFilter,
234
+ limit,
235
+ fields,
236
+ useEnvelope: parsedFilter !== null || limit !== null || fields !== null || opts.summary === true,
237
+ };
238
+ }
198
239
  function applyRigFilter(entries, filter) {
199
240
  return entries.filter((e) => {
200
241
  if (filter.key === "status")
@@ -257,6 +298,65 @@ function applyNodeFilter(entries, filter) {
257
298
  // OPR.0.3.3.19 - build the /api/ps path, opting archived rigs back in when
258
299
  // --include-archived is set. Default (no flag) leaves the daemon's
259
300
  // archived-excluding default in force.
301
+ function fanOutNodesError() {
302
+ return [
303
+ "rig ps --all-hosts/--hosts --nodes: per-node fan-out requires the FULL explicit ladder.",
304
+ " rig ps --all-hosts --nodes -A (fleet nodes per host, projected rows)",
305
+ " rig ps --all-hosts --nodes -A --full (complete per-node records — the last rung)",
306
+ "One rig's seats on one host: rig ps --host <id> --nodes --rig <name>.",
307
+ ].join("\n");
308
+ }
309
+ /**
310
+ * OPR.0.4.4.21 FR-2/FR-3 — THE centralized disclosure-ladder validator.
311
+ * Runs BEFORE the local/HTTP-host/SSH/fan-out dispatch split so every path
312
+ * obeys one grammar (qa1 plan-review: remote mode bypassed the session-rig
313
+ * injection and fanned wide). Principle (arch-ratified): IMPLICIT SCOPE
314
+ * DEFAULTS DON'T CROSS HOST BOUNDARIES — the session-rig default has no
315
+ * stable referent on a remote host (a same-named rig there would silently
316
+ * misresolve), so remote per-node views are explicit-or-error.
317
+ * Returns the teaching error text, or null when the invocation is valid.
318
+ */
319
+ export function validatePsLadder(opts, callerRig) {
320
+ const isFanOut = !!(opts.allHosts || opts.hosts);
321
+ const isSingleRemote = !!opts.host;
322
+ // FR-3: -A/--all-rigs has exactly ONE meaning — the --nodes fleet widener.
323
+ if (opts.allRigs && !opts.nodes) {
324
+ return [
325
+ "rig ps -A: '-A/--all-rigs' now has exactly one meaning — the --nodes fleet widener.",
326
+ "The consolidated all-rigs view IS the default: just run 'rig ps'.",
327
+ "Fleet nodes: rig ps --nodes -A (add --full for complete per-node records).",
328
+ "Archived history: rig ps --include-archived.",
329
+ ].join("\n");
330
+ }
331
+ // FR-5: multi-host fan-out is rollup-only UNLESS the full explicit
332
+ // ladder is requested — `--nodes -A` per host (`--full` for complete
333
+ // records), exactly as the PRD writes it. Anything less explicit under
334
+ // --nodes errors; never silent rig-tier data under a --nodes flag.
335
+ if (opts.nodes && isFanOut && !opts.allRigs) {
336
+ return fanOutNodesError();
337
+ }
338
+ // FR-2: --nodes always names its scope — session default locally,
339
+ // explicit --rig / -A everywhere else. Never an implicit fan-out.
340
+ if (opts.nodes && !opts.rig && !opts.allRigs) {
341
+ if (isFanOut) {
342
+ return fanOutNodesError();
343
+ }
344
+ if (isSingleRemote) {
345
+ return [
346
+ `rig ps --host ${opts.host} --nodes: no target — the local session's rig is not a remote scope`,
347
+ "(implicit scope defaults don't cross host boundaries; a same-named remote rig would silently misresolve).",
348
+ `Name one: rig ps --host ${opts.host} --nodes --rig <name>, or that host's fleet explicitly: rig ps --host ${opts.host} --nodes -A.`,
349
+ ].join("\n");
350
+ }
351
+ if (!callerRig) {
352
+ return [
353
+ "rig ps --nodes: no target — outside a managed session there is no current rig to default to.",
354
+ "Name one: rig ps --nodes --rig <name>, or go fleet-wide explicitly: rig ps --nodes -A.",
355
+ ].join("\n");
356
+ }
357
+ }
358
+ return null;
359
+ }
260
360
  function psApiPath(opts) {
261
361
  return opts.includeArchived ? "/api/ps?includeArchived=true" : "/api/ps";
262
362
  }
@@ -268,6 +368,12 @@ function selectFields(entries, fields) {
268
368
  return out;
269
369
  });
270
370
  }
371
+ function selectFanOutFields(entries, fields) {
372
+ return entries.map((e) => {
373
+ const [selected] = selectFields([e], fields);
374
+ return { ...selected, hostId: e.hostId };
375
+ });
376
+ }
271
377
  function needsAttention(node) {
272
378
  return node.lifecycleState === "attention_required"
273
379
  || node.startupStatus === "attention_required"
@@ -290,6 +396,8 @@ function compactNodeProjection(nodes) {
290
396
  // lifecycle + session/startup state.
291
397
  sessionStatus: n.sessionStatus,
292
398
  startupStatus: n.startupStatus,
399
+ // OPR.0.4.3.06 — challenge-verified orientation, distinct from ready.
400
+ oriented: n.oriented ?? "n-a",
293
401
  lifecycleState: n.lifecycleState,
294
402
  // activity (state always; short reason only when attention — keep it lean).
295
403
  agentActivity: attention
@@ -370,22 +478,39 @@ function abbrevNodeLifecycle(state) {
370
478
  return "—";
371
479
  }
372
480
  /**
373
- * `rig ps` — list running rigs and optionally their nodes.
481
+ * `rig ps` — the consolidated fleet map + explicit disclosure ladder.
374
482
  *
375
- * L3-followup adds CLI-side shaping flags (`--limit`, `--fields`, `--summary`,
376
- * `--filter`, `--full`) for context-window-safe output on large hosts. Default
377
- * `rig ps` (human) truncates to ~50 rigs with an explicit footer naming totals
378
- * and the `--full` opt-out. Default `rig ps --json` keeps the bare-array shape
379
- * for back-compat; the truncation/envelope shape only applies when at least
380
- * one of `--limit`/`--summary`/`--fields`/`--filter` is specified.
483
+ * OPR.0.4.4.21: the default is ALL active rigs, one compact O(rigs) row each
484
+ * (the current-rig-only default is retired). The token-safety invariant: no
485
+ * invocation returns per-node detail across all rigs unless explicitly
486
+ * flagged (--nodes -A), and even then rows are projected unless --full.
487
+ * Default `rig ps --json` keeps the bare-array shape for back-compat; the
488
+ * truncation/envelope shape only applies when at least one of
489
+ * `--limit`/`--summary`/`--fields`/`--filter` is specified.
381
490
  */
382
491
  export function psCommand(depsOverride) {
383
492
  const cmd = new Command("ps")
384
493
  .description("List rigs and their status")
385
494
  .addHelpText("after", `
386
- Default: current-rig scope (derived from OPENRIG_SESSION_NAME's @<rig> suffix).
387
- Shows ALL states (stopped, recoverable, attention, idle non-running IS the
388
- actionable signal for topology/readiness). Use -A/--all-rigs for fleet breadth.
495
+ Default (OPR.0.4.4.21): ALL active rigs, ONE compact row each — O(rigs), never
496
+ a fleet node fan-out — plus the host rollup line ("N rigs · M seats · K need
497
+ attention"), the archived/stopped count line, and the drill-ladder footer.
498
+ STATED contract: default --json is a bare array of ALL non-archived rigs
499
+ INCLUDING stopped ones (existing keys preserved; additive attentionCount);
500
+ only the HUMAN table folds stopped rigs into the count line.
501
+
502
+ The disclosure ladder (each heavier view is an explicit step):
503
+ rig ps the consolidated map (this default)
504
+ rig ps --rig <name> one rig's detail
505
+ rig ps --nodes per-node, CURRENT rig (session default, local only)
506
+ rig ps --nodes --rig <name> per-node, named rig
507
+ rig ps --nodes -A fleet nodes, projected rows
508
+ rig ps --nodes -A --full complete per-node records (the only full fan-out)
509
+
510
+ -A/--all-rigs has exactly ONE meaning: the --nodes fleet widener. Bare -A
511
+ errors (all-rigs IS the default; archived history stays behind
512
+ --include-archived). The session-rig default never crosses host boundaries:
513
+ remote --nodes requires an explicit --rig or -A.
389
514
 
390
515
  Compact defaults: 'rig ps --nodes' shows a compact summary per node
391
516
  (rig, session, lifecycle, activity state, reason when attention, queue counts,
@@ -399,11 +524,10 @@ null even with --full; fetch the full recovery guidance + currentUsage from the
399
524
  single-node detail (/api/rigs/:rigId/nodes/:logicalId) or 'rig whoami'.
400
525
 
401
526
  Examples:
402
- rig ps Current rig summary (compact)
403
- rig ps -A All rigs (fleet breadth)
404
- rig ps --full Current rig, full detail
405
- rig ps --json Current rig, compact JSON
406
- rig ps -A --json All rigs, compact JSON
527
+ rig ps All active rigs, one row each + rollup
528
+ rig ps --rig <name> One rig's detail
529
+ rig ps --full All rigs, no table truncation
530
+ rig ps --json All non-archived rigs, bare JSON array
407
531
  rig ps --json --limit 20 Bounded JSON envelope
408
532
  rig ps --json --summary Aggregate-only JSON
409
533
  rig ps --json --fields rigName,status,lifecycleState
@@ -423,12 +547,16 @@ Examples:
423
547
  rig ps --nodes --running Same as --active
424
548
  rig ps --nodes --filter agentActivity.state=running
425
549
  Same as --active (the explicit form)
426
- rig ps --host vm-claude-test --nodes --json Remote host (no current-rig default)
550
+ rig ps --host vm-1 --nodes --rig <name> --json Remote host per-node (explicit target required)
551
+ rig ps --all-hosts Per-host O(rigs) rollups (AggregatedPayload JSON)
427
552
  rig ps --include-archived Include archived rigs (marked with *); hidden by default
428
553
 
429
- --rig <name> overrides the current-rig default. -A/--all-rigs disables it.
430
- --session <name> filters within the effective rig scope; use -A if the target
431
- session is in a different rig.
554
+ --rig <name> scopes to one rig. -A/--all-rigs is ONLY the --nodes fleet widener.
555
+ --session <name> filters within the effective rig scope; use --nodes -A if the
556
+ target session is in a different rig.
557
+ Multi-host fan-out (--all-hosts/--hosts) is rollup-only by default; the full
558
+ explicit ladder (--all-hosts --nodes -A, --full for complete records) fans out
559
+ per-node with hostId-stamped projected rows.
432
560
 
433
561
  --active/--running narrow to agentActivity.state=running. Cannot combine with --filter.
434
562
 
@@ -436,7 +564,8 @@ session is in a different rig.
436
564
  contextUsage.percent, contextUsage.state. Other keys are rejected.
437
565
 
438
566
  --fields accepts (rig-level): rigId, name, rigName, nodeCount, runningCount,
439
- activeCount, hasWorkCount, status, lifecycleState, uptime, latestSnapshot.
567
+ activeCount, hasWorkCount, attentionCount, status, lifecycleState, uptime,
568
+ latestSnapshot.
440
569
  --fields accepts (node-level, with --nodes): rigId, rigName, logicalId, podId,
441
570
  podNamespace, canonicalSessionName, nodeKind, runtime, sessionStatus,
442
571
  startupStatus, restoreOutcome, lifecycleState, tmuxAttachCommand,
@@ -475,15 +604,49 @@ Exit codes:
475
604
  if (opts.running)
476
605
  opts.active = true;
477
606
  const isRemote = !!(opts.host || opts.allHosts || opts.hosts);
478
- if (!opts.allRigs && !opts.rig && !isRemote) {
479
- const sessionName = readOpenRigEnv("OPENRIG_SESSION_NAME", "RIGGED_SESSION_NAME");
480
- const callerRig = sessionName ? extractRigName(sessionName) : undefined;
481
- if (callerRig)
482
- opts.rig = callerRig;
607
+ // OPR.0.4.4.21 FR-1: the rig tier is consolidated ALL-ACTIVE-RIGS by
608
+ // default (the current-rig-only default is RETIRED — it hid running
609
+ // rigs from the operator's field of view). The session-rig default
610
+ // now applies ONLY to the node tier (FR-2's scoped --nodes), and
611
+ // ONLY locally (the ladder validator enforces explicit-or-error on
612
+ // every remote path).
613
+ const sessionName = readOpenRigEnv("OPENRIG_SESSION_NAME", "RIGGED_SESSION_NAME");
614
+ const callerRig = sessionName ? extractRigName(sessionName) : undefined;
615
+ const ladderError = validatePsLadder(opts, callerRig);
616
+ if (ladderError) {
617
+ console.error(ladderError);
618
+ process.exitCode = 1;
619
+ return;
620
+ }
621
+ if (opts.nodes && !opts.allRigs && !opts.rig && !isRemote && callerRig) {
622
+ opts.rig = callerRig;
483
623
  }
484
624
  const deps = getDepsF();
625
+ // OPR.0.4.4.21 rev1-r2 fixback: the shared shaping controls
626
+ // (--active/--filter/--limit/--fields/--summary) parse + validate
627
+ // BEFORE any dispatch — local, single-host, or fan-out — so every
628
+ // path honors the same composition contract and rejections stay
629
+ // pre-HTTP on remote paths too.
630
+ // Parse shared composition controls once up front so local, single-host,
631
+ // and fan-out paths reject malformed --filter/--limit/--fields before
632
+ // any HTTP call and apply the same shaping semantics.
633
+ const controls = parsePsControls(opts);
634
+ if ("error" in controls) {
635
+ console.error(controls.error);
636
+ process.exitCode = 1;
637
+ return;
638
+ }
639
+ const { parsedFilter, limit, fields, useEnvelope } = controls;
485
640
  if (opts.allHosts || opts.hosts) {
486
- await runFanOutPs(opts, deps);
641
+ // AggregatedPayload is the closed fan-out contract (items + hosts).
642
+ // Do not add an ad-hoc summary member here; teach the per-host form.
643
+ if (opts.summary) {
644
+ console.error("rig ps --all-hosts/--hosts --summary: summary does not compose with the merged fan-out payload.\n" +
645
+ "Summarize one host: rig ps --host <id> --summary; or drop --summary for the merged AggregatedPayload.");
646
+ process.exitCode = 1;
647
+ return;
648
+ }
649
+ await runFanOutPs(opts, deps, { parsedFilter, limit, fields });
487
650
  return;
488
651
  }
489
652
  if (opts.host) {
@@ -496,73 +659,31 @@ Exit codes:
496
659
  process.exitCode = 1;
497
660
  return;
498
661
  }
499
- // Parse filter once up front so unknown keys/malformed filters surface
500
- // as exit-1 errors before any HTTP call.
501
- //
502
- // PL-019 item 1: --active is sugar for --filter agentActivity.state=running.
503
- // Combining --active with --filter is rejected (composition is
504
- // ambiguous — pick one explicit form). Parity is verified end-to-end
505
- // by the focused test: `--active` and the explicit filter must yield
506
- // identical output on the same fixture.
507
- let effectiveFilter = opts.filter;
508
- if (opts.active) {
509
- if (effectiveFilter) {
510
- console.error(`--active and --filter cannot be combined. ` +
511
- `--active is sugar for --filter agentActivity.state=running. ` +
512
- `Pick one form, or compose by upgrading to --filter directly.`);
513
- process.exitCode = 1;
514
- return;
515
- }
516
- effectiveFilter = "agentActivity.state=running";
517
- }
518
- let parsedFilter = null;
519
- if (effectiveFilter) {
520
- const result = parseFilter(effectiveFilter);
521
- if ("error" in result) {
522
- console.error(result.error);
523
- process.exitCode = 1;
524
- return;
525
- }
526
- parsedFilter = result;
527
- }
528
- const limit = opts.limit !== undefined ? Number(opts.limit) : null;
529
- if (limit !== null && (!Number.isInteger(limit) || limit < 0)) {
530
- console.error(`--limit must be a non-negative integer; got '${opts.limit}'`);
531
- process.exitCode = 1;
532
- return;
533
- }
534
- // C9a: validate --fields against the per-level allow-list before any HTTP
535
- // call, mirroring the --filter validation above. Level is determined by
536
- // --nodes; both branches downstream use the same `fields` variable, so
537
- // one up-front validation covers both paths.
538
- let fields = null;
539
- if (opts.fields !== undefined) {
540
- const fieldsLevel = opts.nodes ? "nodes" : "rig";
541
- const fieldsAllowed = fieldsLevel === "nodes" ? ALLOWED_NODE_FIELDS : ALLOWED_RIG_FIELDS;
542
- const fieldsResult = parseFields(opts.fields, fieldsAllowed, fieldsLevel);
543
- if ("error" in fieldsResult) {
544
- console.error(fieldsResult.error);
545
- process.exitCode = 1;
546
- return;
547
- }
548
- fields = fieldsResult;
549
- }
550
- const useEnvelope = parsedFilter !== null || limit !== null || fields !== null || opts.summary === true;
551
662
  const client = deps.clientFactory(getDaemonUrl(status));
552
663
  if (opts.nodes) {
553
664
  await handleNodes(client, opts, parsedFilter, limit, fields, useEnvelope);
554
665
  return;
555
666
  }
556
- const res = await client.get(psApiPath(opts));
667
+ // OPR.0.4.4.21 FR-1: ONE O(rigs) fetch including archived so the
668
+ // count line can be computed; visibility is split client-side below
669
+ // (JSON keeps today's default of excluding archived — parity).
670
+ const res = await client.get("/api/ps?includeArchived=true");
557
671
  if (res.status >= 400) {
558
672
  console.error(`Failed to fetch rig list from daemon (HTTP ${res.status}). Check daemon status with: rig status`);
559
673
  process.exitCode = 2;
560
674
  return;
561
675
  }
562
676
  const all = res.data;
677
+ // OPR.0.4.4.21 — archived visibility parity: the daemon call above
678
+ // always includes archived (for the count line); without
679
+ // --include-archived they are dropped from BOTH renders here, exactly
680
+ // as the daemon default used to do. archivedCount feeds the FR-1
681
+ // count line only.
682
+ const archivedCount = all.filter((e) => e.isArchived === true).length;
683
+ const visible = opts.includeArchived ? all : all.filter((e) => e.isArchived !== true);
563
684
  const rigScoped = opts.rig
564
- ? all.filter((e) => (e.rigName ?? e.name) === opts.rig)
565
- : all;
685
+ ? visible.filter((e) => (e.rigName ?? e.name) === opts.rig)
686
+ : visible;
566
687
  // Apply CLI-side filter (Amendment A: prefer CLI shaping).
567
688
  const filtered = parsedFilter ? applyRigFilter(rigScoped, parsedFilter) : rigScoped;
568
689
  // Summary mode short-circuits per-entry output.
@@ -605,15 +726,32 @@ Exit codes:
605
726
  if (parsedFilter) {
606
727
  console.log(`No rigs match --filter ${parsedFilter.key}=${parsedFilter.value}`);
607
728
  }
729
+ else if (archivedCount > 0 && !opts.includeArchived) {
730
+ // Proven-empty with the history pointer, never a bare "No rigs"
731
+ // while archived history exists.
732
+ console.log(`No active rigs · ${archivedCount} archived (rig ps --include-archived)`);
733
+ }
608
734
  else {
609
735
  console.log("No rigs");
610
736
  }
611
737
  return;
612
738
  }
739
+ // OPR.0.4.4.21 FR-1 (human table ONLY — JSON keeps ALL non-archived
740
+ // entries including stopped; this scope split is a STATED contract
741
+ // sentence in the help text): on the BARE default (no filter, no
742
+ // --rig, no --include-archived) stopped rigs fold into the count
743
+ // line — history is what the default drops, not field of view.
744
+ const bareDefault = !parsedFilter && !opts.rig && !opts.includeArchived;
745
+ const tableRows = bareDefault ? limited.filter((e) => e.status !== "stopped") : limited;
746
+ const stoppedCount = bareDefault ? limited.length - tableRows.length : 0;
747
+ // FR-1 display element 1: the host rollup line.
748
+ const rollupSeats = tableRows.reduce((n, e) => n + e.nodeCount, 0);
749
+ const rollupAttention = tableRows.reduce((n, e) => n + (e.attentionCount ?? 0), 0);
750
+ console.log(`${tableRows.length} rig${tableRows.length === 1 ? "" : "s"} · ${rollupSeats} seat${rollupSeats === 1 ? "" : "s"} · ${rollupAttention} need${rollupAttention === 1 ? "s" : ""} attention`);
613
751
  // Human output: apply default truncation budget unless --full.
614
- const humanList = (opts.full || limit !== null) ? limited : limited.slice(0, HUMAN_RIG_BUDGET);
615
- const humanTruncated = !opts.full && limit === null && filtered.length > HUMAN_RIG_BUDGET;
616
- const header = padRigRow("RIG", "NODES", "RUNNING", "ACTIVE", "WORK", "STATUS", "LIFECYCLE", "UPTIME", "SNAPSHOT");
752
+ const humanList = (opts.full || limit !== null) ? tableRows : tableRows.slice(0, HUMAN_RIG_BUDGET);
753
+ const humanTruncated = !opts.full && limit === null && tableRows.length > HUMAN_RIG_BUDGET;
754
+ const header = padRigRow("RIG", "NODES", "RUNNING", "ACTIVE", "WORK", "ATTN", "STATUS", "LIFECYCLE", "UPTIME", "SNAPSHOT");
617
755
  console.log(header);
618
756
  let anyArchivedShown = false;
619
757
  for (const e of humanList) {
@@ -624,11 +762,27 @@ Exit codes:
624
762
  anyArchivedShown = true;
625
763
  console.log(padRigRow(e.isArchived ? `${e.rigName ?? e.name} *` : (e.rigName ?? e.name), String(e.nodeCount), String(e.runningCount),
626
764
  // Slice 15 — "—" when daemon predates the field; honest absence.
627
- e.activeCount !== undefined ? String(e.activeCount) : "—", e.hasWorkCount !== undefined ? String(e.hasWorkCount) : "—", e.status, abbrevRigLifecycle(e.lifecycleState), e.uptime ?? "—", e.latestSnapshot ?? "—"));
765
+ e.activeCount !== undefined ? String(e.activeCount) : "—", e.hasWorkCount !== undefined ? String(e.hasWorkCount) : "—",
766
+ // OPR.0.4.4.21 — the founder's field-of-view anchor: where is
767
+ // something that might concern me. "—" = daemon predates the field.
768
+ e.attentionCount !== undefined ? (e.attentionCount > 0 ? `▲${e.attentionCount}` : "0") : "—", e.status, abbrevRigLifecycle(e.lifecycleState), e.uptime ?? "—", e.latestSnapshot ?? "—"));
628
769
  }
629
770
  if (anyArchivedShown) {
630
771
  console.log("* = archived (hidden from the default view; shown via --include-archived). Reverse with: rig unarchive <rig>");
631
772
  }
773
+ // FR-1 display element 2: history as ONE count line, never rows.
774
+ if (stoppedCount > 0 || (!opts.includeArchived && archivedCount > 0)) {
775
+ const parts = [];
776
+ if (stoppedCount > 0)
777
+ parts.push(`${stoppedCount} stopped (rig ps --filter status=stopped)`);
778
+ if (!opts.includeArchived && archivedCount > 0)
779
+ parts.push(`${archivedCount} archived (rig ps --include-archived)`);
780
+ console.log(`not shown: ${parts.join(" · ")}`);
781
+ }
782
+ // FR-1 display element 3: the affordance footer — the drill ladder.
783
+ if (bareDefault) {
784
+ console.log("drill: rig ps --rig <name> (one rig) · rig ps --nodes --rig <name> (its seats) · --full (everything)");
785
+ }
632
786
  if (humanTruncated) {
633
787
  const remaining = filtered.length - HUMAN_RIG_BUDGET;
634
788
  console.log(`... and ${remaining} more rig${remaining === 1 ? "" : "s"} (truncated at ${HUMAN_RIG_BUDGET}).`);
@@ -651,14 +805,24 @@ async function handleNodes(client, opts, parsedFilter, limit, fields, useEnvelop
651
805
  const effectiveRigs = opts.rig
652
806
  ? rigRes.data.filter((r) => (r.rigName ?? r.name) === opts.rig)
653
807
  : rigRes.data;
808
+ // OPR.0.4.3 healthz-wedge: the daemon nodes route is CHEAP by default (no
809
+ // per-node tmux capture) — `rig ps --nodes` gets snapshot-based activity. Pass
810
+ // ?full=true only when the operator asks for --full, which opts into the
811
+ // per-node pane-heuristic (freshest needs_input) at the fan-out's cost.
812
+ const nodesQuery = opts.full ? "?full=true" : "";
654
813
  const allNodes = [];
655
814
  for (const rig of effectiveRigs) {
656
- const nodesRes = await client.get(`/api/rigs/${encodeURIComponent(rig.rigId)}/nodes`, requestHeaders ? { headers: requestHeaders } : undefined);
815
+ const nodesRes = await client.get(`/api/rigs/${encodeURIComponent(rig.rigId)}/nodes${nodesQuery}`, requestHeaders ? { headers: requestHeaders } : undefined);
657
816
  if (nodesRes.status >= 400) {
658
817
  console.error(`Warning: failed to fetch nodes for rig "${rig.rigName ?? rig.name}" (HTTP ${nodesRes.status}). List rigs with: rig ps`);
659
818
  continue;
660
819
  }
661
- allNodes.push(...nodesRes.data);
820
+ const parentRigName = rig.rigName ?? rig.name;
821
+ allNodes.push(...nodesRes.data.map((n) => ({
822
+ ...n,
823
+ rigId: n.rigId ?? rig.rigId,
824
+ rigName: n.rigName ?? parentRigName,
825
+ })));
662
826
  }
663
827
  let narrowed = allNodes;
664
828
  if (opts.rig)
@@ -724,14 +888,14 @@ async function handleNodes(client, opts, parsedFilter, limit, fields, useEnvelop
724
888
  }
725
889
  }
726
890
  else {
727
- const header = padNodeRow("RIG", "POD", "MEMBER", "SESSION", "RUNTIME", "STATUS", "STARTUP", "LIFECYCLE", "TERMINAL", "WORK", "ACTIVITY", "CTX", "RESTORE", "ERROR");
891
+ const header = padNodeRow("RIG", "POD", "MEMBER", "SESSION", "RUNTIME", "STATUS", "STARTUP", "ORIENTED", "LIFECYCLE", "TERMINAL", "WORK", "ACTIVITY", "CTX", "RESTORE", "ERROR");
728
892
  console.log(header);
729
893
  for (const n of humanList) {
730
894
  const parts = n.logicalId.split(".");
731
895
  const pod = n.podNamespace ?? (parts.length > 1 ? parts[0] : "—");
732
896
  const member = parts.length > 1 ? parts.slice(1).join(".") : n.logicalId;
733
897
  const rig = `${n.rigName}#${n.rigId}`;
734
- console.log(padNodeRow(rig, pod, member, n.canonicalSessionName ?? "—", n.runtime ?? "—", n.sessionStatus ?? "—", n.startupStatus ?? "—", abbrevNodeLifecycle(n.lifecycleState), formatTerminalActive(n.terminalActive), formatHasWork(n.hasAssignedWork, n.pendingWorkCount), formatActivity(n.agentActivity), formatContextUsage(n.contextUsage), n.restoreOutcome, n.latestError ? truncate(n.latestError, 30) : n.heldReason ? `held: ${truncate(n.heldReason, 25)}` : "—"));
898
+ console.log(padNodeRow(rig, pod, member, n.canonicalSessionName ?? "—", n.runtime ?? "—", n.sessionStatus ?? "—", n.startupStatus ?? "—", n.oriented ?? "—", abbrevNodeLifecycle(n.lifecycleState), formatTerminalActive(n.terminalActive), formatHasWork(n.hasAssignedWork, n.pendingWorkCount), formatActivity(n.agentActivity), formatContextUsage(n.contextUsage), n.restoreOutcome, n.latestError ? truncate(n.latestError, 30) : n.heldReason ? `held: ${truncate(n.heldReason, 25)}` : "—"));
735
899
  }
736
900
  }
737
901
  if (humanTruncated) {
@@ -761,7 +925,7 @@ function truncate(s, max) {
761
925
  function fitCell(value, width) {
762
926
  return truncate(value, width).padEnd(width);
763
927
  }
764
- function padRigRow(rig, nodes, running, active, work, status, lifecycle, uptime, snapshot) {
928
+ function padRigRow(rig, nodes, running, active, work, attn, status, lifecycle, uptime, snapshot) {
765
929
  return [
766
930
  fitCell(rig, 24),
767
931
  fitCell(nodes, 7),
@@ -772,13 +936,15 @@ function padRigRow(rig, nodes, running, active, work, status, lifecycle, uptime,
772
936
  // operators see which dimension differs at a glance.
773
937
  fitCell(active, 8),
774
938
  fitCell(work, 6),
939
+ // OPR.0.4.4.21 — ATTN: seats needing attention (the field-of-view anchor).
940
+ fitCell(attn, 6),
775
941
  fitCell(status, 10),
776
942
  fitCell(lifecycle, 11),
777
943
  fitCell(uptime, 11),
778
944
  snapshot,
779
945
  ].join("");
780
946
  }
781
- function padNodeRow(rig, pod, member, session, runtime, status, startup, lifecycle, terminal, work, activity, ctx, restore, error) {
947
+ function padNodeRow(rig, pod, member, session, runtime, status, startup, oriented, lifecycle, terminal, work, activity, ctx, restore, error) {
782
948
  return [
783
949
  fitCell(rig, 30),
784
950
  fitCell(pod, 10),
@@ -787,6 +953,8 @@ function padNodeRow(rig, pod, member, session, runtime, status, startup, lifecyc
787
953
  fitCell(runtime, 12),
788
954
  fitCell(status, 10),
789
955
  fitCell(startup, 10),
956
+ // OPR.0.4.3.06 — challenge-verified orientation, distinct from STARTUP.
957
+ fitCell(oriented, 9),
790
958
  fitCell(lifecycle, 11),
791
959
  // Slice 15 — distinct TERMINAL + WORK columns.
792
960
  fitCell(terminal, 9),
@@ -868,7 +1036,10 @@ async function runCrossHostPs(hostId, opts, deps) {
868
1036
  // OPR.0.4.0.34: forward the breadth flag so `--host h -A` keeps all-rigs
869
1037
  // breadth across the hop (the current-rig default is local-only and never
870
1038
  // applied to a remote call, but an explicit -A must still reach the remote).
871
- if (opts.allRigs)
1039
+ // OPR.0.4.4.21 FR-3: -A is legal only alongside --nodes (the validator
1040
+ // already rejected the bare form before any dispatch; this guard keeps
1041
+ // the reconstructed remote argv obeying the same grammar).
1042
+ if (opts.allRigs && opts.nodes)
872
1043
  argv.push("--all-rigs");
873
1044
  if (opts.limit !== undefined)
874
1045
  argv.push("--limit", opts.limit);
@@ -922,46 +1093,15 @@ async function runHttpPs(host, opts, deps) {
922
1093
  process.exitCode = 1;
923
1094
  return;
924
1095
  }
925
- let effectiveFilter = opts.filter;
926
- if (opts.active) {
927
- if (effectiveFilter) {
928
- console.error("--active and --filter cannot be combined.");
929
- process.exitCode = 1;
930
- return;
931
- }
932
- effectiveFilter = "agentActivity.state=running";
933
- }
934
- let parsedFilter = null;
935
- if (effectiveFilter) {
936
- const result = parseFilter(effectiveFilter);
937
- if ("error" in result) {
938
- console.error(result.error);
939
- process.exitCode = 1;
940
- return;
941
- }
942
- parsedFilter = result;
943
- }
944
- const limit = opts.limit !== undefined ? Number(opts.limit) : null;
945
- if (limit !== null && (!Number.isInteger(limit) || limit < 0)) {
946
- console.error(`--limit must be a non-negative integer; got '${opts.limit}'`);
1096
+ const controls = parsePsControls(opts);
1097
+ if ("error" in controls) {
1098
+ console.error(controls.error);
947
1099
  process.exitCode = 1;
948
1100
  return;
949
1101
  }
950
- let fields = null;
951
- if (opts.fields !== undefined) {
952
- const fieldsLevel = opts.nodes ? "nodes" : "rig";
953
- const fieldsAllowed = fieldsLevel === "nodes" ? ALLOWED_NODE_FIELDS : ALLOWED_RIG_FIELDS;
954
- const fieldsResult = parseFields(opts.fields, fieldsAllowed, fieldsLevel);
955
- if ("error" in fieldsResult) {
956
- console.error(fieldsResult.error);
957
- process.exitCode = 1;
958
- return;
959
- }
960
- fields = fieldsResult;
961
- }
1102
+ const { parsedFilter, limit, fields, useEnvelope } = controls;
962
1103
  const client = deps.clientFactory(host.url);
963
1104
  const headers = buildRemoteHeaders(bearerResult.token);
964
- const useEnvelope = parsedFilter !== null || limit !== null || fields !== null || opts.summary === true;
965
1105
  try {
966
1106
  if (opts.nodes) {
967
1107
  await handleNodes(client, opts, parsedFilter, limit, fields, useEnvelope, headers);
@@ -1010,7 +1150,22 @@ async function runHttpPs(host, opts, deps) {
1010
1150
  process.exitCode = 1;
1011
1151
  }
1012
1152
  }
1013
- async function runFanOutPs(opts, deps) {
1153
+ /** OPR.0.4.4.21 FR-5 adapter to the intra-P4 shared contract
1154
+ * (fanout-contract.ts, slice 15 first-lander at 0ecd329b; the closed
1155
+ * status enum is THE contract, failedStep rides as additive detail). */
1156
+ function toPerHostStatus(r) {
1157
+ const status = r.ok ? "ok"
1158
+ : r.failedStep === "unsupported-transport" ? "unsupported-transport"
1159
+ : r.failedStep === "permission-gate" ? "auth-failed"
1160
+ : "unreachable";
1161
+ const out = { hostId: r.host, status };
1162
+ if (r.error)
1163
+ out.error = r.error;
1164
+ if (!r.ok && r.failedStep !== "unsupported-transport")
1165
+ out.failedStep = r.failedStep;
1166
+ return out;
1167
+ }
1168
+ async function runFanOutPs(opts, deps, shaping) {
1014
1169
  const loader = deps.hostRegistryLoader ?? loadHostRegistry;
1015
1170
  const registry = loader();
1016
1171
  if (!registry.ok) {
@@ -1030,14 +1185,20 @@ async function runFanOutPs(opts, deps) {
1030
1185
  }
1031
1186
  }
1032
1187
  else {
1033
- targetIds = allHosts.filter((h) => h.transport === "http").map((h) => h.id);
1188
+ // OPR.0.4.4.21 fixback (qa1 F1): target EVERY declared host non-HTTP
1189
+ // hosts must appear in hosts[] as unsupported-transport (R15-2), never
1190
+ // be silently absent. The per-host leg classifies transport.
1191
+ targetIds = allHosts.map((h) => h.id);
1034
1192
  }
1035
1193
  const results = await Promise.all(targetIds.map(async (id) => {
1036
1194
  const host = allHosts.find((h) => h.id === id);
1037
1195
  if (!host)
1038
1196
  return { host: id, ok: false, failedStep: "remote-daemon-unreachable", error: `unknown host ${id}` };
1039
1197
  if (host.transport !== "http") {
1040
- return { host: id, ok: false, failedStep: "remote-command-failed", error: `host ${id} uses transport ${host.transport}; HTTP fan-out requires transport: http` };
1198
+ // R15-2 (shared contract): an SSH-declared host is a STRUCTURED
1199
+ // unsupported-transport status — never prose-only, never silently
1200
+ // thinner output.
1201
+ return { host: id, ok: false, failedStep: "unsupported-transport", error: `host ${id} uses transport ${host.transport}; HTTP fan-out requires transport: http` };
1041
1202
  }
1042
1203
  const httpHost = host;
1043
1204
  const bearerResult = resolveRemoteBearer(httpHost);
@@ -1045,16 +1206,35 @@ async function runFanOutPs(opts, deps) {
1045
1206
  return { host: id, ok: false, failedStep: bearerResult.failedStep, error: bearerResult.error };
1046
1207
  }
1047
1208
  const client = deps.clientFactory(httpHost.url);
1209
+ const headers = buildRemoteHeaders(bearerResult.token);
1048
1210
  try {
1049
1211
  const psQuery = opts.includeArchived ? "?includeArchived=true" : "";
1050
- const res = await client.get(`/api/ps${psQuery}`, {
1051
- headers: buildRemoteHeaders(bearerResult.token),
1052
- });
1212
+ const res = await client.get(`/api/ps${psQuery}`, { headers });
1053
1213
  const failedStep = classifyHttpFailedStep(res.status);
1054
1214
  if (failedStep !== "none") {
1055
1215
  return { host: id, ok: false, failedStep, error: `HTTP ${res.status}` };
1056
1216
  }
1057
- return { host: id, ok: true, failedStep: "none", data: res.data };
1217
+ if (!opts.nodes) {
1218
+ return { host: id, ok: true, failedStep: "none", data: res.data };
1219
+ }
1220
+ // OPR.0.4.4.21 FR-5 — the full explicit ladder (--nodes -A, --full
1221
+ // for complete records): per-host node fan-out, projected unless
1222
+ // --full (the invariant's last rung; validated as -A-only upstream).
1223
+ const rigs = Array.isArray(res.data) ? res.data : [];
1224
+ const nodes = [];
1225
+ for (const rig of rigs) {
1226
+ const nodesRes = await client.get(`/api/rigs/${encodeURIComponent(rig.rigId)}/nodes`, { headers });
1227
+ if (nodesRes.status >= 400) {
1228
+ return { host: id, ok: false, failedStep: classifyHttpFailedStep(nodesRes.status), error: `HTTP ${nodesRes.status} fetching nodes for rig ${rig.rigName ?? rig.name}` };
1229
+ }
1230
+ const parentRigName = rig.rigName ?? rig.name;
1231
+ nodes.push(...nodesRes.data.map((n) => ({
1232
+ ...n,
1233
+ rigId: n.rigId ?? rig.rigId,
1234
+ rigName: n.rigName ?? parentRigName,
1235
+ })));
1236
+ }
1237
+ return { host: id, ok: true, failedStep: "none", data: nodes };
1058
1238
  }
1059
1239
  catch (err) {
1060
1240
  return { host: id, ok: false, failedStep: classifyHttpError(err), error: err.message };
@@ -1062,7 +1242,49 @@ async function runFanOutPs(opts, deps) {
1062
1242
  }));
1063
1243
  const hasFailure = results.some((r) => !r.ok);
1064
1244
  if (opts.json) {
1065
- console.log(JSON.stringify({ hosts: results }));
1245
+ // OPR.0.4.4.21 FR-5 — the intra-P4 shared payload (ONE contract with
1246
+ // slice 15): items = each host's rows stamped with their origin hostId
1247
+ // (flat-mergeable; origin never positional), hosts = the per-host
1248
+ // structured status array. EVERY targeted host appears in hosts[] —
1249
+ // ok or not (no silent thinning).
1250
+ const rawItems = results.flatMap((r) => r.ok && Array.isArray(r.data)
1251
+ ? r.data.map((row) => ({ ...row, hostId: r.host }))
1252
+ : []);
1253
+ const hostStatuses = results.map(toPerHostStatus);
1254
+ let narrowed = rawItems;
1255
+ if (opts.rig)
1256
+ narrowed = narrowed.filter((e) => (e.rigName ?? e.name) === opts.rig);
1257
+ if (opts.nodes && opts.session)
1258
+ narrowed = narrowed.filter((n) => n.canonicalSessionName === opts.session);
1259
+ const filtered = shaping.parsedFilter
1260
+ ? opts.nodes
1261
+ ? applyNodeFilter(narrowed, shaping.parsedFilter)
1262
+ : applyRigFilter(narrowed, shaping.parsedFilter)
1263
+ : narrowed;
1264
+ let items;
1265
+ if (opts.summary) {
1266
+ items = [
1267
+ opts.nodes
1268
+ ? summarizeNodes(filtered)
1269
+ : summarizeRigs(filtered),
1270
+ ];
1271
+ }
1272
+ else {
1273
+ const limited = shaping.limit !== null ? filtered.slice(0, shaping.limit) : filtered;
1274
+ items = shaping.fields
1275
+ ? selectFanOutFields(limited, shaping.fields)
1276
+ : opts.nodes && !opts.full
1277
+ ? compactNodeProjection(limited).map((row, i) => ({
1278
+ ...row,
1279
+ hostId: limited[i]?.hostId,
1280
+ }))
1281
+ : limited;
1282
+ }
1283
+ const payload = {
1284
+ items,
1285
+ hosts: hostStatuses,
1286
+ };
1287
+ console.log(JSON.stringify(payload));
1066
1288
  }
1067
1289
  else {
1068
1290
  for (const r of results) {