@aligndottech/cli 0.50.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +6 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/config.d.ts +8 -0
  6. package/dist/commands/config.d.ts.map +1 -0
  7. package/dist/commands/config.js +33 -0
  8. package/dist/commands/config.js.map +1 -0
  9. package/dist/commands/connect-agents.d.ts.map +1 -1
  10. package/dist/commands/connect-agents.js +3 -1
  11. package/dist/commands/connect-agents.js.map +1 -1
  12. package/dist/commands/env.d.ts.map +1 -1
  13. package/dist/commands/env.js +11 -0
  14. package/dist/commands/env.js.map +1 -1
  15. package/dist/commands/invite.d.ts +8 -18
  16. package/dist/commands/invite.d.ts.map +1 -1
  17. package/dist/commands/invite.js +23 -46
  18. package/dist/commands/invite.js.map +1 -1
  19. package/dist/commands/join.d.ts +5 -0
  20. package/dist/commands/join.d.ts.map +1 -0
  21. package/dist/commands/join.js +49 -0
  22. package/dist/commands/join.js.map +1 -0
  23. package/dist/commands/local-relink.d.ts +35 -0
  24. package/dist/commands/local-relink.d.ts.map +1 -0
  25. package/dist/commands/local-relink.js +72 -0
  26. package/dist/commands/local-relink.js.map +1 -0
  27. package/dist/commands/local.d.ts.map +1 -1
  28. package/dist/commands/local.js +37 -0
  29. package/dist/commands/local.js.map +1 -1
  30. package/dist/commands/login.d.ts.map +1 -1
  31. package/dist/commands/login.js +21 -2
  32. package/dist/commands/login.js.map +1 -1
  33. package/dist/commands/mcp.d.ts +8 -0
  34. package/dist/commands/mcp.d.ts.map +1 -1
  35. package/dist/commands/mcp.js +12 -5
  36. package/dist/commands/mcp.js.map +1 -1
  37. package/dist/commands/registry.d.ts.map +1 -1
  38. package/dist/commands/registry.js +4 -0
  39. package/dist/commands/registry.js.map +1 -1
  40. package/dist/commands/share.d.ts.map +1 -1
  41. package/dist/commands/share.js +81 -12
  42. package/dist/commands/share.js.map +1 -1
  43. package/dist/commands/status.d.ts.map +1 -1
  44. package/dist/commands/status.js +10 -0
  45. package/dist/commands/status.js.map +1 -1
  46. package/dist/detector/detector-version.d.ts +16 -0
  47. package/dist/detector/detector-version.d.ts.map +1 -0
  48. package/dist/detector/detector-version.js +36 -0
  49. package/dist/detector/detector-version.js.map +1 -0
  50. package/dist/detector/relationship-contract.d.ts +30 -0
  51. package/dist/detector/relationship-contract.d.ts.map +1 -0
  52. package/dist/detector/relationship-contract.js +120 -0
  53. package/dist/detector/relationship-contract.js.map +1 -0
  54. package/dist/detector/relationship-contract.v1.json +53 -0
  55. package/dist/detector/relationship-prompt.d.ts +20 -0
  56. package/dist/detector/relationship-prompt.d.ts.map +1 -0
  57. package/dist/detector/relationship-prompt.js +55 -0
  58. package/dist/detector/relationship-prompt.js.map +1 -0
  59. package/dist/detector/relationship-prompt.v1.json +13 -0
  60. package/dist/detector/resolve-pair.d.ts +39 -0
  61. package/dist/detector/resolve-pair.d.ts.map +1 -0
  62. package/dist/detector/resolve-pair.js +345 -0
  63. package/dist/detector/resolve-pair.js.map +1 -0
  64. package/dist/detector/similar-item.d.ts +20 -0
  65. package/dist/detector/similar-item.d.ts.map +1 -0
  66. package/dist/detector/similar-item.js +16 -0
  67. package/dist/detector/similar-item.js.map +1 -0
  68. package/dist/index.js +37 -13
  69. package/dist/index.js.map +1 -1
  70. package/dist/lib/advisory-dedup.d.ts.map +1 -1
  71. package/dist/lib/advisory-dedup.js +16 -8
  72. package/dist/lib/advisory-dedup.js.map +1 -1
  73. package/dist/lib/advisory-verdict.d.ts +19 -0
  74. package/dist/lib/advisory-verdict.d.ts.map +1 -1
  75. package/dist/lib/advisory-verdict.js +6 -3
  76. package/dist/lib/advisory-verdict.js.map +1 -1
  77. package/dist/lib/backup-exclusion.d.ts +10 -0
  78. package/dist/lib/backup-exclusion.d.ts.map +1 -0
  79. package/dist/lib/backup-exclusion.js +58 -0
  80. package/dist/lib/backup-exclusion.js.map +1 -0
  81. package/dist/lib/cli-oauth.d.ts +2 -0
  82. package/dist/lib/cli-oauth.d.ts.map +1 -1
  83. package/dist/lib/cli-oauth.js +61 -10
  84. package/dist/lib/cli-oauth.js.map +1 -1
  85. package/dist/lib/config.d.ts +41 -1
  86. package/dist/lib/config.d.ts.map +1 -1
  87. package/dist/lib/config.js +109 -1
  88. package/dist/lib/config.js.map +1 -1
  89. package/dist/lib/curation/guardrail.d.ts.map +1 -1
  90. package/dist/lib/curation/guardrail.js +2 -0
  91. package/dist/lib/curation/guardrail.js.map +1 -1
  92. package/dist/lib/curation/judgements-db.d.ts +1 -0
  93. package/dist/lib/curation/judgements-db.d.ts.map +1 -1
  94. package/dist/lib/curation/judgements-db.js +9 -3
  95. package/dist/lib/curation/judgements-db.js.map +1 -1
  96. package/dist/lib/curation/mark.js +4 -2
  97. package/dist/lib/curation/mark.js.map +1 -1
  98. package/dist/lib/destination-name.d.ts +24 -0
  99. package/dist/lib/destination-name.d.ts.map +1 -0
  100. package/dist/lib/destination-name.js +39 -0
  101. package/dist/lib/destination-name.js.map +1 -0
  102. package/dist/lib/destination-summary.d.ts +6 -0
  103. package/dist/lib/destination-summary.d.ts.map +1 -0
  104. package/dist/lib/destination-summary.js +27 -0
  105. package/dist/lib/destination-summary.js.map +1 -0
  106. package/dist/lib/destination-trust.d.ts +54 -0
  107. package/dist/lib/destination-trust.d.ts.map +1 -0
  108. package/dist/lib/destination-trust.js +107 -0
  109. package/dist/lib/destination-trust.js.map +1 -0
  110. package/dist/lib/env-resolver.d.ts.map +1 -1
  111. package/dist/lib/env-resolver.js +3 -0
  112. package/dist/lib/env-resolver.js.map +1 -1
  113. package/dist/lib/fatal.d.ts +11 -0
  114. package/dist/lib/fatal.d.ts.map +1 -0
  115. package/dist/lib/fatal.js +22 -0
  116. package/dist/lib/fatal.js.map +1 -0
  117. package/dist/lib/gateway-client.d.ts +8 -16
  118. package/dist/lib/gateway-client.d.ts.map +1 -1
  119. package/dist/lib/gateway-client.js +30 -20
  120. package/dist/lib/gateway-client.js.map +1 -1
  121. package/dist/lib/invite-prompt.d.ts +4 -4
  122. package/dist/lib/invite-prompt.d.ts.map +1 -1
  123. package/dist/lib/invite-prompt.js +4 -4
  124. package/dist/lib/invite-prompt.js.map +1 -1
  125. package/dist/lib/invite-settings.d.ts +18 -0
  126. package/dist/lib/invite-settings.d.ts.map +1 -0
  127. package/dist/lib/invite-settings.js +29 -0
  128. package/dist/lib/invite-settings.js.map +1 -0
  129. package/dist/lib/join-flow.d.ts +60 -0
  130. package/dist/lib/join-flow.d.ts.map +1 -0
  131. package/dist/lib/join-flow.js +282 -0
  132. package/dist/lib/join-flow.js.map +1 -0
  133. package/dist/lib/launch/mcp-child-env.d.ts.map +1 -1
  134. package/dist/lib/launch/mcp-child-env.js +5 -2
  135. package/dist/lib/launch/mcp-child-env.js.map +1 -1
  136. package/dist/lib/lead-prompt.d.ts +106 -0
  137. package/dist/lib/lead-prompt.d.ts.map +1 -0
  138. package/dist/lib/lead-prompt.js +175 -0
  139. package/dist/lib/lead-prompt.js.map +1 -0
  140. package/dist/lib/local-candidate-classify.d.ts +26 -0
  141. package/dist/lib/local-candidate-classify.d.ts.map +1 -0
  142. package/dist/lib/local-candidate-classify.js +53 -0
  143. package/dist/lib/local-candidate-classify.js.map +1 -0
  144. package/dist/lib/local-db-migrate.d.ts +1 -1
  145. package/dist/lib/local-db-migrate.d.ts.map +1 -1
  146. package/dist/lib/local-db-migrate.js +45 -1
  147. package/dist/lib/local-db-migrate.js.map +1 -1
  148. package/dist/lib/local-db-v12.d.ts +17 -0
  149. package/dist/lib/local-db-v12.d.ts.map +1 -0
  150. package/dist/lib/local-db-v12.js +8 -0
  151. package/dist/lib/local-db-v12.js.map +1 -0
  152. package/dist/lib/local-db.d.ts +13 -13
  153. package/dist/lib/local-db.d.ts.map +1 -1
  154. package/dist/lib/local-db.js +41 -6
  155. package/dist/lib/local-db.js.map +1 -1
  156. package/dist/lib/local-gateway-client.d.ts +1 -0
  157. package/dist/lib/local-gateway-client.d.ts.map +1 -1
  158. package/dist/lib/local-gateway-client.js +31 -20
  159. package/dist/lib/local-gateway-client.js.map +1 -1
  160. package/dist/lib/local-ingest.d.ts +2 -0
  161. package/dist/lib/local-ingest.d.ts.map +1 -1
  162. package/dist/lib/local-ingest.js.map +1 -1
  163. package/dist/lib/local-link-pass.d.ts.map +1 -1
  164. package/dist/lib/local-link-pass.js +25 -29
  165. package/dist/lib/local-link-pass.js.map +1 -1
  166. package/dist/lib/local-llm.d.ts +1 -1
  167. package/dist/lib/local-llm.d.ts.map +1 -1
  168. package/dist/lib/local-llm.js +3 -2
  169. package/dist/lib/local-llm.js.map +1 -1
  170. package/dist/lib/local-mode.d.ts +5 -1
  171. package/dist/lib/local-mode.d.ts.map +1 -1
  172. package/dist/lib/local-mode.js +11 -6
  173. package/dist/lib/local-mode.js.map +1 -1
  174. package/dist/lib/local-relationship-classifier.d.ts +4 -0
  175. package/dist/lib/local-relationship-classifier.d.ts.map +1 -1
  176. package/dist/lib/local-relationship-classifier.js +9 -4
  177. package/dist/lib/local-relationship-classifier.js.map +1 -1
  178. package/dist/lib/local-relink.d.ts +59 -0
  179. package/dist/lib/local-relink.d.ts.map +1 -0
  180. package/dist/lib/local-relink.js +171 -0
  181. package/dist/lib/local-relink.js.map +1 -0
  182. package/dist/lib/local-set-classifier.d.ts +36 -0
  183. package/dist/lib/local-set-classifier.d.ts.map +1 -0
  184. package/dist/lib/local-set-classifier.js +81 -0
  185. package/dist/lib/local-set-classifier.js.map +1 -0
  186. package/dist/lib/local-snapshot-loader.d.ts +29 -0
  187. package/dist/lib/local-snapshot-loader.d.ts.map +1 -0
  188. package/dist/lib/local-snapshot-loader.js +108 -0
  189. package/dist/lib/local-snapshot-loader.js.map +1 -0
  190. package/dist/lib/login-flow.d.ts +15 -0
  191. package/dist/lib/login-flow.d.ts.map +1 -1
  192. package/dist/lib/login-flow.js +80 -4
  193. package/dist/lib/login-flow.js.map +1 -1
  194. package/dist/lib/mcp/share-status-tool.d.ts +13 -0
  195. package/dist/lib/mcp/share-status-tool.d.ts.map +1 -0
  196. package/dist/lib/mcp/share-status-tool.js +116 -0
  197. package/dist/lib/mcp/share-status-tool.js.map +1 -0
  198. package/dist/lib/mcp/share-tool.d.ts +18 -14
  199. package/dist/lib/mcp/share-tool.d.ts.map +1 -1
  200. package/dist/lib/mcp/share-tool.js +127 -21
  201. package/dist/lib/mcp/share-tool.js.map +1 -1
  202. package/dist/lib/model-cache.d.ts +10 -0
  203. package/dist/lib/model-cache.d.ts.map +1 -0
  204. package/dist/lib/model-cache.js +35 -0
  205. package/dist/lib/model-cache.js.map +1 -0
  206. package/dist/lib/next-step.d.ts.map +1 -1
  207. package/dist/lib/next-step.js +2 -1
  208. package/dist/lib/next-step.js.map +1 -1
  209. package/dist/lib/private-files.d.ts +38 -0
  210. package/dist/lib/private-files.d.ts.map +1 -0
  211. package/dist/lib/private-files.js +102 -0
  212. package/dist/lib/private-files.js.map +1 -0
  213. package/dist/lib/redaction-notice.d.ts +7 -0
  214. package/dist/lib/redaction-notice.d.ts.map +1 -0
  215. package/dist/lib/redaction-notice.js +32 -0
  216. package/dist/lib/redaction-notice.js.map +1 -0
  217. package/dist/lib/relink-estimate.d.ts +16 -0
  218. package/dist/lib/relink-estimate.d.ts.map +1 -0
  219. package/dist/lib/relink-estimate.js +30 -0
  220. package/dist/lib/relink-estimate.js.map +1 -0
  221. package/dist/lib/resolve-env.d.ts +15 -1
  222. package/dist/lib/resolve-env.d.ts.map +1 -1
  223. package/dist/lib/resolve-env.js +27 -7
  224. package/dist/lib/resolve-env.js.map +1 -1
  225. package/dist/lib/scope-defaults.d.ts.map +1 -1
  226. package/dist/lib/scope-defaults.js +2 -0
  227. package/dist/lib/scope-defaults.js.map +1 -1
  228. package/dist/lib/scope-real.d.ts +57 -0
  229. package/dist/lib/scope-real.d.ts.map +1 -1
  230. package/dist/lib/setup-route.d.ts +4 -2
  231. package/dist/lib/setup-route.d.ts.map +1 -1
  232. package/dist/lib/setup-route.js +12 -6
  233. package/dist/lib/setup-route.js.map +1 -1
  234. package/dist/lib/share/approval.d.ts +71 -0
  235. package/dist/lib/share/approval.d.ts.map +1 -0
  236. package/dist/lib/share/approval.js +139 -0
  237. package/dist/lib/share/approval.js.map +1 -0
  238. package/dist/lib/share/approve-link.d.ts +31 -0
  239. package/dist/lib/share/approve-link.d.ts.map +1 -0
  240. package/dist/lib/share/approve-link.js +46 -0
  241. package/dist/lib/share/approve-link.js.map +1 -0
  242. package/dist/lib/share/browser-flow.d.ts +85 -0
  243. package/dist/lib/share/browser-flow.d.ts.map +1 -0
  244. package/dist/lib/share/browser-flow.js +168 -0
  245. package/dist/lib/share/browser-flow.js.map +1 -0
  246. package/dist/lib/share/command.d.ts +34 -1
  247. package/dist/lib/share/command.d.ts.map +1 -1
  248. package/dist/lib/share/command.js +153 -11
  249. package/dist/lib/share/command.js.map +1 -1
  250. package/dist/lib/share/delivery-options.d.ts +15 -0
  251. package/dist/lib/share/delivery-options.d.ts.map +1 -0
  252. package/dist/lib/share/delivery-options.js +16 -0
  253. package/dist/lib/share/delivery-options.js.map +1 -0
  254. package/dist/lib/share/delivery.d.ts +83 -0
  255. package/dist/lib/share/delivery.d.ts.map +1 -0
  256. package/dist/lib/share/delivery.js +134 -0
  257. package/dist/lib/share/delivery.js.map +1 -0
  258. package/dist/lib/share/envelope.d.ts +61 -0
  259. package/dist/lib/share/envelope.d.ts.map +1 -0
  260. package/dist/lib/share/envelope.js +99 -0
  261. package/dist/lib/share/envelope.js.map +1 -0
  262. package/dist/lib/share/ledger.d.ts.map +1 -1
  263. package/dist/lib/share/ledger.js +3 -1
  264. package/dist/lib/share/ledger.js.map +1 -1
  265. package/dist/lib/share/payload.d.ts +9 -0
  266. package/dist/lib/share/payload.d.ts.map +1 -1
  267. package/dist/lib/share/payload.js +9 -0
  268. package/dist/lib/share/payload.js.map +1 -1
  269. package/dist/lib/share/pending.d.ts +47 -1
  270. package/dist/lib/share/pending.d.ts.map +1 -1
  271. package/dist/lib/share/pending.js +139 -13
  272. package/dist/lib/share/pending.js.map +1 -1
  273. package/dist/lib/share/preview.d.ts +5 -1
  274. package/dist/lib/share/preview.d.ts.map +1 -1
  275. package/dist/lib/share/preview.js +3 -1
  276. package/dist/lib/share/preview.js.map +1 -1
  277. package/dist/lib/share/qr.d.ts +13 -0
  278. package/dist/lib/share/qr.d.ts.map +1 -0
  279. package/dist/lib/share/qr.js +77 -0
  280. package/dist/lib/share/qr.js.map +1 -0
  281. package/dist/lib/share/requests-client.d.ts +63 -0
  282. package/dist/lib/share/requests-client.d.ts.map +1 -0
  283. package/dist/lib/share/requests-client.js +69 -0
  284. package/dist/lib/share/requests-client.js.map +1 -0
  285. package/dist/lib/share/run.d.ts +20 -1
  286. package/dist/lib/share/run.d.ts.map +1 -1
  287. package/dist/lib/share/run.js +27 -10
  288. package/dist/lib/share/run.js.map +1 -1
  289. package/dist/lib/share/secret-scan.d.ts +5 -0
  290. package/dist/lib/share/secret-scan.d.ts.map +1 -1
  291. package/dist/lib/share/secret-scan.js +43 -2
  292. package/dist/lib/share/secret-scan.js.map +1 -1
  293. package/dist/lib/share/target.d.ts +38 -0
  294. package/dist/lib/share/target.d.ts.map +1 -0
  295. package/dist/lib/share/target.js +80 -0
  296. package/dist/lib/share/target.js.map +1 -0
  297. package/dist/lib/share/tty.d.ts +8 -0
  298. package/dist/lib/share/tty.d.ts.map +1 -1
  299. package/dist/lib/share/tty.js +45 -0
  300. package/dist/lib/share/tty.js.map +1 -1
  301. package/dist/lib/source-sync-state.d.ts.map +1 -1
  302. package/dist/lib/source-sync-state.js +2 -0
  303. package/dist/lib/source-sync-state.js.map +1 -1
  304. package/dist/lib/sync/blocked.d.ts.map +1 -1
  305. package/dist/lib/sync/blocked.js +2 -1
  306. package/dist/lib/sync/blocked.js.map +1 -1
  307. package/dist/lib/sync/classify.d.ts.map +1 -1
  308. package/dist/lib/sync/classify.js +2 -0
  309. package/dist/lib/sync/classify.js.map +1 -1
  310. package/dist/lib/sync/real-env.d.ts +57 -0
  311. package/dist/lib/sync/real-env.d.ts.map +1 -1
  312. package/dist/lib/sync/sync-state.d.ts.map +1 -1
  313. package/dist/lib/sync/sync-state.js +3 -1
  314. package/dist/lib/sync/sync-state.js.map +1 -1
  315. package/dist/lib/usage-telemetry.d.ts +2 -2
  316. package/dist/lib/usage-telemetry.d.ts.map +1 -1
  317. package/dist/lib/usage-telemetry.js +21 -3
  318. package/dist/lib/usage-telemetry.js.map +1 -1
  319. package/package.json +4 -2
  320. package/dist/lib/personal-email-domains.d.ts +0 -8
  321. package/dist/lib/personal-email-domains.d.ts.map +0 -1
  322. package/dist/lib/personal-email-domains.js +0 -39
  323. package/dist/lib/personal-email-domains.js.map +0 -1
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Which local relationship detector runs: `ALIGN_DETECTOR`, `v1` (default) or `v0`.
3
+ *
4
+ * v1 is the shared detector (one call per check over every candidate, the hosted base prompt,
5
+ * the `incompatible` verdict resolved deterministically). v0 is the pairwise classifier the CLI
6
+ * shipped first. v1 is the default: the same detector the paid product runs. It passes on a 7B local model (qwen2.5:7b, addendum 7), but not on the 3B llama3.2, so set ALIGN_DETECTOR=v0 if your only local model is small. The Phase A
7
+ * confirmatory run met its rule on claude-haiku-4-5 only, and it lowers recall on llama3.2, so
8
+ * v1 is the default again once qwen2.5:7b passed the same rule (scores in docs/local-mode.md). An unknown value is an error,
9
+ * never a fallback: a typo must not silently run the default.
10
+ */
11
+ export const DETECTOR_VERSIONS = ['v0', 'v1'];
12
+ export function resolveDetectorVersion(env = process.env, saved) {
13
+ const raw = env['ALIGN_DETECTOR'];
14
+ if (raw === undefined || raw.trim() === '') {
15
+ // The saved choice (`align config detector`) applies when the env var is unset. It is validated
16
+ // here too, so a hand-edited value fails loudly instead of quietly running the default.
17
+ if (saved === undefined)
18
+ return 'v1';
19
+ if (DETECTOR_VERSIONS.includes(saved))
20
+ return saved;
21
+ throw new Error(`detector "${saved}" in the saved config is not a detector version. Allowed: ${DETECTOR_VERSIONS.join(', ')}. Fix it with: align config detector v0`);
22
+ }
23
+ const value = raw.trim();
24
+ if (DETECTOR_VERSIONS.includes(value))
25
+ return value;
26
+ throw new Error(`ALIGN_DETECTOR="${raw}" is not a detector version. Allowed: ${DETECTOR_VERSIONS.join(', ')} (unset means v1).`);
27
+ }
28
+ /** Where the active detector comes from, for `align config detector` and `align status`. */
29
+ export function detectorSource(env, saved) {
30
+ if (env['ALIGN_DETECTOR']?.trim())
31
+ return 'ALIGN_DETECTOR';
32
+ if (saved !== undefined)
33
+ return 'saved with align config detector';
34
+ return 'default';
35
+ }
36
+ //# sourceMappingURL=detector-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector-version.js","sourceRoot":"","sources":["../../src/detector/detector-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,IAAI,CAAU,CAAC;AAGvD,MAAM,UAAU,sBAAsB,CACpC,MAA0C,OAAO,CAAC,GAAG,EACrD,KAAc;IAEd,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,gGAAgG;QAChG,wFAAwF;QACxF,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACrC,IAAK,iBAAuC,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,KAAwB,CAAC;QAC9F,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,6DAA6D,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IACxK,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACzB,IAAK,iBAAuC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAwB,CAAC;IAC9F,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,yCAAyC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AACnI,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,GAAuC,EAAE,KAAyB;IAC/F,IAAI,GAAG,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE;QAAE,OAAO,gBAAgB,CAAC;IAC3D,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,kCAAkC,CAAC;IACnE,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The relationship contract, TypeScript half: the data in relationship-contract.v1.json plus the
3
+ * parser for one model answer and the check that the lists in the file agree with each other.
4
+ *
5
+ * Pure: no I/O, no network, no model call. This file is vendored byte for byte into align-stack's
6
+ * packages/llm-eval and pinned there by relationship-contract.lock (the brain keeps a Python twin,
7
+ * app/relationship_contract.py, that runs the same parse-vectors.json). Edit it here and run
8
+ * scripts/sync-relationship-contract.sh in align-stack; never edit a copy.
9
+ *
10
+ * Words are split on ASCII space, tab, CR and LF only, so that a non-breaking space counts the
11
+ * same in both languages. JSON is strict: NaN and Infinity are not JSON and drop the item.
12
+ */
13
+ import contractJson from './relationship-contract.v1.json';
14
+ export type RelationshipContract = typeof contractJson;
15
+ export type OfferName = keyof RelationshipContract['offers'];
16
+ export interface ParsedRelationshipItem {
17
+ type: string;
18
+ confidence: number;
19
+ reason: string;
20
+ evidence: {
21
+ new_quote: string;
22
+ existing_quote: string;
23
+ } | null;
24
+ replaced: 'new' | 'existing' | null;
25
+ }
26
+ export declare const RELATIONSHIP_CONTRACT: RelationshipContract;
27
+ export declare function parseRelationshipItem(raw: string, offer: OfferName, contract?: RelationshipContract): ParsedRelationshipItem | null;
28
+ /** What is wrong with the lists in a contract, one sentence each. Empty means they agree. */
29
+ export declare function contractProblems(contract: RelationshipContract): string[];
30
+ //# sourceMappingURL=relationship-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-contract.d.ts","sourceRoot":"","sources":["../../src/detector/relationship-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,YAAY,MAAM,iCAAiC,CAAuB;AAEjF,MAAM,MAAM,oBAAoB,GAAG,OAAO,YAAY,CAAC;AACvD,MAAM,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AAE7D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/D,QAAQ,EAAE,KAAK,GAAG,UAAU,GAAG,IAAI,CAAC;CACrC;AAED,eAAO,MAAM,qBAAqB,EAAE,oBAAmC,CAAC;AAyCxE,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,EAChB,QAAQ,GAAE,oBAA4C,GACrD,sBAAsB,GAAG,IAAI,CA6B/B;AAED,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAqBzE"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * The relationship contract, TypeScript half: the data in relationship-contract.v1.json plus the
3
+ * parser for one model answer and the check that the lists in the file agree with each other.
4
+ *
5
+ * Pure: no I/O, no network, no model call. This file is vendored byte for byte into align-stack's
6
+ * packages/llm-eval and pinned there by relationship-contract.lock (the brain keeps a Python twin,
7
+ * app/relationship_contract.py, that runs the same parse-vectors.json). Edit it here and run
8
+ * scripts/sync-relationship-contract.sh in align-stack; never edit a copy.
9
+ *
10
+ * Words are split on ASCII space, tab, CR and LF only, so that a non-breaking space counts the
11
+ * same in both languages. JSON is strict: NaN and Infinity are not JSON and drop the item.
12
+ */
13
+ import contractJson from './relationship-contract.v1.json' with { type: 'json' };
14
+ export const RELATIONSHIP_CONTRACT = contractJson;
15
+ /** The first balanced `{...}` in the text, string-aware, or null. No retry on a later object. */
16
+ function firstObjectText(raw) {
17
+ const start = raw.indexOf('{');
18
+ if (start < 0)
19
+ return null;
20
+ let depth = 0;
21
+ let inString = false;
22
+ let escaped = false;
23
+ for (let i = start; i < raw.length; i++) {
24
+ const ch = raw[i];
25
+ if (inString) {
26
+ if (escaped)
27
+ escaped = false;
28
+ else if (ch === '\\')
29
+ escaped = true;
30
+ else if (ch === '"')
31
+ inString = false;
32
+ }
33
+ else if (ch === '"')
34
+ inString = true;
35
+ else if (ch === '{')
36
+ depth++;
37
+ else if (ch === '}') {
38
+ depth--;
39
+ if (depth === 0)
40
+ return raw.slice(start, i + 1);
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ function wordCount(quote) {
46
+ return quote.split(/[ \t\n\r]+/).filter((w) => w !== '').length;
47
+ }
48
+ /** Both quotes present, non-blank and within the cap, or null. A half-populated pair reads as absent. */
49
+ function readEvidence(value, maxWords) {
50
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
51
+ return null;
52
+ const { new_quote: newQuote, existing_quote: existingQuote } = value;
53
+ if (typeof newQuote !== 'string' || typeof existingQuote !== 'string')
54
+ return null;
55
+ for (const quote of [newQuote, existingQuote]) {
56
+ const words = wordCount(quote);
57
+ if (words === 0 || words > maxWords)
58
+ return null;
59
+ }
60
+ return { new_quote: newQuote, existing_quote: existingQuote };
61
+ }
62
+ export function parseRelationshipItem(raw, offer, contract = RELATIONSHIP_CONTRACT) {
63
+ if (!Object.hasOwn(contract.offers, offer)) {
64
+ throw new Error(`Unknown offer list "${offer}". Allowed: ${Object.keys(contract.offers).join(', ')}.`);
65
+ }
66
+ const text = firstObjectText(raw);
67
+ if (text === null)
68
+ return null;
69
+ let obj;
70
+ try {
71
+ obj = JSON.parse(text);
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ const type = obj['type'];
77
+ if (typeof type !== 'string' || !contract.offers[offer].includes(type))
78
+ return null;
79
+ const [low, high] = contract.parse.confidenceClamp;
80
+ const confidence = typeof obj['confidence'] === 'number'
81
+ ? Math.min(Math.max(obj['confidence'], low), high)
82
+ : contract.parse.confidenceDefault;
83
+ const evidence = readEvidence(obj['evidence'], contract.parse.evidenceMaxWords);
84
+ if (evidence === null && contract.verdictTypes.includes(type))
85
+ return null;
86
+ const replaced = obj['replaced'] === 'new' || obj['replaced'] === 'existing' ? obj['replaced'] : null;
87
+ return {
88
+ type,
89
+ confidence,
90
+ reason: typeof obj['reason'] === 'string' ? obj['reason'] : '',
91
+ evidence,
92
+ replaced,
93
+ };
94
+ }
95
+ /** What is wrong with the lists in a contract, one sentence each. Empty means they agree. */
96
+ export function contractProblems(contract) {
97
+ const problems = [];
98
+ const types = contract.types;
99
+ const verdicts = contract.verdictTypes;
100
+ for (const t of new Set(types.filter((x, i) => types.indexOf(x) !== i))) {
101
+ problems.push(`duplicate in types: "${t}"`);
102
+ }
103
+ for (const v of verdicts) {
104
+ if (types.includes(v))
105
+ problems.push(`"${v}" is listed in types but is a verdict type, never a stored one`);
106
+ }
107
+ for (const [name, list] of Object.entries(contract.offers)) {
108
+ for (const t of list) {
109
+ if (!types.includes(t) && !verdicts.includes(t)) {
110
+ problems.push(`offers.${name} lists "${t}", which is not in types`);
111
+ }
112
+ }
113
+ }
114
+ for (const t of contract.conflictFamily) {
115
+ if (!types.includes(t))
116
+ problems.push(`conflictFamily lists "${t}", which is not in types`);
117
+ }
118
+ return problems;
119
+ }
120
+ //# sourceMappingURL=relationship-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-contract.js","sourceRoot":"","sources":["../../src/detector/relationship-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ {
2
+ "contract": "align.relationship",
3
+ "version": 1,
4
+ "revision": "2026-10-11 r2: adds relationship-prompt.v1.txt, the shared v1 base prompt (hosted ANALYSIS_RULES rules 0-3, the similar[] item schema and type list, no hosted-only extensions)",
5
+ "types": [
6
+ "blocks",
7
+ "clarifies",
8
+ "conflicts_with",
9
+ "contradicts",
10
+ "corrects",
11
+ "duplicates",
12
+ "excepts",
13
+ "implements",
14
+ "partially_supersedes",
15
+ "questions",
16
+ "refines",
17
+ "relates",
18
+ "supersedes",
19
+ "supports"
20
+ ],
21
+ "offers": {
22
+ "legacyV0": [
23
+ "supersedes",
24
+ "conflicts_with",
25
+ "contradicts",
26
+ "duplicates",
27
+ "clarifies",
28
+ "relates",
29
+ "refines",
30
+ "supports",
31
+ "questions",
32
+ "blocks"
33
+ ],
34
+ "v1Candidate": [
35
+ "incompatible",
36
+ "relates",
37
+ "duplicates",
38
+ "refines",
39
+ "clarifies",
40
+ "blocks",
41
+ "questions",
42
+ "supports"
43
+ ]
44
+ },
45
+ "verdictTypes": ["incompatible"],
46
+ "conflictFamily": ["conflicts_with", "contradicts"],
47
+ "parse": {
48
+ "confidenceDefault": 0.5,
49
+ "confidenceClamp": [0, 1],
50
+ "evidenceMaxWords": 15
51
+ },
52
+ "temperature": 0
53
+ }
@@ -0,0 +1,20 @@
1
+ export interface V1Subject {
2
+ title: string;
3
+ summary: string;
4
+ decidedAt?: string | null;
5
+ platform?: string | null;
6
+ }
7
+ export interface V1Candidate {
8
+ title: string;
9
+ summary: string;
10
+ similarity: number;
11
+ platform?: string | null;
12
+ decidedAt?: string | null;
13
+ status?: string | null;
14
+ }
15
+ export declare const V1_TEMPLATE_REVISION: string;
16
+ export declare function buildV1SystemPrompt(): string;
17
+ /** max(n * 512, 2048), capped at 8192 - the brain's formula (plan Decision 6). */
18
+ export declare function v1MaxTokens(candidateCount: number): number;
19
+ export declare function buildV1UserPrompt(subject: V1Subject, candidates: readonly V1Candidate[], windowTokens: number, outputTokens: number): string;
20
+ //# sourceMappingURL=relationship-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-prompt.d.ts","sourceRoot":"","sources":["../../src/detector/relationship-prompt.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,oBAAoB,QAAwB,CAAC;AAO1D,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAOD,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,WAAW,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAoB5I"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The v1 detector's prompt: the shared base (three byte-exact slices of the hosted ANALYSIS_RULES,
3
+ * in relationship-prompt.v1.json) wrapped in a CLI head, plus the user prompt listing every
4
+ * candidate in ONE call. The user prompt mirrors the hosted single-path shape (NEW DECISION,
5
+ * SIMILAR DECISIONS numbered, never an id) so one prompt revision means the same thing on both.
6
+ *
7
+ * What the local graph cannot supply is left out rather than invented: there is no author column,
8
+ * so no By line is ever rendered, and the resolver treats every local author as unknown.
9
+ *
10
+ * Pure: no I/O.
11
+ */
12
+ import template from './relationship-prompt.v1.json' with { type: 'json' };
13
+ import { isRetired, parseDecidedAt } from './resolve-pair.js';
14
+ import { charsForTokens, estimateTokens } from '../lib/token-estimate.js';
15
+ export const V1_TEMPLATE_REVISION = template.sourceSha256;
16
+ const HEAD = 'You are an AI that analyzes decisions for conflicts and relationships.\n\n'
17
+ + 'Analyze and respond in JSON format:\n{\n "relationships": {\n "similar": [\n ';
18
+ const TAIL = '\n ]\n }\n}\n\n'
19
+ + 'Give an entry only for a SIMILAR DECISION that is related to the NEW DECISION. Omit any that are not; an omitted decision gets no relationship.\n\n';
20
+ export function buildV1SystemPrompt() {
21
+ const { itemSchema, rules, types } = template.sections;
22
+ return `${HEAD}${itemSchema}${TAIL}${rules}\n\n${types}`;
23
+ }
24
+ const day = (value) => {
25
+ const us = parseDecidedAt(value);
26
+ return us === null ? null : new Date(Number(us / 1000n)).toISOString().slice(0, 10);
27
+ };
28
+ /** max(n * 512, 2048), capped at 8192 - the brain's formula (plan Decision 6). */
29
+ export function v1MaxTokens(candidateCount) {
30
+ return Math.min(Math.max(candidateCount * 512, 2048), 8192);
31
+ }
32
+ export function buildV1UserPrompt(subject, candidates, windowTokens, outputTokens) {
33
+ const subjectDay = day(subject.decidedAt);
34
+ const head = `Analyze this decision for relationships.\n\nNEW DECISION:\nTitle: ${subject.title}\nSummary: `;
35
+ const headTail = `\nPlatform: ${(subject.platform ?? 'cli').toLowerCase()}\n${subjectDay ? `Decided: ${subjectDay}\n` : ''}`
36
+ + '\nCONTEXT:\n- The Decided/By lines are facts captured from the source tools. Use them as context for whether two texts address the same subject; do not infer facts that are not shown.\n\n'
37
+ + 'SIMILAR DECISIONS:\n';
38
+ const lines = candidates.map((c, i) => {
39
+ const bits = [];
40
+ if (isRetired(c.status))
41
+ bits.push('RETIRED');
42
+ const d = day(c.decidedAt);
43
+ if (d)
44
+ bits.push(`decided ${d}`);
45
+ const facts = bits.length ? ` [${bits.join(' | ')}]` : '';
46
+ return `${i + 1}. ${c.title} (similarity: ${c.similarity.toFixed(2)}) [Platform: ${(c.platform ?? 'unknown').toLowerCase()}]${facts}\n `;
47
+ });
48
+ // Titles and labels are never cut; the summaries share what is left of the window equally.
49
+ const fixed = estimateTokens(buildV1SystemPrompt()) + outputTokens + estimateTokens(head + headTail + lines.join('\n\n'));
50
+ const perSummary = charsForTokens(Math.floor(Math.max(0, windowTokens - fixed) / (candidates.length + 1)));
51
+ const cut = (s) => (s.length > perSummary ? s.slice(0, perSummary) : s);
52
+ const body = candidates.map((c, i) => `${lines[i]}${cut(c.summary)}\n`).join('\n');
53
+ return `${head}${cut(subject.summary)}${headTail}${body}`;
54
+ }
55
+ //# sourceMappingURL=relationship-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-prompt.js","sourceRoot":"","sources":["../../src/detector/relationship-prompt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ {
2
+ "template": "align.relationship.prompt",
3
+ "version": 1,
4
+ "note": "Generated by scripts/extract-shared-prompt.py. Each section is a byte-exact slice of the hosted ANALYSIS_RULES. Do not hand-edit.",
5
+ "source": "services/brain/app/prompts/change_analysis.py ANALYSIS_RULES",
6
+ "sourceCommit": "e8ee8c820",
7
+ "sourceSha256": "2d1c378330ac036746bd2197386b3005a90eed2302125343b1127875ca3739b4",
8
+ "sections": {
9
+ "itemSchema": "{\"decision_ref\": <the number of the SIMILAR DECISION above>, \"type\": \"incompatible|relates|duplicates|refines|clarifies|blocks|questions|supports\", \"confidence\": 0.0-1.0, \"reasons\": [\"...\"], \"evidence\": {\"new_quote\": \"...\", \"existing_quote\": \"...\"}, \"replaced\": \"new\" | \"existing\" | null}",
10
+ "rules": "CRITICAL INSTRUCTIONS FOR RELATIONSHIP DETECTION:\n0. **AGREEMENT CHECK (do this BEFORE 1 or 2, on every SIMILAR DECISION pair)**: a decision stating a GENERAL rule (\"when situation X happens, do Y\") and a decision describing one CONCRETE instance of applying that SAME rule (\"this case was situation X, so we did Y\") are NOT incompatible - the second is an example of the first, not an opposing stance. This holds even when the concrete instance names specific details (a version number, a parameter value, a ticket) that the general rule states only abstractly, and even when the general rule was written AFTER the instance, extracted FROM it. Report \"relates\" or \"supports\" for this shape, never \"incompatible\". Only report \"incompatible\" when the two decisions could not BOTH be applied to the SAME concrete situation - never merely because one names a specific case and the other states the rule behind it.\n Worked example: existing decision \"bump the deploy pin and CLI version together whenever a change adds a new CLI flag\" (a general rule) vs new decision \"bump the deploy pin and CLI version to 0.11.0 to add the --title flag\" (one concrete case of following that exact rule) -> COMPLYING, not opposing -> \"relates\" or \"supports\", never \"incompatible\".\n\n1. **INCOMPATIBLE** - report type \"incompatible\" when the NEW DECISION and a SIMILAR DECISION take positions on the SAME subject that cannot both hold (e.g. \"use X\" vs \"use Y\" for the same job, a value set to 50 vs the same value capped at 25) - AFTER the agreement check above has ruled out the general-rule/concrete-instance shape. You do NOT decide whether that is a conflict or a supersession - that verdict is computed outside this call from the Decided/By facts. For every \"incompatible\" entry you MUST also return:\n - \"evidence\": a verbatim quote from EACH side, max 15 words each, showing the two stances that cannot both hold (\"new_quote\" from the NEW DECISION, \"existing_quote\" from that SIMILAR DECISION).\n - \"replaced\": the side whose OWN text explicitly references and replaces the other (\"switching from X\", \"instead of the earlier plan\", \"reverting the Y change\") - \"new\" if the NEW DECISION's text does the replacing, \"existing\" if the SIMILAR DECISION's text does, else null. Merely restating the same topic is NOT an explicit reference.\n \"evidence\" and \"replaced\" are only for \"incompatible\" entries; omit them otherwise.\n\n2. **ARCHITECTURAL INCOMPATIBILITY**: report \"incompatible\" when two decisions propose approaches that CANNOT both be implemented simultaneously:\n - Different technology stacks for the same component (e.g., \"use SQS batching\" vs \"use synchronous REST\")\n - Different architectural patterns for the same pipeline (e.g., \"standalone worker service\" vs \"inline microservice\")\n - Contradictory resource allocation - two proposals each specifying a different value for the same parameter\n - Key signal: decisions about the SAME system that propose mutually exclusive designs\n\n3. **REFINES vs CLARIFIES**:\n - Use \"refines\" when adding CONCRETE IMPLEMENTATION DETAILS (specs, architecture, API design, rate limits, error codes)\n - Use \"clarifies\" when adding CONTEXT or EXPLANATION (why we made this decision, background, rationale)\n - Example: Slack says \"implement rate limiting\" → Jira adds \"429 status, 1000 req/min\" → REFINES\n - Example: Slack says \"use Redis\" → Comment adds \"chosen for performance\" → CLARIFIES",
11
+ "types": "Relationship types (SIMILAR DECISIONS):\n- incompatible: The two decisions take positions on the same subject that cannot both hold - the conflict-vs-supersession verdict is computed outside this call from the Decided/By facts\n- refines: ENHANCES or adds IMPLEMENTATION DETAILS to existing decision (specifications, technical details, concrete steps)\n- clarifies: Adds detail without changing the core decision (explanations, context, rationale)\n- duplicates: Essentially the same decision repeated\n- relates: Related but not conflicting\n- blocks: One decision PREVENTS progress on another (keywords: \"cannot proceed until\", \"blocked by\", \"depends on\")\n- questions: One decision RAISES CONCERNS about another (keywords: \"are we sure\", \"what about\", \"concern about\")\n- supports: One decision provides EVIDENCE for another (keywords: \"this supports\", \"validates\", \"confirms\")"
12
+ }
13
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * TypeScript port of the brain's pure pair resolver, `resolve_incompatible_pair` in align-stack
3
+ * `services/brain/app/relationship_type_rules.py` (ALI-625, ALI-1042, ALI-1050).
4
+ *
5
+ * The model reports only that two decisions are INCOMPATIBLE, plus which side's own text replaces
6
+ * the other. Whether that is a supersession or a conflict is a question about dates, authorship
7
+ * and status, so it is computed here and never taken from the model. Every ambiguous case
8
+ * resolves to the non-destructive `conflicts_with`.
9
+ *
10
+ * Checked against resolver-vectors.json, generated by running the brain's REAL function
11
+ * (scripts/gen-resolver-vectors.py). Change the brain first, regenerate, then port.
12
+ *
13
+ * Pure: no I/O, no clock, no network.
14
+ */
15
+ /** Within this window two decisions are concurrent and a supersession is refused. */
16
+ export declare const CONCURRENT_WINDOW_HOURS = 24;
17
+ export interface PairSide {
18
+ id: string;
19
+ /** ISO 8601 text, or null. Unparseable reads as missing, which refuses the destructive edge. */
20
+ decidedAt: string | null;
21
+ /** null = unknown. Two unknown authors are never the same author. */
22
+ author: string | null;
23
+ status?: string | null;
24
+ standing?: string | null;
25
+ exceptedBy?: readonly string[];
26
+ }
27
+ export interface Resolution {
28
+ type: 'supersedes' | 'conflicts_with';
29
+ fromId: string;
30
+ toId: string;
31
+ }
32
+ /** Microseconds since 1970-01-01T00:00:00Z, or null where Python's parse_decided_at gives None (or raises). */
33
+ export declare function parseDecidedAt(value: unknown): bigint | null;
34
+ export declare function pyStrip(s: string): string;
35
+ export declare function normalizeAuthor(value: unknown): string | null;
36
+ export declare function isRetired(status: string | null | undefined): boolean;
37
+ /** The edge for an incompatible pair, or null for no edge at all. */
38
+ export declare function resolveIncompatiblePair(a: PairSide, b: PairSide, explicitReplacementBy?: string | null): Resolution | null;
39
+ //# sourceMappingURL=resolve-pair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-pair.d.ts","sourceRoot":"","sources":["../../src/detector/resolve-pair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAO1C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,gGAAgG;IAChG,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qEAAqE;IACrE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,GAAG,gBAAgB,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AA4JD,+GAA+G;AAC/G,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAsD5D;AAKD,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMzC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK7D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEpE;AAKD,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,GAAE,MAAM,GAAG,IAAW,GAAG,UAAU,GAAG,IAAI,CAoBhI"}