session-orchestrator 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (484) hide show
  1. package/.agents/skills/architecture/SKILL.md +3 -1
  2. package/.agents/skills/autopilot/SKILL.md +6 -1
  3. package/.agents/skills/autopilot/agents/openai.yaml +5 -0
  4. package/.agents/skills/bootstrap/SKILL.md +7 -1
  5. package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
  6. package/.agents/skills/brainstorm/SKILL.md +8 -1
  7. package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
  8. package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
  9. package/.agents/skills/close/SKILL.md +21 -0
  10. package/.agents/skills/close/agents/openai.yaml +5 -0
  11. package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
  12. package/.agents/skills/debug/SKILL.md +7 -1
  13. package/.agents/skills/debug/agents/openai.yaml +5 -0
  14. package/.agents/skills/discovery/SKILL.md +7 -2
  15. package/.agents/skills/discovery/agents/openai.yaml +5 -0
  16. package/.agents/skills/dispatcher/SKILL.md +7 -1
  17. package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
  18. package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
  19. package/.agents/skills/ecosystem-health/SKILL.md +3 -1
  20. package/.agents/skills/eli5/SKILL.md +7 -1
  21. package/.agents/skills/eli5/agents/openai.yaml +5 -0
  22. package/.agents/skills/eval/SKILL.md +7 -2
  23. package/.agents/skills/eval/agents/openai.yaml +5 -0
  24. package/.agents/skills/evolve/SKILL.md +8 -3
  25. package/.agents/skills/evolve/agents/openai.yaml +5 -0
  26. package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
  27. package/.agents/skills/gitlab-ops/SKILL.md +3 -1
  28. package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
  29. package/.agents/skills/go/SKILL.md +22 -0
  30. package/.agents/skills/go/agents/openai.yaml +5 -0
  31. package/.agents/skills/grill/SKILL.md +7 -1
  32. package/.agents/skills/grill/agents/openai.yaml +5 -0
  33. package/.agents/skills/harness-audit/SKILL.md +20 -0
  34. package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
  35. package/.agents/skills/hook-development/SKILL.md +3 -1
  36. package/.agents/skills/mcp-builder/SKILL.md +3 -1
  37. package/.agents/skills/memory-cleanup/SKILL.md +6 -1
  38. package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
  39. package/.agents/skills/mode-selector/SKILL.md +3 -1
  40. package/.agents/skills/npm-publish/SKILL.md +4 -2
  41. package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
  42. package/.agents/skills/persona-panel/SKILL.md +6 -1
  43. package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
  44. package/.agents/skills/plan/SKILL.md +8 -2
  45. package/.agents/skills/plan/agents/openai.yaml +5 -0
  46. package/.agents/skills/playwright-driver/SKILL.md +3 -1
  47. package/.agents/skills/portfolio/SKILL.md +21 -0
  48. package/.agents/skills/portfolio/agents/openai.yaml +5 -0
  49. package/.agents/skills/quality-gates/SKILL.md +3 -1
  50. package/.agents/skills/reconcile/SKILL.md +6 -1
  51. package/.agents/skills/reconcile/agents/openai.yaml +5 -0
  52. package/.agents/skills/release/SKILL.md +22 -0
  53. package/.agents/skills/release/agents/openai.yaml +5 -0
  54. package/.agents/skills/remote-offload/SKILL.md +3 -1
  55. package/.agents/skills/repo-audit/SKILL.md +6 -1
  56. package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
  57. package/.agents/skills/session/SKILL.md +21 -0
  58. package/.agents/skills/session/agents/openai.yaml +5 -0
  59. package/.agents/skills/session-end/SKILL.md +3 -1
  60. package/.agents/skills/session-plan/SKILL.md +3 -1
  61. package/.agents/skills/session-start/SKILL.md +3 -1
  62. package/.agents/skills/spinout/SKILL.md +6 -1
  63. package/.agents/skills/spinout/agents/openai.yaml +5 -0
  64. package/.agents/skills/sunset-review/SKILL.md +7 -1
  65. package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
  66. package/.agents/skills/templates-ack/SKILL.md +21 -0
  67. package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
  68. package/.agents/skills/test/SKILL.md +21 -0
  69. package/.agents/skills/test/agents/openai.yaml +5 -0
  70. package/.agents/skills/test-runner/SKILL.md +3 -1
  71. package/.agents/skills/tmux-layout/SKILL.md +3 -1
  72. package/.agents/skills/using-orchestrator/SKILL.md +3 -1
  73. package/.agents/skills/ux-grill/SKILL.md +7 -1
  74. package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
  75. package/.agents/skills/vault-mirror/SKILL.md +3 -1
  76. package/.agents/skills/vault-sync/SKILL.md +3 -1
  77. package/.agents/skills/wave-executor/SKILL.md +3 -1
  78. package/.agents/skills/write-executable-plan/SKILL.md +3 -1
  79. package/.claude-plugin/marketplace.json +1 -1
  80. package/.claude-plugin/plugin.json +1 -1
  81. package/.codex-plugin/plugin.json +4 -4
  82. package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
  83. package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
  84. package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
  85. package/.codex-plugin/skills/close/SKILL.md +3 -3
  86. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -1
  87. package/.codex-plugin/skills/debug/SKILL.md +11 -4
  88. package/.codex-plugin/skills/discovery/SKILL.md +8 -4
  89. package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
  90. package/.codex-plugin/skills/eli5/SKILL.md +9 -4
  91. package/.codex-plugin/skills/eval/SKILL.md +9 -4
  92. package/.codex-plugin/skills/evolve/SKILL.md +9 -4
  93. package/.codex-plugin/skills/go/SKILL.md +3 -3
  94. package/.codex-plugin/skills/grill/SKILL.md +11 -4
  95. package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
  96. package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
  97. package/.codex-plugin/skills/npm-publish/SKILL.md +1 -1
  98. package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
  99. package/.codex-plugin/skills/plan/SKILL.md +8 -4
  100. package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
  101. package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
  102. package/.codex-plugin/skills/release/SKILL.md +3 -3
  103. package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
  104. package/.codex-plugin/skills/session/SKILL.md +1 -1
  105. package/.codex-plugin/skills/spinout/SKILL.md +4 -4
  106. package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
  107. package/.codex-plugin/skills/test/SKILL.md +3 -3
  108. package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
  109. package/.cursor/commands/autopilot.md +4 -4
  110. package/.cursor/commands/bootstrap.md +5 -4
  111. package/.cursor/commands/brainstorm.md +5 -4
  112. package/.cursor/commands/close.md +4 -3
  113. package/.cursor/commands/debug.md +4 -4
  114. package/.cursor/commands/discovery.md +4 -4
  115. package/.cursor/commands/dispatcher.md +4 -4
  116. package/.cursor/commands/eli5.md +4 -4
  117. package/.cursor/commands/eval.md +4 -4
  118. package/.cursor/commands/evolve.md +4 -4
  119. package/.cursor/commands/go.md +4 -3
  120. package/.cursor/commands/grill.md +4 -4
  121. package/.cursor/commands/harness-audit.md +3 -3
  122. package/.cursor/commands/memory-cleanup.md +4 -4
  123. package/.cursor/commands/persona-panel.md +4 -4
  124. package/.cursor/commands/plan.md +5 -4
  125. package/.cursor/commands/portfolio.md +3 -3
  126. package/.cursor/commands/reconcile.md +4 -4
  127. package/.cursor/commands/release.md +4 -3
  128. package/.cursor/commands/repo-audit.md +4 -4
  129. package/.cursor/commands/session.md +1 -1
  130. package/.cursor/commands/spinout.md +4 -4
  131. package/.cursor/commands/sunset-review.md +4 -4
  132. package/.cursor/commands/test.md +3 -3
  133. package/.cursor/commands/ux-grill.md +4 -4
  134. package/.cursor/rules/000-session-orchestrator.mdc +0 -2
  135. package/.cursor/rules/010-session-workflow.mdc +2 -2
  136. package/.cursor/rules/050-plan.mdc +1 -1
  137. package/.cursor/skills/bootstrap/SKILL.md +1 -0
  138. package/.cursor/skills/close/SKILL.md +13 -0
  139. package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
  140. package/.cursor/skills/debug/SKILL.md +0 -1
  141. package/.cursor/skills/discovery/SKILL.md +0 -1
  142. package/.cursor/skills/dispatcher/SKILL.md +0 -1
  143. package/.cursor/skills/eli5/SKILL.md +0 -1
  144. package/.cursor/skills/eval/SKILL.md +1 -1
  145. package/.cursor/skills/evolve/SKILL.md +0 -1
  146. package/.cursor/skills/go/SKILL.md +13 -0
  147. package/.cursor/skills/grill/SKILL.md +0 -1
  148. package/.cursor/skills/harness-audit/SKILL.md +12 -0
  149. package/.cursor/skills/npm-publish/SKILL.md +1 -0
  150. package/.cursor/skills/portfolio/SKILL.md +12 -0
  151. package/.cursor/skills/release/SKILL.md +13 -0
  152. package/.cursor/skills/repo-audit/SKILL.md +0 -1
  153. package/.cursor/skills/sunset-review/SKILL.md +0 -1
  154. package/.cursor/skills/test/SKILL.md +12 -0
  155. package/.cursor/skills/ux-grill/SKILL.md +0 -1
  156. package/.cursor-plugin/plugin.json +1 -1
  157. package/.orchestrator/policy/blocked-commands.json +13 -4
  158. package/AGENTS.md +3 -2
  159. package/CHANGELOG.md +197 -0
  160. package/README.md +11 -9
  161. package/SECURITY.md +12 -0
  162. package/agents/dialectic-deriver.md +13 -10
  163. package/agents/eval-judge.md +67 -45
  164. package/agents/skill-applied-judge.md +34 -19
  165. package/commands/session.md +17 -3
  166. package/docs/baseline.md +12 -6
  167. package/docs/ci-setup.md +53 -0
  168. package/docs/codex-setup.md +15 -3
  169. package/docs/components.md +13 -6
  170. package/docs/events-schema.md +59 -9
  171. package/docs/install.md +16 -0
  172. package/docs/persona-panel.md +1 -1
  173. package/docs/pi-setup.md +1 -1
  174. package/docs/rule-authoring.md +135 -14
  175. package/docs/scope-collision-guard.md +2 -0
  176. package/docs/session-config-reference.md +106 -11
  177. package/docs/session-config-template.md +31 -2
  178. package/docs/telemetry.md +2 -0
  179. package/hooks/_lib/hook-import-set.json +125 -8
  180. package/hooks/_lib/subagent-paths.mjs +15 -0
  181. package/hooks/_lib/subagent-transcript.mjs +582 -31
  182. package/hooks/_lib/vcs-create-matcher.mjs +217 -62
  183. package/hooks/config-protection.mjs +11 -3
  184. package/hooks/cwd-change-restore.mjs +11 -3
  185. package/hooks/enforce-commands.mjs +70 -23
  186. package/hooks/enforce-scope.mjs +143 -33
  187. package/hooks/hooks-codex.json +1 -1
  188. package/hooks/hooks.json +1 -1
  189. package/hooks/loop-guard.mjs +11 -3
  190. package/hooks/on-session-end.mjs +72 -25
  191. package/hooks/on-session-start.mjs +48 -11
  192. package/hooks/on-stop.mjs +211 -23
  193. package/hooks/operator-steer.mjs +11 -3
  194. package/hooks/post-bash-issue-budget-refund.mjs +18 -8
  195. package/hooks/post-bash-write-verify.mjs +6 -2
  196. package/hooks/post-edit-import-probe.mjs +17 -9
  197. package/hooks/post-edit-validate.mjs +13 -5
  198. package/hooks/post-subagent-discovery-validator.mjs +98 -13
  199. package/hooks/post-tool-batch-wave-signal.mjs +200 -38
  200. package/hooks/post-tool-failure-corrective-context.mjs +11 -5
  201. package/hooks/post-tooluse-frontend-slop.mjs +10 -4
  202. package/hooks/pre-auq-clarity.mjs +18 -2
  203. package/hooks/pre-bash-destructive-guard.mjs +80 -9
  204. package/hooks/pre-bash-issue-budget.mjs +119 -28
  205. package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
  206. package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
  207. package/hooks/pre-bash-staging-fence.mjs +335 -31
  208. package/hooks/pre-bash-templates-first.mjs +19 -14
  209. package/hooks/pre-task-scope-disjoint.mjs +385 -5
  210. package/hooks/skill-invocation-telemetry.mjs +2 -1
  211. package/hooks/subagent-telemetry.mjs +15 -19
  212. package/hooks/wave-scope-commit-guard.mjs +197 -100
  213. package/monitors/monitors.json +1 -1
  214. package/output-styles/wave-summary.md +1 -1
  215. package/package.json +2 -1
  216. package/pi/prompts/autopilot.md +3 -3
  217. package/pi/prompts/bootstrap.md +3 -3
  218. package/pi/prompts/brainstorm.md +3 -3
  219. package/pi/prompts/close.md +2 -2
  220. package/pi/prompts/debug.md +3 -3
  221. package/pi/prompts/discovery.md +3 -3
  222. package/pi/prompts/dispatcher.md +3 -3
  223. package/pi/prompts/eli5.md +3 -3
  224. package/pi/prompts/eval.md +3 -3
  225. package/pi/prompts/evolve.md +3 -3
  226. package/pi/prompts/go.md +2 -2
  227. package/pi/prompts/grill.md +3 -3
  228. package/pi/prompts/harness-audit.md +2 -3
  229. package/pi/prompts/memory-cleanup.md +3 -3
  230. package/pi/prompts/persona-panel.md +3 -3
  231. package/pi/prompts/plan.md +3 -3
  232. package/pi/prompts/portfolio.md +2 -2
  233. package/pi/prompts/reconcile.md +3 -3
  234. package/pi/prompts/release.md +3 -3
  235. package/pi/prompts/repo-audit.md +3 -4
  236. package/pi/prompts/session.md +2 -2
  237. package/pi/prompts/spinout.md +3 -3
  238. package/pi/prompts/sunset-review.md +3 -3
  239. package/pi/prompts/templates-ack.md +1 -1
  240. package/pi/prompts/test.md +3 -3
  241. package/pi/prompts/ux-grill.md +3 -3
  242. package/rules/README.md +1 -1
  243. package/rules/opt-in-domain/prompt-caching.md +1 -1
  244. package/rules/opt-in-stack/backend-data.md +1 -1
  245. package/rules/opt-in-stack/backend.md +3 -3
  246. package/rules/opt-in-stack/frontend.md +1 -1
  247. package/rules/opt-in-stack/security-web.md +3 -3
  248. package/rules/opt-in-stack/swift.md +1 -1
  249. package/scripts/archive-closed-prds.mjs +2 -2
  250. package/scripts/auq-audit.mjs +2 -3
  251. package/scripts/autopilot.mjs +23 -2
  252. package/scripts/backfill-abandoned-sessions.mjs +171 -15
  253. package/scripts/backfill-evidence-digest.mjs +2 -1
  254. package/scripts/backfill-learnings-from-vault.mjs +2 -2
  255. package/scripts/check-package-manager.mjs +2 -2
  256. package/scripts/check-sessions-integrity.mjs +300 -0
  257. package/scripts/ci/assert-vitest-green.mjs +2 -1
  258. package/scripts/dialectic-deriver.mjs +50 -13
  259. package/scripts/emit-session.mjs +77 -32
  260. package/scripts/eval-session.mjs +65 -3
  261. package/scripts/export-hw-learnings.mjs +2 -1
  262. package/scripts/express-path.mjs +1 -1
  263. package/scripts/gc-stale-worktrees.mjs +2 -1
  264. package/scripts/generate-agents-skills.mjs +102 -29
  265. package/scripts/generate-codex-skills.mjs +48 -4
  266. package/scripts/generate-cursor-adapter.mjs +220 -11
  267. package/scripts/generate-hook-import-set.mjs +12 -27
  268. package/scripts/generate-pi-prompts.mjs +183 -13
  269. package/scripts/github-protection-audit.mjs +2 -3
  270. package/scripts/lib/agent-frontmatter.mjs +23 -1
  271. package/scripts/lib/agent-status.mjs +2 -31
  272. package/scripts/lib/auq/clarity.mjs +10 -2
  273. package/scripts/lib/auq/parse.mjs +12 -31
  274. package/scripts/lib/auq/schema.mjs +56 -41
  275. package/scripts/lib/auto-dialectic.mjs +304 -15
  276. package/scripts/lib/autopilot/flags.mjs +12 -1
  277. package/scripts/lib/autopilot/kill-switches.mjs +6 -3
  278. package/scripts/lib/autopilot/loop.mjs +14 -1
  279. package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
  280. package/scripts/lib/ci-status-banner.mjs +376 -16
  281. package/scripts/lib/claude-md-budget-lint.mjs +2 -5
  282. package/scripts/lib/command-blocker.mjs +408 -33
  283. package/scripts/lib/config/dialectic.mjs +12 -3
  284. package/scripts/lib/config/drift-check.mjs +19 -0
  285. package/scripts/lib/config/gate.mjs +74 -0
  286. package/scripts/lib/config/reaper.mjs +162 -0
  287. package/scripts/lib/config.mjs +14 -0
  288. package/scripts/lib/convergence-monitor.mjs +76 -13
  289. package/scripts/lib/cursor-hook-bridge.mjs +2 -2
  290. package/scripts/lib/description-surface.mjs +2 -5
  291. package/scripts/lib/dispatcher/cli.mjs +2 -1
  292. package/scripts/lib/ecosystem-health.mjs +11 -0
  293. package/scripts/lib/ecosystem-wizard.mjs +2 -1
  294. package/scripts/lib/eval/engine.mjs +421 -53
  295. package/scripts/lib/eval/judge.mjs +463 -40
  296. package/scripts/lib/eval/schema.mjs +10 -1
  297. package/scripts/lib/events-rotation.mjs +221 -25
  298. package/scripts/lib/events-schema.mjs +114 -0
  299. package/scripts/lib/events.mjs +524 -5
  300. package/scripts/lib/fetch-baseline.mjs +3 -8
  301. package/scripts/lib/frontmatter-guard.mjs +21 -10
  302. package/scripts/lib/gates/gate-baseline.mjs +27 -2
  303. package/scripts/lib/gates/gate-full.mjs +28 -3
  304. package/scripts/lib/gates/gate-helpers.mjs +243 -21
  305. package/scripts/lib/gates/gate-incremental.mjs +28 -3
  306. package/scripts/lib/gates/gate-per-file.mjs +27 -2
  307. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
  308. package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
  309. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
  310. package/scripts/lib/instruction-budget-guard.mjs +332 -50
  311. package/scripts/lib/io.mjs +42 -8
  312. package/scripts/lib/is-main-module.mjs +82 -0
  313. package/scripts/lib/issue-close-strip-labels.mjs +207 -49
  314. package/scripts/lib/js-mask.mjs +197 -0
  315. package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
  316. package/scripts/lib/locks/index.mjs +32 -25
  317. package/scripts/lib/maintenance-due-banner.mjs +122 -91
  318. package/scripts/lib/orphan-reaper.mjs +1588 -0
  319. package/scripts/lib/peer-cards/merger.mjs +48 -10
  320. package/scripts/lib/peer-cards/reader.mjs +78 -2
  321. package/scripts/lib/peer-discovery.mjs +2 -5
  322. package/scripts/lib/playwright-driver/runner.mjs +2 -1
  323. package/scripts/lib/process-group.mjs +899 -0
  324. package/scripts/lib/quality-gate.mjs +107 -28
  325. package/scripts/lib/reconcile/backlog.mjs +368 -0
  326. package/scripts/lib/reconcile/engine.mjs +55 -188
  327. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +884 -0
  328. package/scripts/lib/reconcile/sanitize.mjs +69 -3
  329. package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
  330. package/scripts/lib/resource-probe/parsers.mjs +31 -0
  331. package/scripts/lib/rule-loader.mjs +41 -12
  332. package/scripts/lib/rules-sync.mjs +2 -5
  333. package/scripts/lib/scope-echo.mjs +429 -7
  334. package/scripts/lib/scope-gate.mjs +605 -1
  335. package/scripts/lib/session-close-backfill.mjs +91 -12
  336. package/scripts/lib/session-id.mjs +9 -20
  337. package/scripts/lib/session-invocation.mjs +20 -0
  338. package/scripts/lib/session-schema/constants.mjs +30 -2
  339. package/scripts/lib/session-schema/normalizer.mjs +56 -4
  340. package/scripts/lib/session-schema.mjs +8 -3
  341. package/scripts/lib/session-start-probes.mjs +95 -10
  342. package/scripts/lib/sessions-canonical.mjs +23 -0
  343. package/scripts/lib/sessions-integrity-banner.mjs +7 -1
  344. package/scripts/lib/sessions-staleness-banner.mjs +193 -51
  345. package/scripts/lib/skill-evidence-window.mjs +891 -0
  346. package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
  347. package/scripts/lib/skill-evolution/engine.mjs +18 -9
  348. package/scripts/lib/skill-judge.mjs +45 -3
  349. package/scripts/lib/state-md.mjs +84 -3
  350. package/scripts/lib/sunset/walker.mjs +31 -4
  351. package/scripts/lib/tail-window.mjs +56 -0
  352. package/scripts/lib/telemetry/schema.mjs +30 -0
  353. package/scripts/lib/telemetry/sync.mjs +61 -6
  354. package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
  355. package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
  356. package/scripts/lib/tests-src-ratio.mjs +2 -6
  357. package/scripts/lib/tmux-layout/telemetry-stats.mjs +74 -14
  358. package/scripts/lib/user-invocable-skills.mjs +205 -0
  359. package/scripts/lib/ux-grill/reconcile.mjs +48 -22
  360. package/scripts/lib/validate/check-agents-skills.mjs +26 -15
  361. package/scripts/lib/validate/check-banner-parity.mjs +2 -2
  362. package/scripts/lib/validate/check-cursor-adapter.mjs +3 -2
  363. package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
  364. package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
  365. package/scripts/lib/validate/check-entry-guard.mjs +329 -0
  366. package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
  367. package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
  368. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
  369. package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
  370. package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
  371. package/scripts/lib/validate/check-rules.mjs +7 -5
  372. package/scripts/lib/validate/check-skill-links.mjs +35 -6
  373. package/scripts/lib/validate/check-skill-script-paths.mjs +241 -29
  374. package/scripts/lib/validate/check-test-git-config-target.mjs +26 -36
  375. package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
  376. package/scripts/lib/validate/check-untracked-test-deps.mjs +9 -104
  377. package/scripts/lib/validate/check-unwired-features.mjs +220 -33
  378. package/scripts/lib/validate/check-validator-registration.mjs +36 -12
  379. package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
  380. package/scripts/lib/validate/confidential-names.mjs +10 -0
  381. package/scripts/lib/validate-vendored-rules.mjs +39 -12
  382. package/scripts/lib/vault-mirror/namespace.mjs +46 -8
  383. package/scripts/lib/vault-mirror/process.mjs +10 -3
  384. package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
  385. package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
  386. package/scripts/lib/vault-yaml.mjs +118 -0
  387. package/scripts/lib/wave-transcript-tail.mjs +2 -2
  388. package/scripts/lib/worktree/lifecycle.mjs +153 -1
  389. package/scripts/lock-reaper.mjs +2 -1
  390. package/scripts/materialize-wave-scope.mjs +87 -4
  391. package/scripts/migrate-sessions-jsonl.mjs +2 -1
  392. package/scripts/migrate-vault-paths.mjs +2 -3
  393. package/scripts/release-session-lock.mjs +305 -0
  394. package/scripts/release.mjs +109 -39
  395. package/scripts/relocate-vault-corpus.mjs +2 -3
  396. package/scripts/repair-invalid-sessions.mjs +2 -2
  397. package/scripts/resolve-session-invocation.mjs +59 -0
  398. package/scripts/run-quality-gate.mjs +156 -17
  399. package/scripts/session-shape.mjs +2 -2
  400. package/scripts/site-numbers.mjs +35 -11
  401. package/scripts/sweep-expired-rules.mjs +227 -0
  402. package/scripts/validate-plugin.mjs +21 -0
  403. package/scripts/validate-wave-scope.mjs +32 -105
  404. package/scripts/vault-consolidate.mjs +2 -2
  405. package/scripts/vault-mirror.mjs +11 -4
  406. package/scripts/wave-scope-binding.mjs +2 -3
  407. package/skills/_shared/bootstrap-gate.md +1 -1
  408. package/skills/_shared/monitor-patterns.md +1 -1
  409. package/skills/_shared/platform-tools.md +23 -11
  410. package/skills/_shared/research-evidence.md +53 -0
  411. package/skills/_shared/state-ownership.md +3 -0
  412. package/skills/autopilot/SKILL.md +80 -11
  413. package/skills/bootstrap/SKILL.md +51 -1
  414. package/skills/brainstorm/SKILL.md +16 -0
  415. package/skills/claude-md-drift-check/SKILL.md +1 -1
  416. package/skills/claude-md-drift-check/checker.mjs +49 -11
  417. package/{commands/close.md → skills/close/SKILL.md} +9 -3
  418. package/skills/convergence-monitoring/README.md +8 -1
  419. package/skills/convergence-monitoring/SIGNALS.md +50 -6
  420. package/skills/convergence-monitoring/SKILL.md +15 -6
  421. package/skills/debug/SKILL.md +10 -0
  422. package/skills/discovery/SKILL.md +24 -1
  423. package/skills/discovery/probes-session.md +2 -2
  424. package/skills/dispatcher/SKILL.md +38 -7
  425. package/skills/eli5/SKILL.md +11 -0
  426. package/skills/eval/SKILL.md +52 -23
  427. package/skills/eval/rubric-v1.md +1 -0
  428. package/skills/eval/rubric-v2.md +457 -0
  429. package/skills/evolve/SKILL.md +9 -2
  430. package/skills/evolve/references/evolve-dialectic-mode.md +46 -25
  431. package/skills/gitlab-ops/SKILL.md +3 -2
  432. package/{commands/go.md → skills/go/SKILL.md} +9 -1
  433. package/skills/grill/SKILL.md +19 -0
  434. package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
  435. package/skills/hook-development/SKILL.md +46 -41
  436. package/skills/memory-cleanup/SKILL.md +7 -0
  437. package/skills/npm-publish/SKILL.md +2 -2
  438. package/skills/persona-panel/SKILL.md +56 -1
  439. package/skills/persona-panel/persona-format.md +1 -1
  440. package/skills/plan/SKILL.md +28 -1
  441. package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
  442. package/skills/reconcile/SKILL.md +21 -0
  443. package/{commands/release.md → skills/release/SKILL.md} +16 -2
  444. package/skills/repo-audit/SKILL.md +7 -0
  445. package/skills/session-end/SKILL.md +13 -16
  446. package/skills/session-end/discovery-scan.md +1 -1
  447. package/skills/session-end/phase-3-6-tail.md +55 -9
  448. package/skills/session-end/plan-verification.md +2 -2
  449. package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
  450. package/skills/session-end/session-metrics-write.md +10 -0
  451. package/skills/session-plan/SKILL.md +18 -6
  452. package/skills/session-plan/references/session-plan-task-classification.md +2 -2
  453. package/skills/session-start/SKILL.md +5 -4
  454. package/skills/session-start/phase-8-5-express-path.md +6 -6
  455. package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
  456. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
  457. package/skills/session-start/references/phase-4-ssot-environment-check.md +6 -4
  458. package/skills/spinout/SKILL.md +12 -1
  459. package/skills/sunset-review/SKILL.md +13 -0
  460. package/{commands/test.md → skills/test/SKILL.md} +10 -4
  461. package/skills/ux-grill/SKILL.md +20 -2
  462. package/skills/wave-executor/SKILL.md +14 -7
  463. package/skills/wave-executor/circuit-breaker.md +2 -0
  464. package/skills/wave-executor/references/wave-executor-state-init.md +18 -4
  465. package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
  466. package/skills/wave-executor/references/wave-loop-review.md +17 -1
  467. package/commands/autopilot.md +0 -80
  468. package/commands/bootstrap.md +0 -56
  469. package/commands/brainstorm.md +0 -48
  470. package/commands/debug.md +0 -36
  471. package/commands/discovery.md +0 -32
  472. package/commands/dispatcher.md +0 -59
  473. package/commands/eli5.md +0 -33
  474. package/commands/eval.md +0 -28
  475. package/commands/evolve.md +0 -10
  476. package/commands/grill.md +0 -45
  477. package/commands/memory-cleanup.md +0 -26
  478. package/commands/persona-panel.md +0 -121
  479. package/commands/plan.md +0 -15
  480. package/commands/reconcile.md +0 -23
  481. package/commands/repo-audit.md +0 -24
  482. package/commands/spinout.md +0 -15
  483. package/commands/sunset-review.md +0 -27
  484. package/commands/ux-grill.md +0 -51
@@ -58,7 +58,12 @@ export function parseAgentFrontmatter(contents) {
58
58
  };
59
59
  }
60
60
 
61
- const match = FRONTMATTER_RE.exec(contents);
61
+ // A UTF-8 BOM before `---` makes FRONTMATTER_RE miss the block entirely, so a
62
+ // BOM-prefixed SKILL.md would parse as "no frontmatter" and every flag in it
63
+ // (`user-invocable` included) silently disappear.
64
+ const text = contents.charCodeAt(0) === 0xfeff ? contents.slice(1) : contents;
65
+
66
+ const match = FRONTMATTER_RE.exec(text);
62
67
  if (!match) {
63
68
  return {
64
69
  ok: false,
@@ -76,19 +81,36 @@ export function parseAgentFrontmatter(contents) {
76
81
  const frontmatter = /** @type {Record<string,string>} */ ({});
77
82
  const lines = fmText.split(/\r?\n/);
78
83
 
84
+ // Indented lines belong to whatever the preceding top-level key opened (a
85
+ // nested mapping, or a block scalar) — they are NOT top-level keys. Without
86
+ // this, `metadata:\n user-invocable: true` was HOISTED to top level and read
87
+ // as the slash-command marker, so the counters saw a skill no generator did.
88
+ let inNestedBlock = false;
89
+
79
90
  for (const rawLine of lines) {
80
91
  const line = rawLine.replace(/\s+$/, '');
81
92
  if (line === '' || /^\s*#/.test(line)) continue;
82
93
 
94
+ if (/^\s/.test(rawLine)) {
95
+ if (inNestedBlock) continue;
96
+ } else {
97
+ inNestedBlock = false;
98
+ }
99
+
83
100
  // Detect block-scalar indicators on a key line, e.g. `description: >` or `description: |`
84
101
  // These are valid YAML but break Claude Code's parser.
85
102
  const blockScalarMatch = /^(\w[\w-]*):\s*[>|]\s*$/.exec(line);
86
103
  if (blockScalarMatch) {
87
104
  // Store a sentinel so the validator can emit the correct rule.
88
105
  frontmatter[blockScalarMatch[1]] = '__BLOCK_SCALAR__';
106
+ inNestedBlock = true;
89
107
  continue;
90
108
  }
91
109
 
110
+ // A bare `key:` at top level opens a nested mapping (or an empty value);
111
+ // everything indented under it is that mapping's business, not ours.
112
+ if (/^\w[\w-]*:$/.test(line)) inNestedBlock = true;
113
+
92
114
  const idx = line.indexOf(':');
93
115
  if (idx === -1) continue;
94
116
  const key = line.slice(0, idx).trim();
@@ -58,6 +58,7 @@ import { isDeepStrictEqual } from 'node:util';
58
58
  import { appendJsonl } from './common.mjs';
59
59
  import { writeJsonAtomicSync } from './io.mjs';
60
60
  import { tryAcquireFileLock, releaseFileLock } from './file-lock.mjs';
61
+ import { readTailWindow } from './tail-window.mjs';
61
62
 
62
63
  // ---------------------------------------------------------------------------
63
64
  // Constants
@@ -303,36 +304,6 @@ function newestTsMs(entries) {
303
304
  return newest;
304
305
  }
305
306
 
306
- /**
307
- * Read the last `maxBytes` of a file. Returns the raw text plus whether the
308
- * window cut into the file (i.e. the first line in `text` may be partial).
309
- * @param {string} file
310
- * @param {number} maxBytes
311
- * @returns {{ text: string, size: number, cut: boolean }}
312
- */
313
- function readTail(file, maxBytes) {
314
- const fd = fs.openSync(file, 'r');
315
- try {
316
- const size = fs.fstatSync(fd).size;
317
- const want = Math.min(size, maxBytes);
318
- const start = size - want;
319
- const buf = Buffer.allocUnsafe(want);
320
- let read = 0;
321
- while (read < want) {
322
- const n = fs.readSync(fd, buf, read, want - read, start + read);
323
- if (n <= 0) break;
324
- read += n;
325
- }
326
- return { text: buf.subarray(0, read).toString('utf8'), size, cut: start > 0 };
327
- } finally {
328
- try {
329
- fs.closeSync(fd);
330
- } catch {
331
- /* best-effort */
332
- }
333
- }
334
- }
335
-
336
307
  /**
337
308
  * Shared write path for both setters: append the JSONL record, then perform a
338
309
  * lock-serialised read-modify-write of the LWW map. No-throw.
@@ -481,7 +452,7 @@ export function rebuildCurrentFromLedger(opts = {}) {
481
452
 
482
453
  let tail;
483
454
  try {
484
- tail = readTail(jsonlFile, maxBytes);
455
+ tail = readTailWindow(jsonlFile, maxBytes);
485
456
  } catch (err) {
486
457
  const reason = err?.code === 'ENOENT' ? 'ledger-missing' : 'ledger-unreadable';
487
458
  return {
@@ -801,9 +801,17 @@ function checkK5(q, at) {
801
801
  severity: 'fail',
802
802
  target: 'header',
803
803
  ...at,
804
+ // Der Text sagte bis hierher "Das Tool zeigt nur 12 — den Rest sieht der
805
+ // Operator nie". Das ist widerlegt: die 12 steht allein in der Prosa des
806
+ // Schemas (kein `.max(12)`, kein kuerzender Render-Pfad in Bundle
807
+ // 2.1.268), und 125 laengere Kopfzeilen wurden angenommen und beantwortet
808
+ // (ask-via-tool.md AUQ-006). H1 blockt deshalb nicht (BLOCKING_HURDLES =
809
+ // ['H2']) — die Meldung ist ein Hinweis, keine Abweisung, und darf keine
810
+ // Kuerzung begruenden, die es nicht braucht.
804
811
  message:
805
- `Die Kopfzeile "${header}" hat ${literal} Zeichen. Das Tool zeigt nur ` +
806
- `${THRESHOLDS.K5.headerCharsFail} — den Rest sieht der Operator nie.`,
812
+ `Die Kopfzeile "${header}" hat ${literal} Zeichen; Ziel sind ` +
813
+ `${THRESHOLDS.K5.headerCharsFail} Codepoints. Das Tool schneidet nicht ab — ` +
814
+ `Kuerzen ist optional.`,
807
815
  measured: literal,
808
816
  threshold: THRESHOLDS.K5.headerCharsFail,
809
817
  hurdle: HURDLES.H1.id,
@@ -83,6 +83,7 @@ import {
83
83
  isRecommendedOption,
84
84
  } from './schema.mjs';
85
85
  import { scanFenceBlocks } from '../validate/markdown-fences.mjs';
86
+ import { maskSource } from '../js-mask.mjs';
86
87
 
87
88
  // ---------------------------------------------------------------------------
88
89
  // Korpus-Abgrenzung
@@ -142,42 +143,22 @@ export function corpusKindOf(file) {
142
143
  * Anführungspaar, das jeder „nimm das letzte Literal der Zeile"-Ansatz als
143
144
  * Label extrahiert.
144
145
  *
146
+ * Seit #1388 delegiert das an den geteilten Lexer `maskSource`
147
+ * (`../js-mask.mjs`, Modus `keepLiterals`): gleiche Zusicherung (Länge und
148
+ * Zeilenumbrüche erhalten, Literal-Inhalte unangetastet), aber MIT
149
+ * Regex-Literal-Zweig. Die frühere handgeschriebene Fassung hatte keinen —
150
+ * gemessen 2026-09-18 verschluckte `const re = /\/*$/;` als vermeintlicher
151
+ * Block-Kommentar-Anfang alles bis zum nächsten `*` + `/`, sodass ein
152
+ * darunter stehendes `label: "X (Recommended)"` spurlos verschwand: ein
153
+ * STILLER FALSCH-NEGATIVER Befund in der Klarheitsmessung, längentreu und
154
+ * daher an keiner Zeilennummer erkennbar.
155
+ *
145
156
  * @param {string} text
146
157
  * @returns {string} gleiche Länge, Kommentarinhalt durch Leerzeichen ersetzt
147
158
  */
148
159
  export function stripComments(text) {
149
160
  if (typeof text !== 'string' || text === '') return '';
150
- const out = [...text];
151
- let i = 0;
152
- while (i < out.length) {
153
- const c = text[i];
154
- if (c === '"' || c === "'" || c === '`') {
155
- const lit = readStringLiteral(text, i);
156
- i = lit ? lit.end : i + 1;
157
- continue;
158
- }
159
- if (c === '/' && text[i + 1] === '/') {
160
- while (i < out.length && text[i] !== '\n') out[i++] = ' ';
161
- continue;
162
- }
163
- if (c === '/' && text[i + 1] === '*') {
164
- out[i] = ' ';
165
- out[i + 1] = ' ';
166
- i += 2;
167
- while (i < out.length && !(text[i] === '*' && text[i + 1] === '/')) {
168
- if (text[i] !== '\n') out[i] = ' ';
169
- i++;
170
- }
171
- if (i < out.length) {
172
- out[i] = ' ';
173
- out[i + 1] = ' ';
174
- i += 2;
175
- }
176
- continue;
177
- }
178
- i++;
179
- }
180
- return out.join('');
161
+ return maskSource(text, { keepLiterals: true });
181
162
  }
182
163
 
183
164
  /**
@@ -233,50 +233,16 @@ export const CRITERION_IDS = Object.freeze(Object.keys(CRITERIA));
233
233
  */
234
234
  export const TOTAL_WEIGHT = CRITERION_IDS.reduce((sum, id) => sum + CRITERIA[id].weight, 0);
235
235
 
236
- // ---------------------------------------------------------------------------
237
- // Die zwei harten Hürden
238
- // ---------------------------------------------------------------------------
239
-
240
- /**
241
- * Hürden sind KEINE gewichteten Kriterien. Eine gerissene Hürde ergibt Note F,
242
- * unabhängig von der Punktzahl — deshalb stehen sie getrennt und werden in
243
- * `AuqScore.hurdlesBroken` geführt, nicht in `points` verrechnet.
244
- *
245
- * @type {Readonly<Record<'H1'|'H2', Readonly<{id:string,title:string,rule:string,criterion:string,evidence:string}>>>}
246
- */
247
- export const HURDLES = Object.freeze({
248
- H1: Object.freeze({
249
- id: 'H1',
250
- title: 'Kopfzeile höchstens 12 Zeichen',
251
- rule: 'codepointLength(header) <= 12',
252
- criterion: 'K5',
253
- evidence:
254
- 'Gemessen 2026-08-22: 26 von 42 Kopfzeilen-Literalen reißen diese Grenze (62 %), ' +
255
- 'Spitzenwert 54 Zeichen. Die 12 ist die Stilangabe der Tool-Beschreibung ' +
256
- '(`max 12 chars`), KEINE erzwungene Grenze: im Bundle 2.1.268 steht die Zahl nur ' +
257
- 'in ebendieser Beschreibung, es gibt kein `.max(12)` im Zod-Schema und keinen ' +
258
- 'Render-Pfad, der sie liest — 125 längere Kopfzeilen wurden vom Tool angenommen ' +
259
- 'und beantwortet (gemessen 2026-09-11). Deshalb gilt H1 nur für VORLAGEN, wo ein ' +
260
- 'Autor kostenlos kürzen kann; zur Laufzeit meldet der Hook sie und blockt nicht ' +
261
- '(siehe BLOCKING_HURDLES in hooks/pre-auq-clarity.mjs).',
262
- }),
263
- H2: Object.freeze({
264
- id: 'H2',
265
- title: '2–4 Optionen je Frage, genau eine Empfehlung auf Platz 1',
266
- rule: 'options.length zwischen 2 und 4 JE FRAGE, und isRecommended nur bei index 0',
267
- criterion: 'K6',
268
- evidence:
269
- 'Pro Block gezählt meldet skills/plan/SKILL.md:137 fälschlich 14 Optionen — ' +
270
- 'es ist ein legaler Vierfragen-Block. Der Zähler zählt je Frage.',
271
- }),
272
- });
273
-
274
- /** Stabile Reihenfolge. */
275
- export const HURDLE_IDS = Object.freeze(Object.keys(HURDLES));
276
-
277
236
  // ---------------------------------------------------------------------------
278
237
  // Schwellen — die einzige Stelle, an der eine Zahl steht
279
238
  // ---------------------------------------------------------------------------
239
+ //
240
+ // DEFINITIONSREIHENFOLGE IST TRAGEND: `HURDLES` weiter unten baut seine
241
+ // operator-sichtbaren Sätze per Template-Literal aus `THRESHOLDS`, und ein
242
+ // Template-Literal wird beim Laden des Moduls ausgewertet. Steht `THRESHOLDS`
243
+ // wieder unter `HURDLES`, wirft der Import mit `ReferenceError` — auf einem
244
+ // Modul, das `hooks/pre-auq-clarity.mjs` in JEDER Session lädt, ist das eine
245
+ // host-weite Sperre, kein Testfehler.
280
246
 
281
247
  /**
282
248
  * Alle Schwellen, nach Kriterium gruppiert. clarity.mjs liest ausschließlich
@@ -356,6 +322,55 @@ export const THRESHOLDS = Object.freeze({
356
322
  }),
357
323
  });
358
324
 
325
+ // ---------------------------------------------------------------------------
326
+ // Die zwei harten Hürden
327
+ // ---------------------------------------------------------------------------
328
+
329
+ /**
330
+ * Hürden sind KEINE gewichteten Kriterien. Eine gerissene Hürde ergibt Note F,
331
+ * unabhängig von der Punktzahl — deshalb stehen sie getrennt und werden in
332
+ * `AuqScore.hurdlesBroken` geführt, nicht in `points` verrechnet.
333
+ *
334
+ * `title` und `rule` werden dem Operator GEDRUCKT (check-auq-clarity.mjs,
335
+ * auq-audit.mjs, der Deny-Text von hooks/pre-auq-clarity.mjs). Deshalb steht
336
+ * keine Zahl darin, sondern die Schwelle selbst: eine verschobene Schwelle mit
337
+ * einem stehengebliebenen Satz daneben ist eine Zeile, die den Operator belügt.
338
+ *
339
+ * @type {Readonly<Record<'H1'|'H2', Readonly<{id:string,title:string,rule:string,criterion:string,evidence:string}>>>}
340
+ */
341
+ export const HURDLES = Object.freeze({
342
+ H1: Object.freeze({
343
+ id: 'H1',
344
+ title: `Kopfzeile höchstens ${THRESHOLDS.K5.headerCharsFail} Zeichen`,
345
+ rule: `codepointLength(header) <= ${THRESHOLDS.K5.headerCharsFail}`,
346
+ criterion: 'K5',
347
+ // `evidence` zitiert die Tool-Beschreibung des Bundles wörtlich
348
+ // (`max 12 chars`, `.max(12)`). Diese 12 ist die Zahl DES BUNDLES, nicht
349
+ // unsere Schwelle — sie bleibt ein Literal, auch wenn K5 sich bewegt.
350
+ evidence:
351
+ 'Gemessen 2026-08-22: 26 von 42 Kopfzeilen-Literalen reißen diese Grenze (62 %), ' +
352
+ 'Spitzenwert 54 Zeichen. Die 12 ist die Stilangabe der Tool-Beschreibung ' +
353
+ '(`max 12 chars`), KEINE erzwungene Grenze: im Bundle 2.1.268 steht die Zahl nur ' +
354
+ 'in ebendieser Beschreibung, es gibt kein `.max(12)` im Zod-Schema und keinen ' +
355
+ 'Render-Pfad, der sie liest — 125 längere Kopfzeilen wurden vom Tool angenommen ' +
356
+ 'und beantwortet (gemessen 2026-09-11). Deshalb gilt H1 nur für VORLAGEN, wo ein ' +
357
+ 'Autor kostenlos kürzen kann; zur Laufzeit meldet der Hook sie und blockt nicht ' +
358
+ '(siehe BLOCKING_HURDLES in hooks/pre-auq-clarity.mjs).',
359
+ }),
360
+ H2: Object.freeze({
361
+ id: 'H2',
362
+ title: `${THRESHOLDS.K6.optionsMin}–${THRESHOLDS.K6.optionsMax} Optionen je Frage, genau eine Empfehlung auf Platz 1`,
363
+ rule: `options.length zwischen ${THRESHOLDS.K6.optionsMin} und ${THRESHOLDS.K6.optionsMax} JE FRAGE, und isRecommended nur bei index 0`,
364
+ criterion: 'K6',
365
+ evidence:
366
+ 'Pro Block gezählt meldet skills/plan/SKILL.md:137 fälschlich 14 Optionen — ' +
367
+ 'es ist ein legaler Vierfragen-Block. Der Zähler zählt je Frage.',
368
+ }),
369
+ });
370
+
371
+ /** Stabile Reihenfolge. */
372
+ export const HURDLE_IDS = Object.freeze(Object.keys(HURDLES));
373
+
359
374
  /** Höchstlänge eines Zitats in einem Befund (Codepoints). */
360
375
  export const EXCERPT_MAX_CHARS = 80;
361
376