akm-cli 0.9.16 → 0.9.17-alpha.10

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 (403) hide show
  1. package/CHANGELOG.md +2101 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -2,77 +2,27 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * Shared content-quality validators consumed by the improve pipeline
6
- * (`distill`, `consolidate`, `reflect`) and by the `proposal accept` gate.
5
+ * Content-quality validators for the improve stages and `proposal accept`.
7
6
  *
8
- * ## Reflect size gate — calibrated blended formula (2026-05-22)
9
- *
10
- * ### Distribution baseline (n=844 reflect-eligible stash assets)
11
- *
12
- * min=1 p10=371 p25=778 p50=1508 p75=5456 p90=11721 p99=43463 max=298010 bytes
13
- * Buckets: <500=135 (16%), 500–2000=340 (40%), 2000–8000=222 (26%), >8000=147 (17%)
14
- *
15
- * ### Problem with the original fixed-ratio gate
16
- *
17
- * - Small sources (~420 bytes, 16th pct): the 200% expansion ceiling fires at
18
- * only 840 bytes proposed — one good paragraph. Hair-trigger for a terse
19
- * reference note.
20
- * - Large sources (~7KB, 75th pct): 200% ceiling = 14KB; reasonable, but a hard
21
- * cap prevents runaway expansion from LLM hallucinations.
22
- *
23
- * ### Blended-bound formula
24
- *
25
- * Shrinkage floor (accept if proposed >= lower):
26
- * lower = max(REFLECT_SHRINK_RATIO_MIN * sourceLen, REFLECT_ABSOLUTE_FLOOR_BYTES)
27
- * → For tiny sources (sourceLen < 300), the absolute floor dominates so a
28
- * genuinely tightened note still passes.
29
- * → For large sources (>1KB), the ratio floor dominates (50% of 7KB = 3.5KB).
30
- *
31
- * Expansion ceiling (accept if proposed <= upper):
32
- * upper = max(REFLECT_EXPAND_RATIO_MAX * sourceLen, REFLECT_ABSOLUTE_CEILING_BYTES)
33
- * …but always capped at REFLECT_ABSOLUTE_MAX_BYTES.
34
- * → For small sources (≤778 bytes, p25), the absolute ceiling (2000 bytes)
35
- * dominates — one substantive paragraph is always acceptable.
36
- * → For medium/large sources (>1KB), the ratio ceiling dominates.
37
- * → Any proposal exceeding 25000 bytes is always rejected regardless of ratio.
38
- *
39
- * ### Constant calibration rationale
40
- *
41
- * REFLECT_ABSOLUTE_FLOOR_BYTES = 150
42
- * Half of p10 (371) ≈ 185; we set 150 so even very aggressive condensation
43
- * of a seed note is allowed down to roughly a two-sentence summary.
44
- *
45
- * REFLECT_ABSOLUTE_CEILING_BYTES = 2500
46
- * Raised from 2000 (2026-05-22): small-source rejections at 248–281% on
47
- * 900–953 byte assets were borderline false positives. 2500 gives a short
48
- * lesson or command ~1.5KB of room to grow before the absolute kicks in.
49
- *
50
- * REFLECT_ABSOLUTE_MAX_BYTES = 25000
51
- * Below p99 (43463). Catches genuine LLM runaway (whole-chapter insertions)
52
- * without blocking legitimate large rewrites of large sources.
53
- *
54
- * REFLECT_EXPAND_RATIO_MAX = 2.5
55
- * Raised from 2.0 (2026-05-22): 2× was too tight for dense short assets
56
- * (lessons, commands) that have legitimate room to grow. 2.5× resolves
57
- * 248% expansion on a 900-byte lesson while still catching 281%+ on ~1KB
58
- * assets where the absolute ceiling takes over.
7
+ * The reflect size gate bounds a rewrite's body against its source's:
8
+ * shrinking below max(50% of the source, 150 bytes) suggests deleted content,
9
+ * growing past min(max(250% of the source, 2500 bytes), 25000 bytes) suggests
10
+ * speculation. The absolute bounds keep small assets (a p25 source is ~780
11
+ * bytes) from tripping on one good paragraph, and 25000 (below p99) still
12
+ * catches runaway expansion. Sources under 200 bytes are too noisy to judge.
59
13
  */
60
- // ── Reflect-size guard ───────────────────────────────────────────────────────
61
14
  import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
62
15
  import { parseRefInput } from "../../../core/asset/resolve-ref.js";
63
16
  import { DESCRIPTION_MAX_CHARS, DESCRIPTION_MIN_CHARS, WHEN_TO_USE_MAX_CHARS, WHEN_TO_USE_MIN_CHARS, } from "../../../core/authoring-rules.js";
64
17
  import { containsRedactedContent, containsReflectPromptScaffolding, REDACTED_CONTENT_MARKER, REFLECT_AVOID_PATTERNS_HEADING, } from "../../../core/content-safety.js";
65
18
  import { proposalContent } from "../../../core/file-change.js";
66
- /**
67
- * The canonical asset NAME an inputRef names, lower-cased — the tail the
68
- * "just restates the ref" heuristics compare against. WI-8.5c: the ref is the
69
- * conceptId (`<subdir>/<name>`), so the name is parsed off the conceptId.
70
- */
19
+ import { detectTruncatedDescription, TRUNCATION_TRAILING_WORDS } from "../../../core/text-truncation.js";
20
+ import { REFLECT_TRUNCATION_MARKER } from "../../../integrations/agent/prompts.js";
21
+ import { splitFrontmatter } from "../../improve/reflect-noise.js";
22
+ /** The asset name a ref names, lower-cased — what the "just restates the ref" checks compare. */
71
23
  function refNameTail(inputRef) {
72
24
  return parseRefInput(inputRef).name.toLowerCase();
73
25
  }
74
- import { detectTruncatedDescription, TRUNCATION_TRAILING_WORDS } from "../../../core/text-truncation.js";
75
- import { REFLECT_TRUNCATION_MARKER } from "../../../integrations/agent/prompts.js";
76
26
  // ── Description / when_to_use shape ─────────────────────────────────────────
77
27
  export const HEADING_FRAGMENT_PATTERNS = [
78
28
  /^for example\b/i,
@@ -164,9 +114,8 @@ export function detectDoubleFrontmatter(content) {
164
114
  kind: "double-frontmatter-fence",
165
115
  message: `Content contains ${fenceLines.length} \`---\` fence lines; assets with frontmatter must have exactly 2 (one open, one close).`,
166
116
  };
167
- const body = content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
168
- const pseudoLine = body
169
- .split(/\r?\n/)
117
+ const pseudoLine = splitFrontmatter(content)
118
+ .body.split(/\r?\n/)
170
119
  .find((l) => /^\s*(\*\*|__)?\s*(description|when_to_use)\s*(\*\*|__)?\s*:/i.test(l));
171
120
  if (pseudoLine)
172
121
  return {
@@ -191,50 +140,13 @@ export function hasSupersededStatus(frontmatter) {
191
140
  export function hasHotCaptureMode(frontmatter) {
192
141
  return frontmatter?.captureMode === "hot";
193
142
  }
194
- // ── Consolidate merge size gate ──────────────────────────────────────────────
195
- // ── Reflect size gate ────────────────────────────────────────────────────────
196
- /** Ratio lower-bound: proposed body must be at least this fraction of source. */
197
143
  export const REFLECT_SHRINK_RATIO_MIN = 0.5;
198
- /** Ratio upper-bound: proposed body must not exceed this fraction of source. */
199
144
  export const REFLECT_EXPAND_RATIO_MAX = 2.5;
200
- /**
201
- * Below this byte count, ratio checks are too noisy — skip them entirely.
202
- * Unchanged from the original gate.
203
- */
204
145
  export const REFLECT_SIZE_GUARD_MIN_BYTES = 200;
205
- /**
206
- * Absolute shrinkage floor (bytes). Even if `ratio * sourceLen` is lower, a
207
- * proposed body of at least this many bytes is always accepted on the shrinkage
208
- * side. Protects against false positives when the source is small (<300 bytes).
209
- */
210
146
  export const REFLECT_ABSOLUTE_FLOOR_BYTES = 150;
211
- /**
212
- * Absolute expansion ceiling (bytes). Even if `ratio * sourceLen` is lower, a
213
- * proposed body up to this many bytes is always accepted on the expansion side.
214
- * Protects against false positives when the source is small (≤778 bytes, p25).
215
- */
216
147
  export const REFLECT_ABSOLUTE_CEILING_BYTES = 2500;
217
- /**
218
- * Hard expansion cap (bytes). Regardless of ratio, a proposed body exceeding
219
- * this limit is always rejected. Guards against runaway LLM hallucinations on
220
- * large sources.
221
- */
222
148
  export const REFLECT_ABSOLUTE_MAX_BYTES = 25000;
223
- /**
224
- * Calibrated size check: compare proposed body length against source body
225
- * length using a blended-bound formula.
226
- *
227
- * **Shrinkage** — accept if:
228
- * `proposedLen >= max(REFLECT_SHRINK_RATIO_MIN * sourceLen, REFLECT_ABSOLUTE_FLOOR_BYTES)`
229
- *
230
- * **Expansion** — accept if:
231
- * `proposedLen <= min(max(REFLECT_EXPAND_RATIO_MAX * sourceLen, REFLECT_ABSOLUTE_CEILING_BYTES), REFLECT_ABSOLUTE_MAX_BYTES)`
232
- *
233
- * Returns `{ ok: true }` when:
234
- * - `sourceBody` is absent or `undefined`
235
- * - source body is shorter than {@link REFLECT_SIZE_GUARD_MIN_BYTES}
236
- * - the proposed length is within the blended bounds
237
- */
149
+ /** The reflect size gate (see the module note); no source or a tiny one passes. */
238
150
  export function checkReflectSize(sourceBody, proposedBody) {
239
151
  if (typeof sourceBody !== "string")
240
152
  return { ok: true };
@@ -243,19 +155,14 @@ export function checkReflectSize(sourceBody, proposedBody) {
243
155
  return { ok: true };
244
156
  const proposedLen = proposedBody.trim().length;
245
157
  const ratio = proposedLen / sourceLen;
246
- // Shrinkage check: lower bound = max(ratio floor, absolute floor)
247
- const shrinkFloor = Math.max(REFLECT_SHRINK_RATIO_MIN * sourceLen, REFLECT_ABSOLUTE_FLOOR_BYTES);
248
- if (proposedLen < shrinkFloor) {
158
+ if (proposedLen < Math.max(REFLECT_SHRINK_RATIO_MIN * sourceLen, REFLECT_ABSOLUTE_FLOOR_BYTES)) {
249
159
  return { ok: false, code: "EXCESSIVE_SHRINKAGE", ratio };
250
160
  }
251
- // Expansion check: upper bound = min(max(ratio ceiling, absolute ceiling), hard cap)
252
161
  const expandCeiling = Math.min(Math.max(REFLECT_EXPAND_RATIO_MAX * sourceLen, REFLECT_ABSOLUTE_CEILING_BYTES), REFLECT_ABSOLUTE_MAX_BYTES);
253
- if (proposedLen > expandCeiling) {
162
+ if (proposedLen > expandCeiling)
254
163
  return { ok: false, code: "EXCESSIVE_EXPANSION", ratio };
255
- }
256
164
  return { ok: true };
257
165
  }
258
- // ── ProposalValidator entries (registered with proposal-validators.ts) ──────
259
166
  const descriptionQualityValidator = {
260
167
  name: "description-quality",
261
168
  appliesTo(_proposal, ctx) {
@@ -282,6 +189,46 @@ const descriptionQualityValidator = {
282
189
  ];
283
190
  },
284
191
  };
192
+ /**
193
+ * The lesson checks distill and accept share: a valid description and
194
+ * when_to_use that differ, and no pseudo-frontmatter in the body. `text`
195
+ * continues a caller-specific subject.
196
+ */
197
+ export function lessonQualityIssues(fm, content, inputRef) {
198
+ const issues = [];
199
+ const descCheck = isValidDescription(fm.description, inputRef);
200
+ if (!descCheck.ok) {
201
+ issues.push({
202
+ kind: "invalid-description",
203
+ field: "description",
204
+ text: ` has an invalid description: ${descCheck.reason}.`,
205
+ ...(descCheck.severity ? { severity: descCheck.severity } : {}),
206
+ });
207
+ }
208
+ const wtuCheck = isValidWhenToUse(fm.when_to_use, inputRef);
209
+ if (!wtuCheck.ok) {
210
+ issues.push({
211
+ kind: "invalid-when_to_use",
212
+ field: "when_to_use",
213
+ text: ` has an invalid when_to_use: ${wtuCheck.reason}.`,
214
+ });
215
+ }
216
+ if (descCheck.ok &&
217
+ wtuCheck.ok &&
218
+ typeof fm.description === "string" &&
219
+ typeof fm.when_to_use === "string" &&
220
+ fm.description.trim().toLowerCase() === fm.when_to_use.trim().toLowerCase()) {
221
+ issues.push({
222
+ kind: "description-equals-when_to_use",
223
+ field: "description",
224
+ text: " has identical description and when_to_use.",
225
+ });
226
+ }
227
+ const dfm = detectDoubleFrontmatter(content);
228
+ if (dfm)
229
+ issues.push({ kind: dfm.kind, field: "body", text: `: ${dfm.message}` });
230
+ return issues;
231
+ }
285
232
  const lessonContentQualityValidator = {
286
233
  name: "lesson-content-quality",
287
234
  appliesTo(_proposal, ctx) {
@@ -297,34 +244,11 @@ const lessonContentQualityValidator = {
297
244
  catch {
298
245
  return [];
299
246
  }
300
- const findings = [];
301
- const descCheck = isValidDescription(fm.description, proposal.ref);
302
- if (!descCheck.ok)
303
- findings.push({
304
- kind: "invalid-description",
305
- message: `Lesson proposal ${proposal.id} (${proposal.ref}) has an invalid description: ${descCheck.reason}.`,
306
- ...(descCheck.severity ? { severity: descCheck.severity } : {}),
307
- });
308
- const wtuCheck = isValidWhenToUse(fm.when_to_use, proposal.ref);
309
- if (!wtuCheck.ok)
310
- findings.push({
311
- kind: "invalid-when_to_use",
312
- message: `Lesson proposal ${proposal.id} (${proposal.ref}) has an invalid when_to_use: ${wtuCheck.reason}.`,
313
- });
314
- if (descCheck.ok &&
315
- wtuCheck.ok &&
316
- typeof fm.description === "string" &&
317
- typeof fm.when_to_use === "string" &&
318
- fm.description.trim().toLowerCase() === fm.when_to_use.trim().toLowerCase()) {
319
- findings.push({
320
- kind: "description-equals-when_to_use",
321
- message: `Lesson proposal ${proposal.id} (${proposal.ref}) has identical description and when_to_use.`,
322
- });
323
- }
324
- const dfm = detectDoubleFrontmatter(proposalContent(proposal));
325
- if (dfm)
326
- findings.push({ kind: dfm.kind, message: `Lesson proposal ${proposal.id} (${proposal.ref}): ${dfm.message}` });
327
- return findings;
247
+ return lessonQualityIssues(fm, proposalContent(proposal), proposal.ref).map((issue) => ({
248
+ kind: issue.kind,
249
+ message: `Lesson proposal ${proposal.id} (${proposal.ref})${issue.text}`,
250
+ ...(issue.severity ? { severity: issue.severity } : {}),
251
+ }));
328
252
  },
329
253
  };
330
254
  const sourceNotSupersededValidator = {
@@ -344,18 +268,14 @@ const sourceNotSupersededValidator = {
344
268
  return [];
345
269
  },
346
270
  };
347
- /** Strip an opening frontmatter block (`---\n…\n---`) from `content`, returning the body. */
348
- function stripFrontmatterBody(content) {
349
- return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
350
- }
351
271
  const reflectSizeGuardValidator = {
352
272
  name: "reflect-size-guard",
353
273
  appliesTo(proposal, ctx) {
354
274
  return proposal.source === "reflect" && typeof ctx.source?.content === "string";
355
275
  },
356
276
  validate(proposal, ctx) {
357
- const sourceBody = stripFrontmatterBody(ctx.source?.content ?? "");
358
- const proposedBody = typeof proposal.payload?.content === "string" ? stripFrontmatterBody(proposalContent(proposal)) : "";
277
+ const sourceBody = splitFrontmatter(ctx.source?.content ?? "").body;
278
+ const proposedBody = typeof proposal.payload?.content === "string" ? splitFrontmatter(proposalContent(proposal)).body : "";
359
279
  const outcome = checkReflectSize(sourceBody, proposedBody);
360
280
  if (outcome.ok)
361
281
  return [];
@@ -373,16 +293,10 @@ const reflectSizeGuardValidator = {
373
293
  },
374
294
  };
375
295
  /**
376
- * Accept-time data-loss guard (#952 Addendum). `sanitizeReflectPayload`
377
- * already defers a proposal whose body echoes {@link REFLECT_TRUNCATION_MARKER}
378
- * (the notice appended when the source asset was too large to send in full)
379
- * with `reflect-truncation-leak` — but that is a creation-time check, and a
380
- * proposal can reach `proposal accept` / drain promotion without ever going
381
- * through it (e.g. a defer that a human then accepts anyway, or a future
382
- * reflect code path that mints proposals directly). A leaked marker replacing
383
- * real asset content on disk is data loss, not a quality nit, so unlike the
384
- * rest of this file's validators this one is NOT wrapped by {@link advisory}
385
- * — it blocks acceptance the same way the generic/canonical validators do.
296
+ * A body still carrying {@link REFLECT_TRUNCATION_MARKER} (its source was too
297
+ * large to send in full) would overwrite the asset with an incomplete rewrite:
298
+ * data loss, so this blocks at accept even for a proposal that never passed
299
+ * reflect's own creation-time check (#952).
386
300
  */
387
301
  const reflectTruncationMarkerValidator = {
388
302
  name: "reflect-truncation-marker",
@@ -435,19 +349,9 @@ const reflectPromptScaffoldingValidator = {
435
349
  },
436
350
  };
437
351
  /**
438
- * Report a validator's findings as advisory.
439
- *
440
- * These validators judge prose quality — a description that reads like a
441
- * heading, an odd backtick count, a body that grew more than the reflect
442
- * ratio allows. They used to BLOCK `proposal accept`, which a human types
443
- * after reading the diff, and the error told that human to "fix the proposal
444
- * payload and try again" — but there is no `akm proposal edit` and `accept`
445
- * takes no `--force`, so the only way out was hand-editing the proposals
446
- * database. A blocking check whose remedy does not exist is not a check.
447
- *
448
- * Structural findings stay blocking: an empty body, an unparseable ref,
449
- * malformed frontmatter and a broken workflow shape genuinely cannot be
450
- * written, and they live in {@link defaultProposalValidators}.
352
+ * Prose-quality findings only advise: a human accepting after reading the diff
353
+ * has no way to edit the proposal, so blocking on them left no remedy.
354
+ * Structural findings (in the default validators) still block.
451
355
  */
452
356
  function advisory(validator) {
453
357
  return {
@@ -455,14 +359,7 @@ function advisory(validator) {
455
359
  validate: (proposal, ctx) => validator.validate(proposal, ctx).map((finding) => ({ ...finding, severity: "warn" })),
456
360
  };
457
361
  }
458
- /**
459
- * Full set of quality validators in registration order. Appended onto
460
- * {@link defaultProposalValidators} so they run inside `validateProposal` on
461
- * `proposal accept` automatically. All prose-quality checks report without
462
- * blocking (see {@link advisory}). The truncation-marker, redacted-content,
463
- * and reflected-prompt-scaffolding validators block because they protect
464
- * durable content rather than judging prose quality.
465
- */
362
+ /** The quality validators `validateProposal` runs; the last three protect durable content and block. */
466
363
  export const defaultProposalQualityValidators = [
467
364
  ...[
468
365
  descriptionQualityValidator,
@@ -6,7 +6,7 @@ import { parseRefInput } from "../../../core/asset/resolve-ref.js";
6
6
  import { proposalContent } from "../../../core/file-change.js";
7
7
  import { lintLessonContent } from "../../../core/lesson-lint.js";
8
8
  import { parseTaskSource } from "../../../tasks/source/parse-task-source.js";
9
- import { compileWorkflowSource } from "../../../workflows/source-ir/compile.js";
9
+ import { compileWorkflowSource } from "../../../workflows/compile.js";
10
10
  import { defaultProposalQualityValidators } from "./proposal-quality-validators.js";
11
11
  const genericProposalValidator = {
12
12
  name: "generic-proposal-validator",
@@ -1,105 +1,38 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Proposal validation and content repair.
6
- *
7
- * The proposal repository and domain service moved to `../repository.ts` (#578
8
- * storage consolidation). This module keeps only the two proposal *validators* — {@link validateProposal} and
9
- * {@link repairProposalContent}.
10
- */
4
+ /** Proposal validation and the one content repair applied before promotion. */
11
5
  import { repairTruncatedDescription } from "../../../core/text-truncation.js";
6
+ import { splitFrontmatter } from "../../improve/reflect-noise.js";
7
+ import { isRetireProposal } from "../proposal-types.js";
12
8
  import { runProposalValidators } from "./proposal-validators.js";
13
9
  /**
14
- * Validate a proposal payload before promotion. Generic by default — any
15
- * proposal must parse cleanly and carry a non-empty body. Asset types in the
16
- * fail-closed canonical registry run their existing parser or validator; types
17
- * without one remain on generic validation.
10
+ * Validate a proposal before promotion: it must parse and carry a body, and a
11
+ * type with a canonical validator runs it.
12
+ *
13
+ * A retire proposal (alpha.9 consolidate pair pass) writes no new content —
14
+ * it deletes an asset that already passed validation when IT was created —
15
+ * so the content-quality validators (built for new/updated bodies: a
16
+ * description, lesson shape, reflect size ratio, …) do not apply and are
17
+ * skipped entirely rather than misreading the empty body as a defect.
18
18
  */
19
19
  export function validateProposal(proposal) {
20
+ if (isRetireProposal(proposal))
21
+ return { ok: true, findings: [] };
20
22
  return runProposalValidators(proposal);
21
23
  }
22
- // ── Content repair ──────────────────────────────────────────────────────────
23
24
  /**
24
- * Attempt bounded, deterministic repair of mechanically-fixable defects in a
25
- * proposal's markdown content. NEVER fabricates text — only strips known-bad
26
- * structure and applies {@link repairTruncatedDescription} to a truncated
27
- * description when one is detected.
28
- *
29
- * Repairs performed:
30
- * 1. Apply {@link repairTruncatedDescription} to a truncated/hanging
31
- * `description` field in the frontmatter.
32
- *
33
- * It deliberately does NOT delete body lines. Two earlier repairs dropped
34
- * every body line that restated a frontmatter key and every `---` in a body
35
- * with frontmatter. Both fired inside fenced code blocks, so any asset
36
- * documenting frontmatter — a note about akm, Claude Code skills, Jekyll,
37
- * Hugo — was silently gutted on `proposal accept`, and the repaired bytes
38
- * were written back over the original in the proposals database. A repair
39
- * that can destroy content is not a repair.
40
- *
41
- * Returns the repaired content string. When no repairs apply the input is
42
- * returned byte-identical so callers can use strict equality to detect
43
- * whether a repair actually happened.
44
- *
45
- * CRITICAL: This function is CONTENT-PRESERVING. Callers MUST re-validate the
46
- * repaired output via {@link validateProposal} / {@link runProposalValidators}
47
- * before promotion — a repair that makes things *worse* (or is simply
48
- * insufficient) must be caught by the existing gate.
25
+ * Normalize line endings and complete a truncated frontmatter `description`
26
+ * (`repairTruncatedDescription`, with the body as context). Nothing else: an
27
+ * earlier repair that deleted body lines gutted any asset documenting
28
+ * frontmatter. Callers re-validate the result.
49
29
  */
50
30
  export function repairProposalContent(content) {
51
31
  if (typeof content !== "string" || content.trim() === "")
52
32
  return content;
53
- // Determine whether the content has a frontmatter block so we know how
54
- // many `---` fence lines are expected.
55
- const hasFrontmatter = /^---\r?\n[\s\S]*?\r?\n---/.test(content);
56
- // Split into lines for structural repairs.
57
- const lines = content.split(/\r?\n/);
58
- // Track whether we are inside the opening frontmatter block so we can
59
- // leave it untouched and only repair the body.
60
- let inFrontmatter = false;
61
- // Frontmatter fence index tracking: first fence opens FM, second closes it.
62
- let fmOpenSeen = false;
63
- let fmCloseSeen = false;
64
- const repairedLines = [];
65
- for (const line of lines) {
66
- const isFence = /^---\s*$/.test(line);
67
- // Track frontmatter fences (first two `---` fences delimit the FM block).
68
- if (isFence && !fmCloseSeen) {
69
- if (!fmOpenSeen) {
70
- fmOpenSeen = true;
71
- inFrontmatter = true;
72
- repairedLines.push(line);
73
- continue;
74
- }
75
- if (inFrontmatter) {
76
- fmCloseSeen = true;
77
- inFrontmatter = false;
78
- repairedLines.push(line);
79
- continue;
80
- }
81
- }
82
- // We are now in the body (past the frontmatter or no frontmatter).
83
- if (inFrontmatter) {
84
- // Still inside the frontmatter — keep as-is.
85
- repairedLines.push(line);
86
- continue;
87
- }
88
- repairedLines.push(line);
89
- }
90
- let repaired = repairedLines.join("\n");
91
- // Repair 3: Apply repairTruncatedDescription to the description field.
92
- // We operate on the raw text rather than re-parsing YAML to avoid
93
- // reformatting unrelated frontmatter keys.
94
- if (hasFrontmatter) {
95
- // Extract the body text (after the second `---`) so we can pass it to
96
- // repairTruncatedDescription as context for the swap-in heuristic.
97
- const bodyMatch = repaired.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/);
98
- const bodyText = bodyMatch?.[1] ?? "";
99
- repaired = repaired.replace(/^(description:\s*)(.*?)(\r?\n)/m, (_match, prefix, rawDesc, nl) => {
100
- const fixed = repairTruncatedDescription(rawDesc.trim(), bodyText);
101
- return `${prefix}${fixed}${nl}`;
102
- });
103
- }
104
- return repaired;
33
+ const repaired = content.replace(/\r\n/g, "\n");
34
+ const { fmText, body } = splitFrontmatter(repaired);
35
+ if (fmText === null)
36
+ return repaired;
37
+ return repaired.replace(/^(description:\s*)(.*?)(\r?\n)/m, (_match, prefix, rawDesc, nl) => `${prefix}${repairTruncatedDescription(rawDesc.trim(), body)}${nl}`);
105
38
  }