universal-physics-tensor 0.47.0 → 1.0.1

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 (591) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +396 -383
  3. package/bin/upt.mjs +28 -22
  4. package/dist/atlas/benchmark/hybrid-retrieval.d.ts +108 -0
  5. package/dist/atlas/benchmark/hybrid-retrieval.d.ts.map +1 -0
  6. package/dist/atlas/benchmark/hybrid-retrieval.js +241 -0
  7. package/dist/atlas/benchmark/hybrid-retrieval.js.map +1 -0
  8. package/dist/atlas/catalog-formal-ref.d.ts +24 -0
  9. package/dist/atlas/catalog-formal-ref.d.ts.map +1 -0
  10. package/dist/atlas/catalog-formal-ref.js +36 -0
  11. package/dist/atlas/catalog-formal-ref.js.map +1 -0
  12. package/dist/atlas/composition-table.d.ts +11 -46
  13. package/dist/atlas/composition-table.d.ts.map +1 -1
  14. package/dist/atlas/composition-table.js +7 -124
  15. package/dist/atlas/composition-table.js.map +1 -1
  16. package/dist/atlas/conventions.d.ts +4 -58
  17. package/dist/atlas/conventions.d.ts.map +1 -1
  18. package/dist/atlas/conventions.js +3 -88
  19. package/dist/atlas/conventions.js.map +1 -1
  20. package/dist/atlas/derive-evidence.d.ts +50 -3
  21. package/dist/atlas/derive-evidence.d.ts.map +1 -1
  22. package/dist/atlas/derive-evidence.js +73 -4
  23. package/dist/atlas/derive-evidence.js.map +1 -1
  24. package/dist/atlas/diffusion/bridges-closure.d.ts.map +1 -1
  25. package/dist/atlas/diffusion/bridges-closure.js +5 -1
  26. package/dist/atlas/diffusion/bridges-closure.js.map +1 -1
  27. package/dist/atlas/index.d.ts +9 -7
  28. package/dist/atlas/index.d.ts.map +1 -1
  29. package/dist/atlas/index.js +6 -5
  30. package/dist/atlas/index.js.map +1 -1
  31. package/dist/atlas/oscillators/bridges-exact.d.ts.map +1 -1
  32. package/dist/atlas/oscillators/bridges-exact.js +6 -0
  33. package/dist/atlas/oscillators/bridges-exact.js.map +1 -1
  34. package/dist/atlas/oscillators/bridges-limits.d.ts +2 -0
  35. package/dist/atlas/oscillators/bridges-limits.d.ts.map +1 -1
  36. package/dist/atlas/oscillators/bridges-limits.js +20 -19
  37. package/dist/atlas/oscillators/bridges-limits.js.map +1 -1
  38. package/dist/atlas/oscillators/limit-witnesses.d.ts +93 -0
  39. package/dist/atlas/oscillators/limit-witnesses.d.ts.map +1 -0
  40. package/dist/atlas/oscillators/limit-witnesses.js +152 -0
  41. package/dist/atlas/oscillators/limit-witnesses.js.map +1 -0
  42. package/dist/atlas/oscillators/models.d.ts +2 -1
  43. package/dist/atlas/oscillators/models.d.ts.map +1 -1
  44. package/dist/atlas/oscillators/models.js +4 -3
  45. package/dist/atlas/oscillators/models.js.map +1 -1
  46. package/dist/atlas/oscillators/norm-transport-witness.d.ts +37 -0
  47. package/dist/atlas/oscillators/norm-transport-witness.d.ts.map +1 -0
  48. package/dist/atlas/oscillators/norm-transport-witness.js +58 -0
  49. package/dist/atlas/oscillators/norm-transport-witness.js.map +1 -0
  50. package/dist/atlas/oscillators/norm-transport.d.ts +44 -0
  51. package/dist/atlas/oscillators/norm-transport.d.ts.map +1 -0
  52. package/dist/atlas/oscillators/norm-transport.js +66 -0
  53. package/dist/atlas/oscillators/norm-transport.js.map +1 -0
  54. package/dist/atlas/oscillators/norms.d.ts +11 -0
  55. package/dist/atlas/oscillators/norms.d.ts.map +1 -0
  56. package/dist/atlas/oscillators/norms.js +11 -0
  57. package/dist/atlas/oscillators/norms.js.map +1 -0
  58. package/dist/atlas/oscillators/pendulum-motion.d.ts +38 -0
  59. package/dist/atlas/oscillators/pendulum-motion.d.ts.map +1 -0
  60. package/dist/atlas/oscillators/pendulum-motion.js +122 -0
  61. package/dist/atlas/oscillators/pendulum-motion.js.map +1 -0
  62. package/dist/atlas/oscillators/phase-carriage.d.ts +38 -0
  63. package/dist/atlas/oscillators/phase-carriage.d.ts.map +1 -0
  64. package/dist/atlas/oscillators/phase-carriage.js +96 -0
  65. package/dist/atlas/oscillators/phase-carriage.js.map +1 -0
  66. package/dist/atlas/oscillators/phase-translation.d.ts +46 -0
  67. package/dist/atlas/oscillators/phase-translation.d.ts.map +1 -0
  68. package/dist/atlas/oscillators/phase-translation.js +178 -0
  69. package/dist/atlas/oscillators/phase-translation.js.map +1 -0
  70. package/dist/atlas/oscillators/position-translation.d.ts +59 -0
  71. package/dist/atlas/oscillators/position-translation.d.ts.map +1 -0
  72. package/dist/atlas/oscillators/position-translation.js +254 -0
  73. package/dist/atlas/oscillators/position-translation.js.map +1 -0
  74. package/dist/atlas/path-bound.d.ts +126 -9
  75. package/dist/atlas/path-bound.d.ts.map +1 -1
  76. package/dist/atlas/path-bound.js +372 -23
  77. package/dist/atlas/path-bound.js.map +1 -1
  78. package/dist/atlas/physjs-ref.d.ts +112 -0
  79. package/dist/atlas/physjs-ref.d.ts.map +1 -0
  80. package/dist/atlas/physjs-ref.js +842 -0
  81. package/dist/atlas/physjs-ref.js.map +1 -0
  82. package/dist/atlas/regime.d.ts +12 -136
  83. package/dist/atlas/regime.d.ts.map +1 -1
  84. package/dist/atlas/regime.js +8 -254
  85. package/dist/atlas/regime.js.map +1 -1
  86. package/dist/atlas/serialize.d.ts.map +1 -1
  87. package/dist/atlas/serialize.js +25 -0
  88. package/dist/atlas/serialize.js.map +1 -1
  89. package/dist/atlas/translation-registry.d.ts +22 -0
  90. package/dist/atlas/translation-registry.d.ts.map +1 -0
  91. package/dist/atlas/translation-registry.js +31 -0
  92. package/dist/atlas/translation-registry.js.map +1 -0
  93. package/dist/atlas/translation.d.ts +116 -0
  94. package/dist/atlas/translation.d.ts.map +1 -0
  95. package/dist/atlas/translation.js +34 -0
  96. package/dist/atlas/translation.js.map +1 -0
  97. package/dist/atlas/types.d.ts +74 -240
  98. package/dist/atlas/types.d.ts.map +1 -1
  99. package/dist/atlas/types.js +5 -37
  100. package/dist/atlas/types.js.map +1 -1
  101. package/dist/atlas/waves/bridges-closure.d.ts.map +1 -1
  102. package/dist/atlas/waves/bridges-closure.js +4 -0
  103. package/dist/atlas/waves/bridges-closure.js.map +1 -1
  104. package/dist/atlas/waves/bridges.d.ts.map +1 -1
  105. package/dist/atlas/waves/bridges.js +3 -0
  106. package/dist/atlas/waves/bridges.js.map +1 -1
  107. package/dist/atlas/waves/models.js +1 -1
  108. package/dist/atlas/waves/models.js.map +1 -1
  109. package/dist/atlas/witness-dominance.d.ts +50 -0
  110. package/dist/atlas/witness-dominance.d.ts.map +1 -0
  111. package/dist/atlas/witness-dominance.js +86 -0
  112. package/dist/atlas/witness-dominance.js.map +1 -0
  113. package/dist/atlas/witness-specs.d.ts +30 -0
  114. package/dist/atlas/witness-specs.d.ts.map +1 -1
  115. package/dist/atlas/witness-specs.js +107 -0
  116. package/dist/atlas/witness-specs.js.map +1 -1
  117. package/dist/bridges/be53-yang-mills-confrontation.d.ts +81 -0
  118. package/dist/bridges/be53-yang-mills-confrontation.d.ts.map +1 -0
  119. package/dist/bridges/be53-yang-mills-confrontation.js +110 -0
  120. package/dist/bridges/be53-yang-mills-confrontation.js.map +1 -0
  121. package/dist/bridges/confrontations.d.ts +1 -1
  122. package/dist/bridges/confrontations.d.ts.map +1 -1
  123. package/dist/bridges/confrontations.js +291 -3
  124. package/dist/bridges/confrontations.js.map +1 -1
  125. package/dist/bridges/equations/be-13-einstein-trace.d.ts +2 -3
  126. package/dist/bridges/equations/be-13-einstein-trace.d.ts.map +1 -1
  127. package/dist/bridges/equations/be-13-einstein-trace.js +2 -3
  128. package/dist/bridges/equations/be-13-einstein-trace.js.map +1 -1
  129. package/dist/bridges/equations/be-38-mond.d.ts +7 -5
  130. package/dist/bridges/equations/be-38-mond.d.ts.map +1 -1
  131. package/dist/bridges/equations/be-38-mond.js +7 -5
  132. package/dist/bridges/equations/be-38-mond.js.map +1 -1
  133. package/dist/bridges/equations/be-53-yang-mills-beta.d.ts +21 -0
  134. package/dist/bridges/equations/be-53-yang-mills-beta.d.ts.map +1 -1
  135. package/dist/bridges/equations/be-53-yang-mills-beta.js +12 -0
  136. package/dist/bridges/equations/be-53-yang-mills-beta.js.map +1 -1
  137. package/dist/bridges/evaluator-inputs.d.ts +21 -0
  138. package/dist/bridges/evaluator-inputs.d.ts.map +1 -0
  139. package/dist/bridges/evaluator-inputs.js +75 -0
  140. package/dist/bridges/evaluator-inputs.js.map +1 -0
  141. package/dist/bridges/evaluators.d.ts +38 -0
  142. package/dist/bridges/evaluators.d.ts.map +1 -1
  143. package/dist/bridges/evaluators.js +41 -15
  144. package/dist/bridges/evaluators.js.map +1 -1
  145. package/dist/bridges/gravitational-lensing.d.ts +1 -1
  146. package/dist/bridges/gravitational-lensing.js +1 -1
  147. package/dist/bridges/index.d.ts +1 -1
  148. package/dist/bridges/index.d.ts.map +1 -1
  149. package/dist/bridges/index.js +13 -13
  150. package/dist/bridges/index.js.map +1 -1
  151. package/dist/bridges/observations/types.d.ts +110 -2
  152. package/dist/bridges/observations/types.d.ts.map +1 -1
  153. package/dist/bridges/observations/types.js +29 -0
  154. package/dist/bridges/observations/types.js.map +1 -1
  155. package/dist/bridges/perihelion-precession.d.ts +1 -1
  156. package/dist/bridges/perihelion-precession.js +1 -1
  157. package/dist/canonical/canonical-equation.d.ts +1 -1
  158. package/dist/canonical/canonical-equation.d.ts.map +1 -1
  159. package/dist/canonical/entries/atomic.d.ts.map +1 -1
  160. package/dist/canonical/entries/atomic.js +32 -10
  161. package/dist/canonical/entries/atomic.js.map +1 -1
  162. package/dist/canonical/entries/dimensional-classics.js +2 -2
  163. package/dist/canonical/entries/dimensional-classics.js.map +1 -1
  164. package/dist/canonical/entries/mechanics.d.ts.map +1 -1
  165. package/dist/canonical/entries/mechanics.js +10 -6
  166. package/dist/canonical/entries/mechanics.js.map +1 -1
  167. package/dist/canonical/entries/relativity.d.ts.map +1 -1
  168. package/dist/canonical/entries/relativity.js +41 -8
  169. package/dist/canonical/entries/relativity.js.map +1 -1
  170. package/dist/canonical/entries/thermo-nuclear-cosmo.js +5 -5
  171. package/dist/canonical/entries/thermo-nuclear-cosmo.js.map +1 -1
  172. package/dist/canonical/linkage.d.ts.map +1 -1
  173. package/dist/canonical/linkage.js +14 -8
  174. package/dist/canonical/linkage.js.map +1 -1
  175. package/dist/canonical/normal-form.d.ts +17 -0
  176. package/dist/canonical/normal-form.d.ts.map +1 -1
  177. package/dist/canonical/normal-form.js +21 -4
  178. package/dist/canonical/normal-form.js.map +1 -1
  179. package/dist/cases/brownian-sphere.d.ts +68 -0
  180. package/dist/cases/brownian-sphere.d.ts.map +1 -0
  181. package/dist/cases/brownian-sphere.js +353 -0
  182. package/dist/cases/brownian-sphere.js.map +1 -0
  183. package/dist/cases/damped-resonator.d.ts +23 -0
  184. package/dist/cases/damped-resonator.d.ts.map +1 -0
  185. package/dist/cases/damped-resonator.js +224 -0
  186. package/dist/cases/damped-resonator.js.map +1 -0
  187. package/dist/cases/index.d.ts +11 -0
  188. package/dist/cases/index.d.ts.map +1 -0
  189. package/dist/cases/index.js +29 -0
  190. package/dist/cases/index.js.map +1 -0
  191. package/dist/cases/kepler-rv.d.ts +13 -0
  192. package/dist/cases/kepler-rv.d.ts.map +1 -0
  193. package/dist/cases/kepler-rv.js +168 -0
  194. package/dist/cases/kepler-rv.js.map +1 -0
  195. package/dist/cases/lumped-cooling.d.ts +46 -0
  196. package/dist/cases/lumped-cooling.d.ts.map +1 -0
  197. package/dist/cases/lumped-cooling.js +261 -0
  198. package/dist/cases/lumped-cooling.js.map +1 -0
  199. package/dist/cases/quadrature.d.ts +14 -0
  200. package/dist/cases/quadrature.d.ts.map +1 -0
  201. package/dist/cases/quadrature.js +41 -0
  202. package/dist/cases/quadrature.js.map +1 -0
  203. package/dist/cases/resistor-noise.d.ts +14 -0
  204. package/dist/cases/resistor-noise.d.ts.map +1 -0
  205. package/dist/cases/resistor-noise.js +210 -0
  206. package/dist/cases/resistor-noise.js.map +1 -0
  207. package/dist/cases/skin-depth.d.ts +21 -0
  208. package/dist/cases/skin-depth.d.ts.map +1 -0
  209. package/dist/cases/skin-depth.js +174 -0
  210. package/dist/cases/skin-depth.js.map +1 -0
  211. package/dist/cases/types.d.ts +115 -0
  212. package/dist/cases/types.d.ts.map +1 -0
  213. package/dist/cases/types.js +20 -0
  214. package/dist/cases/types.js.map +1 -0
  215. package/dist/cli/commands/_atlas-map.d.ts +409 -0
  216. package/dist/cli/commands/_atlas-map.d.ts.map +1 -0
  217. package/dist/cli/commands/_atlas-map.js +1014 -0
  218. package/dist/cli/commands/_atlas-map.js.map +1 -0
  219. package/dist/cli/commands/_atlas-route.d.ts +111 -0
  220. package/dist/cli/commands/_atlas-route.d.ts.map +1 -0
  221. package/dist/cli/commands/_atlas-route.js +140 -0
  222. package/dist/cli/commands/_atlas-route.js.map +1 -0
  223. package/dist/cli/commands/_discovery-opts.d.ts.map +1 -1
  224. package/dist/cli/commands/_discovery-opts.js +10 -1
  225. package/dist/cli/commands/_discovery-opts.js.map +1 -1
  226. package/dist/cli/commands/atlas.d.ts +20 -0
  227. package/dist/cli/commands/atlas.d.ts.map +1 -1
  228. package/dist/cli/commands/atlas.js +333 -19
  229. package/dist/cli/commands/atlas.js.map +1 -1
  230. package/dist/cli/commands/audit.d.ts.map +1 -1
  231. package/dist/cli/commands/audit.js +14 -5
  232. package/dist/cli/commands/audit.js.map +1 -1
  233. package/dist/cli/commands/axes.js +4 -4
  234. package/dist/cli/commands/candidates.d.ts.map +1 -1
  235. package/dist/cli/commands/candidates.js +6 -5
  236. package/dist/cli/commands/candidates.js.map +1 -1
  237. package/dist/cli/commands/canonical.d.ts.map +1 -1
  238. package/dist/cli/commands/canonical.js +41 -12
  239. package/dist/cli/commands/canonical.js.map +1 -1
  240. package/dist/cli/commands/confront.d.ts.map +1 -1
  241. package/dist/cli/commands/confront.js +210 -28
  242. package/dist/cli/commands/confront.js.map +1 -1
  243. package/dist/cli/commands/connectors.d.ts.map +1 -1
  244. package/dist/cli/commands/connectors.js +11 -10
  245. package/dist/cli/commands/connectors.js.map +1 -1
  246. package/dist/cli/commands/coverage.js +3 -3
  247. package/dist/cli/commands/derive.d.ts.map +1 -1
  248. package/dist/cli/commands/derive.js +22 -10
  249. package/dist/cli/commands/derive.js.map +1 -1
  250. package/dist/cli/commands/discover.d.ts.map +1 -1
  251. package/dist/cli/commands/discover.js +149 -32
  252. package/dist/cli/commands/discover.js.map +1 -1
  253. package/dist/cli/commands/eval.d.ts.map +1 -1
  254. package/dist/cli/commands/eval.js +76 -17
  255. package/dist/cli/commands/eval.js.map +1 -1
  256. package/dist/cli/commands/evaluate.d.ts +30 -0
  257. package/dist/cli/commands/evaluate.d.ts.map +1 -1
  258. package/dist/cli/commands/evaluate.js +440 -25
  259. package/dist/cli/commands/evaluate.js.map +1 -1
  260. package/dist/cli/commands/explain.d.ts.map +1 -1
  261. package/dist/cli/commands/explain.js +38 -11
  262. package/dist/cli/commands/explain.js.map +1 -1
  263. package/dist/cli/commands/frontier.d.ts +3 -0
  264. package/dist/cli/commands/frontier.d.ts.map +1 -0
  265. package/dist/cli/commands/frontier.js +28 -0
  266. package/dist/cli/commands/frontier.js.map +1 -0
  267. package/dist/cli/commands/ground.d.ts.map +1 -1
  268. package/dist/cli/commands/ground.js +37 -12
  269. package/dist/cli/commands/ground.js.map +1 -1
  270. package/dist/cli/commands/index.d.ts +5 -0
  271. package/dist/cli/commands/index.d.ts.map +1 -1
  272. package/dist/cli/commands/index.js +5 -0
  273. package/dist/cli/commands/index.js.map +1 -1
  274. package/dist/cli/commands/map.d.ts +8 -0
  275. package/dist/cli/commands/map.d.ts.map +1 -1
  276. package/dist/cli/commands/map.js +401 -64
  277. package/dist/cli/commands/map.js.map +1 -1
  278. package/dist/cli/commands/metric.d.ts +3 -0
  279. package/dist/cli/commands/metric.d.ts.map +1 -0
  280. package/dist/cli/commands/metric.js +136 -0
  281. package/dist/cli/commands/metric.js.map +1 -0
  282. package/dist/cli/commands/path.d.ts +109 -1
  283. package/dist/cli/commands/path.d.ts.map +1 -1
  284. package/dist/cli/commands/path.js +1019 -109
  285. package/dist/cli/commands/path.js.map +1 -1
  286. package/dist/cli/commands/predict.d.ts.map +1 -1
  287. package/dist/cli/commands/predict.js +6 -5
  288. package/dist/cli/commands/predict.js.map +1 -1
  289. package/dist/cli/commands/priority.d.ts.map +1 -1
  290. package/dist/cli/commands/priority.js +4 -3
  291. package/dist/cli/commands/priority.js.map +1 -1
  292. package/dist/cli/commands/probe.d.ts +7 -0
  293. package/dist/cli/commands/probe.d.ts.map +1 -1
  294. package/dist/cli/commands/probe.js +234 -52
  295. package/dist/cli/commands/probe.js.map +1 -1
  296. package/dist/cli/commands/recover.d.ts.map +1 -1
  297. package/dist/cli/commands/recover.js +17 -6
  298. package/dist/cli/commands/recover.js.map +1 -1
  299. package/dist/cli/commands/regime.d.ts +1 -1
  300. package/dist/cli/commands/regime.d.ts.map +1 -1
  301. package/dist/cli/commands/regime.js +134 -33
  302. package/dist/cli/commands/regime.js.map +1 -1
  303. package/dist/cli/commands/retrieve.d.ts +3 -0
  304. package/dist/cli/commands/retrieve.d.ts.map +1 -0
  305. package/dist/cli/commands/retrieve.js +65 -0
  306. package/dist/cli/commands/retrieve.js.map +1 -0
  307. package/dist/cli/commands/search.d.ts +3 -0
  308. package/dist/cli/commands/search.d.ts.map +1 -0
  309. package/dist/cli/commands/search.js +70 -0
  310. package/dist/cli/commands/search.js.map +1 -0
  311. package/dist/cli/commands/symbolic.d.ts.map +1 -1
  312. package/dist/cli/commands/symbolic.js +112 -21
  313. package/dist/cli/commands/symbolic.js.map +1 -1
  314. package/dist/cli/commands/testplan.d.ts +3 -0
  315. package/dist/cli/commands/testplan.d.ts.map +1 -0
  316. package/dist/cli/commands/testplan.js +131 -0
  317. package/dist/cli/commands/testplan.js.map +1 -0
  318. package/dist/cli/conventions.d.ts +22 -0
  319. package/dist/cli/conventions.d.ts.map +1 -0
  320. package/dist/cli/conventions.js +48 -0
  321. package/dist/cli/conventions.js.map +1 -0
  322. package/dist/cli/euler-guard.d.ts +9 -0
  323. package/dist/cli/euler-guard.d.ts.map +1 -0
  324. package/dist/cli/euler-guard.js +11 -0
  325. package/dist/cli/euler-guard.js.map +1 -0
  326. package/dist/cli/eval-numbers.d.ts +26 -0
  327. package/dist/cli/eval-numbers.d.ts.map +1 -0
  328. package/dist/cli/eval-numbers.js +49 -0
  329. package/dist/cli/eval-numbers.js.map +1 -0
  330. package/dist/cli/expr-print.d.ts +25 -0
  331. package/dist/cli/expr-print.d.ts.map +1 -0
  332. package/dist/cli/expr-print.js +123 -0
  333. package/dist/cli/expr-print.js.map +1 -0
  334. package/dist/cli/graphs.d.ts +26 -0
  335. package/dist/cli/graphs.d.ts.map +1 -1
  336. package/dist/cli/graphs.js +19 -0
  337. package/dist/cli/graphs.js.map +1 -1
  338. package/dist/cli/main.d.ts +4 -10
  339. package/dist/cli/main.d.ts.map +1 -1
  340. package/dist/cli/main.js +370 -167
  341. package/dist/cli/main.js.map +1 -1
  342. package/dist/cli/map-evidence.d.ts +47 -0
  343. package/dist/cli/map-evidence.d.ts.map +1 -0
  344. package/dist/cli/map-evidence.js +53 -0
  345. package/dist/cli/map-evidence.js.map +1 -0
  346. package/dist/cli/output.d.ts +21 -1
  347. package/dist/cli/output.d.ts.map +1 -1
  348. package/dist/cli/output.js +5 -1
  349. package/dist/cli/output.js.map +1 -1
  350. package/dist/{composition → cli}/poster-source.d.ts +3 -2
  351. package/dist/cli/poster-source.d.ts.map +1 -0
  352. package/dist/{composition → cli}/poster-source.js +2 -1
  353. package/dist/cli/poster-source.js.map +1 -0
  354. package/dist/cli/record-reach.d.ts +44 -0
  355. package/dist/cli/record-reach.d.ts.map +1 -0
  356. package/dist/cli/record-reach.js +228 -0
  357. package/dist/cli/record-reach.js.map +1 -0
  358. package/dist/cli/record-tables.d.ts +38 -0
  359. package/dist/cli/record-tables.d.ts.map +1 -0
  360. package/dist/cli/record-tables.js +110 -0
  361. package/dist/cli/record-tables.js.map +1 -0
  362. package/dist/cli/record.d.ts +141 -0
  363. package/dist/cli/record.d.ts.map +1 -0
  364. package/dist/cli/record.js +612 -0
  365. package/dist/cli/record.js.map +1 -0
  366. package/dist/cli/search-index.d.ts +56 -0
  367. package/dist/cli/search-index.d.ts.map +1 -0
  368. package/dist/cli/search-index.js +190 -0
  369. package/dist/cli/search-index.js.map +1 -0
  370. package/dist/cli/statuses.d.ts +25 -0
  371. package/dist/cli/statuses.d.ts.map +1 -0
  372. package/dist/cli/statuses.js +276 -0
  373. package/dist/cli/statuses.js.map +1 -0
  374. package/dist/cli/version.d.ts +8 -0
  375. package/dist/cli/version.d.ts.map +1 -1
  376. package/dist/cli/version.js +32 -1
  377. package/dist/cli/version.js.map +1 -1
  378. package/dist/cli-api.d.ts +33 -15
  379. package/dist/cli-api.d.ts.map +1 -1
  380. package/dist/cli-api.js +31 -11
  381. package/dist/cli-api.js.map +1 -1
  382. package/dist/composition/bridge-analysis.d.ts.map +1 -1
  383. package/dist/composition/bridge-analysis.js +2 -1
  384. package/dist/composition/bridge-analysis.js.map +1 -1
  385. package/dist/composition/canonical-compare.d.ts +36 -11
  386. package/dist/composition/canonical-compare.d.ts.map +1 -1
  387. package/dist/composition/canonical-compare.js +424 -56
  388. package/dist/composition/canonical-compare.js.map +1 -1
  389. package/dist/composition/canonical-graph.d.ts +2 -2
  390. package/dist/composition/canonical-graph.js +3 -3
  391. package/dist/composition/canonical-graph.js.map +1 -1
  392. package/dist/composition/canonical-prefactors.d.ts +24 -1
  393. package/dist/composition/canonical-prefactors.d.ts.map +1 -1
  394. package/dist/composition/canonical-prefactors.js +97 -1
  395. package/dist/composition/canonical-prefactors.js.map +1 -1
  396. package/dist/composition/compose-symbolic.js +1 -1
  397. package/dist/composition/compose-symbolic.js.map +1 -1
  398. package/dist/composition/compose.d.ts.map +1 -1
  399. package/dist/composition/compose.js +17 -9
  400. package/dist/composition/compose.js.map +1 -1
  401. package/dist/composition/composition-recovery.d.ts +47 -0
  402. package/dist/composition/composition-recovery.d.ts.map +1 -0
  403. package/dist/composition/composition-recovery.js +77 -0
  404. package/dist/composition/composition-recovery.js.map +1 -0
  405. package/dist/composition/consequence.d.ts +39 -0
  406. package/dist/composition/consequence.d.ts.map +1 -1
  407. package/dist/composition/consequence.js +37 -0
  408. package/dist/composition/consequence.js.map +1 -1
  409. package/dist/composition/discovery.d.ts +20 -0
  410. package/dist/composition/discovery.d.ts.map +1 -1
  411. package/dist/composition/discovery.js +27 -5
  412. package/dist/composition/discovery.js.map +1 -1
  413. package/dist/composition/edge.d.ts +1 -1
  414. package/dist/composition/edge.d.ts.map +1 -1
  415. package/dist/composition/edge.js.map +1 -1
  416. package/dist/composition/edges/_catalog-helpers.d.ts +4 -0
  417. package/dist/composition/edges/_catalog-helpers.d.ts.map +1 -1
  418. package/dist/composition/edges/_catalog-helpers.js +15 -0
  419. package/dist/composition/edges/_catalog-helpers.js.map +1 -1
  420. package/dist/composition/edges/calibration.d.ts.map +1 -1
  421. package/dist/composition/edges/calibration.js +57 -0
  422. package/dist/composition/edges/calibration.js.map +1 -1
  423. package/dist/composition/edges/catalog-fields.d.ts.map +1 -1
  424. package/dist/composition/edges/catalog-fields.js +2 -1
  425. package/dist/composition/edges/catalog-fields.js.map +1 -1
  426. package/dist/composition/edges/catalog-gravitation-cosmology.d.ts.map +1 -1
  427. package/dist/composition/edges/catalog-gravitation-cosmology.js +2 -1
  428. package/dist/composition/edges/catalog-gravitation-cosmology.js.map +1 -1
  429. package/dist/composition/edges/catalog-quantum.d.ts.map +1 -1
  430. package/dist/composition/edges/catalog-quantum.js +26 -0
  431. package/dist/composition/edges/catalog-quantum.js.map +1 -1
  432. package/dist/composition/edges/catalog-tranche.d.ts.map +1 -1
  433. package/dist/composition/edges/catalog-tranche.js +2 -1
  434. package/dist/composition/edges/catalog-tranche.js.map +1 -1
  435. package/dist/composition/expr-eval.d.ts.map +1 -1
  436. package/dist/composition/expr-eval.js +4 -1
  437. package/dist/composition/expr-eval.js.map +1 -1
  438. package/dist/composition/expr-simplify.js +1 -1
  439. package/dist/composition/expr-simplify.js.map +1 -1
  440. package/dist/composition/frontier-account.d.ts +78 -0
  441. package/dist/composition/frontier-account.d.ts.map +1 -0
  442. package/dist/composition/frontier-account.js +127 -0
  443. package/dist/composition/frontier-account.js.map +1 -0
  444. package/dist/composition/graph-viz.d.ts +7 -22
  445. package/dist/composition/graph-viz.d.ts.map +1 -1
  446. package/dist/composition/graph-viz.js +4 -27
  447. package/dist/composition/graph-viz.js.map +1 -1
  448. package/dist/composition/grounding.d.ts +49 -0
  449. package/dist/composition/grounding.d.ts.map +1 -1
  450. package/dist/composition/grounding.js +38 -0
  451. package/dist/composition/grounding.js.map +1 -1
  452. package/dist/composition/index.d.ts +0 -2
  453. package/dist/composition/index.d.ts.map +1 -1
  454. package/dist/composition/index.js +1 -1
  455. package/dist/composition/index.js.map +1 -1
  456. package/dist/composition/probe/expression-gaps.d.ts +27 -0
  457. package/dist/composition/probe/expression-gaps.d.ts.map +1 -0
  458. package/dist/composition/probe/expression-gaps.js +47 -0
  459. package/dist/composition/probe/expression-gaps.js.map +1 -0
  460. package/dist/composition/probe/frontier.d.ts +2 -1
  461. package/dist/composition/probe/frontier.d.ts.map +1 -1
  462. package/dist/composition/probe/frontier.js +3 -2
  463. package/dist/composition/probe/frontier.js.map +1 -1
  464. package/dist/composition/probe/index.d.ts +4 -1
  465. package/dist/composition/probe/index.d.ts.map +1 -1
  466. package/dist/composition/probe/index.js +3 -1
  467. package/dist/composition/probe/index.js.map +1 -1
  468. package/dist/composition/probe/pipeline.d.ts +2 -0
  469. package/dist/composition/probe/pipeline.d.ts.map +1 -1
  470. package/dist/composition/probe/pipeline.js +6 -2
  471. package/dist/composition/probe/pipeline.js.map +1 -1
  472. package/dist/composition/probe/problem.d.ts +7 -0
  473. package/dist/composition/probe/problem.d.ts.map +1 -1
  474. package/dist/composition/probe/problem.js +15 -5
  475. package/dist/composition/probe/problem.js.map +1 -1
  476. package/dist/composition/probe/search-budget.d.ts.map +1 -1
  477. package/dist/composition/probe/search-budget.js +6 -2
  478. package/dist/composition/probe/search-budget.js.map +1 -1
  479. package/dist/composition/probe/study.d.ts +340 -0
  480. package/dist/composition/probe/study.d.ts.map +1 -0
  481. package/dist/composition/probe/study.js +1519 -0
  482. package/dist/composition/probe/study.js.map +1 -0
  483. package/dist/composition/proposed-bridges.js +1 -1
  484. package/dist/composition/proposed-bridges.js.map +1 -1
  485. package/dist/composition/quantities.d.ts +4 -16
  486. package/dist/composition/quantities.d.ts.map +1 -1
  487. package/dist/composition/quantities.js +4 -16
  488. package/dist/composition/quantities.js.map +1 -1
  489. package/dist/composition/retrodiction.d.ts.map +1 -1
  490. package/dist/composition/retrodiction.js +6 -2
  491. package/dist/composition/retrodiction.js.map +1 -1
  492. package/dist/composition/uncertainty.d.ts +1 -1
  493. package/dist/composition/uncertainty.d.ts.map +1 -1
  494. package/dist/composition/user-equation.d.ts +87 -14
  495. package/dist/composition/user-equation.d.ts.map +1 -1
  496. package/dist/composition/user-equation.js +266 -59
  497. package/dist/composition/user-equation.js.map +1 -1
  498. package/dist/core/constants.d.ts +9 -4
  499. package/dist/core/constants.d.ts.map +1 -1
  500. package/dist/core/constants.js +9 -4
  501. package/dist/core/constants.js.map +1 -1
  502. package/dist/core/types.d.ts +1 -1
  503. package/dist/core/types.d.ts.map +1 -1
  504. package/dist/core/types.js +2 -1
  505. package/dist/core/types.js.map +1 -1
  506. package/dist/dimensional/curvature.d.ts +7 -56
  507. package/dist/dimensional/curvature.d.ts.map +1 -1
  508. package/dist/dimensional/curvature.js +7 -76
  509. package/dist/dimensional/curvature.js.map +1 -1
  510. package/dist/dimensional/dimension-spec.d.ts +7 -1
  511. package/dist/dimensional/dimension-spec.d.ts.map +1 -1
  512. package/dist/dimensional/dimension-spec.js +131 -3
  513. package/dist/dimensional/dimension-spec.js.map +1 -1
  514. package/dist/dimensional/formula-names.d.ts +30 -0
  515. package/dist/dimensional/formula-names.d.ts.map +1 -0
  516. package/dist/dimensional/formula-names.js +37 -0
  517. package/dist/dimensional/formula-names.js.map +1 -0
  518. package/dist/dimensional/natural-units.d.ts +33 -0
  519. package/dist/dimensional/natural-units.d.ts.map +1 -0
  520. package/dist/dimensional/natural-units.js +58 -0
  521. package/dist/dimensional/natural-units.js.map +1 -0
  522. package/dist/{composition → dimensional}/symbolic-constants.d.ts +21 -2
  523. package/dist/dimensional/symbolic-constants.d.ts.map +1 -0
  524. package/dist/dimensional/symbolic-constants.js +116 -0
  525. package/dist/dimensional/symbolic-constants.js.map +1 -0
  526. package/dist/dimensional/unit-convention.d.ts +30 -0
  527. package/dist/dimensional/unit-convention.d.ts.map +1 -0
  528. package/dist/dimensional/unit-convention.js +60 -0
  529. package/dist/dimensional/unit-convention.js.map +1 -0
  530. package/dist/dimensional/units.d.ts +43 -0
  531. package/dist/dimensional/units.d.ts.map +1 -0
  532. package/dist/dimensional/units.js +197 -0
  533. package/dist/dimensional/units.js.map +1 -0
  534. package/dist/index.d.ts +8 -4
  535. package/dist/index.d.ts.map +1 -1
  536. package/dist/index.js +7 -3
  537. package/dist/index.js.map +1 -1
  538. package/dist/numerical/bianchi-residual.d.ts +55 -0
  539. package/dist/numerical/bianchi-residual.d.ts.map +1 -0
  540. package/dist/numerical/bianchi-residual.js +79 -0
  541. package/dist/numerical/bianchi-residual.js.map +1 -0
  542. package/dist/numerical/binding-value.d.ts +67 -0
  543. package/dist/numerical/binding-value.d.ts.map +1 -0
  544. package/dist/numerical/binding-value.js +347 -0
  545. package/dist/numerical/binding-value.js.map +1 -0
  546. package/dist/numerical/connection-lowering-helpers.d.ts.map +1 -1
  547. package/dist/numerical/connection-lowering-helpers.js +13 -26
  548. package/dist/numerical/connection-lowering-helpers.js.map +1 -1
  549. package/dist/numerical/curvature-lowering-helpers.d.ts.map +1 -1
  550. package/dist/numerical/curvature-lowering-helpers.js +9 -0
  551. package/dist/numerical/curvature-lowering-helpers.js.map +1 -1
  552. package/dist/numerical/float64-engine.d.ts.map +1 -1
  553. package/dist/numerical/float64-engine.js +23 -16
  554. package/dist/numerical/float64-engine.js.map +1 -1
  555. package/dist/numerical/formula-dimension.d.ts +7 -0
  556. package/dist/numerical/formula-dimension.d.ts.map +1 -1
  557. package/dist/numerical/formula-dimension.js +23 -6
  558. package/dist/numerical/formula-dimension.js.map +1 -1
  559. package/dist/numerical/formula-mathts.d.ts.map +1 -1
  560. package/dist/numerical/formula-mathts.js +68 -6
  561. package/dist/numerical/formula-mathts.js.map +1 -1
  562. package/dist/numerical/formula.d.ts +27 -0
  563. package/dist/numerical/formula.d.ts.map +1 -1
  564. package/dist/numerical/formula.js +68 -6
  565. package/dist/numerical/formula.js.map +1 -1
  566. package/dist/numerical/spacetime-metrics.d.ts +243 -0
  567. package/dist/numerical/spacetime-metrics.d.ts.map +1 -0
  568. package/dist/numerical/spacetime-metrics.js +1056 -0
  569. package/dist/numerical/spacetime-metrics.js.map +1 -0
  570. package/dist/relations/composition-table.d.ts +61 -0
  571. package/dist/relations/composition-table.d.ts.map +1 -0
  572. package/dist/relations/composition-table.js +140 -0
  573. package/dist/relations/composition-table.js.map +1 -0
  574. package/dist/relations/conventions.d.ts +63 -0
  575. package/dist/relations/conventions.d.ts.map +1 -0
  576. package/dist/relations/conventions.js +93 -0
  577. package/dist/relations/conventions.js.map +1 -0
  578. package/dist/relations/regime.d.ts +159 -0
  579. package/dist/relations/regime.d.ts.map +1 -0
  580. package/dist/relations/regime.js +304 -0
  581. package/dist/relations/regime.js.map +1 -0
  582. package/dist/relations/types.d.ts +259 -0
  583. package/dist/relations/types.d.ts.map +1 -0
  584. package/dist/relations/types.js +37 -0
  585. package/dist/relations/types.js.map +1 -0
  586. package/package.json +166 -163
  587. package/dist/composition/poster-source.d.ts.map +0 -1
  588. package/dist/composition/poster-source.js.map +0 -1
  589. package/dist/composition/symbolic-constants.d.ts.map +0 -1
  590. package/dist/composition/symbolic-constants.js +0 -65
  591. package/dist/composition/symbolic-constants.js.map +0 -1
@@ -2,26 +2,855 @@ import { registerCommand } from '../command.js';
2
2
  import { CliError, EXIT_CHECK_FAILED } from '../errors.js';
3
3
  import { emitJson } from '../output.js';
4
4
  import { parseAt, resolveAtPoint, showInequality } from './regime.js';
5
+ import { readBinding } from '../../numerical/binding-value.js';
6
+ import { explainsRefusal, missingForComposite, routeClaim, selectRoute, transportReport } from './_atlas-route.js';
5
7
  const FLAGS = [
6
8
  { name: '--at', valueStyle: 'either', repeatable: true },
9
+ { name: '--sweep', valueStyle: 'either' },
10
+ { name: '--compare', valueStyle: 'either' },
11
+ { name: '--csv', valueStyle: 'none' },
12
+ { name: '--tolerance', valueStyle: 'attached' },
7
13
  { name: '--json', valueStyle: 'none' },
8
14
  ];
9
- const HELP = `upt path <from> <to> [--at group=value ...] [--json]
10
- The chain of bridges from one model to another, the relation the chain
11
- composes to, the composed (K, delta) with the norm it holds in,
12
- whether every bridge's REGIME holds at --at (the bound is claimed only
13
- inside it), and whether every horizon still holds (pass t=<time> plus
14
- the parameters, e.g. --at theta0=0.2 T0=1 t=10).
15
- When the composition table declines to compose the relations, the path
16
- carries NO bound: the command prints 'no composite claim' and exits 0.
17
- That refusal is the answer, and no number is invented in its place.
18
- e.g. upt path model-pendulum model-spring --at theta0=0.2 T0=1 t=10`;
15
+ const HELP = `upt path <from> <to> [--at group=value ...] [--tolerance=[observable:]EPS]
16
+ [--sweep name=lo:hi:n[:log]] [--compare=<to2>] [--csv] [--json]
17
+ The chain of bridges from one model to another (across families when
18
+ a bridge ends in another family's model), the relation the chain
19
+ composes to, the composed (K, delta) with the norm it holds in,
20
+ whether every bridge's REGIME holds at --at (the bound is claimed only
21
+ inside it), and whether every horizon still holds (pass t=<time> plus
22
+ the parameters, e.g. --at theta0=0.2 T0=1 t=10).
23
+ When the composition table declines to compose the relations, the path
24
+ carries NO bound: the command prints 'no composite claim' and exits 0.
25
+ That refusal is the answer, and no number is invented in its place.
26
+ An exact equivalence carries a bound only in a norm it DECLARES a
27
+ transport of, in the declared direction, with a witness (ab-spring-lc:
28
+ relative period error, spring → lc; the horizon is restated on the
29
+ circuit clock). Any other norm or direction is refused as
30
+ 'norm-not-stated', and the refusal names the missing declaration.
31
+ --at values are numbers, units, or constant expressions (theta0=pi/2).
32
+ A regime or horizon that was checked and failed prints no bound
33
+ number: the domain supremum is not a claim at that point.
34
+ --sweep name=lo:hi:n[:log] evaluates the path at n samples (2 to 200,
35
+ endpoints included) of one parameter not fixed by --at: per row the
36
+ regime, the horizon and the closed-form point error. A row outside a
37
+ regime or past a horizon carries no error, because no bound is claimed
38
+ there. A sweep exits 0: each row is its own verdict. --csv writes the
39
+ rows as CSV.
40
+ --compare=<to2> (with --sweep) sweeps a second route from the same
41
+ source beside the first: per row each route's regime, horizon and
42
+ error in its OWN norm (never compared with the other's), and NEITHER,
43
+ with no number, where no encoded limit applies.
44
+ --tolerance=EPS asks whether the path is accurate enough: ADEQUATE only
45
+ when every regime holds, every horizon holds at the given t, and the
46
+ closed-form point error is <= EPS; INADEQUATE (exit 3) when any of
47
+ them fails; UNDETERMINED when the point does not settle it. EPS is in
48
+ the bound's own norm. With --sweep, each row is judged.
49
+ --tolerance=<observable>:EPS asks in ANOTHER observable, only through
50
+ a translation the first bridge declares (ab-pendulum-linear: phase, the
51
+ exact accumulated error, and position, an upper bound on |θ − θ_lin|;
52
+ both in rad, t in the unit of T0), carried past each later bridge only
53
+ by that bridge's declared carriage (ab-spring-lc carries phase). It
54
+ reports the horizon t* at which the error reaches EPS, ADEQUATE iff
55
+ t <= t* (inclusive; past t* an upper bound is UNDETERMINED, not
56
+ INADEQUATE), and the evidence of the translation and of each carriage,
57
+ derived by running their witnesses, plus the translation's witness
58
+ run at this point with a control. Otherwise it is UNDETERMINED.
59
+ A route may cross families. A bound crosses only through the relation,
60
+ uniformity, Lipschitz, and norm gates, and, across families, only
61
+ through a witnessed norm transport; a matching norm name is not one.
62
+ A bridge with two or more premises is named when both models appear
63
+ among its premises and its conclusion. That line is the bridge; it is
64
+ not a step of the chain, and a missing chain still exits 0.
65
+ e.g. upt path model-pendulum model-spring --at theta0=0.2 T0=1 t=10
66
+ upt path model-pendulum model-spring --at T0=1 t=10 --sweep theta0=0.1:0.8:8
67
+ upt path model-pendulum model-lc --at theta0=0.3 T0=2 t=5 --tolerance=phase:0.1
68
+ upt path model-telegraph model-fick --compare=model-wave-1d --at D=1 q=1 t=1 --sweep tau=0.001:1000:13:log`;
19
69
  const EPISTEMICS = 'a path EXISTING is not a warrant: the bound is the warrant. A no-claim carries no number, ' +
20
70
  'and none is synthesized for it.';
71
+ /** Bridges with two or more premises that mention both endpoints. Not a chain. */
72
+ function multiPremiseBridges(api, from, to) {
73
+ const rows = [];
74
+ const seen = new Set();
75
+ for (const f of api.ATLAS_FAMILIES) {
76
+ for (const b of f.bridges) {
77
+ if (b.premises.length < 2 || seen.has(b.id))
78
+ continue;
79
+ const ends = new Set([...b.premises, b.conclusion]);
80
+ if (!ends.has(from) || !ends.has(to))
81
+ continue;
82
+ seen.add(b.id);
83
+ rows.push({ id: b.id, premises: [...b.premises], conclusion: b.conclusion });
84
+ }
85
+ }
86
+ return rows;
87
+ }
88
+ function printMultiPremise(out, rows) {
89
+ for (const b of rows) {
90
+ out(` multi-premise bridge: ${b.id}: ${b.premises.join(' + ')} → ${b.conclusion}`);
91
+ }
92
+ if (rows.length > 0) {
93
+ out(' A path composes one premise at a time. This bridge needs every premise named above.');
94
+ }
95
+ }
21
96
  /** The literal phrase the no-composite-claim case must print. */
22
97
  const NO_COMPOSITE_PHRASE = 'no composite claim';
98
+ const MAX_SAMPLES = 200;
99
+ /** `name=lo:hi:n` or `name=lo:hi:n:log`, bounded; the endpoints are both sampled. */
100
+ export function parseSweep(spec) {
101
+ const m = /^([^=\s]+)=([^:]+):([^:]+):([^:]+)(?::(linear|log))?$/.exec(spec);
102
+ if (m === null)
103
+ throw new CliError(`upt path: --sweep '${spec}' is not name=lo:hi:n[:log], e.g. --sweep theta0=0.05:0.9:18`);
104
+ const [, name, loS, hiS, nS, sp] = m;
105
+ const endpoint = (raw) => {
106
+ try {
107
+ return readBinding(raw).value;
108
+ }
109
+ catch {
110
+ return Number.NaN;
111
+ }
112
+ };
113
+ const lo = endpoint(loS);
114
+ const hi = endpoint(hiS);
115
+ const n = Number(nS);
116
+ const spacing = sp === 'log' ? 'log' : 'linear';
117
+ if (!Number.isFinite(lo) || !Number.isFinite(hi) || !(lo < hi)) {
118
+ throw new CliError(`upt path: --sweep '${spec}' needs finite lo < hi`);
119
+ }
120
+ if (!Number.isInteger(n) || n < 2 || n > MAX_SAMPLES) {
121
+ throw new CliError(`upt path: --sweep '${spec}' needs an integer sample count from 2 to ${MAX_SAMPLES}`);
122
+ }
123
+ if (spacing === 'log' && lo <= 0)
124
+ throw new CliError(`upt path: --sweep '${spec}' is log-spaced, so lo must be > 0`);
125
+ const values = Array.from({ length: n }, (_, i) => spacing === 'log' ? lo * (hi / lo) ** (i / (n - 1)) : lo + ((hi - lo) * i) / (n - 1));
126
+ return { name, values, spacing };
127
+ }
128
+ function modelFamilyOf(api, id) {
129
+ if (id === null)
130
+ return undefined;
131
+ return api.ATLAS_FAMILIES.find((f) => f.models.some((m) => m.id === id))?.family;
132
+ }
133
+ function filingFamilyOf(api, id) {
134
+ return api.ATLAS_FAMILIES.find((f) => f.bridges.some((b) => b.id === id))?.family;
135
+ }
136
+ /** The model a step leaves, entered at `entry`. An exact step may leave by either end. */
137
+ function leaveModel(bridge, entry) {
138
+ if (bridge.premises.length !== 1)
139
+ return null;
140
+ const premise = bridge.premises[0];
141
+ if (entry === premise)
142
+ return bridge.conclusion;
143
+ if (bridge.relation === 'exact-equivalence' && entry === bridge.conclusion)
144
+ return premise;
145
+ return null;
146
+ }
147
+ /**
148
+ * Families of the models a route visits, in order, starting at `from`.
149
+ * Adjacent duplicates collapse. A family visited again after a different one
150
+ * stays in the list. `family` on each step stays the filing family.
151
+ */
152
+ function routeFamilyLabels(api, bridges, from) {
153
+ const modelFamilies = [];
154
+ const push = (id) => {
155
+ const family = modelFamilyOf(api, id);
156
+ if (family !== undefined && modelFamilies[modelFamilies.length - 1] !== family)
157
+ modelFamilies.push(family);
158
+ return family;
159
+ };
160
+ if (bridges.length === 0) {
161
+ push(from);
162
+ return { crossFamily: false, modelFamilies, steps: [] };
163
+ }
164
+ let at = from;
165
+ const steps = bridges.map((b) => {
166
+ const entry = at;
167
+ const exit = entry === null ? null : leaveModel(b, entry);
168
+ const fromModelFamily = modelFamilies.length === 0 ? push(entry) : modelFamilyOf(api, entry);
169
+ const toModelFamily = push(exit);
170
+ at = exit;
171
+ return {
172
+ id: b.id,
173
+ relation: b.relation,
174
+ from: b.premises[0],
175
+ to: b.conclusion,
176
+ family: filingFamilyOf(api, b.id),
177
+ fromModelFamily,
178
+ toModelFamily,
179
+ };
180
+ });
181
+ return { crossFamily: modelFamilies.length > 1, modelFamilies, steps };
182
+ }
183
+ /**
184
+ * The translation a path can use for `observable`: the first bridge's, carried
185
+ * through every later bridge only by that bridge's declared carriage of the
186
+ * same observable. A later bridge without one stops it, whatever its relation.
187
+ */
188
+ function pathTranslation(api, bridges, observable) {
189
+ const tr = api.translationsOf(bridges[0].id).find((x) => x.observable === observable);
190
+ if (tr === undefined)
191
+ return { tr: null, blockedBy: null };
192
+ const carriages = [];
193
+ for (const b of bridges.slice(1)) {
194
+ const c = api.carriageOf(b.id, observable);
195
+ if (c === undefined)
196
+ return { tr: null, blockedBy: b.id };
197
+ carriages.push(c);
198
+ }
199
+ return { tr, carriages };
200
+ }
201
+ /** The observables a path can be asked in: its first bridge's translations that every later bridge carries. */
202
+ function pathObservables(api, bridges) {
203
+ return api
204
+ .translationsOf(bridges[0].id)
205
+ .filter((t) => pathTranslation(api, bridges, t.observable).tr !== null);
206
+ }
207
+ /** What a tolerance in the bound's own norm is judged in, and what else this path can be asked in. */
208
+ function toleranceScope(api, bridges) {
209
+ const declared = pathObservables(api, bridges);
210
+ if (declared.length === 0) {
211
+ return "judged in the bound's own norm only; no translation to another observable (phase, trajectory, amplitude) is encoded for this path";
212
+ }
213
+ const asks = declared.map((t) => `--tolerance=${t.observable}:EPS (${t.unit})`).join(', ');
214
+ const through = bridges.length > 1 ? `, carried by ${bridges.slice(1).map((b) => b.id).join(', ')}` : '';
215
+ return `judged in the bound's own norm only; ${bridges[0].id} declares a translation${through}, asked with ${asks}`;
216
+ }
217
+ /**
218
+ * Whether the path is adequate for a requested tolerance at one point: every
219
+ * regime holds, every horizon holds at the given t, and the closed-form point
220
+ * error is within it. Anything the point does not settle is `undetermined`,
221
+ * never `adequate`.
222
+ * @internal
223
+ */
224
+ export function judgeTolerance(tolerance, e, tGiven) {
225
+ if (e.allRegimesHold === false)
226
+ return { verdict: 'inadequate', reason: 'outside a regime on the path: no bound is claimed' };
227
+ if (e.allRegimesHold === 'unknown')
228
+ return { verdict: 'undetermined', reason: 'a regime coordinate was not supplied' };
229
+ if (e.horizons.length > 0 && !tGiven)
230
+ return { verdict: 'undetermined', reason: 'no t= given, so the horizon was not evaluated' };
231
+ if (e.horizons.some((h) => h.holds === false))
232
+ return { verdict: 'inadequate', reason: 'past the horizon: the bound is not claimed there' };
233
+ if (e.horizons.some((h) => h.holds === null)) {
234
+ return { verdict: 'undetermined', reason: 'a family change did not restate a horizon onto the next clock' };
235
+ }
236
+ if (e.horizons.length > 0 && !e.allHold)
237
+ return { verdict: 'inadequate', reason: 'past the horizon: the bound is not claimed there' };
238
+ if (e.pointBound === null)
239
+ return { verdict: 'undetermined', reason: e.pointBoundReason ?? 'no point bound' };
240
+ return e.pointBound.delta <= tolerance
241
+ ? { verdict: 'adequate', reason: `error ${e.pointBound.delta} <= tolerance ${tolerance}` }
242
+ : { verdict: 'inadequate', reason: `error ${e.pointBound.delta} exceeds tolerance ${tolerance}` };
243
+ }
244
+ export function parseTolerance(raw) {
245
+ if (raw === undefined)
246
+ return null;
247
+ const colon = raw.indexOf(':');
248
+ const observable = colon === -1 ? null : raw.slice(0, colon);
249
+ const number = colon === -1 ? raw : raw.slice(colon + 1);
250
+ if (observable !== null && !/^[a-z][a-z-]*$/.test(observable)) {
251
+ throw new CliError(`upt path: --tolerance=${raw} is not EPS or <observable>:EPS, e.g. --tolerance=phase:0.1`);
252
+ }
253
+ let v = Number.NaN;
254
+ try {
255
+ if (number !== '')
256
+ v = readBinding(number).value;
257
+ }
258
+ catch {
259
+ v = Number.NaN;
260
+ }
261
+ if (number === '' || !Number.isFinite(v) || v <= 0)
262
+ throw new CliError(`upt path: --tolerance=${raw} must be a finite number > 0`);
263
+ return { observable, value: v };
264
+ }
265
+ const showTolerance = (r, unit) => r.observable === null ? String(r.value) : `${r.observable}:${r.value}${unit === undefined ? '' : ` ${unit}`}`;
266
+ /**
267
+ * Whether the path is adequate for a tolerance in another observable. Only a
268
+ * path whose first bridge declares a translation into that observable, and
269
+ * whose every later bridge declares that it carries it, is judged; every
270
+ * regime, the translated bridge's closed-form point value and its own horizon
271
+ * still gate it, and anything the point does not settle is `undetermined`. An
272
+ * upper-bound translation past its horizon is `undetermined` too: there the
273
+ * BOUND exceeds the tolerance, which does not show that the error does.
274
+ * @internal
275
+ */
276
+ export function judgeObservable(api, request, bridges, e, at) {
277
+ const found = pathTranslation(api, bridges, request.observable);
278
+ const tr = found.tr;
279
+ const first = bridges[0];
280
+ const bound = first.bound;
281
+ const pointValue = e.allRegimesHold === true && bound?.deltaAt !== undefined && bound.deltaAtBasis === 'closed-form' ? bound.deltaAt(at) : null;
282
+ const none = {
283
+ translation: tr,
284
+ carriages: found.tr === null ? [] : found.carriages,
285
+ boundErrorAtPoint: pointValue !== null && Number.isFinite(pointValue) ? pointValue : null,
286
+ domainSupremum: tr === null ? (bridges.length === 1 ? (bound?.delta ?? null) : null) : (bound?.delta ?? null),
287
+ observableErrorAt: null,
288
+ horizon: null,
289
+ domainHorizon: null,
290
+ bridgeHorizonHolds: null,
291
+ };
292
+ if (found.tr === null) {
293
+ const declared = api.translationsOf(first.id);
294
+ const reason = found.blockedBy !== null
295
+ ? `${first.id}'s translation into '${request.observable}' does not reach the end of this path: '${found.blockedBy}' ` +
296
+ `declares no carriage of '${request.observable}' through its map, and a translation composes only through one`
297
+ : `no translation from '${bound?.norm ?? 'no bound'}' into '${request.observable}' is encoded for ${first.id}` +
298
+ (declared.length === 0 ? '' : ` (it declares: ${declared.map((x) => x.observable).join(', ')})`);
299
+ return { verdict: 'undetermined', reason, ...none };
300
+ }
301
+ if (e.allRegimesHold === false)
302
+ return { verdict: 'inadequate', reason: 'outside a regime on the path: no bound is claimed', ...none };
303
+ if (e.allRegimesHold === 'unknown')
304
+ return { verdict: 'undetermined', reason: 'a regime coordinate was not supplied', ...none };
305
+ if (bound === undefined || bound.deltaAt === undefined || bound.deltaAtBasis !== 'closed-form') {
306
+ return { verdict: 'undetermined', reason: `${first.id} states no closed-form point value to translate`, ...none };
307
+ }
308
+ if (none.boundErrorAtPoint === null) {
309
+ return { verdict: 'undetermined', reason: 'a parameter the point value needs was not supplied', ...none };
310
+ }
311
+ const missing = found.tr.parameters.filter((p) => at[p] === undefined);
312
+ if (missing.length > 0) {
313
+ return { verdict: 'undetermined', reason: `the translation needs ${missing.join(', ')} (via --at)`, ...none };
314
+ }
315
+ const tx = found.tr;
316
+ const eps = none.boundErrorAtPoint;
317
+ const floor = tx.floor?.(eps);
318
+ if (floor !== undefined && floor > request.value) {
319
+ return {
320
+ verdict: 'undetermined',
321
+ reason: `the translation's time-independent term is ${floor} ${tx.unit} at this point, above the tolerance: this bound ` +
322
+ 'certifies no t, which is not a finding that the error exceeds the tolerance',
323
+ ...none,
324
+ };
325
+ }
326
+ const horizon = tx.horizonFor(eps, request.value, at);
327
+ const domainHorizon = tx.horizonFor(bound.delta, request.value, at);
328
+ if (Number.isNaN(horizon))
329
+ return { verdict: 'undetermined', reason: 'the translation is undefined at this point', ...none };
330
+ const t = at['t'];
331
+ const judged = { ...none, horizon, domainHorizon: Number.isNaN(domainHorizon) ? null : domainHorizon };
332
+ if (t === undefined) {
333
+ return {
334
+ verdict: 'undetermined',
335
+ reason: `no t= given; the ${tx.observable} horizon here is t* = ${horizon} (${tx.timeUnit})`,
336
+ ...judged,
337
+ };
338
+ }
339
+ const observableErrorAt = tx.errorAt(eps, t, at);
340
+ const bridgeHorizonHolds = bound.horizonHolds(t, at);
341
+ const withT = { ...judged, observableErrorAt, bridgeHorizonHolds };
342
+ if (!bridgeHorizonHolds) {
343
+ return { verdict: 'inadequate', reason: `past ${first.id}'s own horizon (${bound.horizon}): the bound is not claimed there`, ...withT };
344
+ }
345
+ const what = tx.errorKind === 'upper-bound' ? `bound on the ${tx.observable} error` : `accumulated ${tx.observable} error`;
346
+ if (t <= horizon) {
347
+ return { verdict: 'adequate', reason: `t = ${t} <= t* = ${horizon}: ${what} ${observableErrorAt} ${tx.unit} <= ${request.value}`, ...withT };
348
+ }
349
+ return tx.errorKind === 'upper-bound'
350
+ ? {
351
+ verdict: 'undetermined',
352
+ reason: `t = ${t} > t* = ${horizon}: the ${what} is ${observableErrorAt} ${tx.unit}, above ${request.value}; ` +
353
+ 'the error itself may still be within it, so this is not shown inadequate',
354
+ ...withT,
355
+ }
356
+ : { verdict: 'inadequate', reason: `t = ${t} > t* = ${horizon}: ${what} ${observableErrorAt} ${tx.unit} exceeds ${request.value}`, ...withT };
357
+ }
358
+ /** Evidence DERIVED by running a record's witnesses now. */
359
+ function runWitnesses(api, witnesses, checks) {
360
+ const runs = checks.map((c) => api.runTranslationCheck(c));
361
+ const passing = new Set(runs.filter((r) => r.status === 'checked').map((r) => r.witnessId));
362
+ return {
363
+ tags: [...api.deriveEvidence({ witnesses }, passing)].sort(),
364
+ witnesses: runs.map((r) => ({
365
+ id: r.witnessId,
366
+ claim: witnesses.find((w) => w.id === r.witnessId)?.tolerance ?? null,
367
+ status: r.status,
368
+ detail: r.detail,
369
+ })),
370
+ };
371
+ }
372
+ /** The translation's evidence, and each carriage's, DERIVED by running their witnesses now; never the bound's. */
373
+ function translationEvidence(api, tr, carriages) {
374
+ return {
375
+ ...runWitnesses(api, tr.witnesses, tr.checks),
376
+ note: "the translation's own evidence, distinct from the bound's; it carries no formal reference",
377
+ carriages: carriages.map((c) => ({
378
+ bridgeId: c.bridgeId,
379
+ observable: c.observable,
380
+ map: c.map,
381
+ derivation: c.derivation,
382
+ premisesNotChecked: [...c.premises],
383
+ timeMap: c.timeMap,
384
+ ...runWitnesses(api, c.witnesses, c.checks),
385
+ })),
386
+ };
387
+ }
388
+ /**
389
+ * The translation's witness run at THIS point, bounded in cost, and, when
390
+ * asked, its control: the same measurement against a plausible wrong map. A
391
+ * control that is not refuted here shows this witness cannot tell the two
392
+ * maps apart at this point, and is reported as such.
393
+ */
394
+ function pointWitness(api, tr, eps, withControl) {
395
+ const pc = tr.pointCheck(eps);
396
+ if ('unavailable' in pc)
397
+ return { unavailable: pc.unavailable };
398
+ const r = api.runTranslationCheck(pc.check);
399
+ const base = { id: r.witnessId, claim: pc.claim, status: r.status, detail: r.detail };
400
+ if (!withControl)
401
+ return base;
402
+ const c = api.runTranslationCheck(pc.control);
403
+ return {
404
+ ...base,
405
+ control: { id: c.witnessId, claim: pc.controlClaim, status: c.status, detail: c.detail, discriminates: c.status === 'refuted' },
406
+ };
407
+ }
408
+ /** A judgement with its point witness; a witness refuted here withdraws any verdict to undetermined. @internal */
409
+ export function judgeAtPoint(api, request, bridges, e, at, withControl) {
410
+ const j = judgeObservable(api, request, bridges, e, at);
411
+ if (j.translation === null || j.horizon === null || j.boundErrorAtPoint === null)
412
+ return { ...j, pointWitness: null };
413
+ const pw = pointWitness(api, j.translation, j.boundErrorAtPoint, withControl);
414
+ if ('status' in pw && pw.status === 'refuted') {
415
+ return {
416
+ ...j,
417
+ verdict: 'undetermined',
418
+ reason: `the translation's witness at this point (${pw.id}) is refuted, so its horizon is not relied on here: ${pw.detail}`,
419
+ pointWitness: pw,
420
+ };
421
+ }
422
+ return { ...j, pointWitness: pw };
423
+ }
424
+ function thresholdOrigin(request, tr, bridges) {
425
+ return (`t* is derived from the requested tolerance ${request.value} ${tr.unit} through the translation; ` +
426
+ `${bridges[0].id}'s own horizon (${bridges[0].bound.horizon}) is the record's fixed threshold, judged separately`);
427
+ }
428
+ function observableReport(request, j, bridges, evidence) {
429
+ const tr = j.translation;
430
+ return {
431
+ observable: request.observable,
432
+ value: request.value,
433
+ verdict: j.verdict,
434
+ reason: j.reason,
435
+ ...(tr === null
436
+ ? { translation: null }
437
+ : {
438
+ unit: tr.unit,
439
+ translation: {
440
+ bridgeId: tr.bridgeId,
441
+ fromNorm: tr.fromNorm,
442
+ errorKind: tr.errorKind,
443
+ definition: tr.definition,
444
+ derivation: tr.derivation,
445
+ premisesNotChecked: [...tr.premises],
446
+ timeUnit: tr.timeUnit,
447
+ notCovered: [...tr.notCovered],
448
+ carriedBy: j.carriages.map((c) => c.bridgeId),
449
+ },
450
+ boundErrorAtPoint: j.boundErrorAtPoint,
451
+ domainSupremum: j.domainSupremum,
452
+ observableErrorAt: j.observableErrorAt,
453
+ horizon: j.horizon,
454
+ domainHorizon: j.domainHorizon,
455
+ horizonConvention: tr.boundary,
456
+ thresholdOrigin: thresholdOrigin(request, tr, bridges),
457
+ bridgeHorizon: { horizon: bridges[0].bound.horizon, holds: j.bridgeHorizonHolds },
458
+ evidence,
459
+ pointWitness: j.pointWitness,
460
+ }),
461
+ };
462
+ }
463
+ function printObservable(out, request, j, bridges, evidence, composite) {
464
+ const tr = j.translation;
465
+ out(` tolerance ${showTolerance(request, tr?.unit)}: ${j.verdict.toUpperCase()} — ${j.reason}`);
466
+ if (tr === null)
467
+ return;
468
+ const show = (v) => (v === null ? 'not evaluated' : String(v));
469
+ const first = bridges[0];
470
+ const kind = tr.errorKind === 'upper-bound' ? 'an UPPER BOUND on the error, not the error' : 'the error itself';
471
+ out(` translation (${tr.bridgeId}; ${kind}): ${tr.fromNorm} → ${tr.observable}: ${tr.definition}`);
472
+ out(` ${tr.derivation}`);
473
+ out(` premises not machine-checked: ${tr.premises.join('; ')}`);
474
+ for (const c of evidence?.carriages ?? []) {
475
+ out(` carried by ${c.bridgeId} (its declared carriage of ${c.observable}): ${c.map}`);
476
+ out(` ${c.derivation}; ${c.timeMap}`);
477
+ out(` premises not machine-checked: ${c.premisesNotChecked.join('; ')}`);
478
+ }
479
+ if (j.carriages.length > 0 && composite === 'no-claim') {
480
+ out(` the composite bound is still '${NO_COMPOSITE_PHRASE}': the composition table is not consulted or widened; ` +
481
+ `this judgement is in ${tr.observable} only, through the declared carriage`);
482
+ }
483
+ else if (j.carriages.length > 0) {
484
+ out(` the composite bound above is in '${first.bound.norm}', carried by a declared norm transport; ` +
485
+ `this judgement is a separate claim in ${tr.observable}, through the declared carriage, and neither implies the other`);
486
+ }
487
+ const what = tr.errorKind === 'upper-bound' ? `bound on the ${tr.observable} error` : `accumulated ${tr.observable} error`;
488
+ out(` ${first.bound.norm} at this point: ${show(j.boundErrorAtPoint)}; domain supremum: ${show(j.domainSupremum)}`);
489
+ out(` ${what} at t: ${j.observableErrorAt === null ? 'not evaluated' : `${j.observableErrorAt} ${tr.unit}`}`);
490
+ out(` ${tr.observable} horizon t* at this point: ${show(j.horizon)} (${tr.timeUnit}; ${tr.boundary})`);
491
+ out(` ${tr.observable} horizon over the whole domain ${first.bound.domain} (from the supremum): ${show(j.domainHorizon)}`);
492
+ out(` threshold origin: ${thresholdOrigin(request, tr, bridges)}`);
493
+ out(` ${first.id}'s own horizon at t: ${j.bridgeHorizonHolds === null ? 'not evaluated' : j.bridgeHorizonHolds ? 'holds' : 'VIOLATED'} — ${first.bound.horizon}`);
494
+ out(` not covered by this translation: ${tr.notCovered.join('; ')}`);
495
+ if (evidence !== null) {
496
+ out(` translation evidence (fixture, the regression anchor): ${evidence.tags.join(', ')} (${evidence.note})`);
497
+ for (const w of evidence.witnesses)
498
+ out(` - ${w.id} [numeric, run now; ${w.claim ?? 'no claim stated'}]: ${w.status} — ${w.detail}`);
499
+ for (const c of evidence.carriages) {
500
+ out(` carriage evidence (${c.bridgeId}): ${c.tags.join(', ')}`);
501
+ for (const w of c.witnesses)
502
+ out(` - ${w.id} [numeric, run now; ${w.claim ?? 'no claim stated'}]: ${w.status} — ${w.detail}`);
503
+ }
504
+ }
505
+ const pw = j.pointWitness;
506
+ if (pw === null) {
507
+ out(' witness at this point: not run (the translation was not evaluated here)');
508
+ }
509
+ else if ('unavailable' in pw) {
510
+ out(` witness at this point: not run — ${pw.unavailable}`);
511
+ }
512
+ else {
513
+ out(` witness at this point: ${pw.id} [${pw.claim}]: ${pw.status} — ${pw.detail}`);
514
+ if (pw.control !== undefined) {
515
+ out(` control ${pw.control.id} [${pw.control.claim}]: ${pw.control.status}` +
516
+ (pw.control.discriminates
517
+ ? ' — the check fails on the wrong map here, so it can fail'
518
+ : ' — NOT refuted: at this point the witness cannot tell the wrong map from the declared one') +
519
+ ` (${pw.control.detail})`);
520
+ }
521
+ }
522
+ }
523
+ /** A row's status on one path: what the point command would say there, with no error where no bound is claimed. */
524
+ function sweepCell(e, result) {
525
+ const regime = e.allRegimesHold === true ? 'holds' : e.allRegimesHold === false ? 'violated' : 'unknown';
526
+ const horizon = e.horizons.length === 0 || e.horizons[0].holds === null ? 'not-evaluated' : e.allHold ? 'holds' : 'violated';
527
+ let error = e.pointBound?.delta ?? null;
528
+ let reason = result.kind === 'no-claim' ? 'no composite claim' : e.pointBoundReason;
529
+ if (error !== null && horizon === 'violated') {
530
+ error = null;
531
+ reason = 'past the horizon: the bound is not claimed there';
532
+ }
533
+ return { regime, horizon, error, reason };
534
+ }
535
+ function checkSweep(spec, point) {
536
+ const sweep = parseSweep(spec);
537
+ if (sweep.name in point) {
538
+ throw new CliError(`upt path: '${sweep.name}' is both swept and fixed by --at; give it one role`);
539
+ }
540
+ return sweep;
541
+ }
542
+ /**
543
+ * One path evaluated at every sample of one parameter. Each row is what the
544
+ * point command would say there; no trajectory is interpolated. A row outside
545
+ * a regime or past a horizon carries no error, because no bound is claimed
546
+ * there, and it is not joined to its neighbours.
547
+ */
548
+ function runSweep(ctx, s) {
549
+ const { out, args } = ctx;
550
+ const sweep = checkSweep(s.spec, s.point);
551
+ const rows = sweep.values.map((value) => {
552
+ const at = { ...s.point, [sweep.name]: value };
553
+ const e = s.evaluateAt(at);
554
+ const { regime, horizon, error, reason } = sweepCell(e, s.result);
555
+ const judged = s.tolerance === null ? null : s.judgeRow(e, at, 't' in s.point || sweep.name === 't');
556
+ const pw = judged?.pointWitness;
557
+ return {
558
+ value,
559
+ regime,
560
+ horizon,
561
+ error,
562
+ ...(error === null && reason !== null ? { reason } : {}),
563
+ ...(judged === null ? {} : { adequacy: judged.verdict }),
564
+ ...(judged?.horizon === undefined ? {} : { observableHorizon: judged.horizon, observableError: judged.observableErrorAt ?? null }),
565
+ ...(pw === undefined || pw === null ? {} : { pointWitness: 'unavailable' in pw ? { status: 'not-run', reason: pw.unavailable } : { id: pw.id, status: pw.status } }),
566
+ };
567
+ });
568
+ const tally = (k, v) => rows.filter((r) => r[k] === v).length;
569
+ const integrated = s.translation === null
570
+ ? 'nothing is integrated and no trajectory is produced'
571
+ : `the only integration is the translation's witness, run at each judged row's point (${s.translation.tr.bridgeId}, ` +
572
+ 'RK4, bounded); no trajectory is output';
573
+ const evaluated = s.result.kind === 'bound'
574
+ ? `the path's closed-form point bound at each sample (the exact error of the reduced model in the norm '${s.result.norm ?? 'none stated'}'); ` +
575
+ integrated
576
+ : `no error: the path carries no composite claim ('${s.result.reason}'); rows report regime and horizon status only` +
577
+ (s.translation === null ? '' : `; ${integrated}`);
578
+ const fixed = Object.entries(s.point).map(([k, v]) => `${k}=${v}`).join(' ') || 'none';
579
+ if (args.flags.has('json')) {
580
+ emitJson({
581
+ command: 'path',
582
+ epistemics: EPISTEMICS,
583
+ options: { from: s.from, to: s.to, at: s.point, sweep: { parameter: sweep.name, spacing: sweep.spacing, samples: rows.length } },
584
+ result: {
585
+ path: s.bridges.map((b) => b.id),
586
+ kind: s.result.kind,
587
+ ...(s.result.kind === 'bound' ? { domainSupremum: s.result.bound, norm: s.result.norm } : { reason: s.result.reason }),
588
+ evaluated,
589
+ ...(s.tolerance === null
590
+ ? {}
591
+ : s.tolerance.observable === null
592
+ ? { tolerance: s.tolerance.value, toleranceScope: s.scope }
593
+ : {
594
+ tolerance: s.tolerance.value,
595
+ toleranceObservable: s.tolerance.observable,
596
+ ...(s.translation === null
597
+ ? { translation: null }
598
+ : {
599
+ translation: {
600
+ bridgeId: s.translation.tr.bridgeId,
601
+ unit: s.translation.tr.unit,
602
+ errorKind: s.translation.tr.errorKind,
603
+ boundary: s.translation.tr.boundary,
604
+ evidence: s.translation.evidence,
605
+ },
606
+ }),
607
+ }),
608
+ rows,
609
+ tally: {
610
+ inRegime: tally('regime', 'holds'),
611
+ outsideRegime: tally('regime', 'violated'),
612
+ regimeUnknown: tally('regime', 'unknown'),
613
+ pastHorizon: tally('horizon', 'violated'),
614
+ },
615
+ },
616
+ }, ctx.write);
617
+ return 0;
618
+ }
619
+ if (args.flags.has('csv')) {
620
+ const tol = s.tolerance !== null;
621
+ const pwCol = s.translation !== null;
622
+ ctx.write(`${sweep.name},regime,horizon,error,${tol ? 'adequacy,' : ''}${pwCol ? 'point_witness,' : ''}reason\n`);
623
+ for (const r of rows) {
624
+ ctx.write(`${r.value},${r.regime},${r.horizon},${r.error ?? ''},${tol ? `${r.adequacy},` : ''}${pwCol ? `${r.pointWitness?.status ?? ''},` : ''}` +
625
+ `"${(r.reason ?? '').replace(/"/g, '""')}"\n`);
626
+ }
627
+ return 0;
628
+ }
629
+ out(`\nupt path ${s.from} → ${s.to} — sweep ${sweep.name} over [${sweep.values[0]}, ${sweep.values[sweep.values.length - 1]}], ${rows.length} samples (${sweep.spacing}); fixed: ${fixed}`);
630
+ out(` path: ${s.bridges.map((b) => b.id).join(' → ')}`);
631
+ if (s.result.kind === 'bound')
632
+ out(` domain supremum: K = ${s.result.bound.K} · delta = ${s.result.bound.delta}`);
633
+ out(` evaluated: ${evaluated}`);
634
+ if (s.tolerance !== null && s.tolerance.observable === null)
635
+ out(` tolerance: ${s.tolerance.value}, ${s.scope}`);
636
+ if (s.tolerance !== null && s.tolerance.observable !== null) {
637
+ const carried = s.translation?.evidence.carriages.map((c) => c.bridgeId) ?? [];
638
+ out(s.translation === null
639
+ ? ` tolerance: ${showTolerance(s.tolerance)} — no translation into '${s.tolerance.observable}' reaches the end of this path; every row is undetermined`
640
+ : ` tolerance: ${showTolerance(s.tolerance, s.translation.tr.unit)} through ${s.translation.tr.bridgeId}'s declared translation` +
641
+ `${carried.length > 0 ? `, carried by ${carried.join(', ')}` : ''} (${s.translation.tr.boundary}); ` +
642
+ `translation evidence (fixture): ${s.translation.evidence.tags.join(', ')}`);
643
+ }
644
+ const tolCol = s.tolerance === null ? '' : `${'adequacy'.padEnd(13)} `;
645
+ const pwCol = s.translation === null ? '' : `${'point witness'.padEnd(14)} `;
646
+ out(` ${sweep.name.padEnd(12)} ${'regime'.padEnd(10)} ${'horizon'.padEnd(14)} ${tolCol}${pwCol}error`);
647
+ for (const r of rows) {
648
+ const a = r.adequacy === undefined ? '' : `${r.adequacy.padEnd(13)} `;
649
+ const w = s.translation === null ? '' : `${(r.pointWitness?.status ?? '—').padEnd(14)} `;
650
+ out(` ${String(Number(r.value.toPrecision(6))).padEnd(12)} ${String(r.regime).padEnd(10)} ${String(r.horizon).padEnd(14)} ${a}${w}${r.error === null ? `— ${r.reason ?? ''}` : r.error}`);
651
+ }
652
+ out(` in regime: ${tally('regime', 'holds')} · outside: ${tally('regime', 'violated')} · unknown: ${tally('regime', 'unknown')} · ` +
653
+ `past the horizon: ${tally('horizon', 'violated')} of ${rows.length}`);
654
+ out(' (a row with no error is not joined to its neighbours: no bound is claimed there. A sweep exits 0; each row is its own verdict.)');
655
+ return 0;
656
+ }
657
+ /**
658
+ * Two routes from one source, swept side by side over one parameter. Per row,
659
+ * each route is `claimed` (regime and horizon hold, and a closed-form error
660
+ * exists), `not-claimed` (a regime or horizon is violated, or the route carries
661
+ * no composite claim) or `unsettled` (a coordinate or t was not supplied). A
662
+ * row where every route is not-claimed is NEITHER: no encoded limit applies,
663
+ * and it carries no number. The two errors are in their own norms and are
664
+ * never compared with each other.
665
+ */
666
+ function runCompare(ctx, from, point, spec, sides) {
667
+ const { out, args } = ctx;
668
+ const sweep = checkSweep(spec, point);
669
+ const rows = sweep.values.map((value) => {
670
+ const at = { ...point, [sweep.name]: value };
671
+ const paths = sides.map((side) => {
672
+ const e = side.evaluateAt(at);
673
+ const cell = sweepCell(e, side.result);
674
+ const state = cell.error !== null && cell.regime === 'holds' && cell.horizon !== 'not-evaluated'
675
+ ? 'claimed'
676
+ : cell.error !== null && cell.regime === 'holds' && e.horizons.length === 0
677
+ ? 'claimed'
678
+ : cell.regime === 'violated' || cell.horizon === 'violated' || side.result.kind === 'no-claim'
679
+ ? 'not-claimed'
680
+ : 'unsettled';
681
+ return {
682
+ to: side.to,
683
+ regime: cell.regime,
684
+ horizon: cell.horizon,
685
+ state,
686
+ error: state === 'claimed' ? cell.error : null,
687
+ ...(state !== 'claimed' && cell.reason !== null ? { reason: cell.reason } : {}),
688
+ ...(state === 'unsettled' && cell.error !== null ? { reason: 'no t= given, so the horizon was not evaluated' } : {}),
689
+ };
690
+ });
691
+ const claimedBy = paths.filter((p) => p.state === 'claimed').map((p) => p.to);
692
+ const coverage = claimedBy.length > 0 ? 'covered' : paths.every((p) => p.state === 'not-claimed') ? 'neither' : 'unsettled';
693
+ return { value, paths, claimedBy, coverage };
694
+ });
695
+ const count = (c) => rows.filter((r) => r.coverage === c).length;
696
+ const tally = {
697
+ covered: count('covered'),
698
+ neither: count('neither'),
699
+ unsettled: count('unsettled'),
700
+ claimedBy: Object.fromEntries(sides.map((sd) => [sd.to, rows.filter((r) => r.claimedBy.includes(sd.to)).length])),
701
+ claimedByAll: rows.filter((r) => r.claimedBy.length === sides.length).length,
702
+ };
703
+ const evaluated = "each route's closed-form point bound at each sample, in that route's OWN norm; the two errors are different " +
704
+ 'quantities and are not compared with each other. Nothing is integrated and no trajectory is produced';
705
+ const neitherNote = `no encoded limit applies: the parent ${from} is the model to use there, and no reduced-model number is given`;
706
+ const fixed = Object.entries(point).map(([k, v]) => `${k}=${v}`).join(' ') || 'none';
707
+ if (args.flags.has('json')) {
708
+ emitJson({
709
+ command: 'path',
710
+ epistemics: EPISTEMICS,
711
+ options: {
712
+ from,
713
+ to: sides.map((sd) => sd.to),
714
+ at: point,
715
+ sweep: { parameter: sweep.name, spacing: sweep.spacing, samples: rows.length },
716
+ },
717
+ result: {
718
+ compare: sides.map((sd) => ({
719
+ to: sd.to,
720
+ path: sd.bridges.map((b) => b.id),
721
+ kind: sd.result.kind,
722
+ ...(sd.result.kind === 'bound' ? { domainSupremum: sd.result.bound, norm: sd.result.norm } : { reason: sd.result.reason }),
723
+ })),
724
+ evaluated,
725
+ neither: neitherNote,
726
+ rows,
727
+ tally,
728
+ },
729
+ }, ctx.write);
730
+ return 0;
731
+ }
732
+ if (args.flags.has('csv')) {
733
+ const head = sides.flatMap((sd) => [`${sd.to}_regime`, `${sd.to}_horizon`, `${sd.to}_state`, `${sd.to}_error`]);
734
+ ctx.write(`${sweep.name},${head.join(',')},coverage\n`);
735
+ for (const r of rows) {
736
+ const cells = r.paths.flatMap((p) => [p.regime, p.horizon, p.state, p.error ?? '']);
737
+ ctx.write(`${r.value},${cells.join(',')},${r.coverage === 'covered' ? r.claimedBy.join('+') : r.coverage}\n`);
738
+ }
739
+ return 0;
740
+ }
741
+ out(`\nupt path ${from} → ${sides.map((sd) => sd.to).join(' | ')} — sweep ${sweep.name} over [${sweep.values[0]}, ` +
742
+ `${sweep.values[sweep.values.length - 1]}], ${rows.length} samples (${sweep.spacing}); fixed: ${fixed}`);
743
+ for (const sd of sides) {
744
+ out(` → ${sd.to}: ${sd.bridges.map((b) => b.id).join(' → ')}; ` +
745
+ (sd.result.kind === 'bound' ? `norm: ${sd.result.norm ?? 'none stated'}` : `no composite claim ('${sd.result.reason}')`));
746
+ }
747
+ out(` evaluated: ${evaluated}`);
748
+ const heads = sides.map((sd) => `${sd.to}: regime/horizon error`);
749
+ const w = Math.max(30, ...heads.map((h) => h.length));
750
+ out(` ${sweep.name.padEnd(12)} ${heads.map((h) => h.padEnd(w)).join(' ')} covered by`);
751
+ for (const r of rows) {
752
+ const cells = r.paths.map((p) => `${p.regime}/${p.horizon} ${p.error === null ? '—' : Number(p.error.toPrecision(6))}`.padEnd(w));
753
+ const cov = r.coverage === 'covered' ? r.claimedBy.join(' + ') : r.coverage === 'neither' ? `NEITHER — ${neitherNote}` : 'UNSETTLED — a coordinate or t was not supplied';
754
+ out(` ${String(Number(r.value.toPrecision(6))).padEnd(12)} ${cells.join(' ')} ${cov}`);
755
+ }
756
+ out(` covered: ${tally.covered} (${sides.map((sd) => `${sd.to}: ${tally.claimedBy[sd.to]}`).join(', ')}; by all: ${tally.claimedByAll}) · ` +
757
+ `NEITHER: ${tally.neither} · unsettled: ${tally.unsettled} of ${rows.length}`);
758
+ out(' (errors to 6 significant digits, --json for full precision; a row with no error is not joined to its neighbours. A sweep exits 0.)');
759
+ return 0;
760
+ }
761
+ /** The point evaluation of one route: regimes, horizons and the proven point bound at `at`. */
762
+ function makeEvaluator(api, bridges, result) {
763
+ return (at) => {
764
+ // A horizon is read at the END of the route: a transport applied after a
765
+ // bound restates it on the later model's clock (ADR, time map).
766
+ const carried = result.kind === 'bound' ? (result.transports ?? []) : [];
767
+ const horizons = bridges.flatMap((b, i) => {
768
+ const h = api.horizonOnRoute(bridges, carried, i);
769
+ if (h === null)
770
+ return [];
771
+ // A later step in another family keeps its horizon unevaluated unless a
772
+ // transport on that step restates the clock. The earlier horizon stays
773
+ // in the time of the model where it was stated.
774
+ const blocked = api.familyChangeBlocksHorizon(bridges, carried, i);
775
+ return [
776
+ {
777
+ bridgeId: b.id,
778
+ horizon: h.horizon,
779
+ holds: at['t'] === undefined || blocked ? null : h.holds(at['t'], at),
780
+ ...(h.restatedBy.length > 0
781
+ ? { restatedBy: h.restatedBy.map((a) => ({ transport: a.transport.id, horizon: a.transport.timeMap.horizon })) }
782
+ : {}),
783
+ ...(blocked ? { unevaluated: 'cross-family' } : {}),
784
+ },
785
+ ];
786
+ });
787
+ const allHold = horizons.every((h) => h.holds === true);
788
+ // Each step is checked against its own regime. A group name whose π-groups
789
+ // differ is dropped on every step that defines it, so the supplied number
790
+ // is not applied to either definition. intersectRegimes is not called.
791
+ const regimeCollisions = api.collidingRegimeGroups(bridges.map((b) => b.regime));
792
+ const blockedGroups = new Set(regimeCollisions.map((c) => c.group));
793
+ const regimes = bridges.map((b) => {
794
+ const { values } = resolveAtPoint(at, [b.regime]);
795
+ for (const name of blockedGroups)
796
+ delete values[name];
797
+ const check = api.regimeHolds(b.regime, values);
798
+ return {
799
+ bridgeId: b.id,
800
+ ok: check.ok,
801
+ violated: check.violated.map(showInequality),
802
+ unchecked: check.unchecked.map(showInequality),
803
+ ...(b.sideConditions.length > 0 ? { premisesNotChecked: [...b.sideConditions] } : {}),
804
+ };
805
+ });
806
+ const allRegimesHold = regimes.some((r) => r.ok === false)
807
+ ? false
808
+ : regimes.some((r) => r.ok === 'unknown')
809
+ ? 'unknown'
810
+ : true;
811
+ // The bound AT the --at point (persona finding L9). Printed only when it is
812
+ // PROVEN: every regime holds, every step has a closed-form deltaAt (the exact
813
+ // error), and every value is finite. It is composed by the same rule as the
814
+ // domain supremum, by substituting each step's point value for its delta.
815
+ let pointBound = null;
816
+ let pointBoundReason = null;
817
+ if (result.kind === 'bound' && Object.keys(at).length > 0) {
818
+ const steps = bridges.filter((b) => b.bound !== undefined);
819
+ const numerical = steps.find((b) => b.bound.deltaAtBasis !== 'closed-form');
820
+ if (allRegimesHold !== true) {
821
+ pointBoundReason = 'a regime on the path is violated or unchecked';
822
+ }
823
+ else if (steps.length === 0) {
824
+ pointBoundReason = 'no step carries a bound';
825
+ }
826
+ else if (numerical !== undefined) {
827
+ pointBoundReason =
828
+ numerical.bound.deltaAt === undefined
829
+ ? `${numerical.id} states no point bound`
830
+ : `${numerical.id}'s point bound is numerically supported, not proven`;
831
+ }
832
+ else {
833
+ // A transport's K is substituted by its closed form at the point, as a
834
+ // bound's delta is (for ab-spring-lc both are the constant 1).
835
+ const atPoint = bridges.map((b) => b.bound === undefined
836
+ ? b.normTransports === undefined
837
+ ? b
838
+ : { ...b, normTransports: b.normTransports.map((nt) => ({ ...nt, K: nt.KAt(at) })) }
839
+ : { ...b, bound: { ...b.bound, delta: b.bound.deltaAt(at) } });
840
+ const composed = atPoint.every((b) => b.bound === undefined || Number.isFinite(b.bound.delta))
841
+ ? api.boundPath(atPoint)
842
+ : null;
843
+ if (composed !== null && composed.kind === 'bound')
844
+ pointBound = composed.bound;
845
+ else
846
+ pointBoundReason = 'a parameter the point bound needs was not supplied';
847
+ }
848
+ }
849
+ return { regimes, allRegimesHold, horizons, allHold, pointBound, pointBoundReason, regimeCollisions };
850
+ };
851
+ }
23
852
  async function run(ctx) {
24
- const { args, api, out } = ctx;
853
+ const { args, api, out, err } = ctx;
25
854
  const wantJson = args.flags.has('json');
26
855
  const endpoints = args.positionals.filter((p) => !p.includes('='));
27
856
  const assignments = [...(args.flags.get('at') ?? []), ...args.positionals.filter((p) => p.includes('='))];
@@ -30,135 +859,158 @@ async function run(ctx) {
30
859
  '`upt path model-pendulum model-spring`');
31
860
  }
32
861
  const [from, to] = endpoints;
33
- const point = parseAt(assignments, 'path');
862
+ const atNotes = [];
863
+ const point = parseAt(assignments, 'path', atNotes);
864
+ for (const note of atNotes)
865
+ err(note);
34
866
  const t = point['t'];
35
- // The family is read off the endpoints — this used to be the oscillator
36
- // family, hard-coded, so no path in the diffusion or wave families could be
37
- // asked for. A route stays inside ONE family (findPath's scope); two
38
- // endpoints in different families are reported as exactly that.
39
- const familyOf = (id) => api.ATLAS_FAMILIES.find((f) => f.models.some((m) => m.id === id))?.family;
40
- const fromFamily = familyOf(from);
41
- const toFamily = familyOf(to);
42
- if (fromFamily !== undefined && toFamily !== undefined && fromFamily !== toFamily) {
43
- throw new CliError(`upt path: '${from}' is in family '${fromFamily}' and '${to}' in '${toFamily}'; ` +
44
- 'routes are searched within one family, and cross-family routes are not supported');
45
- }
46
- const family = fromFamily ?? toFamily ?? api.ATLAS_FAMILIES[0].family;
47
- let bridges;
48
- try {
49
- bridges = api.findPath(family, from, to);
50
- }
51
- catch (e) {
52
- // RangeError: an unknown endpoint. Reported as a CliError (exit 1) rather
53
- // than surfaced as a crash — and NOT as `null`, which would be
54
- // indistinguishable from a genuinely disconnected pair.
55
- throw new CliError(`upt path: ${e instanceof Error ? e.message : String(e)}`);
56
- }
867
+ const { bridges, fromFamily, toFamily } = selectRoute(api, from, to, 'path');
868
+ const multi = from === to ? [] : multiPremiseBridges(api, from, to);
57
869
  if (bridges === null) {
58
870
  if (wantJson) {
59
- emitJson({ command: 'path', epistemics: EPISTEMICS, options: { from, to, at: point }, result: { path: null } }, ctx.write);
871
+ emitJson({
872
+ command: 'path',
873
+ epistemics: EPISTEMICS,
874
+ options: { from, to, at: point },
875
+ result: { path: null, ...(multi.length === 0 ? {} : { multiPremise: multi }) },
876
+ }, ctx.write);
60
877
  return 0;
61
878
  }
62
879
  out(`\nupt path ${from} → ${to}`);
63
880
  out(' no chain of bridges connects these models; there is nothing to compose.');
881
+ printMultiPremise(out, multi);
64
882
  return 0;
65
883
  }
884
+ const labels = routeFamilyLabels(api, bridges, from);
66
885
  if (bridges.length === 0) {
67
886
  if (wantJson) {
68
- emitJson({ command: 'path', epistemics: EPISTEMICS, options: { from, to, at: point }, result: { path: [] } }, ctx.write);
887
+ emitJson({
888
+ command: 'path',
889
+ epistemics: EPISTEMICS,
890
+ options: { from, to, at: point },
891
+ result: { path: [], crossFamily: labels.crossFamily, modelFamilies: labels.modelFamilies },
892
+ }, ctx.write);
69
893
  return 0;
70
894
  }
71
895
  out(`\nupt path ${from} → ${to}`);
72
896
  out(' the endpoints are the same model: the path is empty and composes nothing.');
73
897
  return 0;
74
898
  }
75
- const result = api.boundPath(bridges);
76
- const horizons = bridges
77
- .filter((b) => b.bound !== undefined)
78
- .map((b) => ({
79
- bridgeId: b.id,
80
- horizon: b.bound.horizon,
81
- holds: t === undefined ? null : b.bound.horizonHolds(t, point),
82
- }));
83
- const allHold = horizons.every((h) => h.holds === true);
84
- // The same --at resolution as `upt regime`: group spellings, and groups derived
85
- // from their parameters. Unknown keys are not reported here, because horizon
86
- // parameters such as T0 and t are legitimate --at keys on a path.
87
- const { values: resolved } = resolveAtPoint(point, bridges.map((b) => b.regime));
88
- const regimes = bridges.map((b) => {
89
- const check = api.regimeHolds(b.regime, resolved);
90
- return {
91
- bridgeId: b.id,
92
- ok: check.ok,
93
- violated: check.violated.map(showInequality),
94
- unchecked: check.unchecked.map(showInequality),
95
- };
96
- });
97
- const allRegimesHold = regimes.some((r) => r.ok === false)
98
- ? false
99
- : regimes.some((r) => r.ok === 'unknown')
100
- ? 'unknown'
101
- : true;
102
- // The bound AT the --at point (persona finding L9). Printed only when it is
103
- // PROVEN: every regime holds, every step has a closed-form deltaAt (the exact
104
- // error), and every value is finite. It is composed by the same rule as the
105
- // domain supremum, by substituting each step's point value for its delta.
106
- let pointBound = null;
107
- let pointBoundReason = null;
108
- if (result.kind === 'bound' && Object.keys(point).length > 0) {
109
- const steps = bridges.filter((b) => b.bound !== undefined);
110
- const numerical = steps.find((b) => b.bound.deltaAtBasis !== 'closed-form');
111
- if (allRegimesHold !== true) {
112
- pointBoundReason = 'a regime on the path is violated or unchecked';
113
- }
114
- else if (steps.length === 0) {
115
- pointBoundReason = 'no step carries a bound';
116
- }
117
- else if (numerical !== undefined) {
118
- pointBoundReason =
119
- numerical.bound.deltaAt === undefined
120
- ? `${numerical.id} states no point bound`
121
- : `${numerical.id}'s point bound is numerically supported, not proven`;
899
+ const result = routeClaim(api, bridges);
900
+ const missing = explainsRefusal(result) ? missingForComposite(api, bridges) : [];
901
+ const evaluateAt = makeEvaluator(api, bridges, result);
902
+ const tolValues = args.flags.get('tolerance');
903
+ const tolerance = parseTolerance(tolValues === undefined ? undefined : tolValues[tolValues.length - 1]);
904
+ const scope = toleranceScope(api, bridges);
905
+ const found = tolerance?.observable == null ? null : pathTranslation(api, bridges, tolerance.observable);
906
+ const translation = found?.tr ?? undefined;
907
+ const carriages = found !== null && found.tr !== null ? found.carriages : [];
908
+ const sweepSpec = args.flags.get('sweep');
909
+ const compareTo = args.flags.get('compare');
910
+ if (compareTo !== undefined && compareTo.length > 0) {
911
+ if (sweepSpec === undefined || sweepSpec.length === 0) {
912
+ throw new CliError('upt path: --compare needs --sweep (two routes are compared row by row over a sweep)');
122
913
  }
123
- else {
124
- const atPoint = bridges.map((b) => b.bound === undefined ? b : { ...b, bound: { ...b.bound, delta: b.bound.deltaAt(point) } });
125
- const composed = atPoint.every((b) => b.bound === undefined || Number.isFinite(b.bound.delta))
126
- ? api.boundPath(atPoint)
127
- : null;
128
- if (composed !== null && composed.kind === 'bound')
129
- pointBound = composed.bound;
130
- else
131
- pointBoundReason = 'a parameter the point bound needs was not supplied';
132
- }
133
- }
134
- // A violated regime or horizon is a failed check: exit 3 (persona finding F2).
135
- // UNKNOWN, where a coordinate or t was not supplied, is not a failure.
136
- const exitCode = allRegimesHold === false || (t !== undefined && !allHold) ? EXIT_CHECK_FAILED : 0;
914
+ if (tolerance !== null) {
915
+ throw new CliError('upt path: --compare does not take --tolerance: judge each route on its own, since their errors are in different norms');
916
+ }
917
+ const to2 = compareTo[compareTo.length - 1];
918
+ if (to2 === to)
919
+ throw new CliError(`upt path: --compare=${to2} is the same target as the path; name a different model`);
920
+ const other = selectRoute(api, from, to2, 'path').bridges;
921
+ if (other === null || other.length === 0) {
922
+ throw new CliError(`upt path: --compare=${to2}: ${other === null ? `no chain of bridges connects ${from} to ${to2}` : 'the endpoints are the same model'}; there is nothing to compare`);
923
+ }
924
+ const otherResult = routeClaim(api, other);
925
+ return runCompare(ctx, from, point, sweepSpec[sweepSpec.length - 1], [
926
+ { to, bridges, result, evaluateAt },
927
+ { to: to2, bridges: other, result: otherResult, evaluateAt: makeEvaluator(api, other, otherResult) },
928
+ ]);
929
+ }
930
+ if (sweepSpec !== undefined && sweepSpec.length > 0) {
931
+ return runSweep(ctx, {
932
+ from,
933
+ to,
934
+ point,
935
+ bridges,
936
+ result,
937
+ evaluateAt,
938
+ spec: sweepSpec[sweepSpec.length - 1],
939
+ tolerance,
940
+ scope,
941
+ judgeRow: (e, at, tGiven) => tolerance === null || tolerance.observable === null
942
+ ? judgeTolerance(tolerance?.value ?? 0, e, tGiven)
943
+ : judgeAtPoint(api, { ...tolerance, observable: tolerance.observable }, bridges, e, at, false),
944
+ translation: translation === undefined ? null : { tr: translation, evidence: translationEvidence(api, translation, carriages) },
945
+ });
946
+ }
947
+ if (args.flags.has('csv'))
948
+ throw new CliError('upt path: --csv needs --sweep (a single point is not a table)');
949
+ const evaluation = evaluateAt(point);
950
+ const { regimes, allRegimesHold, horizons, allHold, pointBound, pointBoundReason, regimeCollisions } = evaluation;
951
+ const adequacy = tolerance === null || tolerance.observable !== null ? null : judgeTolerance(tolerance.value, evaluation, t !== undefined);
952
+ const observed = tolerance === null || tolerance.observable === null
953
+ ? null
954
+ : judgeAtPoint(api, { ...tolerance, observable: tolerance.observable }, bridges, evaluation, point, true);
955
+ const evidence = translation === undefined ? null : translationEvidence(api, translation, carriages);
956
+ // A violated regime or horizon is a failed check: exit 3 (persona finding F2),
957
+ // and so is a tolerance the point error exceeds. UNKNOWN, where a coordinate
958
+ // or t was not supplied, is not a failure.
959
+ const horizonViolated = t !== undefined && horizons.some((h) => h.holds === false);
960
+ // The domain supremum is the route's answer only while the point check has
961
+ // not withdrawn it. A violated regime or horizon is not a claim, so neither
962
+ // text nor JSON carries a bound number.
963
+ const claimWithdrawn = allRegimesHold === false || horizonViolated;
964
+ const withdrawnReason = allRegimesHold === false
965
+ ? (pointBoundReason ?? 'a regime on the path is violated or unchecked')
966
+ : 'past the horizon, no bound is claimed at this point';
967
+ const exitCode = allRegimesHold === false ||
968
+ horizonViolated ||
969
+ adequacy?.verdict === 'inadequate' ||
970
+ observed?.verdict === 'inadequate'
971
+ ? EXIT_CHECK_FAILED
972
+ : 0;
137
973
  if (wantJson) {
138
974
  emitJson({
139
975
  command: 'path',
140
976
  epistemics: EPISTEMICS,
141
977
  options: { from, to, at: point },
142
978
  result: {
143
- path: bridges.map((b) => ({ id: b.id, relation: b.relation, from: b.premises[0], to: b.conclusion })),
979
+ families: { from: fromFamily, to: toFamily },
980
+ crossFamily: labels.crossFamily,
981
+ modelFamilies: labels.modelFamilies,
982
+ path: labels.steps,
144
983
  // A no-claim has no `bound` key at all — the type refuses it, and so
145
984
  // does this envelope.
146
985
  ...(result.kind === 'bound'
147
986
  ? {
148
987
  kind: 'bound',
149
988
  relation: result.relation,
150
- bound: result.bound,
989
+ ...(claimWithdrawn ? {} : { bound: result.bound }),
151
990
  norm: result.norm,
152
991
  terminal: result.terminal,
992
+ ...(result.transports === undefined ? {} : { transports: result.transports.map(transportReport) }),
153
993
  }
154
- : { kind: 'no-claim', reason: result.reason, detail: result.detail, phrase: NO_COMPOSITE_PHRASE }),
994
+ : {
995
+ kind: 'no-claim',
996
+ reason: result.reason,
997
+ detail: result.detail,
998
+ phrase: NO_COMPOSITE_PHRASE,
999
+ ...(missing.length > 0 ? { missing } : {}),
1000
+ }),
155
1001
  regimes,
156
1002
  allRegimesHold,
157
- pointBound,
158
- ...(pointBoundReason !== null ? { pointBoundReason } : {}),
1003
+ pointBound: claimWithdrawn ? null : pointBound,
1004
+ ...((claimWithdrawn ? withdrawnReason : pointBoundReason) !== null
1005
+ ? { pointBoundReason: claimWithdrawn ? withdrawnReason : pointBoundReason }
1006
+ : {}),
159
1007
  horizons,
1008
+ ...(multi.length === 0 ? {} : { multiPremise: multi }),
1009
+ ...(regimeCollisions.length === 0 ? {} : { regimeCollisions }),
160
1010
  horizonsEvaluated: t !== undefined,
161
- allHorizonsHold: t === undefined ? null : allHold,
1011
+ allHorizonsHold: t === undefined || horizons.some((h) => h.holds === null) ? (horizonViolated ? false : null) : allHold,
1012
+ ...(adequacy === null ? {} : { tolerance: { value: tolerance.value, ...adequacy, scope } }),
1013
+ ...(observed === null ? {} : { tolerance: observableReport(tolerance, observed, bridges, evidence) }),
162
1014
  },
163
1015
  }, ctx.write);
164
1016
  return exitCode;
@@ -167,25 +1019,50 @@ async function run(ctx) {
167
1019
  out(` ${bridges.length} bridge(s):`);
168
1020
  for (const b of bridges)
169
1021
  out(` ${b.premises[0]} --[${b.relation}]--> ${b.conclusion} (${b.id})`);
1022
+ printMultiPremise(out, multi);
1023
+ if (labels.crossFamily) {
1024
+ out(` crosses families: ${labels.modelFamilies.join(' → ')}`);
1025
+ }
170
1026
  out('');
171
1027
  if (result.kind === 'bound') {
172
1028
  out(` composite relation: ${result.relation}`);
173
- out(` composed bound: K = ${result.bound.K} · delta = ${result.bound.delta}`);
1029
+ if (claimWithdrawn) {
1030
+ out(' composed bound: none — no bound is claimed at this point');
1031
+ }
1032
+ else {
1033
+ out(` composed bound: K = ${result.bound.K} · delta = ${result.bound.delta}`);
1034
+ }
174
1035
  out(` norm: ${result.norm ?? '(none stated — the claim is the vacuous identity)'}`);
175
1036
  if (result.terminal)
176
1037
  out(' terminal: the last step states no Lipschitz constant; the claim ends there');
177
- if (pointBound !== null) {
1038
+ for (const a of result.transports ?? []) {
1039
+ const nt = a.transport;
1040
+ out(` why the bound crosses '${a.bridgeId}' (exact): it declares the norm transport '${nt.id}'`);
1041
+ out(` ${nt.fromModel} → ${nt.toModel}: '${nt.from}' → '${nt.to}', K = ${nt.K} (${nt.domain})`);
1042
+ out(` because: ${nt.derivation}`);
1043
+ out(` time map: ${nt.timeMap.map} (uniform)`);
1044
+ out(` horizon: ${nt.timeMap.horizon}`);
1045
+ out(` witness: ${nt.witness.id} (${nt.witness.kind}; ${nt.basis} when it checks, never formally proved) — ` +
1046
+ `\`upt map --route=${from},${to} --run\` runs it`);
1047
+ out(' no other norm or direction through this map is declared, and each stays refused');
1048
+ }
1049
+ if (!claimWithdrawn && pointBound !== null) {
178
1050
  out(` bound at this point: K = ${pointBound.K} · delta = ${pointBound.delta} (closed-form: the exact error; ` +
179
1051
  "the composed bound above is the supremum over the bridge's domain)");
180
1052
  }
181
- else if (pointBoundReason !== null) {
182
- out(` bound at this point: none — ${pointBoundReason}`);
1053
+ else if ((claimWithdrawn ? withdrawnReason : pointBoundReason) !== null) {
1054
+ out(` bound at this point: none — ${claimWithdrawn ? withdrawnReason : pointBoundReason}`);
183
1055
  }
184
1056
  }
185
1057
  else {
186
1058
  out(` composite relation: ${NO_COMPOSITE_PHRASE}`);
187
1059
  out(` bound: ${NO_COMPOSITE_PHRASE} — reason '${result.reason}'`);
188
1060
  out(` ${result.detail}`);
1061
+ if (missing.length > 0) {
1062
+ out(' to compose, this path would need:');
1063
+ for (const m of missing)
1064
+ out(` - ${m}`);
1065
+ }
189
1066
  }
190
1067
  out('');
191
1068
  if (allRegimesHold === false) {
@@ -206,20 +1083,53 @@ async function run(ctx) {
206
1083
  else if (r.ok === 'unknown')
207
1084
  out(` ${r.bridgeId}: unknown — unchecked: ${r.unchecked.join('; ')}`);
208
1085
  }
1086
+ if (regimeCollisions.length > 0) {
1087
+ out(' regime group names collide (unchecked on each step that defines them):');
1088
+ for (const c of regimeCollisions)
1089
+ out(` ${c.group}: ${c.families.join(', ')}`);
1090
+ }
1091
+ if (regimes.some((r) => r.premisesNotChecked !== undefined)) {
1092
+ out(' premises not machine-checked (your judgment or measurement):');
1093
+ for (const r of regimes) {
1094
+ if (r.premisesNotChecked !== undefined)
1095
+ out(` ${r.bridgeId}: ${r.premisesNotChecked.join('; ')}`);
1096
+ }
1097
+ }
209
1098
  if (horizons.length === 0) {
210
1099
  out(' horizons: none on this path (no step carries a bound)');
211
1100
  }
212
1101
  else if (t === undefined) {
213
1102
  out(' horizons: NOT EVALUATED (no t= supplied via --at); an unevaluated horizon is not a passing one');
214
- for (const h of horizons)
1103
+ for (const h of horizons) {
215
1104
  out(` ${h.bridgeId}: ${h.horizon}`);
1105
+ for (const r of h.restatedBy ?? [])
1106
+ out(` restated by '${r.transport}': ${r.horizon}`);
1107
+ }
1108
+ }
1109
+ else if (horizons.some((h) => h.unevaluated === 'cross-family') && !horizonViolated) {
1110
+ out(` horizons at t=${t}: NOT EVALUATED — a family change does not restate a horizon onto the next clock`);
1111
+ for (const h of horizons) {
1112
+ const state = h.holds === null ? 'unevaluated' : h.holds ? 'holds' : 'VIOLATED';
1113
+ out(` ${h.bridgeId}: ${state} — ${h.horizon}`);
1114
+ for (const r of h.restatedBy ?? [])
1115
+ out(` restated by '${r.transport}': ${r.horizon}`);
1116
+ }
216
1117
  }
217
1118
  else {
218
1119
  out(` horizons at t=${t}: ${allHold ? 'all hold' : 'NOT all hold'}`);
219
1120
  for (const h of horizons) {
220
- out(` ${h.bridgeId}: ${h.holds ? 'holds' : 'VIOLATED'} — ${h.horizon}`);
1121
+ const state = h.holds === null ? 'unevaluated' : h.holds ? 'holds' : 'VIOLATED';
1122
+ out(` ${h.bridgeId}: ${state} — ${h.horizon}`);
1123
+ for (const r of h.restatedBy ?? [])
1124
+ out(` restated by '${r.transport}': ${r.horizon}`);
221
1125
  }
222
1126
  }
1127
+ if (adequacy !== null) {
1128
+ out(` tolerance ${tolerance.value}: ${adequacy.verdict.toUpperCase()} — ${adequacy.reason}`);
1129
+ out(` (${scope})`);
1130
+ }
1131
+ if (observed !== null)
1132
+ printObservable(out, tolerance, observed, bridges, evidence, result.kind);
223
1133
  out(` (${EPISTEMICS})`);
224
1134
  return exitCode;
225
1135
  }