@topogram/cli 0.3.63 → 0.3.65

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 (344) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +703 -0
  3. package/src/adoption/plan.d.ts +6 -0
  4. package/src/adoption/plan.js +12 -703
  5. package/src/adoption/reporting.d.ts +10 -0
  6. package/src/adoption/review-groups.d.ts +6 -0
  7. package/src/agent-brief.d.ts +3 -0
  8. package/src/agent-brief.js +495 -0
  9. package/src/agent-ops/query-builders/auth.js +375 -0
  10. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  12. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  13. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  14. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  15. package/src/agent-ops/query-builders/change-risk.js +25 -0
  16. package/src/agent-ops/query-builders/common.js +149 -0
  17. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  18. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  19. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  20. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  21. package/src/agent-ops/query-builders/work-packets.js +417 -0
  22. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  23. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  24. package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
  25. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  26. package/src/agent-ops/query-builders.d.ts +26 -0
  27. package/src/agent-ops/query-builders.js +42 -5021
  28. package/src/archive/archive.d.ts +2 -0
  29. package/src/archive/compact.d.ts +1 -0
  30. package/src/archive/unarchive.d.ts +1 -0
  31. package/src/catalog/constants.js +10 -0
  32. package/src/catalog/copy.js +60 -0
  33. package/src/catalog/diagnostics.js +15 -0
  34. package/src/catalog/entries.js +42 -0
  35. package/src/catalog/files.js +67 -0
  36. package/src/catalog/provenance.js +122 -0
  37. package/src/catalog/source.js +150 -0
  38. package/src/catalog/validation.js +252 -0
  39. package/src/catalog.d.ts +12 -0
  40. package/src/catalog.js +18 -750
  41. package/src/cli/catalog-alias.d.ts +1 -0
  42. package/src/cli/command-parser.js +38 -0
  43. package/src/cli/command-parsers/core.js +102 -0
  44. package/src/cli/command-parsers/generator.js +39 -0
  45. package/src/cli/command-parsers/import.js +44 -0
  46. package/src/cli/command-parsers/legacy-workflow.js +21 -0
  47. package/src/cli/command-parsers/project.js +47 -0
  48. package/src/cli/command-parsers/sdlc.js +47 -0
  49. package/src/cli/command-parsers/shared.js +51 -0
  50. package/src/cli/command-parsers/template.js +48 -0
  51. package/src/cli/commands/agent.js +47 -0
  52. package/src/cli/commands/catalog/check.js +31 -0
  53. package/src/cli/commands/catalog/copy.js +59 -0
  54. package/src/cli/commands/catalog/doctor.js +248 -0
  55. package/src/cli/commands/catalog/help.js +21 -0
  56. package/src/cli/commands/catalog/list.js +52 -0
  57. package/src/cli/commands/catalog/runner.js +92 -0
  58. package/src/cli/commands/catalog/shared.js +17 -0
  59. package/src/cli/commands/catalog/show.js +134 -0
  60. package/src/cli/commands/catalog.js +32 -0
  61. package/src/cli/commands/check.js +268 -0
  62. package/src/cli/commands/doctor.js +268 -0
  63. package/src/cli/commands/emit.js +149 -0
  64. package/src/cli/commands/generate.js +96 -0
  65. package/src/cli/commands/generator-policy/package-info.js +162 -0
  66. package/src/cli/commands/generator-policy/payloads.js +372 -0
  67. package/src/cli/commands/generator-policy/printers.js +159 -0
  68. package/src/cli/commands/generator-policy/runner.js +81 -0
  69. package/src/cli/commands/generator-policy/shared.js +39 -0
  70. package/src/cli/commands/generator-policy.js +17 -0
  71. package/src/cli/commands/generator.js +443 -0
  72. package/src/cli/commands/import/adopt.js +170 -0
  73. package/src/cli/commands/import/check.js +91 -0
  74. package/src/cli/commands/import/diff.js +84 -0
  75. package/src/cli/commands/import/help.js +47 -0
  76. package/src/cli/commands/import/paths.js +277 -0
  77. package/src/cli/commands/import/plan.js +284 -0
  78. package/src/cli/commands/import/refresh.js +470 -0
  79. package/src/cli/commands/import/status-history.js +196 -0
  80. package/src/cli/commands/import/workspace.js +230 -0
  81. package/src/cli/commands/import-runner.js +157 -0
  82. package/src/cli/commands/import.js +35 -0
  83. package/src/cli/commands/inspect.js +55 -0
  84. package/src/cli/commands/new.js +94 -0
  85. package/src/cli/commands/package/constants.js +17 -0
  86. package/src/cli/commands/package/doctor.js +240 -0
  87. package/src/cli/commands/package/help.js +27 -0
  88. package/src/cli/commands/package/lockfile.js +135 -0
  89. package/src/cli/commands/package/npm.js +97 -0
  90. package/src/cli/commands/package/reporting.js +35 -0
  91. package/src/cli/commands/package/runner.js +33 -0
  92. package/src/cli/commands/package/shared.js +9 -0
  93. package/src/cli/commands/package/update-cli.js +252 -0
  94. package/src/cli/commands/package/versions.js +35 -0
  95. package/src/cli/commands/package.js +31 -0
  96. package/src/cli/commands/query/change-plan.js +68 -0
  97. package/src/cli/commands/query/definitions.js +202 -0
  98. package/src/cli/commands/query/import-adopt.js +121 -0
  99. package/src/cli/commands/query/runner/artifacts.js +102 -0
  100. package/src/cli/commands/query/runner/boundaries.js +211 -0
  101. package/src/cli/commands/query/runner/change.js +182 -0
  102. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  103. package/src/cli/commands/query/runner/index.js +31 -0
  104. package/src/cli/commands/query/runner/output.js +12 -0
  105. package/src/cli/commands/query/runner/workflow.js +241 -0
  106. package/src/cli/commands/query/runner.js +3 -0
  107. package/src/cli/commands/query/workflow-context.js +5 -0
  108. package/src/cli/commands/query/workspace.js +274 -0
  109. package/src/cli/commands/query.js +11 -0
  110. package/src/cli/commands/release-rollout.js +257 -0
  111. package/src/cli/commands/release-shared.js +528 -0
  112. package/src/cli/commands/release-status.js +429 -0
  113. package/src/cli/commands/release.js +107 -0
  114. package/src/cli/commands/sdlc.js +168 -0
  115. package/src/cli/commands/setup.js +76 -0
  116. package/src/cli/commands/source.js +291 -0
  117. package/src/cli/commands/template/baseline.js +100 -0
  118. package/src/cli/commands/template/check.js +466 -0
  119. package/src/cli/commands/template/constants.js +8 -0
  120. package/src/cli/commands/template/diagnostics.js +26 -0
  121. package/src/cli/commands/template/help.js +28 -0
  122. package/src/cli/commands/template/lifecycle.js +404 -0
  123. package/src/cli/commands/template/list-show.js +287 -0
  124. package/src/cli/commands/template/policy.js +422 -0
  125. package/src/cli/commands/template/shared.js +127 -0
  126. package/src/cli/commands/template/updates.js +352 -0
  127. package/src/cli/commands/template-runner.js +198 -0
  128. package/src/cli/commands/template.js +43 -0
  129. package/src/cli/commands/trust.js +219 -0
  130. package/src/cli/commands/version.js +40 -0
  131. package/src/cli/commands/widget.js +168 -0
  132. package/src/cli/commands/workflow.js +63 -0
  133. package/src/cli/dispatcher.js +392 -0
  134. package/src/cli/help-dispatch.js +188 -0
  135. package/src/cli/help.js +296 -0
  136. package/src/cli/migration-guidance.js +59 -0
  137. package/src/cli/options.js +96 -0
  138. package/src/cli/output-safety.js +107 -0
  139. package/src/cli/path-normalization.js +29 -0
  140. package/src/cli.js +47 -11711
  141. package/src/example-implementation.d.ts +2 -0
  142. package/src/format.d.ts +1 -0
  143. package/src/generator/api/contracts.js +497 -0
  144. package/src/generator/api/metadata.js +221 -0
  145. package/src/generator/api/openapi.js +559 -0
  146. package/src/generator/api/schema.js +124 -0
  147. package/src/generator/api/types.d.ts +98 -0
  148. package/src/generator/api.js +3 -1195
  149. package/src/generator/check.d.ts +1 -0
  150. package/src/generator/context/bundle.d.ts +1 -0
  151. package/src/generator/context/shared/domain-sdlc.js +282 -0
  152. package/src/generator/context/shared/maintained-boundary.js +665 -0
  153. package/src/generator/context/shared/metrics.js +85 -0
  154. package/src/generator/context/shared/primitives.js +64 -0
  155. package/src/generator/context/shared/relationships.js +453 -0
  156. package/src/generator/context/shared/summaries.js +263 -0
  157. package/src/generator/context/shared/types.d.ts +207 -0
  158. package/src/generator/context/shared.d.ts +44 -0
  159. package/src/generator/context/shared.js +80 -1390
  160. package/src/generator/context/slice/core.js +397 -0
  161. package/src/generator/context/slice/sdlc.js +417 -0
  162. package/src/generator/context/slice/ui-packets.js +183 -0
  163. package/src/generator/context/slice.js +2 -859
  164. package/src/generator/native/parity-bundle.js +2 -1
  165. package/src/generator/registry/index.js +507 -0
  166. package/src/generator/registry.js +18 -504
  167. package/src/generator/runtime/environment/index.js +666 -0
  168. package/src/generator/runtime/environment.js +4 -666
  169. package/src/generator/runtime/runtime-check/index.js +554 -0
  170. package/src/generator/runtime/runtime-check.js +4 -554
  171. package/src/generator/runtime/shared/index.js +572 -0
  172. package/src/generator/runtime/shared.js +19 -570
  173. package/src/generator/shared.d.ts +2 -0
  174. package/src/generator/surfaces/shared.d.ts +3 -0
  175. package/src/generator/surfaces/web/html-escape.js +22 -0
  176. package/src/generator/surfaces/web/react.js +10 -8
  177. package/src/generator/surfaces/web/sveltekit.js +7 -5
  178. package/src/generator/surfaces/web/vanilla.js +8 -4
  179. package/src/generator/widget-conformance/behavior-report.js +258 -0
  180. package/src/generator/widget-conformance/checks.js +371 -0
  181. package/src/generator/widget-conformance/projection-context.js +200 -0
  182. package/src/generator/widget-conformance/report.js +166 -0
  183. package/src/generator/widget-conformance/types.d.ts +121 -0
  184. package/src/generator/widget-conformance.js +3 -824
  185. package/src/generator.d.ts +2 -0
  186. package/src/github-client.js +520 -0
  187. package/src/import/core/context.d.ts +3 -0
  188. package/src/import/core/contracts.d.ts +1 -0
  189. package/src/import/core/registry.d.ts +4 -0
  190. package/src/import/core/runner/candidates.js +217 -0
  191. package/src/import/core/runner/options.js +22 -0
  192. package/src/import/core/runner/reports.js +50 -0
  193. package/src/import/core/runner/run.js +79 -0
  194. package/src/import/core/runner/tracks.js +150 -0
  195. package/src/import/core/runner/ui-drafts.js +337 -0
  196. package/src/import/core/runner.js +3 -698
  197. package/src/import/core/shared/api-routes.js +221 -0
  198. package/src/import/core/shared/candidates.js +97 -0
  199. package/src/import/core/shared/files.js +177 -0
  200. package/src/import/core/shared/next-app.js +389 -0
  201. package/src/import/core/shared/types.d.ts +51 -0
  202. package/src/import/core/shared/ui-routes.js +230 -0
  203. package/src/import/core/shared.js +67 -910
  204. package/src/import/extractors/api/flutter-dio.js +4 -8
  205. package/src/import/extractors/api/react-native-repository.js +4 -8
  206. package/src/import/index.d.ts +4 -0
  207. package/src/import/provenance.d.ts +4 -0
  208. package/src/new-project/constants.js +128 -0
  209. package/src/new-project/create.js +83 -0
  210. package/src/new-project/json.js +28 -0
  211. package/src/new-project/metadata.js +96 -0
  212. package/src/new-project/package-spec.js +161 -0
  213. package/src/new-project/project-files.js +348 -0
  214. package/src/new-project/template-policy.js +269 -0
  215. package/src/new-project/template-resolution.js +368 -0
  216. package/src/new-project/template-snapshots.js +430 -0
  217. package/src/new-project/template-updates.js +512 -0
  218. package/src/new-project/types.d.ts +83 -0
  219. package/src/new-project.js +6 -2188
  220. package/src/npm-safety.js +79 -0
  221. package/src/parser.d.ts +87 -0
  222. package/src/parser.js +118 -0
  223. package/src/path-helpers.d.ts +1 -0
  224. package/src/path-helpers.js +20 -0
  225. package/src/policy/review-boundaries.d.ts +15 -0
  226. package/src/project-config/index.js +564 -0
  227. package/src/project-config.js +19 -560
  228. package/src/reconcile/docs.d.ts +8 -0
  229. package/src/reconcile/journeys.d.ts +1 -0
  230. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  231. package/src/resolver/enrich/bug.js +2 -0
  232. package/src/resolver/enrich/pitch.js +2 -0
  233. package/src/resolver/enrich/requirement.js +2 -0
  234. package/src/resolver/enrich/task.js +2 -0
  235. package/src/resolver/index.js +19 -2089
  236. package/src/resolver/normalize.js +384 -1
  237. package/src/resolver/plans.js +168 -0
  238. package/src/resolver/projections-api.js +494 -0
  239. package/src/resolver/projections-db.js +133 -0
  240. package/src/resolver/projections-ui.js +317 -0
  241. package/src/resolver/shapes.js +251 -0
  242. package/src/resolver/shared.js +278 -0
  243. package/src/resolver/widgets.js +132 -0
  244. package/src/resolver.d.ts +1 -0
  245. package/src/runtime-support.js +29 -0
  246. package/src/sdlc/adopt.d.ts +1 -0
  247. package/src/sdlc/check.d.ts +1 -0
  248. package/src/sdlc/explain.d.ts +1 -0
  249. package/src/sdlc/release.d.ts +1 -0
  250. package/src/sdlc/scaffold.d.ts +1 -0
  251. package/src/sdlc/transition.d.ts +1 -0
  252. package/src/template-trust/constants.js +62 -0
  253. package/src/template-trust/content.js +258 -0
  254. package/src/template-trust/diff.js +92 -0
  255. package/src/template-trust/policy.js +61 -0
  256. package/src/template-trust/record.js +90 -0
  257. package/src/template-trust/status.js +182 -0
  258. package/src/template-trust.js +24 -687
  259. package/src/text-helpers.d.ts +7 -0
  260. package/src/text-helpers.js +245 -0
  261. package/src/topogram-config.js +306 -0
  262. package/src/topogram-types.d.ts +69 -0
  263. package/src/validator/common.js +488 -0
  264. package/src/validator/data-model.js +237 -0
  265. package/src/validator/docs.js +167 -0
  266. package/src/validator/expressions.js +146 -1
  267. package/src/validator/index.d.ts +23 -0
  268. package/src/validator/index.js +32 -3585
  269. package/src/validator/kinds.d.ts +41 -0
  270. package/src/validator/kinds.js +2 -0
  271. package/src/validator/model-helpers.js +46 -0
  272. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  273. package/src/validator/per-kind/bug.js +6 -0
  274. package/src/validator/per-kind/domain.js +15 -2
  275. package/src/validator/per-kind/pitch.js +7 -0
  276. package/src/validator/per-kind/requirement.js +5 -0
  277. package/src/validator/per-kind/task.js +7 -0
  278. package/src/validator/per-kind/widget.js +14 -0
  279. package/src/validator/projections/api-http-async.js +410 -0
  280. package/src/validator/projections/api-http-authz.js +88 -0
  281. package/src/validator/projections/api-http-core.js +205 -0
  282. package/src/validator/projections/api-http-policies.js +339 -0
  283. package/src/validator/projections/api-http-responses.js +233 -0
  284. package/src/validator/projections/api-http.js +44 -0
  285. package/src/validator/projections/db.js +353 -0
  286. package/src/validator/projections/generator-defaults.js +45 -0
  287. package/src/validator/projections/helpers.js +87 -0
  288. package/src/validator/projections/ui-helpers.js +214 -0
  289. package/src/validator/projections/ui-navigation.js +344 -0
  290. package/src/validator/projections/ui-structure.js +364 -0
  291. package/src/validator/projections/ui-widgets.js +493 -0
  292. package/src/validator/projections/ui.js +46 -0
  293. package/src/validator/registry.js +48 -1
  294. package/src/validator/utils.d.ts +20 -0
  295. package/src/validator/utils.js +115 -12
  296. package/src/validator.d.ts +2 -0
  297. package/src/widget-behavior.d.ts +1 -0
  298. package/src/workflows/adoption/index.js +26 -0
  299. package/src/workflows/docs-generate.js +262 -0
  300. package/src/workflows/docs-scan.js +703 -0
  301. package/src/workflows/docs.js +15 -0
  302. package/src/workflows/import-app/api/collect.js +221 -0
  303. package/src/workflows/import-app/api/openapi.js +257 -0
  304. package/src/workflows/import-app/api/routes.js +327 -0
  305. package/src/workflows/import-app/api/sources.js +22 -0
  306. package/src/workflows/import-app/api.js +4 -0
  307. package/src/workflows/import-app/db.js +538 -0
  308. package/src/workflows/import-app/index.js +30 -0
  309. package/src/workflows/import-app/shared.js +218 -0
  310. package/src/workflows/import-app/ui.js +443 -0
  311. package/src/workflows/import-app/workflow.js +159 -0
  312. package/src/workflows/reconcile/adoption-plan/build.js +208 -0
  313. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  314. package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
  315. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  316. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  317. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  318. package/src/workflows/reconcile/adoption-plan.js +32 -0
  319. package/src/workflows/reconcile/auth/closures.js +115 -0
  320. package/src/workflows/reconcile/auth/formatters.js +142 -0
  321. package/src/workflows/reconcile/auth/inference.js +330 -0
  322. package/src/workflows/reconcile/auth/roles.js +122 -0
  323. package/src/workflows/reconcile/auth.js +37 -0
  324. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  325. package/src/workflows/reconcile/bundle-core.js +14 -0
  326. package/src/workflows/reconcile/bundle-shared.js +75 -0
  327. package/src/workflows/reconcile/candidate-model.js +477 -0
  328. package/src/workflows/reconcile/canonical-surface.js +264 -0
  329. package/src/workflows/reconcile/gap-report.js +333 -0
  330. package/src/workflows/reconcile/ids.js +6 -0
  331. package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
  332. package/src/workflows/reconcile/impacts/indexes.js +101 -0
  333. package/src/workflows/reconcile/impacts/patches.js +252 -0
  334. package/src/workflows/reconcile/impacts/reports.js +80 -0
  335. package/src/workflows/reconcile/impacts.js +16 -0
  336. package/src/workflows/reconcile/index.js +7 -0
  337. package/src/workflows/reconcile/renderers.js +461 -0
  338. package/src/workflows/reconcile/summary.js +90 -0
  339. package/src/workflows/reconcile/workflow.js +309 -0
  340. package/src/workflows/shared.js +189 -0
  341. package/src/workflows/types.d.ts +93 -0
  342. package/src/workflows.d.ts +1 -0
  343. package/src/workflows.js +10 -7652
  344. package/src/workspace-docs.d.ts +29 -0
@@ -0,0 +1,177 @@
1
+ // @ts-check
2
+
3
+ import { ensureTrailingNewline } from "../../../text-helpers.js";
4
+
5
+ /** @param {any[]} lines @param {string} blockName @returns {any} */
6
+ export function ensureProjectionBlock(lines, blockName) {
7
+ const startIndex = lines.findIndex((/** @type {any} */ line) => new RegExp(`^\\s*${blockName}\\s*\\{\\s*$`).test(line));
8
+ if (startIndex !== -1) {
9
+ let endIndex = -1;
10
+ for (let index = startIndex + 1; index < lines.length; index += 1) {
11
+ if (/^\s*\}\s*$/.test(lines[index])) {
12
+ endIndex = index;
13
+ break;
14
+ }
15
+ }
16
+ if (endIndex !== -1) {
17
+ return { lines, startIndex, endIndex };
18
+ }
19
+ }
20
+ const insertBeforeStatus = lines.findIndex((/** @type {any} */ line) => /^\s*status\s+\w+/.test(line));
21
+ const insertIndex = insertBeforeStatus === -1 ? lines.length : insertBeforeStatus;
22
+ const blockLines = ["", ` ${blockName} {`, " }"];
23
+ lines.splice(insertIndex, 0, ...blockLines);
24
+ return {
25
+ lines,
26
+ startIndex: insertIndex + 1,
27
+ endIndex: insertIndex + 2
28
+ };
29
+ }
30
+
31
+ /** @param {any[]} lines @param {any[]} capabilityIds @returns {any} */
32
+ export function ensureProjectionRealizes(lines, capabilityIds) {
33
+ const startIndex = lines.findIndex((/** @type {any} */ line) => /^\s*realizes\s*\[/.test(line));
34
+ if (startIndex === -1) {
35
+ return { changed: false, lines };
36
+ }
37
+ let endIndex = startIndex;
38
+ while (endIndex < lines.length && !/\]/.test(lines[endIndex])) {
39
+ endIndex += 1;
40
+ }
41
+ if (endIndex >= lines.length) {
42
+ return { changed: false, lines };
43
+ }
44
+ /** @type {any[]} */
45
+ const existingItems = [];
46
+ for (let index = startIndex; index <= endIndex; index += 1) {
47
+ const text = lines[index]
48
+ .replace(/^\s*realizes\s*\[/, "")
49
+ .replace(/\]\s*$/, "")
50
+ .trim();
51
+ if (!text) {
52
+ continue;
53
+ }
54
+ for (const item of text.split(",").map((/** @type {any} */ entry) => entry.trim()).filter(Boolean)) {
55
+ existingItems.push(item);
56
+ }
57
+ }
58
+ const merged = [...new Set([...existingItems, ...(capabilityIds || [])])];
59
+ if (merged.length === existingItems.length) {
60
+ return { changed: false, lines };
61
+ }
62
+ const replacement = [" realizes [", ...merged.map((/** @type {any} */ item, /** @type {any} */ index) => ` ${item}${index === merged.length - 1 ? "" : ","}`), " ]"];
63
+ lines.splice(startIndex, endIndex - startIndex + 1, ...replacement);
64
+ return { changed: true, lines };
65
+ }
66
+
67
+ /** @param {string} baseContents @param {WorkflowRecord} item @returns {any} */
68
+ export function applyProjectionAuthPatchToTopogram(baseContents, item) {
69
+ const lines = String(baseContents || "").replace(/\r\n/g, "\n").split("\n");
70
+ const capabilities = [...new Set(item.related_capabilities || [])];
71
+ let changed = false;
72
+
73
+ const realizesResult = ensureProjectionRealizes(lines, capabilities);
74
+ changed = changed || realizesResult.changed;
75
+
76
+ if (item.projection_surface === "authorization") {
77
+ const block = ensureProjectionBlock(lines, "authorization");
78
+ for (const capabilityId of capabilities) {
79
+ const lineIndex = lines.findIndex((/** @type {any} */ line, /** @type {any} */ index) =>
80
+ index > block.startIndex &&
81
+ index < block.endIndex &&
82
+ new RegExp(`^\\s*${capabilityId}(\\s|$)`).test(line)
83
+ );
84
+ if (item.suggested_action === "apply_projection_ownership_patch") {
85
+ const ownershipFragment = `ownership ${item.ownership}${item.ownership_field ? ` ownership_field ${item.ownership_field}` : ""}`;
86
+ if (lineIndex !== -1) {
87
+ if (!/\bownership\s+/.test(lines[lineIndex])) {
88
+ lines[lineIndex] = `${lines[lineIndex].trimEnd()} ${ownershipFragment}`;
89
+ changed = true;
90
+ }
91
+ continue;
92
+ }
93
+ lines.splice(block.endIndex, 0, ` ${capabilityId} ${ownershipFragment}`);
94
+ block.endIndex += 1;
95
+ changed = true;
96
+ continue;
97
+ }
98
+
99
+ if (item.suggested_action === "apply_projection_permission_patch") {
100
+ const permissionFragment = `permission ${item.permission}`;
101
+ if (lineIndex !== -1) {
102
+ if (!/\bpermission\s+/.test(lines[lineIndex])) {
103
+ lines[lineIndex] = `${lines[lineIndex].trimEnd()} ${permissionFragment}`;
104
+ changed = true;
105
+ }
106
+ continue;
107
+ }
108
+ lines.splice(block.endIndex, 0, ` ${capabilityId} ${permissionFragment}`);
109
+ block.endIndex += 1;
110
+ changed = true;
111
+ continue;
112
+ }
113
+
114
+ const claimFragment = `claim ${item.claim}${item.claim_value != null ? ` claim_value ${item.claim_value}` : ""}`;
115
+ if (lineIndex !== -1) {
116
+ if (!/\bclaim\s+/.test(lines[lineIndex])) {
117
+ lines[lineIndex] = `${lines[lineIndex].trimEnd()} ${claimFragment}`;
118
+ changed = true;
119
+ }
120
+ continue;
121
+ }
122
+ lines.splice(block.endIndex, 0, ` ${capabilityId} ${claimFragment}`);
123
+ block.endIndex += 1;
124
+ changed = true;
125
+ }
126
+ }
127
+
128
+ if (item.projection_surface === "visibility_rules") {
129
+ const block = ensureProjectionBlock(lines, "visibility_rules");
130
+ for (const capabilityId of capabilities) {
131
+ if (item.suggested_action === "apply_projection_permission_patch") {
132
+ const hasExistingPermissionRule = lines.some((/** @type {any} */ line, /** @type {any} */ index) =>
133
+ index > block.startIndex &&
134
+ index < block.endIndex &&
135
+ new RegExp(`^\\s*action\\s+${capabilityId}\\s+visible_if\\s+permission\\s+${item.permission}(\\s|$)`).test(line)
136
+ );
137
+ if (hasExistingPermissionRule) {
138
+ continue;
139
+ }
140
+ const hasAnyVisibilityRule = lines.some((/** @type {any} */ line, /** @type {any} */ index) =>
141
+ index > block.startIndex &&
142
+ index < block.endIndex &&
143
+ new RegExp(`^\\s*action\\s+${capabilityId}\\s+visible_if\\s+`).test(line)
144
+ );
145
+ if (hasAnyVisibilityRule) {
146
+ continue;
147
+ }
148
+ lines.splice(block.endIndex, 0, ` action ${capabilityId} visible_if permission ${item.permission}`);
149
+ block.endIndex += 1;
150
+ changed = true;
151
+ continue;
152
+ }
153
+
154
+ const hasExistingClaimRule = lines.some((/** @type {any} */ line, /** @type {any} */ index) =>
155
+ index > block.startIndex &&
156
+ index < block.endIndex &&
157
+ new RegExp(`^\\s*action\\s+${capabilityId}\\s+visible_if\\s+claim\\s+${item.claim}(\\s|$)`).test(line)
158
+ );
159
+ if (hasExistingClaimRule) {
160
+ continue;
161
+ }
162
+ const hasAnyVisibilityRule = lines.some((/** @type {any} */ line, /** @type {any} */ index) =>
163
+ index > block.startIndex &&
164
+ index < block.endIndex &&
165
+ new RegExp(`^\\s*action\\s+${capabilityId}\\s+visible_if\\s+`).test(line)
166
+ );
167
+ if (hasAnyVisibilityRule) {
168
+ continue;
169
+ }
170
+ lines.splice(block.endIndex, 0, ` action ${capabilityId} visible_if claim ${item.claim}${item.claim_value != null ? ` claim_value ${item.claim_value}` : ""}`);
171
+ block.endIndex += 1;
172
+ changed = true;
173
+ }
174
+ }
175
+
176
+ return changed ? ensureTrailingNewline(lines.join("\n")) : baseContents;
177
+ }
@@ -0,0 +1,107 @@
1
+ // @ts-check
2
+
3
+ /** @param {WorkflowRecord} step @returns {any} */
4
+ export function reasonForAdoptionItem(step) {
5
+ switch (step.action) {
6
+ case "promote_actor":
7
+ return "Promote this imported actor into canonical Topogram.";
8
+ case "promote_role":
9
+ return "Promote this imported role into canonical Topogram.";
10
+ case "promote_entity":
11
+ return "No canonical entity exists for this imported concept.";
12
+ case "promote_enum":
13
+ return step.target ? `Promote this enum to support merged concept ${step.target}.` : "Promote this imported enum into canonical Topogram.";
14
+ case "promote_shape":
15
+ return step.target ? `Promote this shape to support concept ${step.target}.` : "Promote this imported shape into canonical Topogram.";
16
+ case "promote_capability":
17
+ return "Promote this imported capability into canonical Topogram.";
18
+ case "promote_widget":
19
+ return "Promote this imported reusable UI widget into canonical Topogram.";
20
+ case "merge_capability_into_existing_entity":
21
+ return `Adopt this capability while preserving the existing canonical entity ${step.target}.`;
22
+ case "promote_doc":
23
+ return "Promote this imported companion doc into canonical Topogram docs.";
24
+ case "promote_workflow_doc":
25
+ return "Promote this imported workflow doc into canonical Topogram workflow docs.";
26
+ case "promote_workflow_decision":
27
+ return "Promote this imported workflow decision into canonical Topogram decisions.";
28
+ case "promote_verification":
29
+ return "Promote this imported verification into canonical Topogram verifications.";
30
+ case "promote_ui_report":
31
+ return "Promote this imported UI review report into canonical Topogram docs.";
32
+ case "apply_projection_permission_patch":
33
+ return `Apply inferred permission-based auth rules to canonical projection ${step.target}.`;
34
+ case "apply_projection_auth_patch":
35
+ return `Apply inferred claim-based auth rules to canonical projection ${step.target}.`;
36
+ case "apply_projection_ownership_patch":
37
+ return `Apply inferred ownership-based auth rules to canonical projection ${step.target}.`;
38
+ case "apply_doc_link_patch":
39
+ return "Apply this suggested actor/role metadata update to an existing canonical doc.";
40
+ case "apply_doc_metadata_patch":
41
+ return "Apply this suggested safe metadata update to an existing canonical doc.";
42
+ case "skip_duplicate_shape":
43
+ return step.target ? `Skip this shape because it duplicates canonical shape ${step.target}.` : "Skip this shape because it duplicates existing canonical surface.";
44
+ default:
45
+ return "Review this adoption suggestion before applying it.";
46
+ }
47
+ }
48
+
49
+ /** @param {WorkflowRecord} step @returns {any} */
50
+ export function recommendationForAdoptionItem(step) {
51
+ if (step.action === "apply_doc_link_patch") {
52
+ return `Update \`${step.target}\` with the suggested related actor/role links.`;
53
+ }
54
+ if (step.action === "apply_doc_metadata_patch") {
55
+ return `Update \`${step.target}\` with the suggested safe metadata changes.`;
56
+ }
57
+ if (step.action === "apply_projection_permission_patch") {
58
+ return `Update \`${step.target}\` with inferred permission auth rules for ${(step.related_capabilities || []).map((/** @type {any} */ item) => `\`${item}\``).join(", ") || "the related capabilities"}.`;
59
+ }
60
+ if (step.action === "apply_projection_auth_patch") {
61
+ return `Update \`${step.target}\` with inferred claim auth rules for ${(step.related_capabilities || []).map((/** @type {any} */ item) => `\`${item}\``).join(", ") || "the related capabilities"}.`;
62
+ }
63
+ if (step.action === "apply_projection_ownership_patch") {
64
+ return `Update \`${step.target}\` with inferred ownership auth rules for ${(step.related_capabilities || []).map((/** @type {any} */ item) => `\`${item}\``).join(", ") || "the related capabilities"}.`;
65
+ }
66
+ if (step.action === "promote_widget") {
67
+ return "Promote this reviewed widget candidate before binding or reusing it from canonical projections.";
68
+ }
69
+ if (!["promote_actor", "promote_role"].includes(step.action)) {
70
+ return null;
71
+ }
72
+ const kindLabel = step.action === "promote_actor" ? "actor" : "role";
73
+ /** @type {any[]} */
74
+ const linkHints = [];
75
+ if ((step.related_docs || []).length > 0) {
76
+ linkHints.push(`link to docs ${step.related_docs.map((/** @type {any} */ item) => `\`${item}\``).join(", ")}`);
77
+ }
78
+ if ((step.related_capabilities || []).length > 0) {
79
+ linkHints.push(`check capabilities ${step.related_capabilities.map((/** @type {any} */ item) => `\`${item}\``).join(", ")}`);
80
+ }
81
+ return `Promote this ${kindLabel}${step.confidence ? ` (${step.confidence})` : ""}${linkHints.length ? ` and ${linkHints.join("; ")}` : ""}.`;
82
+ }
83
+
84
+ /** @param {WorkflowRecord} item @returns {any} */
85
+ export function formatDocLinkSuggestionInline(item) {
86
+ return `doc \`${item.doc_id}\`` +
87
+ `${item.add_related_actors?.length ? ` add-actors=${item.add_related_actors.map((/** @type {any} */ entry) => `\`${entry}\``).join(", ")}` : ""}` +
88
+ `${item.add_related_roles?.length ? ` add-roles=${item.add_related_roles.map((/** @type {any} */ entry) => `\`${entry}\``).join(", ")}` : ""}` +
89
+ `${item.add_related_capabilities?.length ? ` add-capabilities=${item.add_related_capabilities.map((/** @type {any} */ entry) => `\`${entry}\``).join(", ")}` : ""}` +
90
+ `${item.add_related_rules?.length ? ` add-rules=${item.add_related_rules.map((/** @type {any} */ entry) => `\`${entry}\``).join(", ")}` : ""}` +
91
+ `${item.add_related_workflows?.length ? ` add-workflows=${item.add_related_workflows.map((/** @type {any} */ entry) => `\`${entry}\``).join(", ")}` : ""}` +
92
+ `${item.patch_rel_path ? ` draft=\`${item.patch_rel_path}\`` : ""}`;
93
+ }
94
+
95
+ /** @param {WorkflowRecord} item @returns {any} */
96
+ export function formatDocDriftSummaryInline(item) {
97
+ return `doc \`${item.doc_id}\` (${item.recommendation_type}) fields=${item.differing_fields.map((/** @type {any} */ entry) => entry.field).join(", ")} confidence=${item.imported_confidence}`;
98
+ }
99
+
100
+ /** @param {WorkflowRecord} item @returns {any} */
101
+ export function formatDocMetadataPatchInline(item) {
102
+ return `doc \`${item.doc_id}\`` +
103
+ `${item.summary ? " set-summary=yes" : ""}` +
104
+ `${item.success_outcome ? " set-success_outcome=yes" : ""}` +
105
+ `${item.actors?.length ? ` add-actors=${item.actors.map((/** @type {any} */ entry) => `\`${entry}\``).join(", ")}` : ""}` +
106
+ `${item.patch_rel_path ? ` draft=\`${item.patch_rel_path}\`` : ""}`;
107
+ }
@@ -0,0 +1,32 @@
1
+ // @ts-check
2
+
3
+ export { ADOPT_SELECTORS, buildAdoptionPlan } from "./adoption-plan/build.js";
4
+ export {
5
+ adoptionStatusForStep,
6
+ blockingDependenciesForProjectionImpacts,
7
+ blockingDependenciesForUiImpacts,
8
+ blockingDependenciesForWorkflowImpacts,
9
+ projectionImpactsForAdoptionItem
10
+ } from "./adoption-plan/dependencies.js";
11
+ export {
12
+ buildCanonicalAdoptionOutputs,
13
+ buildPromotedCanonicalItems,
14
+ readAdoptionPlan
15
+ } from "./adoption-plan/outputs.js";
16
+ export {
17
+ canonicalDisplayPathForItem,
18
+ canonicalRelativePathForItem,
19
+ candidateSourcePathForItem
20
+ } from "./adoption-plan/paths.js";
21
+ export {
22
+ applyProjectionAuthPatchToTopogram,
23
+ ensureProjectionBlock,
24
+ ensureProjectionRealizes
25
+ } from "./adoption-plan/projection-patches.js";
26
+ export {
27
+ formatDocDriftSummaryInline,
28
+ formatDocLinkSuggestionInline,
29
+ formatDocMetadataPatchInline,
30
+ reasonForAdoptionItem,
31
+ recommendationForAdoptionItem
32
+ } from "./adoption-plan/reasons.js";
@@ -0,0 +1,115 @@
1
+ // @ts-check
2
+
3
+ /** @param {any[]} items @returns {any} */
4
+ export function summarizeHintClosureState(items) {
5
+ const statuses = (items || []).map((/** @type {any} */ item) => item.status).filter(Boolean);
6
+ if (statuses.length === 0) {
7
+ return {
8
+ closure_state: "unresolved",
9
+ closure_reason: "No reviewed projection patch has been applied for this inferred auth hint yet."
10
+ };
11
+ }
12
+ if (statuses.every((/** @type {any} */ status) => status === "applied")) {
13
+ return {
14
+ closure_state: "adopted",
15
+ closure_reason: "All matching projection patch actions for this inferred auth hint have been applied."
16
+ };
17
+ }
18
+ if (statuses.every((/** @type {any} */ status) => ["applied", "approved", "skipped"].includes(status))) {
19
+ return {
20
+ closure_state: "deferred",
21
+ closure_reason: "This inferred auth hint has been reviewed or intentionally held back, but not every matching projection patch has been applied yet."
22
+ };
23
+ }
24
+ return {
25
+ closure_state: "unresolved",
26
+ closure_reason: "At least one matching projection patch for this inferred auth hint is still blocked on review or waiting to be applied."
27
+ };
28
+ }
29
+
30
+ /** @param {WorkflowRecord} bundle @param {any[]} planItems @returns {any} */
31
+ export function annotateBundleAuthHintClosures(bundle, planItems) {
32
+ const bundleItems = (planItems || []).filter((/** @type {any} */ item) => item.bundle === bundle.slug);
33
+ const annotatedPermissions = (bundle.authPermissionHints || []).map((/** @type {any} */ hint) => ({
34
+ ...hint,
35
+ ...summarizeHintClosureState(bundleItems.filter((/** @type {any} */ item) =>
36
+ item.suggested_action === "apply_projection_permission_patch" &&
37
+ item.permission === hint.permission
38
+ ))
39
+ }));
40
+ const annotatedClaims = (bundle.authClaimHints || []).map((/** @type {any} */ hint) => ({
41
+ ...hint,
42
+ ...summarizeHintClosureState(bundleItems.filter((/** @type {any} */ item) =>
43
+ item.suggested_action === "apply_projection_auth_patch" &&
44
+ item.claim === hint.claim &&
45
+ item.claim_value === (Object.prototype.hasOwnProperty.call(hint, "claim_value") ? hint.claim_value : null)
46
+ ))
47
+ }));
48
+ const annotatedOwnerships = (bundle.authOwnershipHints || []).map((/** @type {any} */ hint) => ({
49
+ ...hint,
50
+ ...summarizeHintClosureState(bundleItems.filter((/** @type {any} */ item) =>
51
+ item.suggested_action === "apply_projection_ownership_patch" &&
52
+ item.ownership === hint.ownership &&
53
+ item.ownership_field === hint.ownership_field
54
+ ))
55
+ }));
56
+ return {
57
+ ...bundle,
58
+ authPermissionHints: annotatedPermissions,
59
+ authClaimHints: annotatedClaims,
60
+ authOwnershipHints: annotatedOwnerships
61
+ };
62
+ }
63
+
64
+ /** @param {WorkflowRecord} bundle @returns {any} */
65
+ export function buildAuthHintClosureSummary(bundle) {
66
+ const hints = [
67
+ ...(bundle.authPermissionHints || []),
68
+ ...(bundle.authClaimHints || []),
69
+ ...(bundle.authOwnershipHints || [])
70
+ ];
71
+ const counts = hints.reduce(
72
+ (/** @type {any} */ acc, /** @type {any} */ hint) => {
73
+ const state = hint.closure_state || "unresolved";
74
+ if (state === "adopted") {
75
+ acc.adopted += 1;
76
+ } else if (state === "deferred") {
77
+ acc.deferred += 1;
78
+ } else {
79
+ acc.unresolved += 1;
80
+ }
81
+ return acc;
82
+ },
83
+ { total: hints.length, adopted: 0, deferred: 0, unresolved: 0 }
84
+ );
85
+ if (counts.total === 0) {
86
+ return {
87
+ status: "no_auth_hints",
88
+ label: "no auth hints",
89
+ reason: "This bundle does not currently carry inferred permission, claim, or ownership hints.",
90
+ ...counts
91
+ };
92
+ }
93
+ if (counts.unresolved === 0 && counts.deferred === 0) {
94
+ return {
95
+ status: "mostly_closed",
96
+ label: "mostly closed",
97
+ reason: "All inferred auth hints for this bundle have been adopted into canonical projection rules.",
98
+ ...counts
99
+ };
100
+ }
101
+ if (counts.unresolved === 0) {
102
+ return {
103
+ status: "partially_closed",
104
+ label: "partially closed",
105
+ reason: "Every inferred auth hint has been reviewed, but at least one is still intentionally deferred instead of adopted.",
106
+ ...counts
107
+ };
108
+ }
109
+ return {
110
+ status: "high_risk",
111
+ label: "high risk",
112
+ reason: "At least one inferred auth hint is still unresolved, so the recovered auth story for this bundle is not closed yet.",
113
+ ...counts
114
+ };
115
+ }
@@ -0,0 +1,142 @@
1
+ // @ts-check
2
+
3
+ /** @param {string} text @param {any[]} patterns @returns {any} */
4
+ export function authClaimPatternMatches(text, patterns = []) {
5
+ return patterns.some((/** @type {any} */ pattern) => pattern.test(text));
6
+ }
7
+
8
+ /** @param {any[]} entries @param {any[]} patterns @param {any} toText @returns {any} */
9
+ export function collectAuthClaimSignalMatches(entries, patterns, toText) {
10
+ return (entries || []).filter((/** @type {any} */ entry) => authClaimPatternMatches(toText(entry), patterns));
11
+ }
12
+
13
+ /** @param {string} value @returns {any} */
14
+ export function formatAuthClaimValueInline(value) {
15
+ return value == null ? "_dynamic_" : `\`${value}\``;
16
+ }
17
+
18
+ /** @param {WorkflowRecord} hint @returns {any} */
19
+ export function formatAuthClaimHintInline(hint) {
20
+ return `claim \`${hint.claim}\` = ${formatAuthClaimValueInline(hint.claim_value)} (${hint.confidence})`;
21
+ }
22
+
23
+ /** @param {WorkflowRecord} hint @returns {any} */
24
+ export function formatAuthPermissionHintInline(hint) {
25
+ return `permission \`${hint.permission}\` (${hint.confidence})`;
26
+ }
27
+
28
+ /** @param {WorkflowRecord} hint @returns {any} */
29
+ export function formatAuthOwnershipHintInline(hint) {
30
+ return `ownership \`${hint.ownership}\` field \`${hint.ownership_field}\` (${hint.confidence})`;
31
+ }
32
+
33
+ /** @param {WorkflowRecord} hint @returns {any} */
34
+ export function describeAuthPermissionWhyInferred(hint) {
35
+ /** @type {any[]} */
36
+ const signals = [];
37
+ if (hint?.evidence?.capability_hits) {
38
+ signals.push(`${hint.evidence.capability_hits} secured capability match${hint.evidence.capability_hits === 1 ? "" : "es"}`);
39
+ }
40
+ if (hint?.evidence?.route_hits) {
41
+ signals.push(`${hint.evidence.route_hits} route/resource match${hint.evidence.route_hits === 1 ? "" : "es"}`);
42
+ }
43
+ if (hint?.evidence?.doc_hits) {
44
+ signals.push(`${hint.evidence.doc_hits} imported doc or policy match${hint.evidence.doc_hits === 1 ? "" : "es"}`);
45
+ }
46
+ if (hint?.evidence?.provenance_hits) {
47
+ signals.push(`${hint.evidence.provenance_hits} auth middleware or policy hint${hint.evidence.provenance_hits === 1 ? "" : "s"}`);
48
+ }
49
+ if (signals.length === 0) {
50
+ return hint?.explanation || "Imported auth evidence suggests a permission rule may gate this surface.";
51
+ }
52
+ return `${hint?.explanation || "Imported auth evidence suggests a permission rule may gate this surface."} This inference is based on ${signals.join(", ")}.`;
53
+ }
54
+
55
+ /** @param {WorkflowRecord} hint @returns {any} */
56
+ export function buildAuthPermissionReviewGuidance(hint) {
57
+ return `Confirm whether permission \`${hint.permission}\` should gate the related auth-sensitive capabilities before promoting this bundle into canonical auth rules or UI visibility.`;
58
+ }
59
+
60
+ /** @param {WorkflowRecord} hint @returns {any} */
61
+ export function describeAuthClaimWhyInferred(hint) {
62
+ /** @type {any[]} */
63
+ const signals = [];
64
+ if (hint?.evidence?.capability_hits) {
65
+ signals.push(`${hint.evidence.capability_hits} secured capability match${hint.evidence.capability_hits === 1 ? "" : "es"}`);
66
+ }
67
+ if (hint?.evidence?.route_hits) {
68
+ signals.push(`${hint.evidence.route_hits} route match${hint.evidence.route_hits === 1 ? "" : "es"}`);
69
+ }
70
+ if (hint?.evidence?.participant_hits) {
71
+ signals.push(`${hint.evidence.participant_hits} participant match${hint.evidence.participant_hits === 1 ? "" : "es"}`);
72
+ }
73
+ if (hint?.evidence?.doc_hits) {
74
+ signals.push(`${hint.evidence.doc_hits} imported doc match${hint.evidence.doc_hits === 1 ? "" : "es"}`);
75
+ }
76
+ if (signals.length === 0) {
77
+ return hint?.explanation || "Imported auth-related evidence suggests this claim may matter here.";
78
+ }
79
+ return `${hint?.explanation || "Imported auth-related evidence suggests this claim may matter here."} This inference is based on ${signals.join(", ")}.`;
80
+ }
81
+
82
+ /** @param {WorkflowRecord} hint @returns {any} */
83
+ export function buildAuthClaimReviewGuidance(hint) {
84
+ const claimTarget = `claim \`${hint.claim}\` = ${formatAuthClaimValueInline(hint.claim_value)}`;
85
+ return `Confirm whether ${claimTarget} should gate the related auth-sensitive capabilities before promoting this bundle into canonical auth rules or UI visibility.`;
86
+ }
87
+
88
+ /** @param {WorkflowRecord} hint @returns {any} */
89
+ export function describeAuthOwnershipWhyInferred(hint) {
90
+ /** @type {any[]} */
91
+ const signals = [];
92
+ if (hint?.evidence?.field_hits) {
93
+ signals.push(`${hint.evidence.field_hits} ownership-style field match${hint.evidence.field_hits === 1 ? "" : "es"}`);
94
+ }
95
+ if (hint?.evidence?.capability_hits) {
96
+ signals.push(`${hint.evidence.capability_hits} secured lifecycle/detail capability match${hint.evidence.capability_hits === 1 ? "" : "es"}`);
97
+ }
98
+ if (hint?.evidence?.doc_hits) {
99
+ signals.push(`${hint.evidence.doc_hits} imported doc match${hint.evidence.doc_hits === 1 ? "" : "es"}`);
100
+ }
101
+ if (signals.length === 0) {
102
+ return hint?.explanation || "Imported field and auth evidence suggests ownership-based access control may matter here.";
103
+ }
104
+ return `${hint?.explanation || "Imported field and auth evidence suggests ownership-based access control may matter here."} This inference is based on ${signals.join(", ")}.`;
105
+ }
106
+
107
+ /** @param {WorkflowRecord} hint @returns {any} */
108
+ export function buildAuthOwnershipReviewGuidance(hint) {
109
+ return `Confirm whether field \`${hint.ownership_field}\` should drive \`${hint.ownership}\` access for the related auth-sensitive capabilities before promoting this bundle into canonical auth rules or UI visibility.`;
110
+ }
111
+
112
+ /** @param {WorkflowRecord} entry @returns {any} */
113
+ export function formatAuthRoleGuidanceInline(entry) {
114
+ return `role \`${entry.role_id}\` (${entry.confidence})`;
115
+ }
116
+
117
+ /** @param {WorkflowRecord} entry @returns {any} */
118
+ export function buildAuthRoleReviewGuidance(entry) {
119
+ if (entry.followup_action === "promote_role") {
120
+ return `Promote role \`${entry.role_id}\` first, then confirm it remains the primary participant for the related auth-sensitive capabilities before promoting linked auth changes from this bundle.`;
121
+ }
122
+ if (entry.followup_action === "link_role_to_docs") {
123
+ const docList = (entry.followup_doc_ids || []).length
124
+ ? ` docs ${(entry.followup_doc_ids || []).map((/** @type {any} */ item) => `\`${item}\``).join(", ")}`
125
+ : " the existing canonical docs";
126
+ return `Link role \`${entry.role_id}\` into${docList} before promoting more auth-sensitive changes from this bundle.`;
127
+ }
128
+ return `Confirm whether role \`${entry.role_id}\` should remain the primary participant for the related auth-sensitive capabilities before promoting role or auth changes from this bundle.`;
129
+ }
130
+
131
+ /** @param {WorkflowRecord} entry @returns {any} */
132
+ export function formatAuthRoleFollowupInline(entry) {
133
+ if (entry.followup_action === "promote_role") {
134
+ return "promote role";
135
+ }
136
+ if (entry.followup_action === "link_role_to_docs") {
137
+ return entry.followup_doc_ids?.length
138
+ ? `link role to docs ${(entry.followup_doc_ids || []).map((/** @type {any} */ item) => `\`${item}\``).join(", ")}`
139
+ : "link role to docs";
140
+ }
141
+ return "review only";
142
+ }