@zigrivers/scaffold 3.28.0 → 3.30.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 (721) hide show
  1. package/README.md +5 -1
  2. package/content/guides/.gitkeep +0 -0
  3. package/content/guides/AUTHORING.md +143 -0
  4. package/content/guides/cli/index.html +1502 -0
  5. package/content/guides/cli/index.md +206 -0
  6. package/content/guides/concepts/index.html +1617 -0
  7. package/content/guides/concepts/index.md +347 -0
  8. package/content/guides/dashboard/index.html +1560 -0
  9. package/content/guides/dashboard/index.md +264 -0
  10. package/content/guides/index.html +1188 -0
  11. package/content/guides/install/.diagrams/diagram-0.svg +1 -0
  12. package/content/guides/install/.diagrams/manifest.json +3 -0
  13. package/content/guides/install/index.html +1300 -0
  14. package/content/guides/install/index.md +186 -0
  15. package/content/guides/knowledge/.diagrams/diagram-0.svg +1 -0
  16. package/content/guides/knowledge/.diagrams/manifest.json +3 -0
  17. package/content/guides/knowledge/index.html +1412 -0
  18. package/content/guides/knowledge/index.md +209 -0
  19. package/content/guides/knowledge-freshness/.diagrams/diagram-0.svg +1 -0
  20. package/content/guides/knowledge-freshness/.diagrams/manifest.json +3 -0
  21. package/content/guides/knowledge-freshness/index.html +2442 -0
  22. package/content/guides/knowledge-freshness/index.md +893 -0
  23. package/content/guides/mmr/.diagrams/diagram-0.svg +1 -0
  24. package/content/guides/mmr/.diagrams/manifest.json +3 -0
  25. package/content/guides/mmr/index.html +1746 -0
  26. package/content/guides/mmr/index.md +426 -0
  27. package/content/guides/multi-agent/.diagrams/diagram-0.svg +1 -0
  28. package/content/guides/multi-agent/.diagrams/manifest.json +3 -0
  29. package/content/guides/multi-agent/index.html +1362 -0
  30. package/content/guides/multi-agent/index.md +243 -0
  31. package/content/guides/observability/.diagrams/diagram-0.svg +1 -0
  32. package/content/guides/observability/.diagrams/diagram-1.svg +1 -0
  33. package/content/guides/observability/.diagrams/diagram-2.svg +1 -0
  34. package/content/guides/observability/.diagrams/diagram-3.svg +1 -0
  35. package/content/guides/observability/.diagrams/manifest.json +6 -0
  36. package/content/guides/observability/index.html +2904 -0
  37. package/content/guides/observability/index.md +1097 -0
  38. package/content/guides/pipeline/.diagrams/diagram-0.svg +1 -0
  39. package/content/guides/pipeline/.diagrams/diagram-1.svg +1 -0
  40. package/content/guides/pipeline/.diagrams/manifest.json +4 -0
  41. package/content/guides/pipeline/index.html +1632 -0
  42. package/content/guides/pipeline/index.md +387 -0
  43. package/content/guides/review-workflow/.diagrams/diagram-0.svg +1 -0
  44. package/content/guides/review-workflow/.diagrams/diagram-1.svg +1 -0
  45. package/content/guides/review-workflow/.diagrams/manifest.json +4 -0
  46. package/content/guides/review-workflow/index.html +1437 -0
  47. package/content/guides/review-workflow/index.md +248 -0
  48. package/content/knowledge/VERSION +1 -0
  49. package/content/knowledge/backend/backend-api-design.md +8 -0
  50. package/content/knowledge/backend/backend-architecture.md +8 -0
  51. package/content/knowledge/backend/backend-async-patterns.md +7 -0
  52. package/content/knowledge/backend/backend-auth-patterns.md +9 -0
  53. package/content/knowledge/backend/backend-conventions.md +6 -0
  54. package/content/knowledge/backend/backend-data-modeling.md +7 -0
  55. package/content/knowledge/backend/backend-deployment.md +8 -0
  56. package/content/knowledge/backend/backend-dev-environment.md +5 -0
  57. package/content/knowledge/backend/backend-fintech-broker-integration.md +6 -0
  58. package/content/knowledge/backend/backend-fintech-compliance.md +10 -2
  59. package/content/knowledge/backend/backend-fintech-data-modeling.md +6 -0
  60. package/content/knowledge/backend/backend-fintech-ledger.md +6 -0
  61. package/content/knowledge/backend/backend-fintech-observability.md +6 -0
  62. package/content/knowledge/backend/backend-fintech-order-lifecycle.md +6 -0
  63. package/content/knowledge/backend/backend-fintech-risk-management.md +6 -0
  64. package/content/knowledge/backend/backend-fintech-testing.md +6 -0
  65. package/content/knowledge/backend/backend-observability.md +7 -0
  66. package/content/knowledge/backend/backend-project-structure.md +6 -0
  67. package/content/knowledge/backend/backend-requirements.md +6 -0
  68. package/content/knowledge/backend/backend-security.md +7 -0
  69. package/content/knowledge/backend/backend-testing.md +7 -0
  70. package/content/knowledge/backend/backend-worker-patterns.md +6 -0
  71. package/content/knowledge/browser-extension/browser-extension-architecture.md +6 -0
  72. package/content/knowledge/browser-extension/browser-extension-content-scripts.md +6 -0
  73. package/content/knowledge/browser-extension/browser-extension-conventions.md +6 -0
  74. package/content/knowledge/browser-extension/browser-extension-cross-browser.md +6 -0
  75. package/content/knowledge/browser-extension/browser-extension-dev-environment.md +6 -0
  76. package/content/knowledge/browser-extension/browser-extension-manifest.md +6 -0
  77. package/content/knowledge/browser-extension/browser-extension-project-structure.md +6 -0
  78. package/content/knowledge/browser-extension/browser-extension-requirements.md +6 -0
  79. package/content/knowledge/browser-extension/browser-extension-security.md +7 -0
  80. package/content/knowledge/browser-extension/browser-extension-service-workers.md +6 -0
  81. package/content/knowledge/browser-extension/browser-extension-store-submission.md +6 -0
  82. package/content/knowledge/browser-extension/browser-extension-testing.md +6 -0
  83. package/content/knowledge/cli/cli-architecture.md +4 -0
  84. package/content/knowledge/cli/cli-conventions.md +4 -0
  85. package/content/knowledge/cli/cli-dev-environment.md +5 -0
  86. package/content/knowledge/cli/cli-distribution-patterns.md +5 -0
  87. package/content/knowledge/cli/cli-interactivity-patterns.md +4 -0
  88. package/content/knowledge/cli/cli-output-patterns.md +4 -0
  89. package/content/knowledge/cli/cli-project-structure.md +4 -0
  90. package/content/knowledge/cli/cli-requirements.md +4 -0
  91. package/content/knowledge/cli/cli-shell-integration.md +4 -0
  92. package/content/knowledge/cli/cli-testing.md +4 -0
  93. package/content/knowledge/core/adr-craft.md +8 -0
  94. package/content/knowledge/core/ai-memory-management.md +6 -0
  95. package/content/knowledge/core/api-design.md +6 -0
  96. package/content/knowledge/core/automated-review-tooling.md +8 -0
  97. package/content/knowledge/core/claude-md-patterns.md +8 -0
  98. package/content/knowledge/core/coding-conventions.md +8 -0
  99. package/content/knowledge/core/database-design.md +8 -0
  100. package/content/knowledge/core/design-system-tokens.md +8 -0
  101. package/content/knowledge/core/dev-environment.md +4 -0
  102. package/content/knowledge/core/domain-modeling.md +6 -0
  103. package/content/knowledge/core/eval-craft.md +6 -0
  104. package/content/knowledge/core/git-workflow-patterns.md +8 -0
  105. package/content/knowledge/core/multi-model-research-dispatch.md +6 -0
  106. package/content/knowledge/core/multi-model-review-dispatch.md +6 -0
  107. package/content/knowledge/core/multi-service-api-contracts.md +5 -0
  108. package/content/knowledge/core/multi-service-architecture.md +5 -0
  109. package/content/knowledge/core/multi-service-auth.md +6 -0
  110. package/content/knowledge/core/multi-service-data-ownership.md +8 -0
  111. package/content/knowledge/core/multi-service-observability.md +8 -0
  112. package/content/knowledge/core/multi-service-resilience.md +5 -0
  113. package/content/knowledge/core/multi-service-task-decomposition.md +8 -0
  114. package/content/knowledge/core/multi-service-testing.md +5 -0
  115. package/content/knowledge/core/operations-runbook.md +8 -0
  116. package/content/knowledge/core/project-structure-patterns.md +4 -0
  117. package/content/knowledge/core/review-step-template.md +4 -0
  118. package/content/knowledge/core/security-best-practices.md +6 -0
  119. package/content/knowledge/core/system-architecture.md +6 -0
  120. package/content/knowledge/core/task-decomposition.md +5 -0
  121. package/content/knowledge/core/task-tracking.md +8 -0
  122. package/content/knowledge/core/tech-stack-selection.md +8 -0
  123. package/content/knowledge/core/test-skeleton-generation.md +4 -0
  124. package/content/knowledge/core/testing-strategy.md +8 -0
  125. package/content/knowledge/core/user-stories.md +8 -0
  126. package/content/knowledge/core/user-story-innovation.md +4 -0
  127. package/content/knowledge/core/ux-specification.md +5 -0
  128. package/content/knowledge/data-pipeline/data-pipeline-architecture.md +5 -0
  129. package/content/knowledge/data-pipeline/data-pipeline-batch-patterns.md +4 -0
  130. package/content/knowledge/data-pipeline/data-pipeline-conventions.md +4 -0
  131. package/content/knowledge/data-pipeline/data-pipeline-dev-environment.md +4 -0
  132. package/content/knowledge/data-pipeline/data-pipeline-orchestration.md +4 -0
  133. package/content/knowledge/data-pipeline/data-pipeline-project-structure.md +4 -0
  134. package/content/knowledge/data-pipeline/data-pipeline-quality.md +4 -0
  135. package/content/knowledge/data-pipeline/data-pipeline-requirements.md +4 -0
  136. package/content/knowledge/data-pipeline/data-pipeline-schema-management.md +4 -0
  137. package/content/knowledge/data-pipeline/data-pipeline-security.md +4 -0
  138. package/content/knowledge/data-pipeline/data-pipeline-streaming-patterns.md +4 -0
  139. package/content/knowledge/data-pipeline/data-pipeline-testing.md +4 -0
  140. package/content/knowledge/data-science/data-science-architecture.md +5 -0
  141. package/content/knowledge/data-science/data-science-conventions.md +6 -0
  142. package/content/knowledge/data-science/data-science-data-versioning.md +6 -0
  143. package/content/knowledge/data-science/data-science-dev-environment.md +6 -0
  144. package/content/knowledge/data-science/data-science-experiment-tracking.md +8 -0
  145. package/content/knowledge/data-science/data-science-model-evaluation.md +5 -0
  146. package/content/knowledge/data-science/data-science-notebook-discipline.md +5 -0
  147. package/content/knowledge/data-science/data-science-observability.md +6 -0
  148. package/content/knowledge/data-science/data-science-project-structure.md +5 -0
  149. package/content/knowledge/data-science/data-science-reproducibility.md +8 -0
  150. package/content/knowledge/data-science/data-science-requirements.md +5 -0
  151. package/content/knowledge/data-science/data-science-security.md +8 -0
  152. package/content/knowledge/data-science/data-science-testing.md +5 -0
  153. package/content/knowledge/execution/enhancement-workflow.md +4 -0
  154. package/content/knowledge/execution/multi-agent-coordination.md +12 -1
  155. package/content/knowledge/execution/task-claiming-strategy.md +4 -0
  156. package/content/knowledge/execution/tdd-execution-loop.md +5 -0
  157. package/content/knowledge/execution/worktree-management.md +5 -0
  158. package/content/knowledge/finalization/apply-fixes-and-freeze.md +4 -0
  159. package/content/knowledge/finalization/developer-onboarding.md +4 -0
  160. package/content/knowledge/finalization/implementation-playbook.md +4 -0
  161. package/content/knowledge/game/game-accessibility.md +4 -0
  162. package/content/knowledge/game/game-ai-patterns.md +4 -0
  163. package/content/knowledge/game/game-asset-pipeline.md +4 -0
  164. package/content/knowledge/game/game-audio-design.md +4 -0
  165. package/content/knowledge/game/game-binary-vcs-strategy.md +5 -0
  166. package/content/knowledge/game/game-design-document.md +4 -0
  167. package/content/knowledge/game/game-domain-patterns.md +4 -0
  168. package/content/knowledge/game/game-economy-design.md +4 -0
  169. package/content/knowledge/game/game-engine-selection.md +4 -0
  170. package/content/knowledge/game/game-ideation.md +4 -0
  171. package/content/knowledge/game/game-input-systems.md +4 -0
  172. package/content/knowledge/game/game-level-content-design.md +4 -0
  173. package/content/knowledge/game/game-liveops-analytics.md +4 -0
  174. package/content/knowledge/game/game-localization.md +4 -0
  175. package/content/knowledge/game/game-milestone-definitions.md +4 -0
  176. package/content/knowledge/game/game-modding-ugc.md +4 -0
  177. package/content/knowledge/game/game-narrative-design.md +4 -0
  178. package/content/knowledge/game/game-networking.md +4 -0
  179. package/content/knowledge/game/game-performance-budgeting.md +4 -0
  180. package/content/knowledge/game/game-platform-certification.md +6 -0
  181. package/content/knowledge/game/game-project-structure.md +4 -0
  182. package/content/knowledge/game/game-save-systems.md +4 -0
  183. package/content/knowledge/game/game-testing-strategy.md +4 -0
  184. package/content/knowledge/game/game-ui-patterns.md +4 -0
  185. package/content/knowledge/game/game-vr-ar-design.md +6 -0
  186. package/content/knowledge/library/library-api-design.md +4 -0
  187. package/content/knowledge/library/library-architecture.md +4 -0
  188. package/content/knowledge/library/library-bundling.md +4 -0
  189. package/content/knowledge/library/library-conventions.md +5 -0
  190. package/content/knowledge/library/library-dev-environment.md +4 -0
  191. package/content/knowledge/library/library-documentation.md +4 -0
  192. package/content/knowledge/library/library-project-structure.md +4 -0
  193. package/content/knowledge/library/library-requirements.md +4 -0
  194. package/content/knowledge/library/library-security.md +4 -0
  195. package/content/knowledge/library/library-testing.md +4 -0
  196. package/content/knowledge/library/library-type-definitions.md +4 -0
  197. package/content/knowledge/library/library-versioning.md +5 -0
  198. package/content/knowledge/ml/ml-architecture.md +4 -0
  199. package/content/knowledge/ml/ml-conventions.md +4 -0
  200. package/content/knowledge/ml/ml-dev-environment.md +4 -0
  201. package/content/knowledge/ml/ml-experiment-tracking.md +6 -0
  202. package/content/knowledge/ml/ml-model-evaluation.md +4 -0
  203. package/content/knowledge/ml/ml-observability.md +5 -0
  204. package/content/knowledge/ml/ml-project-structure.md +4 -0
  205. package/content/knowledge/ml/ml-requirements.md +4 -0
  206. package/content/knowledge/ml/ml-security.md +5 -0
  207. package/content/knowledge/ml/ml-serving-patterns.md +4 -0
  208. package/content/knowledge/ml/ml-testing.md +4 -0
  209. package/content/knowledge/ml/ml-training-patterns.md +4 -0
  210. package/content/knowledge/mobile-app/mobile-app-architecture.md +6 -0
  211. package/content/knowledge/mobile-app/mobile-app-conventions.md +6 -0
  212. package/content/knowledge/mobile-app/mobile-app-deployment.md +6 -0
  213. package/content/knowledge/mobile-app/mobile-app-dev-environment.md +6 -0
  214. package/content/knowledge/mobile-app/mobile-app-distribution.md +6 -0
  215. package/content/knowledge/mobile-app/mobile-app-observability.md +7 -0
  216. package/content/knowledge/mobile-app/mobile-app-offline-patterns.md +7 -0
  217. package/content/knowledge/mobile-app/mobile-app-project-structure.md +6 -0
  218. package/content/knowledge/mobile-app/mobile-app-push-notifications.md +6 -0
  219. package/content/knowledge/mobile-app/mobile-app-requirements.md +7 -0
  220. package/content/knowledge/mobile-app/mobile-app-security.md +8 -0
  221. package/content/knowledge/mobile-app/mobile-app-testing.md +6 -0
  222. package/content/knowledge/product/gap-analysis.md +4 -0
  223. package/content/knowledge/product/ideation-craft.md +4 -0
  224. package/content/knowledge/product/prd-craft.md +4 -0
  225. package/content/knowledge/product/prd-innovation.md +4 -0
  226. package/content/knowledge/product/vision-craft.md +4 -0
  227. package/content/knowledge/product/vision-innovation.md +4 -0
  228. package/content/knowledge/research/research-architecture.md +4 -0
  229. package/content/knowledge/research/research-conventions.md +6 -0
  230. package/content/knowledge/research/research-dev-environment.md +6 -0
  231. package/content/knowledge/research/research-experiment-loop.md +4 -0
  232. package/content/knowledge/research/research-experiment-tracking.md +6 -0
  233. package/content/knowledge/research/research-ml-architecture-search.md +4 -0
  234. package/content/knowledge/research/research-ml-evaluation.md +4 -0
  235. package/content/knowledge/research/research-ml-experiment-tracking.md +6 -0
  236. package/content/knowledge/research/research-ml-training-patterns.md +4 -0
  237. package/content/knowledge/research/research-observability.md +5 -0
  238. package/content/knowledge/research/research-overfitting-prevention.md +5 -0
  239. package/content/knowledge/research/research-project-structure.md +5 -0
  240. package/content/knowledge/research/research-quant-backtesting.md +4 -0
  241. package/content/knowledge/research/research-quant-market-data.md +4 -0
  242. package/content/knowledge/research/research-quant-metrics.md +4 -0
  243. package/content/knowledge/research/research-quant-requirements.md +4 -0
  244. package/content/knowledge/research/research-quant-risk.md +4 -0
  245. package/content/knowledge/research/research-quant-strategy-patterns.md +4 -0
  246. package/content/knowledge/research/research-requirements.md +5 -0
  247. package/content/knowledge/research/research-security.md +5 -0
  248. package/content/knowledge/research/research-sim-compute-management.md +4 -0
  249. package/content/knowledge/research/research-sim-engine-patterns.md +4 -0
  250. package/content/knowledge/research/research-sim-parameter-spaces.md +4 -0
  251. package/content/knowledge/research/research-sim-validation.md +4 -0
  252. package/content/knowledge/research/research-testing.md +5 -0
  253. package/content/knowledge/review/review-adr.md +6 -0
  254. package/content/knowledge/review/review-api-design.md +6 -0
  255. package/content/knowledge/review/review-art-bible.md +4 -0
  256. package/content/knowledge/review/review-database-design.md +5 -0
  257. package/content/knowledge/review/review-domain-modeling.md +5 -0
  258. package/content/knowledge/review/review-game-design.md +4 -0
  259. package/content/knowledge/review/review-game-economy.md +4 -0
  260. package/content/knowledge/review/review-game-ui.md +5 -0
  261. package/content/knowledge/review/review-implementation-tasks.md +5 -0
  262. package/content/knowledge/review/review-methodology.md +4 -0
  263. package/content/knowledge/review/review-netcode.md +4 -0
  264. package/content/knowledge/review/review-operations.md +6 -0
  265. package/content/knowledge/review/review-platform-cert.md +4 -0
  266. package/content/knowledge/review/review-prd.md +4 -0
  267. package/content/knowledge/review/review-security.md +7 -0
  268. package/content/knowledge/review/review-system-architecture.md +6 -0
  269. package/content/knowledge/review/review-testing-strategy.md +6 -0
  270. package/content/knowledge/review/review-user-stories.md +5 -0
  271. package/content/knowledge/review/review-ux-specification.md +6 -0
  272. package/content/knowledge/review/review-vision.md +4 -0
  273. package/content/knowledge/tools/post-implementation-review-methodology.md +4 -0
  274. package/content/knowledge/tools/release-management.md +5 -0
  275. package/content/knowledge/tools/session-analysis.md +4 -0
  276. package/content/knowledge/tools/version-strategy.md +4 -0
  277. package/content/knowledge/validation/critical-path-analysis.md +4 -0
  278. package/content/knowledge/validation/cross-phase-consistency.md +4 -0
  279. package/content/knowledge/validation/decision-completeness.md +5 -0
  280. package/content/knowledge/validation/dependency-validation.md +4 -0
  281. package/content/knowledge/validation/implementability-review.md +4 -0
  282. package/content/knowledge/validation/scope-management.md +4 -0
  283. package/content/knowledge/validation/traceability.md +4 -0
  284. package/content/knowledge/web-app/web-app-api-patterns.md +6 -0
  285. package/content/knowledge/web-app/web-app-architecture.md +6 -0
  286. package/content/knowledge/web-app/web-app-auth-patterns.md +9 -0
  287. package/content/knowledge/web-app/web-app-conventions.md +5 -0
  288. package/content/knowledge/web-app/web-app-data-patterns.md +6 -0
  289. package/content/knowledge/web-app/web-app-deployment-workflow.md +6 -0
  290. package/content/knowledge/web-app/web-app-deployment.md +6 -0
  291. package/content/knowledge/web-app/web-app-design-system.md +6 -0
  292. package/content/knowledge/web-app/web-app-dev-environment.md +6 -0
  293. package/content/knowledge/web-app/web-app-observability.md +6 -0
  294. package/content/knowledge/web-app/web-app-project-structure.md +5 -0
  295. package/content/knowledge/web-app/web-app-rendering-strategies.md +6 -0
  296. package/content/knowledge/web-app/web-app-requirements.md +6 -0
  297. package/content/knowledge/web-app/web-app-security.md +8 -0
  298. package/content/knowledge/web-app/web-app-session-patterns.md +7 -0
  299. package/content/knowledge/web-app/web-app-testing.md +6 -0
  300. package/content/knowledge/web-app/web-app-ux-patterns.md +6 -0
  301. package/content/knowledge/web3/web3-access-control.md +8 -0
  302. package/content/knowledge/web3/web3-architecture.md +7 -0
  303. package/content/knowledge/web3/web3-audit-workflow.md +7 -0
  304. package/content/knowledge/web3/web3-common-vulnerabilities.md +8 -0
  305. package/content/knowledge/web3/web3-conventions.md +6 -0
  306. package/content/knowledge/web3/web3-deployment-and-verification.md +7 -0
  307. package/content/knowledge/web3/web3-dev-environment.md +6 -0
  308. package/content/knowledge/web3/web3-gas-optimization.md +7 -0
  309. package/content/knowledge/web3/web3-oracles-and-external-data.md +7 -0
  310. package/content/knowledge/web3/web3-project-structure.md +6 -0
  311. package/content/knowledge/web3/web3-requirements.md +6 -0
  312. package/content/knowledge/web3/web3-security.md +8 -0
  313. package/content/knowledge/web3/web3-testing.md +6 -0
  314. package/content/knowledge/web3/web3-upgradeability.md +7 -0
  315. package/content/tools/knowledge-audit-entry.md +79 -0
  316. package/content/tools/review-code.md +41 -14
  317. package/content/tools/review-pr.md +32 -14
  318. package/dist/cli/commands/dashboard.d.ts +1 -1
  319. package/dist/cli/commands/dashboard.d.ts.map +1 -1
  320. package/dist/cli/commands/dashboard.js +10 -10
  321. package/dist/cli/commands/dashboard.js.map +1 -1
  322. package/dist/cli/commands/dashboard.test.js +1 -1
  323. package/dist/cli/commands/dashboard.test.js.map +1 -1
  324. package/dist/cli/commands/guides.d.ts +24 -0
  325. package/dist/cli/commands/guides.d.ts.map +1 -0
  326. package/dist/cli/commands/guides.js +103 -0
  327. package/dist/cli/commands/guides.js.map +1 -0
  328. package/dist/cli/commands/knowledge-freshness-anti-over-rewrite.d.ts +9 -0
  329. package/dist/cli/commands/knowledge-freshness-anti-over-rewrite.d.ts.map +1 -0
  330. package/dist/cli/commands/knowledge-freshness-anti-over-rewrite.js +112 -0
  331. package/dist/cli/commands/knowledge-freshness-anti-over-rewrite.js.map +1 -0
  332. package/dist/cli/commands/knowledge-freshness-audit-apply.d.ts +8 -0
  333. package/dist/cli/commands/knowledge-freshness-audit-apply.d.ts.map +1 -0
  334. package/dist/cli/commands/knowledge-freshness-audit-apply.js +96 -0
  335. package/dist/cli/commands/knowledge-freshness-audit-apply.js.map +1 -0
  336. package/dist/cli/commands/knowledge-freshness-audit-prefilter.d.ts +7 -0
  337. package/dist/cli/commands/knowledge-freshness-audit-prefilter.d.ts.map +1 -0
  338. package/dist/cli/commands/knowledge-freshness-audit-prefilter.js +42 -0
  339. package/dist/cli/commands/knowledge-freshness-audit-prefilter.js.map +1 -0
  340. package/dist/cli/commands/knowledge-freshness-audit-run-entry.d.ts +9 -0
  341. package/dist/cli/commands/knowledge-freshness-audit-run-entry.d.ts.map +1 -0
  342. package/dist/cli/commands/knowledge-freshness-audit-run-entry.js +63 -0
  343. package/dist/cli/commands/knowledge-freshness-audit-run-entry.js.map +1 -0
  344. package/dist/cli/commands/knowledge-freshness-bump-version.d.ts +8 -0
  345. package/dist/cli/commands/knowledge-freshness-bump-version.d.ts.map +1 -0
  346. package/dist/cli/commands/knowledge-freshness-bump-version.js +34 -0
  347. package/dist/cli/commands/knowledge-freshness-bump-version.js.map +1 -0
  348. package/dist/cli/commands/knowledge-freshness-deep-guidance-check.d.ts +7 -0
  349. package/dist/cli/commands/knowledge-freshness-deep-guidance-check.d.ts.map +1 -0
  350. package/dist/cli/commands/knowledge-freshness-deep-guidance-check.js +51 -0
  351. package/dist/cli/commands/knowledge-freshness-deep-guidance-check.js.map +1 -0
  352. package/dist/cli/commands/knowledge-freshness-link-check.d.ts +7 -0
  353. package/dist/cli/commands/knowledge-freshness-link-check.d.ts.map +1 -0
  354. package/dist/cli/commands/knowledge-freshness-link-check.js +57 -0
  355. package/dist/cli/commands/knowledge-freshness-link-check.js.map +1 -0
  356. package/dist/cli/commands/knowledge-freshness-lint-unsourced.d.ts +8 -0
  357. package/dist/cli/commands/knowledge-freshness-lint-unsourced.d.ts.map +1 -0
  358. package/dist/cli/commands/knowledge-freshness-lint-unsourced.js +58 -0
  359. package/dist/cli/commands/knowledge-freshness-lint-unsourced.js.map +1 -0
  360. package/dist/cli/commands/knowledge-freshness.d.ts +4 -0
  361. package/dist/cli/commands/knowledge-freshness.d.ts.map +1 -0
  362. package/dist/cli/commands/knowledge-freshness.js +25 -0
  363. package/dist/cli/commands/knowledge-freshness.js.map +1 -0
  364. package/dist/cli/commands/observe.d.ts +4 -0
  365. package/dist/cli/commands/observe.d.ts.map +1 -1
  366. package/dist/cli/commands/observe.js +13 -0
  367. package/dist/cli/commands/observe.js.map +1 -1
  368. package/dist/cli/commands/observe.test.js +46 -0
  369. package/dist/cli/commands/observe.test.js.map +1 -1
  370. package/dist/cli/commands/validate-knowledge.d.ts +4 -0
  371. package/dist/cli/commands/validate-knowledge.d.ts.map +1 -0
  372. package/dist/cli/commands/validate-knowledge.js +32 -0
  373. package/dist/cli/commands/validate-knowledge.js.map +1 -0
  374. package/dist/cli/index.d.ts.map +1 -1
  375. package/dist/cli/index.js +6 -0
  376. package/dist/cli/index.js.map +1 -1
  377. package/dist/core/adapters/claude-code.d.ts.map +1 -1
  378. package/dist/core/adapters/claude-code.js +6 -3
  379. package/dist/core/adapters/claude-code.js.map +1 -1
  380. package/dist/core/adapters/claude-code.test.js +45 -1
  381. package/dist/core/adapters/claude-code.test.js.map +1 -1
  382. package/dist/core/assembly/engine.d.ts.map +1 -1
  383. package/dist/core/assembly/engine.js +7 -3
  384. package/dist/core/assembly/engine.js.map +1 -1
  385. package/dist/core/assembly/engine.test.js +45 -1
  386. package/dist/core/assembly/engine.test.js.map +1 -1
  387. package/dist/core/assembly/gap-signal-tail.d.ts +18 -0
  388. package/dist/core/assembly/gap-signal-tail.d.ts.map +1 -0
  389. package/dist/core/assembly/gap-signal-tail.js +43 -0
  390. package/dist/core/assembly/gap-signal-tail.js.map +1 -0
  391. package/dist/core/assembly/gap-signal-tail.test.d.ts +2 -0
  392. package/dist/core/assembly/gap-signal-tail.test.d.ts.map +1 -0
  393. package/dist/core/assembly/gap-signal-tail.test.js +49 -0
  394. package/dist/core/assembly/gap-signal-tail.test.js.map +1 -0
  395. package/dist/core/assembly/knowledge-loader.d.ts +11 -0
  396. package/dist/core/assembly/knowledge-loader.d.ts.map +1 -1
  397. package/dist/core/assembly/knowledge-loader.js +54 -1
  398. package/dist/core/assembly/knowledge-loader.js.map +1 -1
  399. package/dist/core/assembly/knowledge-loader.test.js +73 -0
  400. package/dist/core/assembly/knowledge-loader.test.js.map +1 -1
  401. package/dist/guides/build.d.ts +12 -0
  402. package/dist/guides/build.d.ts.map +1 -0
  403. package/dist/guides/build.js +55 -0
  404. package/dist/guides/build.js.map +1 -0
  405. package/dist/guides/build.test.d.ts +2 -0
  406. package/dist/guides/build.test.d.ts.map +1 -0
  407. package/dist/guides/build.test.js +82 -0
  408. package/dist/guides/build.test.js.map +1 -0
  409. package/dist/guides/chrome.d.ts +24 -0
  410. package/dist/guides/chrome.d.ts.map +1 -0
  411. package/dist/guides/chrome.js +118 -0
  412. package/dist/guides/chrome.js.map +1 -0
  413. package/dist/guides/cli-guides.test.d.ts +2 -0
  414. package/dist/guides/cli-guides.test.d.ts.map +1 -0
  415. package/dist/guides/cli-guides.test.js +41 -0
  416. package/dist/guides/cli-guides.test.js.map +1 -0
  417. package/dist/guides/dashboard-theme.css +1073 -0
  418. package/dist/guides/directives-callout.test.d.ts +2 -0
  419. package/dist/guides/directives-callout.test.d.ts.map +1 -0
  420. package/dist/guides/directives-callout.test.js +22 -0
  421. package/dist/guides/directives-callout.test.js.map +1 -0
  422. package/dist/guides/directives-chart.test.d.ts +2 -0
  423. package/dist/guides/directives-chart.test.d.ts.map +1 -0
  424. package/dist/guides/directives-chart.test.js +25 -0
  425. package/dist/guides/directives-chart.test.js.map +1 -0
  426. package/dist/guides/directives-cite.test.d.ts +2 -0
  427. package/dist/guides/directives-cite.test.d.ts.map +1 -0
  428. package/dist/guides/directives-cite.test.js +26 -0
  429. package/dist/guides/directives-cite.test.js.map +1 -0
  430. package/dist/guides/directives-filter-table.test.d.ts +2 -0
  431. package/dist/guides/directives-filter-table.test.d.ts.map +1 -0
  432. package/dist/guides/directives-filter-table.test.js +22 -0
  433. package/dist/guides/directives-filter-table.test.js.map +1 -0
  434. package/dist/guides/directives-sev.test.d.ts +2 -0
  435. package/dist/guides/directives-sev.test.d.ts.map +1 -0
  436. package/dist/guides/directives-sev.test.js +15 -0
  437. package/dist/guides/directives-sev.test.js.map +1 -0
  438. package/dist/guides/directives-tabs.test.d.ts +2 -0
  439. package/dist/guides/directives-tabs.test.d.ts.map +1 -0
  440. package/dist/guides/directives-tabs.test.js +52 -0
  441. package/dist/guides/directives-tabs.test.js.map +1 -0
  442. package/dist/guides/directives.d.ts +8 -0
  443. package/dist/guides/directives.d.ts.map +1 -0
  444. package/dist/guides/directives.js +182 -0
  445. package/dist/guides/directives.js.map +1 -0
  446. package/dist/guides/fs-guides.test.d.ts +2 -0
  447. package/dist/guides/fs-guides.test.d.ts.map +1 -0
  448. package/dist/guides/fs-guides.test.js +14 -0
  449. package/dist/guides/fs-guides.test.js.map +1 -0
  450. package/dist/guides/index-page.d.ts +3 -0
  451. package/dist/guides/index-page.d.ts.map +1 -0
  452. package/dist/guides/index-page.js +14 -0
  453. package/dist/guides/index-page.js.map +1 -0
  454. package/dist/guides/links.d.ts +14 -0
  455. package/dist/guides/links.d.ts.map +1 -0
  456. package/dist/guides/links.js +56 -0
  457. package/dist/guides/links.js.map +1 -0
  458. package/dist/guides/links.test.d.ts +2 -0
  459. package/dist/guides/links.test.d.ts.map +1 -0
  460. package/dist/guides/links.test.js +72 -0
  461. package/dist/guides/links.test.js.map +1 -0
  462. package/dist/guides/lint.d.ts +6 -0
  463. package/dist/guides/lint.d.ts.map +1 -0
  464. package/dist/guides/lint.js +27 -0
  465. package/dist/guides/lint.js.map +1 -0
  466. package/dist/guides/lint.test.d.ts +2 -0
  467. package/dist/guides/lint.test.d.ts.map +1 -0
  468. package/dist/guides/lint.test.js +24 -0
  469. package/dist/guides/lint.test.js.map +1 -0
  470. package/dist/guides/loader.d.ts +4 -0
  471. package/dist/guides/loader.d.ts.map +1 -0
  472. package/dist/guides/loader.js +63 -0
  473. package/dist/guides/loader.js.map +1 -0
  474. package/dist/guides/loader.test.d.ts +2 -0
  475. package/dist/guides/loader.test.d.ts.map +1 -0
  476. package/dist/guides/loader.test.js +85 -0
  477. package/dist/guides/loader.test.js.map +1 -0
  478. package/dist/guides/mermaid-sanitize.test.d.ts +2 -0
  479. package/dist/guides/mermaid-sanitize.test.d.ts.map +1 -0
  480. package/dist/guides/mermaid-sanitize.test.js +57 -0
  481. package/dist/guides/mermaid-sanitize.test.js.map +1 -0
  482. package/dist/guides/mermaid.d.ts +18 -0
  483. package/dist/guides/mermaid.d.ts.map +1 -0
  484. package/dist/guides/mermaid.js +137 -0
  485. package/dist/guides/mermaid.js.map +1 -0
  486. package/dist/guides/mermaid.test.d.ts +2 -0
  487. package/dist/guides/mermaid.test.d.ts.map +1 -0
  488. package/dist/guides/mermaid.test.js +105 -0
  489. package/dist/guides/mermaid.test.js.map +1 -0
  490. package/dist/guides/render.d.ts +13 -0
  491. package/dist/guides/render.d.ts.map +1 -0
  492. package/dist/guides/render.js +58 -0
  493. package/dist/guides/render.js.map +1 -0
  494. package/dist/guides/render.test.d.ts +2 -0
  495. package/dist/guides/render.test.d.ts.map +1 -0
  496. package/dist/guides/render.test.js +46 -0
  497. package/dist/guides/render.test.js.map +1 -0
  498. package/dist/guides/sanitize.d.ts +3 -0
  499. package/dist/guides/sanitize.d.ts.map +1 -0
  500. package/dist/guides/sanitize.js +77 -0
  501. package/dist/guides/sanitize.js.map +1 -0
  502. package/dist/guides/sanitize.test.d.ts +2 -0
  503. package/dist/guides/sanitize.test.d.ts.map +1 -0
  504. package/dist/guides/sanitize.test.js +45 -0
  505. package/dist/guides/sanitize.test.js.map +1 -0
  506. package/dist/guides/template.d.ts +11 -0
  507. package/dist/guides/template.d.ts.map +1 -0
  508. package/dist/guides/template.js +38 -0
  509. package/dist/guides/template.js.map +1 -0
  510. package/dist/guides/template.test.d.ts +2 -0
  511. package/dist/guides/template.test.d.ts.map +1 -0
  512. package/dist/guides/template.test.js +41 -0
  513. package/dist/guides/template.test.js.map +1 -0
  514. package/dist/guides/types.d.ts +20 -0
  515. package/dist/guides/types.d.ts.map +1 -0
  516. package/dist/guides/types.js +2 -0
  517. package/dist/guides/types.js.map +1 -0
  518. package/dist/knowledge-freshness/audit-apply-pr.d.ts +64 -0
  519. package/dist/knowledge-freshness/audit-apply-pr.d.ts.map +1 -0
  520. package/dist/knowledge-freshness/audit-apply-pr.js +309 -0
  521. package/dist/knowledge-freshness/audit-apply-pr.js.map +1 -0
  522. package/dist/knowledge-freshness/audit-apply-pr.test.d.ts +2 -0
  523. package/dist/knowledge-freshness/audit-apply-pr.test.d.ts.map +1 -0
  524. package/dist/knowledge-freshness/audit-apply-pr.test.js +211 -0
  525. package/dist/knowledge-freshness/audit-apply-pr.test.js.map +1 -0
  526. package/dist/knowledge-freshness/audit-apply.d.ts +16 -0
  527. package/dist/knowledge-freshness/audit-apply.d.ts.map +1 -0
  528. package/dist/knowledge-freshness/audit-apply.js +193 -0
  529. package/dist/knowledge-freshness/audit-apply.js.map +1 -0
  530. package/dist/knowledge-freshness/audit-apply.test.d.ts +2 -0
  531. package/dist/knowledge-freshness/audit-apply.test.d.ts.map +1 -0
  532. package/dist/knowledge-freshness/audit-apply.test.js +482 -0
  533. package/dist/knowledge-freshness/audit-apply.test.js.map +1 -0
  534. package/dist/knowledge-freshness/audit-prefilter.d.ts +12 -0
  535. package/dist/knowledge-freshness/audit-prefilter.d.ts.map +1 -0
  536. package/dist/knowledge-freshness/audit-prefilter.js +74 -0
  537. package/dist/knowledge-freshness/audit-prefilter.js.map +1 -0
  538. package/dist/knowledge-freshness/audit-prefilter.test.d.ts +2 -0
  539. package/dist/knowledge-freshness/audit-prefilter.test.d.ts.map +1 -0
  540. package/dist/knowledge-freshness/audit-prefilter.test.js +78 -0
  541. package/dist/knowledge-freshness/audit-prefilter.test.js.map +1 -0
  542. package/dist/knowledge-freshness/audit-runner.d.ts +135 -0
  543. package/dist/knowledge-freshness/audit-runner.d.ts.map +1 -0
  544. package/dist/knowledge-freshness/audit-runner.js +168 -0
  545. package/dist/knowledge-freshness/audit-runner.js.map +1 -0
  546. package/dist/knowledge-freshness/audit-runner.test.d.ts +2 -0
  547. package/dist/knowledge-freshness/audit-runner.test.d.ts.map +1 -0
  548. package/dist/knowledge-freshness/audit-runner.test.js +130 -0
  549. package/dist/knowledge-freshness/audit-runner.test.js.map +1 -0
  550. package/dist/knowledge-freshness/bump-version.d.ts +24 -0
  551. package/dist/knowledge-freshness/bump-version.d.ts.map +1 -0
  552. package/dist/knowledge-freshness/bump-version.js +69 -0
  553. package/dist/knowledge-freshness/bump-version.js.map +1 -0
  554. package/dist/knowledge-freshness/bump-version.test.d.ts +2 -0
  555. package/dist/knowledge-freshness/bump-version.test.d.ts.map +1 -0
  556. package/dist/knowledge-freshness/bump-version.test.js +82 -0
  557. package/dist/knowledge-freshness/bump-version.test.js.map +1 -0
  558. package/dist/knowledge-freshness/gates/anti-over-rewrite.d.ts +86 -0
  559. package/dist/knowledge-freshness/gates/anti-over-rewrite.d.ts.map +1 -0
  560. package/dist/knowledge-freshness/gates/anti-over-rewrite.js +210 -0
  561. package/dist/knowledge-freshness/gates/anti-over-rewrite.js.map +1 -0
  562. package/dist/knowledge-freshness/gates/anti-over-rewrite.test.d.ts +2 -0
  563. package/dist/knowledge-freshness/gates/anti-over-rewrite.test.d.ts.map +1 -0
  564. package/dist/knowledge-freshness/gates/anti-over-rewrite.test.js +115 -0
  565. package/dist/knowledge-freshness/gates/anti-over-rewrite.test.js.map +1 -0
  566. package/dist/knowledge-freshness/gates/changed-files.d.ts +53 -0
  567. package/dist/knowledge-freshness/gates/changed-files.d.ts.map +1 -0
  568. package/dist/knowledge-freshness/gates/changed-files.js +128 -0
  569. package/dist/knowledge-freshness/gates/changed-files.js.map +1 -0
  570. package/dist/knowledge-freshness/gates/deep-guidance-check.d.ts +23 -0
  571. package/dist/knowledge-freshness/gates/deep-guidance-check.d.ts.map +1 -0
  572. package/dist/knowledge-freshness/gates/deep-guidance-check.js +27 -0
  573. package/dist/knowledge-freshness/gates/deep-guidance-check.js.map +1 -0
  574. package/dist/knowledge-freshness/gates/deep-guidance-check.test.d.ts +2 -0
  575. package/dist/knowledge-freshness/gates/deep-guidance-check.test.d.ts.map +1 -0
  576. package/dist/knowledge-freshness/gates/deep-guidance-check.test.js +23 -0
  577. package/dist/knowledge-freshness/gates/deep-guidance-check.test.js.map +1 -0
  578. package/dist/knowledge-freshness/gates/link-check.d.ts +55 -0
  579. package/dist/knowledge-freshness/gates/link-check.d.ts.map +1 -0
  580. package/dist/knowledge-freshness/gates/link-check.js +161 -0
  581. package/dist/knowledge-freshness/gates/link-check.js.map +1 -0
  582. package/dist/knowledge-freshness/gates/link-check.test.d.ts +2 -0
  583. package/dist/knowledge-freshness/gates/link-check.test.d.ts.map +1 -0
  584. package/dist/knowledge-freshness/gates/link-check.test.js +76 -0
  585. package/dist/knowledge-freshness/gates/link-check.test.js.map +1 -0
  586. package/dist/knowledge-freshness/gates/lint-unsourced.d.ts +40 -0
  587. package/dist/knowledge-freshness/gates/lint-unsourced.d.ts.map +1 -0
  588. package/dist/knowledge-freshness/gates/lint-unsourced.js +143 -0
  589. package/dist/knowledge-freshness/gates/lint-unsourced.js.map +1 -0
  590. package/dist/knowledge-freshness/gates/lint-unsourced.test.d.ts +2 -0
  591. package/dist/knowledge-freshness/gates/lint-unsourced.test.d.ts.map +1 -0
  592. package/dist/knowledge-freshness/gates/lint-unsourced.test.js +68 -0
  593. package/dist/knowledge-freshness/gates/lint-unsourced.test.js.map +1 -0
  594. package/dist/knowledge-freshness/gates/parse-entry.d.ts +25 -0
  595. package/dist/knowledge-freshness/gates/parse-entry.d.ts.map +1 -0
  596. package/dist/knowledge-freshness/gates/parse-entry.js +41 -0
  597. package/dist/knowledge-freshness/gates/parse-entry.js.map +1 -0
  598. package/dist/knowledge-freshness/gates/parse-entry.test.d.ts +2 -0
  599. package/dist/knowledge-freshness/gates/parse-entry.test.d.ts.map +1 -0
  600. package/dist/knowledge-freshness/gates/parse-entry.test.js +34 -0
  601. package/dist/knowledge-freshness/gates/parse-entry.test.js.map +1 -0
  602. package/dist/knowledge-freshness/providers/anthropic.d.ts +33 -0
  603. package/dist/knowledge-freshness/providers/anthropic.d.ts.map +1 -0
  604. package/dist/knowledge-freshness/providers/anthropic.js +36 -0
  605. package/dist/knowledge-freshness/providers/anthropic.js.map +1 -0
  606. package/dist/knowledge-freshness/providers/anthropic.test.d.ts +2 -0
  607. package/dist/knowledge-freshness/providers/anthropic.test.d.ts.map +1 -0
  608. package/dist/knowledge-freshness/providers/anthropic.test.js +32 -0
  609. package/dist/knowledge-freshness/providers/anthropic.test.js.map +1 -0
  610. package/dist/knowledge-freshness/providers/deepseek.d.ts +33 -0
  611. package/dist/knowledge-freshness/providers/deepseek.d.ts.map +1 -0
  612. package/dist/knowledge-freshness/providers/deepseek.js +157 -0
  613. package/dist/knowledge-freshness/providers/deepseek.js.map +1 -0
  614. package/dist/knowledge-freshness/providers/deepseek.test.d.ts +2 -0
  615. package/dist/knowledge-freshness/providers/deepseek.test.d.ts.map +1 -0
  616. package/dist/knowledge-freshness/providers/deepseek.test.js +142 -0
  617. package/dist/knowledge-freshness/providers/deepseek.test.js.map +1 -0
  618. package/dist/knowledge-freshness/providers/index.d.ts +41 -0
  619. package/dist/knowledge-freshness/providers/index.d.ts.map +1 -0
  620. package/dist/knowledge-freshness/providers/index.js +108 -0
  621. package/dist/knowledge-freshness/providers/index.js.map +1 -0
  622. package/dist/knowledge-freshness/providers/index.test.d.ts +2 -0
  623. package/dist/knowledge-freshness/providers/index.test.d.ts.map +1 -0
  624. package/dist/knowledge-freshness/providers/index.test.js +97 -0
  625. package/dist/knowledge-freshness/providers/index.test.js.map +1 -0
  626. package/dist/knowledge-freshness/source-hash.d.ts +39 -0
  627. package/dist/knowledge-freshness/source-hash.d.ts.map +1 -0
  628. package/dist/knowledge-freshness/source-hash.js +180 -0
  629. package/dist/knowledge-freshness/source-hash.js.map +1 -0
  630. package/dist/knowledge-freshness/source-hash.test.d.ts +2 -0
  631. package/dist/knowledge-freshness/source-hash.test.d.ts.map +1 -0
  632. package/dist/knowledge-freshness/source-hash.test.js +63 -0
  633. package/dist/knowledge-freshness/source-hash.test.js.map +1 -0
  634. package/dist/knowledge-freshness/source-url-validator.d.ts +57 -0
  635. package/dist/knowledge-freshness/source-url-validator.d.ts.map +1 -0
  636. package/dist/knowledge-freshness/source-url-validator.js +304 -0
  637. package/dist/knowledge-freshness/source-url-validator.js.map +1 -0
  638. package/dist/knowledge-freshness/source-url-validator.test.d.ts +2 -0
  639. package/dist/knowledge-freshness/source-url-validator.test.d.ts.map +1 -0
  640. package/dist/knowledge-freshness/source-url-validator.test.js +167 -0
  641. package/dist/knowledge-freshness/source-url-validator.test.js.map +1 -0
  642. package/dist/observability/checks/lens-i-knowledge-gaps.d.ts +3 -0
  643. package/dist/observability/checks/lens-i-knowledge-gaps.d.ts.map +1 -0
  644. package/dist/observability/checks/lens-i-knowledge-gaps.js +165 -0
  645. package/dist/observability/checks/lens-i-knowledge-gaps.js.map +1 -0
  646. package/dist/observability/checks/lens-i-knowledge-gaps.test.d.ts +2 -0
  647. package/dist/observability/checks/lens-i-knowledge-gaps.test.d.ts.map +1 -0
  648. package/dist/observability/checks/lens-i-knowledge-gaps.test.js +421 -0
  649. package/dist/observability/checks/lens-i-knowledge-gaps.test.js.map +1 -0
  650. package/dist/observability/checks/lens-i-lessons-scanner.d.ts +16 -0
  651. package/dist/observability/checks/lens-i-lessons-scanner.d.ts.map +1 -0
  652. package/dist/observability/checks/lens-i-lessons-scanner.js +106 -0
  653. package/dist/observability/checks/lens-i-lessons-scanner.js.map +1 -0
  654. package/dist/observability/checks/lens-i-lessons-scanner.test.d.ts +2 -0
  655. package/dist/observability/checks/lens-i-lessons-scanner.test.d.ts.map +1 -0
  656. package/dist/observability/checks/lens-i-lessons-scanner.test.js +174 -0
  657. package/dist/observability/checks/lens-i-lessons-scanner.test.js.map +1 -0
  658. package/dist/observability/engine/api.d.ts +4 -0
  659. package/dist/observability/engine/api.d.ts.map +1 -1
  660. package/dist/observability/engine/api.js +17 -1
  661. package/dist/observability/engine/api.js.map +1 -1
  662. package/dist/observability/engine/checks/observability-config.d.ts +4 -0
  663. package/dist/observability/engine/checks/observability-config.d.ts.map +1 -1
  664. package/dist/observability/engine/checks/observability-config.js +1 -0
  665. package/dist/observability/engine/checks/observability-config.js.map +1 -1
  666. package/dist/observability/engine/checks/registry.d.ts.map +1 -1
  667. package/dist/observability/engine/checks/registry.js +7 -0
  668. package/dist/observability/engine/checks/registry.js.map +1 -1
  669. package/dist/observability/engine/checks/registry.test.js +3 -2
  670. package/dist/observability/engine/checks/registry.test.js.map +1 -1
  671. package/dist/observability/engine/checks/runner.d.ts +30 -0
  672. package/dist/observability/engine/checks/runner.d.ts.map +1 -1
  673. package/dist/observability/engine/checks/runner.js +8 -1
  674. package/dist/observability/engine/checks/runner.js.map +1 -1
  675. package/dist/observability/engine/checks/runner.test.js +74 -0
  676. package/dist/observability/engine/checks/runner.test.js.map +1 -1
  677. package/dist/observability/engine/event-schemas.d.ts.map +1 -1
  678. package/dist/observability/engine/event-schemas.js +41 -3
  679. package/dist/observability/engine/event-schemas.js.map +1 -1
  680. package/dist/observability/engine/event-schemas.test.js +105 -0
  681. package/dist/observability/engine/event-schemas.test.js.map +1 -1
  682. package/dist/observability/engine/fix-flow.d.ts +7 -0
  683. package/dist/observability/engine/fix-flow.d.ts.map +1 -1
  684. package/dist/observability/engine/fix-flow.js +5 -3
  685. package/dist/observability/engine/fix-flow.js.map +1 -1
  686. package/dist/observability/engine/knowledge-root-integration.test.d.ts +2 -0
  687. package/dist/observability/engine/knowledge-root-integration.test.d.ts.map +1 -0
  688. package/dist/observability/engine/knowledge-root-integration.test.js +103 -0
  689. package/dist/observability/engine/knowledge-root-integration.test.js.map +1 -0
  690. package/dist/observability/engine/types.d.ts +20 -1
  691. package/dist/observability/engine/types.d.ts.map +1 -1
  692. package/dist/observability/engine/types.test.js +1 -1
  693. package/dist/observability/engine/types.test.js.map +1 -1
  694. package/dist/observability/knowledge-index.d.ts +145 -0
  695. package/dist/observability/knowledge-index.d.ts.map +1 -0
  696. package/dist/observability/knowledge-index.js +353 -0
  697. package/dist/observability/knowledge-index.js.map +1 -0
  698. package/dist/observability/knowledge-index.test.d.ts +2 -0
  699. package/dist/observability/knowledge-index.test.d.ts.map +1 -0
  700. package/dist/observability/knowledge-index.test.js +364 -0
  701. package/dist/observability/knowledge-index.test.js.map +1 -0
  702. package/dist/observability/renderers/markdown.d.ts.map +1 -1
  703. package/dist/observability/renderers/markdown.js +14 -0
  704. package/dist/observability/renderers/markdown.js.map +1 -1
  705. package/dist/observability/renderers/markdown.test.js +30 -0
  706. package/dist/observability/renderers/markdown.test.js.map +1 -1
  707. package/dist/types/assembly.d.ts +10 -0
  708. package/dist/types/assembly.d.ts.map +1 -1
  709. package/dist/utils/fs.d.ts +6 -0
  710. package/dist/utils/fs.d.ts.map +1 -1
  711. package/dist/utils/fs.js +13 -0
  712. package/dist/utils/fs.js.map +1 -1
  713. package/dist/validation/knowledge-frontmatter-validator.d.ts +15 -0
  714. package/dist/validation/knowledge-frontmatter-validator.d.ts.map +1 -0
  715. package/dist/validation/knowledge-frontmatter-validator.js +131 -0
  716. package/dist/validation/knowledge-frontmatter-validator.js.map +1 -0
  717. package/dist/validation/knowledge-frontmatter-validator.test.d.ts +2 -0
  718. package/dist/validation/knowledge-frontmatter-validator.test.d.ts.map +1 -0
  719. package/dist/validation/knowledge-frontmatter-validator.test.js +66 -0
  720. package/dist/validation/knowledge-frontmatter-validator.test.js.map +1 -0
  721. package/package.json +13 -4
@@ -0,0 +1,2904 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-chrome-version="1">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Build Observability</title>
7
+ <!-- scaffold:chrome v1 -->
8
+ <style>/* Scaffold Dashboard Theme
9
+ * All CSS for the generated pipeline dashboard.
10
+ * Embedded into HTML by scripts/generate-dashboard.sh.
11
+ * Design system reference: docs/design-system.md
12
+ *
13
+ * Aesthetic: "Precision Industrial" — Swiss-typographic control room.
14
+ * Deep navy dark mode with indigo accents, clean cool-white light mode.
15
+ */
16
+
17
+ /* ─── Design Tokens (Light Mode) ──────────────── */
18
+ :root {
19
+ /* Surface */
20
+ --bg: #f5f6fa;
21
+ --bg-card: #ffffff;
22
+ --bg-hover: #eef0f6;
23
+ --bg-inset: #e8eaf2;
24
+
25
+ /* Text */
26
+ --text: #1a1d2e;
27
+ --text-muted: #6b7294;
28
+ --text-faint: #9ba1c0;
29
+
30
+ /* Borders & Structure */
31
+ --border: #dde0ed;
32
+ --border-light: #eceef5;
33
+ --radius: 10px;
34
+ --radius-sm: 6px;
35
+
36
+ /* Accent */
37
+ --accent: #4f46e5;
38
+ --accent-hover: #4338ca;
39
+ --accent-glow: rgba(79, 70, 229, 0.10);
40
+
41
+ /* Semantic: Status */
42
+ --green: #059669;
43
+ --green-bg: #ecfdf5;
44
+ --green-border: #a7f3d0;
45
+ --blue: #2563eb;
46
+ --blue-bg: #eff6ff;
47
+ --blue-border: #bfdbfe;
48
+ --yellow: #d97706;
49
+ --yellow-bg: #fffbeb;
50
+ --yellow-border:#fde68a;
51
+ --gray: #9ca3af;
52
+ --gray-bg: #f3f4f6;
53
+ --gray-border: #e5e7eb;
54
+
55
+ /* Semantic: Next Banner */
56
+ --next-bg: #eef2ff;
57
+ --next-border: #4f46e5;
58
+ --next-glow: rgba(79, 70, 229, 0.06);
59
+
60
+ /* Semantic: Progress */
61
+ --progress-bg: #e5e7eb;
62
+ --progress-h: 10px;
63
+
64
+ /* Depth */
65
+ --shadow-sm: 0 1px 2px rgba(30, 34, 60, 0.04);
66
+ --shadow: 0 1px 3px rgba(30, 34, 60, 0.07), 0 1px 2px rgba(30, 34, 60, 0.04);
67
+ --shadow-md: 0 4px 12px rgba(30, 34, 60, 0.08), 0 1px 3px rgba(30, 34, 60, 0.05);
68
+ --shadow-lg: 0 8px 24px rgba(30, 34, 60, 0.10), 0 2px 6px rgba(30, 34, 60, 0.04);
69
+
70
+ /* Spacing scale (4px base) */
71
+ --sp-1: 4px;
72
+ --sp-2: 8px;
73
+ --sp-3: 12px;
74
+ --sp-4: 16px;
75
+ --sp-5: 20px;
76
+ --sp-6: 24px;
77
+ --sp-8: 32px;
78
+ --sp-10: 40px;
79
+
80
+ /* Typography */
81
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
82
+ --font-mono: "SF Mono", "Cascadia Code", "Fira Code", "JetBrains Mono", Menlo, Consolas, monospace;
83
+ --text-xs: 0.75rem;
84
+ --text-sm: 0.8125rem;
85
+ --text-base: 0.9375rem;
86
+ --text-lg: 1.125rem;
87
+ --text-xl: 1.375rem;
88
+ --text-2xl: 1.75rem;
89
+ --lh-tight: 1.25;
90
+ --lh-normal: 1.5;
91
+ --lh-relaxed: 1.625;
92
+ --ls-tight: -0.01em;
93
+ --ls-wide: 0.025em;
94
+ --fw-normal: 400;
95
+ --fw-medium: 500;
96
+ --fw-semi: 600;
97
+ --fw-bold: 700;
98
+
99
+ /* Layout */
100
+ --max-w: 960px;
101
+ --page-pad: 24px;
102
+ }
103
+
104
+ /* ─── Design Tokens (Dark Mode) ───────────────── */
105
+ [data-theme="dark"] {
106
+ /* Surface */
107
+ --bg: #0f1117;
108
+ --bg-card: #1a1d2e;
109
+ --bg-hover: #252940;
110
+ --bg-inset: #141724;
111
+
112
+ /* Text */
113
+ --text: #e2e5f0;
114
+ --text-muted: #7c82a8;
115
+ --text-faint: #555c80;
116
+
117
+ /* Borders & Structure */
118
+ --border: #2a2f45;
119
+ --border-light: #21253a;
120
+
121
+ /* Accent */
122
+ --accent: #818cf8;
123
+ --accent-hover: #a5b4fc;
124
+ --accent-glow: rgba(129, 140, 248, 0.12);
125
+
126
+ /* Semantic: Status */
127
+ --green: #34d399;
128
+ --green-bg: rgba(6, 78, 59, 0.25);
129
+ --green-border: rgba(52, 211, 153, 0.25);
130
+ --blue: #60a5fa;
131
+ --blue-bg: rgba(30, 58, 95, 0.30);
132
+ --blue-border: rgba(96, 165, 250, 0.25);
133
+ --yellow: #fbbf24;
134
+ --yellow-bg: rgba(120, 53, 15, 0.25);
135
+ --yellow-border:rgba(251, 191, 36, 0.20);
136
+ --gray: #6b7294;
137
+ --gray-bg: #252940;
138
+ --gray-border: #363c58;
139
+
140
+ /* Semantic: Next Banner */
141
+ --next-bg: rgba(30, 27, 75, 0.50);
142
+ --next-border: #818cf8;
143
+ --next-glow: rgba(129, 140, 248, 0.08);
144
+
145
+ /* Semantic: Progress */
146
+ --progress-bg: #1f2337;
147
+
148
+ /* Depth */
149
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.20);
150
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.30), 0 1px 2px rgba(0, 0, 0, 0.15);
151
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35), 0 1px 3px rgba(0, 0, 0, 0.20);
152
+ --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.40), 0 2px 6px rgba(0, 0, 0, 0.20);
153
+ }
154
+
155
+ /* ─── Theme Toggle ───────────────────────────── */
156
+ .theme-toggle {
157
+ background: var(--bg-inset);
158
+ border: 1px solid var(--border);
159
+ border-radius: var(--radius-sm);
160
+ padding: var(--sp-1) var(--sp-2);
161
+ cursor: pointer;
162
+ font-size: var(--text-base);
163
+ line-height: 1;
164
+ color: var(--text-muted);
165
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
166
+ display: flex;
167
+ align-items: center;
168
+ margin-left: auto;
169
+ }
170
+
171
+ .theme-toggle:hover {
172
+ border-color: var(--accent);
173
+ color: var(--accent);
174
+ background: var(--accent-glow);
175
+ }
176
+
177
+ /* ─── Base ────────────────────────────────────── */
178
+ *, *::before, *::after {
179
+ margin: 0;
180
+ padding: 0;
181
+ box-sizing: border-box;
182
+ }
183
+
184
+ body {
185
+ font-family: var(--font-sans);
186
+ font-size: var(--text-base);
187
+ line-height: var(--lh-normal);
188
+ color: var(--text);
189
+ background: var(--bg);
190
+ -webkit-font-smoothing: antialiased;
191
+ -moz-osx-font-smoothing: grayscale;
192
+ }
193
+
194
+ /* ─── Layout ──────────────────────────────────── */
195
+ .wrap {
196
+ max-width: var(--max-w);
197
+ margin: 0 auto;
198
+ padding: var(--sp-8) var(--page-pad);
199
+ }
200
+
201
+ /* ─── Header ──────────────────────────────────── */
202
+ .header {
203
+ display: flex;
204
+ align-items: baseline;
205
+ gap: var(--sp-3);
206
+ margin-bottom: var(--sp-2);
207
+ flex-wrap: wrap;
208
+ }
209
+
210
+ h1 {
211
+ font-size: var(--text-2xl);
212
+ font-weight: var(--fw-bold);
213
+ letter-spacing: var(--ls-tight);
214
+ line-height: var(--lh-tight);
215
+ }
216
+
217
+ h2 {
218
+ font-size: var(--text-lg);
219
+ font-weight: var(--fw-semi);
220
+ letter-spacing: var(--ls-tight);
221
+ line-height: var(--lh-tight);
222
+ margin-bottom: var(--sp-3);
223
+ }
224
+
225
+ .header-meta {
226
+ font-size: var(--text-xs);
227
+ color: var(--text-faint);
228
+ margin-bottom: var(--sp-6);
229
+ letter-spacing: var(--ls-wide);
230
+ text-transform: uppercase;
231
+ }
232
+
233
+ /* ─── Badge ───────────────────────────────────── */
234
+ .badge {
235
+ display: inline-block;
236
+ padding: 2px var(--sp-2);
237
+ border-radius: 99px;
238
+ font-size: var(--text-xs);
239
+ font-weight: var(--fw-semi);
240
+ letter-spacing: var(--ls-wide);
241
+ background: var(--accent);
242
+ color: #fff;
243
+ text-transform: uppercase;
244
+ }
245
+
246
+ .badge-optional {
247
+ background: var(--yellow-bg);
248
+ color: var(--yellow);
249
+ border: 1px solid var(--yellow-border);
250
+ }
251
+
252
+ /* ─── Progress Bar ────────────────────────────── */
253
+ .progress-bar {
254
+ width: 100%;
255
+ height: var(--progress-h);
256
+ background: var(--progress-bg);
257
+ border-radius: 99px;
258
+ overflow: hidden;
259
+ margin-bottom: var(--sp-6);
260
+ display: flex;
261
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.06);
262
+ }
263
+
264
+ .progress-bar .seg-done {
265
+ background: linear-gradient(135deg, var(--green), #10b981);
266
+ box-shadow: 0 0 8px rgba(5, 150, 105, 0.3);
267
+ }
268
+
269
+ .progress-bar .seg-likely {
270
+ background: linear-gradient(135deg, var(--blue), #3b82f6);
271
+ box-shadow: 0 0 8px rgba(37, 99, 235, 0.25);
272
+ }
273
+
274
+ .progress-bar .seg-skip {
275
+ background: var(--gray);
276
+ opacity: 0.7;
277
+ }
278
+
279
+ /* ─── Summary Cards ───────────────────────────── */
280
+ .cards {
281
+ display: grid;
282
+ grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
283
+ gap: var(--sp-3);
284
+ margin-bottom: var(--sp-6);
285
+ }
286
+
287
+ .card {
288
+ background: var(--bg-card);
289
+ border: 1px solid var(--border);
290
+ border-radius: var(--radius);
291
+ padding: var(--sp-4) var(--sp-5);
292
+ box-shadow: var(--shadow);
293
+ transition: box-shadow 0.15s ease, transform 0.15s ease;
294
+ }
295
+
296
+ .card:hover {
297
+ box-shadow: var(--shadow-md);
298
+ transform: translateY(-1px);
299
+ }
300
+
301
+ .card-num {
302
+ font-size: var(--text-2xl);
303
+ font-weight: var(--fw-bold);
304
+ font-family: var(--font-mono);
305
+ letter-spacing: var(--ls-tight);
306
+ line-height: 1;
307
+ }
308
+
309
+ .card-lbl {
310
+ font-size: var(--text-xs);
311
+ color: var(--text-muted);
312
+ margin-top: var(--sp-1);
313
+ letter-spacing: var(--ls-wide);
314
+ text-transform: uppercase;
315
+ font-weight: var(--fw-medium);
316
+ }
317
+
318
+ /* ─── What's Next Banner ──────────────────────── */
319
+ .next-banner {
320
+ background: var(--next-bg);
321
+ border: 1px solid var(--next-border);
322
+ border-left: 4px solid var(--next-border);
323
+ border-radius: var(--radius);
324
+ padding: var(--sp-5) var(--sp-6);
325
+ margin-bottom: var(--sp-6);
326
+ box-shadow: 0 0 0 1px var(--next-glow), var(--shadow);
327
+ position: relative;
328
+ overflow: hidden;
329
+ }
330
+
331
+ .next-banner::before {
332
+ content: "";
333
+ position: absolute;
334
+ top: 0;
335
+ left: 0;
336
+ width: 4px;
337
+ height: 100%;
338
+ background: var(--next-border);
339
+ animation: pulse-border 2.5s ease-in-out infinite;
340
+ }
341
+
342
+ @keyframes pulse-border {
343
+ 0%, 100% { opacity: 1; }
344
+ 50% { opacity: 0.5; }
345
+ }
346
+
347
+ .next-banner h2 {
348
+ color: var(--accent);
349
+ margin-bottom: var(--sp-1);
350
+ font-size: var(--text-base);
351
+ font-weight: var(--fw-semi);
352
+ letter-spacing: var(--ls-wide);
353
+ text-transform: uppercase;
354
+ }
355
+
356
+ .next-banner p {
357
+ color: var(--text);
358
+ font-size: var(--text-base);
359
+ }
360
+
361
+ .next-cmd {
362
+ font-family: var(--font-mono);
363
+ background: var(--bg-card);
364
+ padding: var(--sp-1) var(--sp-3);
365
+ border-radius: var(--radius-sm);
366
+ font-size: var(--text-sm);
367
+ display: inline-flex;
368
+ align-items: center;
369
+ gap: var(--sp-2);
370
+ margin-top: var(--sp-3);
371
+ border: 1px solid var(--border);
372
+ }
373
+
374
+ /* ─── Phase Headers (Collapsible) ─────────────── */
375
+ .phase {
376
+ margin-bottom: var(--sp-6);
377
+ }
378
+
379
+ .phase-hdr {
380
+ display: flex;
381
+ align-items: center;
382
+ gap: var(--sp-2);
383
+ cursor: pointer;
384
+ padding: var(--sp-2) 0;
385
+ user-select: none;
386
+ border-bottom: 2px solid var(--border);
387
+ margin-bottom: var(--sp-3);
388
+ transition: border-color 0.15s ease;
389
+ }
390
+
391
+ .phase-hdr:hover {
392
+ border-bottom-color: var(--accent);
393
+ }
394
+
395
+ .phase-hdr:hover h2 {
396
+ color: var(--accent);
397
+ }
398
+
399
+ .phase-hdr h2 {
400
+ transition: color 0.15s ease;
401
+ }
402
+
403
+ .phase-hdr .arr {
404
+ transition: transform 0.2s ease;
405
+ font-size: var(--text-xs);
406
+ color: var(--text-muted);
407
+ }
408
+
409
+ .phase-hdr.closed .arr {
410
+ transform: rotate(-90deg);
411
+ }
412
+
413
+ .phase-cnt {
414
+ font-size: var(--text-xs);
415
+ font-family: var(--font-mono);
416
+ color: var(--text-faint);
417
+ margin-left: auto;
418
+ letter-spacing: var(--ls-wide);
419
+ }
420
+
421
+ /* ─── Prompt List ─────────────────────────────── */
422
+ .plist {
423
+ display: flex;
424
+ flex-direction: column;
425
+ gap: var(--sp-2);
426
+ }
427
+
428
+ /* ─── Prompt Cards ────────────────────────────── */
429
+ .pcard {
430
+ background: var(--bg-card);
431
+ border: 1px solid var(--border);
432
+ border-radius: var(--radius);
433
+ padding: var(--sp-3) var(--sp-4);
434
+ box-shadow: var(--shadow-sm);
435
+ display: grid;
436
+ grid-template-columns: auto 1fr auto;
437
+ gap: var(--sp-2) var(--sp-3);
438
+ align-items: start;
439
+ transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
440
+ }
441
+
442
+ .pcard:hover {
443
+ box-shadow: var(--shadow);
444
+ transform: translateY(-1px);
445
+ border-color: var(--accent-glow);
446
+ }
447
+
448
+ /* ─── Status Badges ──────────────────────────── */
449
+ .status-badge {
450
+ display: inline-flex;
451
+ align-items: center;
452
+ gap: var(--sp-1);
453
+ font-size: var(--text-xs);
454
+ font-weight: var(--fw-medium);
455
+ padding: 2px var(--sp-2);
456
+ border-radius: 99px;
457
+ white-space: nowrap;
458
+ flex-shrink: 0;
459
+ letter-spacing: var(--ls-wide);
460
+ line-height: var(--lh-tight);
461
+ }
462
+
463
+ .st-completed {
464
+ background: var(--green-bg);
465
+ color: var(--green);
466
+ border: 1px solid var(--green-border);
467
+ }
468
+
469
+ .st-likely-completed {
470
+ background: var(--blue-bg);
471
+ color: var(--blue);
472
+ border: 1px solid var(--blue-border);
473
+ }
474
+
475
+ .st-skipped {
476
+ background: var(--gray-bg);
477
+ color: var(--gray);
478
+ border: 1px solid var(--gray-border);
479
+ }
480
+
481
+ .st-pending {
482
+ background: var(--bg-inset);
483
+ color: var(--text-faint);
484
+ border: 1px solid var(--border);
485
+ }
486
+
487
+ /* ─── Status Legend ──────────────────────────── */
488
+ .status-legend {
489
+ display: flex;
490
+ flex-wrap: wrap;
491
+ gap: var(--sp-2);
492
+ margin-bottom: var(--sp-4);
493
+ padding: var(--sp-2) 0;
494
+ }
495
+
496
+ .status-legend .status-badge {
497
+ cursor: default;
498
+ }
499
+
500
+ /* ─── Prompt Card Inner ───────────────────────── */
501
+ .pinfo {
502
+ min-width: 0;
503
+ }
504
+
505
+ .pname {
506
+ font-weight: var(--fw-semi);
507
+ font-size: var(--text-base);
508
+ }
509
+
510
+ .pstep {
511
+ font-size: var(--text-xs);
512
+ font-family: var(--font-mono);
513
+ color: var(--text-faint);
514
+ letter-spacing: var(--ls-wide);
515
+ }
516
+
517
+ .pdesc {
518
+ font-size: var(--text-sm);
519
+ color: var(--text-muted);
520
+ margin-top: 2px;
521
+ line-height: var(--lh-relaxed);
522
+ }
523
+
524
+ .pdesc-long {
525
+ font-size: var(--text-xs);
526
+ color: var(--text-faint);
527
+ margin-top: 2px;
528
+ }
529
+
530
+ .pdeps {
531
+ font-size: var(--text-xs);
532
+ color: var(--yellow);
533
+ margin-top: var(--sp-1);
534
+ font-weight: var(--fw-medium);
535
+ }
536
+
537
+ /* ─── Copy Command Button ─────────────────────── */
538
+ .pcmd {
539
+ font-family: var(--font-mono);
540
+ font-size: var(--text-xs);
541
+ background: var(--bg-inset);
542
+ padding: 3px var(--sp-2);
543
+ border-radius: var(--radius-sm);
544
+ cursor: pointer;
545
+ border: 1px solid var(--border);
546
+ white-space: nowrap;
547
+ align-self: center;
548
+ color: var(--text-muted);
549
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
550
+ letter-spacing: var(--ls-wide);
551
+ }
552
+
553
+ .pcmd:hover {
554
+ border-color: var(--accent);
555
+ color: var(--accent);
556
+ background: var(--accent-glow);
557
+ }
558
+
559
+ .pcmd.copied {
560
+ border-color: var(--green);
561
+ color: var(--green);
562
+ background: var(--green-bg);
563
+ }
564
+
565
+ /* ─── Prompt Modal ────────────────────────────── */
566
+ .modal-overlay {
567
+ position: fixed;
568
+ inset: 0;
569
+ background: rgba(0, 0, 0, 0.6);
570
+ display: flex;
571
+ align-items: center;
572
+ justify-content: center;
573
+ z-index: 1000;
574
+ padding: var(--sp-4);
575
+ }
576
+
577
+ .modal {
578
+ background: var(--bg-card);
579
+ border: 1px solid var(--border);
580
+ border-radius: var(--radius);
581
+ box-shadow: var(--shadow-lg);
582
+ max-width: 720px;
583
+ width: 100%;
584
+ max-height: 85vh;
585
+ display: flex;
586
+ flex-direction: column;
587
+ }
588
+
589
+ .modal-header {
590
+ display: flex;
591
+ align-items: center;
592
+ gap: var(--sp-3);
593
+ padding: var(--sp-4) var(--sp-5);
594
+ border-bottom: 1px solid var(--border);
595
+ flex-shrink: 0;
596
+ }
597
+
598
+ .modal-header h3 {
599
+ font-size: var(--text-lg);
600
+ font-weight: var(--fw-semi);
601
+ flex: 1;
602
+ min-width: 0;
603
+ }
604
+
605
+ .modal-close {
606
+ background: var(--bg-inset);
607
+ border: 1px solid var(--border);
608
+ border-radius: var(--radius-sm);
609
+ padding: var(--sp-1) var(--sp-2);
610
+ cursor: pointer;
611
+ font-size: var(--text-base);
612
+ color: var(--text-muted);
613
+ line-height: 1;
614
+ transition: border-color 0.15s ease, color 0.15s ease;
615
+ }
616
+
617
+ .modal-close:hover {
618
+ border-color: var(--accent);
619
+ color: var(--accent);
620
+ }
621
+
622
+ .modal-body {
623
+ padding: var(--sp-5);
624
+ overflow-y: auto;
625
+ flex: 1;
626
+ }
627
+
628
+ .modal-body pre {
629
+ font-family: var(--font-mono);
630
+ font-size: var(--text-sm);
631
+ line-height: var(--lh-relaxed);
632
+ white-space: pre-wrap;
633
+ word-break: break-word;
634
+ color: var(--text);
635
+ }
636
+
637
+ .modal-body pre .md-heading {
638
+ font-weight: var(--fw-bold);
639
+ color: var(--accent);
640
+ }
641
+
642
+ .modal-body pre .md-code {
643
+ background: var(--bg-inset);
644
+ padding: 1px 4px;
645
+ border-radius: 3px;
646
+ font-size: var(--text-xs);
647
+ }
648
+
649
+ .modal-footer {
650
+ display: flex;
651
+ gap: var(--sp-2);
652
+ padding: var(--sp-3) var(--sp-5);
653
+ border-top: 1px solid var(--border);
654
+ flex-shrink: 0;
655
+ }
656
+
657
+ .modal-copy-btn {
658
+ background: var(--accent);
659
+ color: #fff;
660
+ border: none;
661
+ border-radius: var(--radius-sm);
662
+ padding: var(--sp-2) var(--sp-4);
663
+ font-size: var(--text-sm);
664
+ font-weight: var(--fw-medium);
665
+ cursor: pointer;
666
+ transition: background 0.15s ease;
667
+ }
668
+
669
+ .modal-copy-btn:hover {
670
+ background: var(--accent-hover);
671
+ }
672
+
673
+ .modal-copy-btn.copied {
674
+ background: var(--green);
675
+ }
676
+
677
+ /* ─── Beads Task Section ─────────────────────── */
678
+ .beads-section {
679
+ margin-top: var(--sp-8);
680
+ margin-bottom: var(--sp-6);
681
+ }
682
+
683
+ .beads-filters {
684
+ display: flex;
685
+ gap: var(--sp-2);
686
+ margin-bottom: var(--sp-3);
687
+ }
688
+
689
+ .beads-filter {
690
+ background: var(--bg-inset);
691
+ border: 1px solid var(--border);
692
+ border-radius: 99px;
693
+ padding: var(--sp-1) var(--sp-3);
694
+ font-size: var(--text-xs);
695
+ font-weight: var(--fw-medium);
696
+ color: var(--text-muted);
697
+ cursor: pointer;
698
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
699
+ letter-spacing: var(--ls-wide);
700
+ }
701
+
702
+ .beads-filter:hover {
703
+ border-color: var(--accent);
704
+ color: var(--accent);
705
+ }
706
+
707
+ .beads-filter.active {
708
+ background: var(--accent);
709
+ color: #fff;
710
+ border-color: var(--accent);
711
+ }
712
+
713
+ /* ─── Beads Status Badges ────────────────────── */
714
+ .st-bead-open {
715
+ background: var(--accent-glow);
716
+ color: var(--accent);
717
+ border: 1px solid var(--accent);
718
+ }
719
+
720
+ .st-bead-progress {
721
+ background: var(--blue-bg);
722
+ color: var(--blue);
723
+ border: 1px solid var(--blue-border);
724
+ }
725
+
726
+ .st-bead-blocked {
727
+ background: var(--yellow-bg);
728
+ color: var(--yellow);
729
+ border: 1px solid var(--yellow-border);
730
+ }
731
+
732
+ .st-bead-deferred {
733
+ background: var(--gray-bg);
734
+ color: var(--gray);
735
+ border: 1px solid var(--gray-border);
736
+ }
737
+
738
+ .st-bead-closed {
739
+ background: var(--green-bg);
740
+ color: var(--green);
741
+ border: 1px solid var(--green-border);
742
+ }
743
+
744
+ /* ─── Beads Filter Separator ─────────────────── */
745
+ .beads-filter-sep {
746
+ width: 1px;
747
+ background: var(--border);
748
+ align-self: stretch;
749
+ margin: 0 var(--sp-1);
750
+ }
751
+
752
+ /* ─── Beads Priority Filter ──────────────────── */
753
+ .beads-prio-filter {
754
+ background: var(--bg-inset);
755
+ border: 1px solid var(--border);
756
+ border-radius: 99px;
757
+ padding: var(--sp-1) var(--sp-3);
758
+ font-size: var(--text-xs);
759
+ font-weight: var(--fw-medium);
760
+ color: var(--text-muted);
761
+ cursor: pointer;
762
+ transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
763
+ letter-spacing: var(--ls-wide);
764
+ }
765
+
766
+ .beads-prio-filter:hover {
767
+ border-color: var(--accent);
768
+ color: var(--accent);
769
+ }
770
+
771
+ .beads-prio-filter.active {
772
+ background: var(--accent);
773
+ color: #fff;
774
+ border-color: var(--accent);
775
+ }
776
+
777
+ /* ─── Beads Task Detail Modal ────────────────── */
778
+ .bead-meta-grid {
779
+ display: grid;
780
+ grid-template-columns: 1fr 1fr;
781
+ gap: var(--sp-3);
782
+ padding: var(--sp-4) 0;
783
+ border-bottom: 1px solid var(--border-light);
784
+ }
785
+
786
+ .bead-meta-item {
787
+ display: flex;
788
+ flex-direction: column;
789
+ gap: 2px;
790
+ }
791
+
792
+ .bead-meta-label {
793
+ font-size: var(--text-xs);
794
+ color: var(--text-faint);
795
+ text-transform: uppercase;
796
+ letter-spacing: var(--ls-wide);
797
+ font-weight: var(--fw-medium);
798
+ }
799
+
800
+ .bead-meta-value {
801
+ font-size: var(--text-sm);
802
+ font-weight: var(--fw-medium);
803
+ color: var(--text);
804
+ }
805
+
806
+ .bead-description {
807
+ padding: var(--sp-4) 0;
808
+ border-bottom: 1px solid var(--border-light);
809
+ white-space: pre-wrap;
810
+ font-size: var(--text-sm);
811
+ line-height: var(--lh-relaxed);
812
+ color: var(--text-muted);
813
+ }
814
+
815
+ .bead-deps {
816
+ padding: var(--sp-4) 0;
817
+ border-bottom: 1px solid var(--border-light);
818
+ }
819
+
820
+ .bead-dep-group {
821
+ display: flex;
822
+ flex-wrap: wrap;
823
+ align-items: center;
824
+ gap: var(--sp-2);
825
+ margin-bottom: var(--sp-2);
826
+ }
827
+
828
+ .bead-dep-group:last-child {
829
+ margin-bottom: 0;
830
+ }
831
+
832
+ .bead-dep-label {
833
+ font-size: var(--text-xs);
834
+ color: var(--text-faint);
835
+ text-transform: uppercase;
836
+ letter-spacing: var(--ls-wide);
837
+ font-weight: var(--fw-medium);
838
+ min-width: 80px;
839
+ }
840
+
841
+ .bead-dep-link {
842
+ display: inline-block;
843
+ font-family: var(--font-mono);
844
+ font-size: var(--text-xs);
845
+ padding: 2px var(--sp-2);
846
+ border-radius: 99px;
847
+ background: var(--accent-glow);
848
+ color: var(--accent);
849
+ border: 1px solid var(--accent);
850
+ cursor: pointer;
851
+ transition: background 0.15s ease, color 0.15s ease;
852
+ text-decoration: none;
853
+ }
854
+
855
+ .bead-dep-link:hover {
856
+ background: var(--accent);
857
+ color: #fff;
858
+ }
859
+
860
+ .bead-timestamps {
861
+ display: flex;
862
+ flex-wrap: wrap;
863
+ gap: var(--sp-4);
864
+ padding: var(--sp-4) 0;
865
+ }
866
+
867
+ .bead-ts-item {
868
+ display: flex;
869
+ flex-direction: column;
870
+ gap: 2px;
871
+ }
872
+
873
+ .bead-ts-label {
874
+ font-size: var(--text-xs);
875
+ color: var(--text-faint);
876
+ text-transform: uppercase;
877
+ letter-spacing: var(--ls-wide);
878
+ font-weight: var(--fw-medium);
879
+ }
880
+
881
+ .bead-ts-value {
882
+ font-size: var(--text-sm);
883
+ color: var(--text-muted);
884
+ }
885
+
886
+ .bead-ts-value[title] {
887
+ border-bottom: 1px dotted var(--text-faint);
888
+ cursor: help;
889
+ }
890
+
891
+ /* ─── Standalone Commands Section ─────────────── */
892
+ .ongoing {
893
+ margin-top: var(--sp-10);
894
+ }
895
+
896
+ .ongoing h2 {
897
+ letter-spacing: var(--ls-wide);
898
+ text-transform: uppercase;
899
+ font-size: var(--text-sm);
900
+ color: var(--text-muted);
901
+ margin-bottom: var(--sp-4);
902
+ border-bottom: 2px solid var(--border);
903
+ padding-bottom: var(--sp-2);
904
+ }
905
+
906
+ /* ─── Footer ──────────────────────────────────── */
907
+ .footer {
908
+ text-align: center;
909
+ font-size: var(--text-xs);
910
+ color: var(--text-faint);
911
+ margin-top: var(--sp-10);
912
+ padding-top: var(--sp-4);
913
+ border-top: 1px solid var(--border-light);
914
+ letter-spacing: var(--ls-wide);
915
+ }
916
+
917
+ /* ─── Utilities ───────────────────────────────── */
918
+ .hidden {
919
+ display: none;
920
+ }
921
+
922
+ /* Build-observability severity + verdict tokens (Plan 4) */
923
+ :root {
924
+ --sev-p0: #dc2626; /* red 600 */
925
+ --sev-p1: #ea580c; /* orange 600 */
926
+ --sev-p2: #ca8a04; /* yellow 600 */
927
+ --sev-p3: #2563eb; /* blue 600 */
928
+ --sev-pass: #16a34a; /* green 600 */
929
+ }
930
+ [data-theme="dark"] {
931
+ --sev-p0: #f87171;
932
+ --sev-p1: #fb923c;
933
+ --sev-p2: #facc15;
934
+ --sev-p3: #60a5fa;
935
+ --sev-pass: #4ade80;
936
+ }
937
+
938
+ /* Build-observability panel layout */
939
+ .panel {
940
+ background: var(--bg-card);
941
+ border: 1px solid var(--border);
942
+ border-radius: var(--radius);
943
+ padding: var(--sp-4) var(--sp-6);
944
+ margin-bottom: var(--sp-6);
945
+ }
946
+ .panel > header {
947
+ display: flex;
948
+ align-items: center;
949
+ gap: var(--sp-3);
950
+ margin-bottom: var(--sp-4);
951
+ flex-wrap: wrap;
952
+ }
953
+ .panel > header h2 {
954
+ margin: 0;
955
+ font-size: var(--text-base);
956
+ font-weight: var(--fw-semi);
957
+ }
958
+ .panel .meta {
959
+ color: var(--text-muted);
960
+ font-size: var(--text-sm);
961
+ }
962
+ .grid { display: grid; gap: var(--sp-4); }
963
+ .grid-2 { grid-template-columns: repeat(2, 1fr); }
964
+ @media (max-width: 640px) { .grid-2 { grid-template-columns: 1fr; } }
965
+
966
+ /* Finding filters */
967
+ .finding-filters {
968
+ display: flex;
969
+ gap: var(--sp-2);
970
+ flex-wrap: wrap;
971
+ margin-bottom: var(--sp-4);
972
+ }
973
+ .finding-filters button {
974
+ padding: var(--sp-1) var(--sp-3);
975
+ border: 1px solid var(--border);
976
+ border-radius: var(--radius-sm);
977
+ background: var(--bg-inset);
978
+ color: var(--text);
979
+ font-size: var(--text-sm);
980
+ cursor: pointer;
981
+ }
982
+ .finding-filters button:hover,
983
+ .finding-filters button.active {
984
+ background: var(--accent);
985
+ border-color: var(--accent);
986
+ color: #fff;
987
+ }
988
+
989
+ /* Findings list */
990
+ .findings {
991
+ list-style: none;
992
+ padding: 0;
993
+ margin: 0;
994
+ display: flex;
995
+ flex-direction: column;
996
+ gap: var(--sp-3);
997
+ }
998
+ .finding {
999
+ background: var(--bg-inset);
1000
+ border: 1px solid var(--border-light);
1001
+ border-radius: var(--radius-sm);
1002
+ padding: var(--sp-3) var(--sp-4);
1003
+ }
1004
+ .finding header {
1005
+ display: flex;
1006
+ align-items: center;
1007
+ gap: var(--sp-2);
1008
+ flex-wrap: wrap;
1009
+ margin-bottom: var(--sp-2);
1010
+ }
1011
+ .finding-id {
1012
+ font-family: var(--font-mono, monospace);
1013
+ font-size: var(--text-xs);
1014
+ color: var(--text-muted);
1015
+ background: var(--bg-card);
1016
+ border: 1px solid var(--border);
1017
+ border-radius: 4px;
1018
+ padding: 1px var(--sp-1);
1019
+ }
1020
+ .finding .lens {
1021
+ font-size: var(--text-xs);
1022
+ color: var(--text-muted);
1023
+ }
1024
+ .finding .title {
1025
+ font-size: var(--text-sm);
1026
+ font-weight: var(--fw-semi);
1027
+ flex: 1;
1028
+ }
1029
+ .finding p { margin: 0; font-size: var(--text-sm); color: var(--text-muted); }
1030
+ .empty { color: var(--text-muted); font-size: var(--text-sm); text-align: center; padding: var(--sp-4); }
1031
+
1032
+ /* ── Mermaid diagrams ─────────────────────────────────────────────────────────
1033
+ The build renders mermaid to inline SVG via mmdc, then sanitizeSvg() +
1034
+ rehype-sanitize strip the SVG's own <script>, <foreignObject>, AND <style>
1035
+ for security. Stripping <style> means the diagram arrives unstyled (nodes
1036
+ default to a black fill). These theme-token rules restyle the SVG so nodes,
1037
+ edges, arrowheads, and labels render correctly — and follow light/dark mode.
1038
+ Authors must render with htmlLabels:false (the generator forces this) so node
1039
+ labels are native <text>/<tspan> rather than stripped <foreignObject> HTML. */
1040
+ figure.mermaid { margin: var(--sp-5) 0; text-align: center; }
1041
+ figure.mermaid svg { max-width: 100%; height: auto; }
1042
+ /* Node shapes */
1043
+ figure.mermaid svg .node rect,
1044
+ figure.mermaid svg .node circle,
1045
+ figure.mermaid svg .node ellipse,
1046
+ figure.mermaid svg .node polygon,
1047
+ figure.mermaid svg .node path {
1048
+ fill: var(--bg-inset);
1049
+ stroke: var(--border);
1050
+ stroke-width: 1px;
1051
+ }
1052
+ /* Background helper rects mermaid emits behind labels */
1053
+ figure.mermaid svg .node .label-container { fill: var(--bg-inset); stroke: var(--border); }
1054
+ figure.mermaid svg rect.background { fill: none; stroke: none; }
1055
+ /* Labels (rendered as <text>/<tspan> when htmlLabels:false) */
1056
+ figure.mermaid svg .nodeLabel,
1057
+ figure.mermaid svg .node text,
1058
+ figure.mermaid svg text.nodeLabel,
1059
+ figure.mermaid svg .label text,
1060
+ figure.mermaid svg span.nodeLabel {
1061
+ fill: var(--text);
1062
+ color: var(--text);
1063
+ font-family: var(--font-sans);
1064
+ }
1065
+ /* Edges: thin strokes, not filled blobs */
1066
+ figure.mermaid svg .edgePath path,
1067
+ figure.mermaid svg path.flowchart-link,
1068
+ figure.mermaid svg .flowchart-link {
1069
+ fill: none;
1070
+ stroke: var(--text-faint);
1071
+ stroke-width: 1.5px;
1072
+ }
1073
+ /* Arrowheads */
1074
+ figure.mermaid svg marker path,
1075
+ figure.mermaid svg .marker {
1076
+ fill: var(--text-faint);
1077
+ stroke: var(--text-faint);
1078
+ }
1079
+ figure.mermaid svg .edgeLabel,
1080
+ figure.mermaid svg .edgeLabel text { fill: var(--text-muted); color: var(--text-muted); }
1081
+ </style>
1082
+ <script>(function(){try{var t=localStorage.getItem('guide-theme');if(!t&&window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches)t='dark';if(t)document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
1083
+ </head>
1084
+ <body>
1085
+ <header class="topbar">
1086
+ <button data-action="nav" class="nav-toggle" aria-label="Toggle navigation">☰</button>
1087
+ <h1>Build Observability</h1>
1088
+ <button data-action="theme" class="theme-toggle" aria-label="Toggle theme">◐</button>
1089
+ </header>
1090
+ <div class="layout">
1091
+ <aside class="rail"><nav class="toc" aria-label="Table of contents"><ul><li class="toc-2"><a href="#why-build-observability-exists">Why Build Observability exists</a></li><li class="toc-3"><a href="#what-goes-wrong-without-it">What goes wrong without it</a></li><li class="toc-3"><a href="#what-good-build-observability-produces">What good build observability produces</a></li><li class="toc-2"><a href="#system-map">System map</a></li><li class="toc-2"><a href="#the-ledger">The ledger</a></li><li class="toc-3"><a href="#how-a-write-happens">How a write happens</a></li><li class="toc-3"><a href="#the-nine-event-types">The nine event types</a></li><li class="toc-3"><a href="#building-a-scaffold-observe-event-command">Building a scaffold observe event command</a></li><li class="toc-3"><a href="#redaction-write-time-and-render-time">Redaction — write time and render time</a></li><li class="toc-3"><a href="#worktree-identity">Worktree identity</a></li><li class="toc-2"><a href="#adapters">Adapters</a></li><li class="toc-3"><a href="#source-priority">Source priority</a></li><li class="toc-2"><a href="#the-nine-lens-audit">The nine-lens audit</a></li><li class="toc-3"><a href="#scope-order-and-skips">Scope, order and skips</a></li><li class="toc-3"><a href="#what-feeds-the-doc-graph">What feeds the doc-graph</a></li><li class="toc-3"><a href="#all-nine-at-a-glance">All nine at a glance</a></li><li class="toc-3"><a href="#lens-by-lens">Lens by lens</a></li><li class="toc-2"><a href="#the-observe-audit-cli">The observe audit CLI</a></li><li class="toc-3"><a href="#every-flag">Every flag</a></li><li class="toc-3"><a href="#verdict-taxonomy">Verdict taxonomy</a></li><li class="toc-3"><a href="#companion-verb-scaffold-observe-ack">Companion verb — scaffold observe ack</a></li><li class="toc-3"><a href="#exit-codes-across-observe-verbs">Exit codes across observe verbs</a></li><li class="toc-2"><a href="#progress-replay-stall">Progress, replay &amp; stall</a></li><li class="toc-3"><a href="#fusion-how-the-timeline-is-built">Fusion — how the timeline is built</a></li><li class="toc-3"><a href="#stall-detection-the-six-signals">Stall detection — the six signals</a></li><li class="toc-3"><a href="#flags">Flags</a></li><li class="toc-2"><a href="#phase-boundary-triggers">Phase-boundary triggers</a></li><li class="toc-3"><a href="#the-hook">The hook</a></li><li class="toc-3"><a href="#config-timestamps">Config &amp; timestamps</a></li><li class="toc-2"><a href="#mmr-doc-conformance-channel">MMR doc-conformance channel</a></li><li class="toc-3"><a href="#finding-translation">Finding translation</a></li><li class="toc-3"><a href="#enabling-it">Enabling it</a></li><li class="toc-2"><a href="#the---fix-flow">The --fix flow</a></li><li class="toc-3"><a href="#the-five-phases">The five phases</a></li><li class="toc-3"><a href="#abort-safety">Abort safety</a></li><li class="toc-3"><a href="#config-the-fix-block">Config — the fix: block</a></li><li class="toc-2"><a href="#harvest-recover-teardown">Harvest, recover &amp; teardown</a></li><li class="toc-3"><a href="#commands">Commands</a></li><li class="toc-2"><a href="#renderers">Renderers</a></li><li class="toc-2"><a href="#config-reference">Config reference</a></li><li class="toc-2"><a href="#where-it-lives">Where it lives</a></li><li class="toc-2"><a href="#shipping-history">Shipping history</a></li><li class="toc-2"><a href="#known-divergences">Known divergences</a></li></ul></nav></aside>
1092
+ <main class="content"><h2 id="why-build-observability-exists">Why Build Observability exists</h2>
1093
+ <p>When several agents (and humans) build a project in parallel worktrees, the
1094
+ <em>reasoning</em> behind the build evaporates. A decision gets made in a throwaway
1095
+ branch, a blocker is hit and worked around, a story is silently dropped — and
1096
+ none of it survives the squash-merge. The docs say one thing; the code drifts to
1097
+ another. Build Observability turns those ephemeral moments into a durable,
1098
+ queryable record, then audits the record against the planning docs.</p>
1099
+ <h3 id="what-goes-wrong-without-it">What goes wrong without it</h3>
1100
+ <ul>
1101
+ <li><strong>Decisions die at teardown.</strong> An agent records "we switched the cache to
1102
+ Redis" in its head, opens a PR, the worktree is removed — and the rationale is
1103
+ gone. The next agent re-litigates it.</li>
1104
+ <li><strong>Blockers go unaddressed.</strong> A dependency block is hit at hour 0 and nobody
1105
+ notices it is still open at hour 6.</li>
1106
+ <li><strong>Phase boundaries pass unaudited.</strong> <code>tech-stack.md</code> is marked complete while
1107
+ it still contradicts the PRD; the contradiction is found three phases later.</li>
1108
+ <li><strong>Doc/code drift accumulates.</strong> A component is used outside its declared
1109
+ layer; an acceptance criterion never gets a test; a coding-standard erodes
1110
+ file by file.</li>
1111
+ </ul>
1112
+ <p>The classic failure mode is a worktree teardown that takes the ledger with it:
1113
+ an agent worktree's <code>.scaffold/activity.jsonl</code> is lost the moment the worktree
1114
+ is removed, <em>unless</em> it is harvested into the primary archive first. The
1115
+ harvester (<code>src/observability/engine/harvester.ts</code>) and
1116
+ <code>scripts/teardown-agent-worktree.sh</code> exist precisely to close this gap — see
1117
+ <a href="#harvest-recover--teardown">Harvest, recover &#x26; teardown</a>.</p>
1118
+ <h3 id="what-good-build-observability-produces">What good build observability produces</h3>
1119
+ <ul>
1120
+ <li><strong>Decisions captured</strong> the moment they're made (<code>decision_recorded</code>),
1121
+ surviving teardown via harvest.</li>
1122
+ <li><strong>Blockers surfaced</strong> when they linger past a threshold (stall detection).</li>
1123
+ <li><strong>Phase-boundary audits</strong> that run automatically when a planning doc is
1124
+ completed, non-gating so they never block progress.</li>
1125
+ <li><strong>Doc/code drift caught</strong> by the nine-lens audit and routed into code review
1126
+ via the MMR <code>doc-conformance</code> channel.</li>
1127
+ </ul>
1128
+ <p>The system ships <strong>9 durable event types</strong>, <strong>8 adapters</strong> fusing external
1129
+ signals, a <strong>9-lens audit (A–I)</strong>, and <strong>5 active stall signals</strong> (a 6th reserved) on the "Needs
1130
+ Attention" surface.</p>
1131
+ <div class="callout callout-note"><p><strong>Two subsystems, one config file.</strong> Build Observability and the separate
1132
+ <em>knowledge-freshness</em> system both read <code>.scaffold/observability.yaml</code>. This guide
1133
+ documents Build Observability; the shared config keys are reconciled in
1134
+ <a href="#config-reference">Config reference</a>.</p></div>
1135
+ <h2 id="system-map">System map</h2>
1136
+ <p>One write path, one fusion point, two read paths. Agents write events to the
1137
+ append-only ledger; adapters synthesize events from git, GitHub, MMR and
1138
+ pipeline state; the synthesizer fuses them with correlation-id dedup; the engine
1139
+ API drives the audit lenses and the progress timeline; renderers emit terminal,
1140
+ markdown, dashboard and MMR-findings output.</p>
1141
+ <figure class="mermaid"><svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 2427.98px; background-color: transparent;" viewBox="0 0 2427.984375 825.7999877929688" role="graphics-document document">#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#666;stroke:#666;}#my-svg .marker.cross{stroke:#666;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg p{margin:0;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span{color:#333;}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#000000;color:#000000;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#eee;stroke:#999;stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#my-svg .arrowheadPath{fill:#333333;}#my-svg .edgePath .path{stroke:#666;stroke-width:1px;}#my-svg .flowchart-link{stroke:#666;fill:none;}#my-svg .edgeLabel{background-color:white;text-align:center;}#my-svg .edgeLabel p{background-color:white;}#my-svg .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#my-svg .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#my-svg .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:white;text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:white;padding:2px;}#my-svg .icon-shape .label rect,#my-svg .image-shape .label rect{opacity:0.5;background-color:white;fill:white;}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg .node .neo-node{stroke:#999;}#my-svg [data-look="neo"].node rect,#my-svg [data-look="neo"].cluster rect,#my-svg [data-look="neo"].node polygon{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node path{stroke:url(#my-svg-gradient);stroke-width:1px;}#my-svg [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node .neo-line path{stroke:#999;filter:none;}#my-svg [data-look="neo"].node circle{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node circle .state-start{fill:#000000;}#my-svg [data-look="neo"].icon-shape .icon{fill:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].icon-shape .icon-neo path{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}<g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></polygon></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"></path></marker><marker id="my-svg_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"></path></marker><g class="root"><g class="clusters"><g class="cluster" id="my-svg-render"><rect style="" x="400.88671875" y="701.1999969482422" width="949.5" height="116.5999984741211"></rect><g class="cluster-label" transform="translate(851.86328125, 701.1999969482422)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">render</tspan></tspan></text></g></g></g><g class="cluster" id="my-svg-adapters"><rect style="" x="566.125" y="149.5999984741211" width="1853.859375" height="116.5999984741211"></rect><g class="cluster-label" transform="translate(1461.8515625, 149.5999984741211)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">adapters</tspan></tspan></text></g></g></g><g class="cluster" id="my-svg-write"><rect style="" x="8" y="8" width="538.125" height="258.1999969482422"></rect><g class="cluster-label" transform="translate(258.1796875, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">write</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M169.445,99.6L169.445,103.767C169.445,107.933,169.445,116.267,169.445,124.6C169.445,132.933,169.445,141.267,186.561,149.956C203.677,158.645,237.909,167.691,255.025,172.213L272.141,176.736" id="my-svg-L_LW_LF_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M418.023,99.6L418.023,103.767C418.023,107.933,418.023,116.267,418.023,124.6C418.023,132.933,418.023,141.267,416.314,148.999C414.605,156.731,411.187,163.862,409.478,167.427L407.769,170.993" id="my-svg-L_HV_LF_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M390.078,241.2L390.078,245.367C390.078,249.533,390.078,257.867,390.078,266.2C390.078,274.533,390.078,282.867,472.673,297.313C555.268,311.759,720.458,332.317,803.053,342.597L885.648,352.876" id="my-svg-L_LF_SY_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M681.617,232.4L681.617,238.033C681.617,243.667,681.617,254.933,681.617,264.733C681.617,274.533,681.617,282.867,715.635,295.145C749.654,307.423,817.69,323.645,851.708,331.756L885.726,339.868" id="my-svg-L_A1_SY_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M891.523,232.4L891.523,238.033C891.523,243.667,891.523,254.933,891.523,264.733C891.523,274.533,891.523,282.867,896.929,290.818C902.334,298.769,913.145,306.337,918.551,310.122L923.956,313.906" id="my-svg-L_A2_SY_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1108.352,232.4L1108.352,238.033C1108.352,243.667,1108.352,254.933,1108.352,264.733C1108.352,274.533,1108.352,282.867,1102.946,290.818C1097.541,298.769,1086.73,306.337,1081.324,310.122L1075.919,313.906" id="my-svg-L_A3_SY_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1334.938,232.4L1334.938,238.033C1334.938,243.667,1334.938,254.933,1334.938,264.733C1334.938,274.533,1334.938,282.867,1298.141,295.37C1261.345,307.874,1187.752,324.547,1150.955,332.884L1114.159,341.221" id="my-svg-L_A4_SY_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1562.32,232.4L1562.32,238.033C1562.32,243.667,1562.32,254.933,1562.32,264.733C1562.32,274.533,1562.32,282.867,1487.637,297.113C1412.954,311.359,1263.588,331.517,1188.905,341.597L1114.222,351.676" id="my-svg-L_A5_SY_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M999.938,418L999.938,422.167C999.938,426.333,999.938,434.667,999.938,442.333C999.938,450,999.938,457,999.938,460.5L999.938,464" id="my-svg-L_SY_API_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1109.859,519.139L1151.412,525.882C1192.965,532.626,1276.07,546.113,1317.623,556.356C1359.176,566.6,1359.176,573.6,1359.176,577.1L1359.176,580.6" id="my-svg-L_API_LENS_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1437.91,651.2L1447.761,655.367C1457.613,659.533,1477.316,667.867,1487.168,676.2C1497.02,684.533,1497.02,692.867,1497.02,700.533C1497.02,708.2,1497.02,715.2,1497.02,718.7L1497.02,722.2" id="my-svg-L_LENS_FIX_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M890.016,514.642L828.281,522.135C766.546,529.628,643.076,544.614,581.34,561.824C519.605,579.033,519.605,598.467,519.605,617.9C519.605,637.333,519.605,656.767,519.605,670.65C519.605,684.533,519.605,692.867,519.605,702C519.605,711.133,519.605,721.067,519.605,726.033L519.605,731" id="my-svg-L_API_R1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M890.016,527.466L867.516,532.821C845.017,538.177,800.018,548.889,777.519,563.961C755.02,579.033,755.02,598.467,755.02,617.9C755.02,637.333,755.02,656.767,755.02,670.65C755.02,684.533,755.02,692.867,755.02,702C755.02,711.133,755.02,721.067,755.02,726.033L755.02,731" id="my-svg-L_API_R2_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1004.969,534.6L1005.598,538.767C1006.228,542.933,1007.487,551.267,1008.117,565.15C1008.746,579.033,1008.746,598.467,1008.746,617.9C1008.746,637.333,1008.746,656.767,1008.746,670.65C1008.746,684.533,1008.746,692.867,1008.746,702C1008.746,711.133,1008.746,721.067,1008.746,726.033L1008.746,731" id="my-svg-L_API_R3_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1290.009,651.2L1281.355,655.367C1272.7,659.533,1255.391,667.867,1246.737,676.2C1238.082,684.533,1238.082,692.867,1238.082,702C1238.082,711.133,1238.082,721.067,1238.082,726.033L1238.082,731" id="my-svg-L_LENS_R4_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g></g><g class="nodes"><g class="node default" id="my-svg-flowchart-LW-0" transform="translate(169.4453125, 66.29999923706055)"><rect class="basic label-container" style="" x="-126.4453125" y="-33.29999923706055" width="252.890625" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">ledger</tspan><tspan class="text-inner-tspan"> writer</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">ledger-writer.ts</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> 4</tspan><tspan class="text-inner-tspan"> KiB</tspan><tspan class="text-inner-tspan"> cap</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-HV-1" transform="translate(418.0234375, 66.29999923706055)"><rect class="basic label-container" style="" x="-72.1328125" y="-33.29999923706055" width="144.265625" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">harvester</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">harvester.ts</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-LF-2" transform="translate(390.078125, 207.89999771118164)"><rect class="basic label-container" style="" x="-114.0703125" y="-33.29999923706055" width="228.140625" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">.scaffold/activity.jsonl</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">append-only</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> redacted</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A1-3" transform="translate(681.6171875, 207.89999771118164)"><rect class="basic label-container" style="" x="-80.4921875" y="-24.5" width="160.984375" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">git</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> priority</tspan><tspan class="text-inner-tspan"> 3</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A2-4" transform="translate(891.5234375, 207.89999771118164)"><rect class="basic label-container" style="" x="-79.4140625" y="-24.5" width="158.828125" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">gh</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> priority</tspan><tspan class="text-inner-tspan"> 2</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A3-5" transform="translate(1108.3515625, 207.89999771118164)"><rect class="basic label-container" style="" x="-87.4140625" y="-24.5" width="174.828125" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">mmr</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> priority</tspan><tspan class="text-inner-tspan"> 1</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A4-6" transform="translate(1334.9375, 207.89999771118164)"><rect class="basic label-container" style="" x="-89.171875" y="-24.5" width="178.34375" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">state</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> priority</tspan><tspan class="text-inner-tspan"> 4</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A5-7" transform="translate(1562.3203125, 207.89999771118164)"><rect class="basic label-container" style="" x="-88.2109375" y="-24.5" width="176.421875" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">tests</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> priority</tspan><tspan class="text-inner-tspan"> 5</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A6-8" transform="translate(1808.0546875, 207.89999771118164)"><rect class="basic label-container" style="" x="-107.5234375" y="-24.5" width="215.046875" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">pipeline_docs</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> probe</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A7-9" transform="translate(2044.7421875, 207.89999771118164)"><rect class="basic label-container" style="" x="-79.1640625" y="-24.5" width="158.328125" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">beads</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> probe</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-A8-10" transform="translate(2279.4453125, 207.89999771118164)"><rect class="basic label-container" style="" x="-105.5390625" y="-24.5" width="211.078125" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">audit_history</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> probe</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-SY-11" transform="translate(999.9375, 367.0999984741211)"><rect class="basic label-container" style="" x="-110.3203125" y="-50.900001525878906" width="220.640625" height="101.80000305175781"></rect><g class="label" style="" transform="translate(0, -35.900001525878906)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">synthesizer</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">composeReplay</tspan><tspan class="text-inner-tspan"> ·</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">correlation_id</tspan><tspan class="text-inner-tspan"> dedup</tspan><tspan class="text-inner-tspan"> ·</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="3.2em"><tspan class="text-inner-tspan">source</tspan><tspan class="text-inner-tspan"> priority</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-API-12" transform="translate(999.9375, 501.29999923706055)"><rect class="basic label-container" style="" x="-109.921875" y="-33.29999923706055" width="219.84375" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">engine</tspan><tspan class="text-inner-tspan"> API</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">runAudit</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> runProgress</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-LENS-13" transform="translate(1359.17578125, 617.8999977111816)"><rect class="basic label-container" style="" x="-98.4140625" y="-33.29999923706055" width="196.828125" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">checks</tspan><tspan class="text-inner-tspan"> —</tspan><tspan class="text-inner-tspan"> lenses</tspan><tspan class="text-inner-tspan"> A–I</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">registry</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> runner</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-FIX-14" transform="translate(1497.01953125, 759.4999961853027)"><rect class="basic label-container" style="" x="-111.6328125" y="-33.29999923706055" width="223.265625" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">--fix</tspan><tspan class="text-inner-tspan"> loop</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">plan</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> dispatch</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> verify</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-R1-15" transform="translate(519.60546875, 759.4999961853027)"><rect class="basic label-container" style="" x="-83.71875" y="-24.5" width="167.4375" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">terminal</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> ANSI</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-R2-16" transform="translate(755.01953125, 759.4999961853027)"><rect class="basic label-container" style="" x="-101.6953125" y="-24.5" width="203.390625" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">markdown</tspan><tspan class="text-inner-tspan"> +</tspan><tspan class="text-inner-tspan"> sidecar</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-R3-17" transform="translate(1008.74609375, 759.4999961853027)"><rect class="basic label-container" style="" x="-102.03125" y="-24.5" width="204.0625" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">dashboard</tspan><tspan class="text-inner-tspan"> fragment</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-R4-18" transform="translate(1238.08203125, 759.4999961853027)"><rect class="basic label-container" style="" x="-77.3046875" y="-24.5" width="154.609375" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">mmr-findings</tspan></tspan></text></g></g></g></g></g></g><defs></defs><defs></defs></svg></figure>
1142
+ <p>The boxes labelled <em>probe</em> (<code>pipeline_docs</code>, <code>beads</code>, <code>audit_history</code>)
1143
+ contribute availability checks — and, for <code>audit_history</code>, trend data — but do
1144
+ not emit events into the replay timeline. The five remaining adapters (<code>git</code>,
1145
+ <code>gh</code>, <code>mmr</code>, <code>state</code>, <code>tests</code>) synthesize replay events that flow into the
1146
+ synthesizer.</p>
1147
+ <h2 id="the-ledger">The ledger</h2>
1148
+ <p>Every durable observation is one JSON object on one line of
1149
+ <code>.scaffold/activity.jsonl</code>. Writes are append-only and lock-guarded so parallel
1150
+ worktrees never corrupt the file; each event is capped at <strong>4 KiB</strong>; and secrets
1151
+ and home-directory paths are scrubbed both when the event is written and again
1152
+ when output is rendered.</p>
1153
+ <h3 id="how-a-write-happens">How a write happens</h3>
1154
+ <ul>
1155
+ <li><strong>Validate</strong> against the per-type payload allow-list
1156
+ (<code>src/observability/engine/event-schemas.ts</code>). Unknown payload keys are dropped
1157
+ (reported in <code>dropped_fields</code>); a bad <code>ts</code> or missing required field rejects
1158
+ the event.</li>
1159
+ <li><strong>Redact</strong> the payload (<code>src/observability/engine/redact.ts</code>) — see below.</li>
1160
+ <li><strong>Size-check</strong>: <code>Buffer.byteLength(line, 'utf8')</code> must be ≤ 4096, else the
1161
+ write throws (<span class="fp" data-path="src/observability/engine/ledger-writer.ts:10">src/observability/engine/ledger-writer.ts:10</span>,
1162
+ <span class="fp" data-path="src/observability/engine/ledger-writer.ts:58">src/observability/engine/ledger-writer.ts:58</span>).</li>
1163
+ <li><strong>Append</strong> under a <code>proper-lockfile</code> lock (10 retries, exponential backoff) so
1164
+ concurrent agents serialize cleanly.</li>
1165
+ <li><strong>Link (fail-soft)</strong>: a <code>task_claimed</code> event with a <code>task_id</code> also links the
1166
+ task into Beads via <code>bd</code> (<code>beadsAdapter.claimWithEvent</code>); any Beads error is
1167
+ swallowed — the ledger write already succeeded.</li>
1168
+ </ul>
1169
+ <h3 id="the-nine-event-types">The nine event types</h3>
1170
+ <p>Each event carries a common envelope — <code>event_id</code> (ULID), <code>worktree_id</code>,
1171
+ <code>actor_label</code>, <code>branch</code>, <code>task_id</code> (string <em>or null</em>), <code>type</code>, <code>ts</code> (ISO-8601
1172
+ UTC) — plus a type-specific <code>payload</code>. The CLI verb is
1173
+ <code>scaffold observe event &#x3C;type> …</code>.</p>
1174
+ <div class="filter-table"><input type="text" class="filter-input" placeholder="Filter…" aria-label="Filter table rows" disabled>
1175
+
1176
+
1177
+
1178
+
1179
+
1180
+
1181
+
1182
+
1183
+
1184
+
1185
+
1186
+
1187
+
1188
+
1189
+
1190
+
1191
+
1192
+
1193
+
1194
+
1195
+
1196
+
1197
+
1198
+
1199
+
1200
+
1201
+
1202
+
1203
+
1204
+
1205
+
1206
+
1207
+
1208
+
1209
+
1210
+
1211
+
1212
+
1213
+
1214
+
1215
+
1216
+
1217
+
1218
+
1219
+
1220
+
1221
+
1222
+
1223
+
1224
+
1225
+
1226
+
1227
+
1228
+
1229
+ <table><thead><tr><th>event_type</th><th>payload fields</th><th>key constraints</th></tr></thead><tbody><tr><td><code>task_claimed</code></td><td><code>task_title</code>*, <code>story_id</code>, <code>wave</code>, <code>unplanned</code></td><td>if <code>task_id</code> is null, <code>unplanned</code> must be true</td></tr><tr><td><code>task_completed</code></td><td><code>outcome</code>*, <code>pr_number</code>, <code>commit_sha</code></td><td>outcome ∈ {pr_submitted, dropped, superseded}; <code>pr_number</code> required if pr_submitted</td></tr><tr><td><code>decision_recorded</code></td><td><code>key</code><em>, <code>summary</code></em>, <code>affects</code>* (string[]), <code>links</code></td><td>summary ≤ 500 chars; consumed by Lens G</td></tr><tr><td><code>blocker_hit</code></td><td><code>kind</code><em>, <code>summary</code></em></td><td>kind ∈ {dependency, ambiguity, external, environment}; summary ≤ 500 chars</td></tr><tr><td><code>blocker_resolved</code></td><td><code>summary</code><em>, <code>references</code></em> (string[])</td><td>closes a prior blocker_hit</td></tr><tr><td><code>pr_opened</code></td><td><code>pr_number</code>*</td><td>positive integer</td></tr><tr><td><code>progress_heartbeat</code></td><td><code>note</code>*</td><td>note ≤ 200 chars; resets task_stale clock</td></tr><tr><td><code>finding_acknowledged</code></td><td><code>finding_id</code><em>, <code>status</code></em>, <code>note</code></td><td>task_id must be null; status ∈ {acknowledged, open}; written by <code>observe ack</code></td></tr><tr><td><code>knowledge_gap_signal</code></td><td><code>topic</code><em>, <code>source</code></em>, <code>project_id</code>*, <code>step_name</code>, <code>agent_excerpt</code></td><td>topic kebab-case ≤ 80 chars; consumed by Lens I</td></tr></tbody></table></div>
1230
+ <p><code>*</code> marks required fields. Allow-lists live in <code>EVENT_PAYLOAD_KEYS</code> at
1231
+ <span class="fp" data-path="src/observability/engine/event-schemas.ts:3-13">src/observability/engine/event-schemas.ts:3-13</span>.</p>
1232
+ <div class="callout callout-warning"><p><strong>Naming note.</strong> The PR-opened event is <code>pr_opened</code> (past tense) in code, not
1233
+ <code>pr_open</code> as the CLAUDE.md prose abbreviates it. The ledger never emits
1234
+ <code>pr_open</code>.</p></div>
1235
+ <h3 id="building-a-scaffold-observe-event-command">Building a <code>scaffold observe event</code> command</h3>
1236
+ <p><code>--branch</code> is required; <code>--task-id</code> is optional (it correlates the event to a
1237
+ claimed task) but must be <em>omitted</em> for <code>finding_acknowledged</code> (whose <code>task_id</code>
1238
+ must be null), and a <code>task_claimed</code> without one must pass <code>--unplanned true</code>.
1239
+ Payload keys are passed as <code>--kebab-case</code> flags, snake-cased before validation;
1240
+ unknown keys are dropped.</p>
1241
+ <pre><code class="language-bash">scaffold observe event task_claimed --branch &#x3C;branch> --task-id &#x3C;id> \
1242
+ --task-title "&#x3C;title>" [--story-id &#x3C;id>] [--wave &#x3C;n>] [--unplanned true]
1243
+ scaffold observe event task_completed --branch &#x3C;branch> --task-id &#x3C;id> \
1244
+ --outcome pr_submitted --pr-number &#x3C;n> [--commit-sha &#x3C;sha>]
1245
+ scaffold observe event decision_recorded --branch &#x3C;branch> --task-id &#x3C;id> \
1246
+ --key &#x3C;key> --summary "&#x3C;text>" --affects "src/**,docs/**" [--links "ADR-1,ADR-2"]
1247
+ scaffold observe event blocker_hit --branch &#x3C;branch> --task-id &#x3C;id> \
1248
+ --kind dependency --summary "&#x3C;text>"
1249
+ scaffold observe event blocker_resolved --branch &#x3C;branch> --task-id &#x3C;id> \
1250
+ --summary "&#x3C;text>" --references "ref1,ref2"
1251
+ scaffold observe event pr_opened --branch &#x3C;branch> --task-id &#x3C;id> --pr-number &#x3C;n>
1252
+ scaffold observe event progress_heartbeat --branch &#x3C;branch> --task-id &#x3C;id> --note "&#x3C;text>"
1253
+ scaffold observe event finding_acknowledged --branch &#x3C;branch> \
1254
+ --finding-id &#x3C;id> --status acknowledged [--note "&#x3C;text>"]
1255
+ scaffold observe event knowledge_gap_signal --branch &#x3C;branch> \
1256
+ --topic &#x3C;kebab-slug> --source agent_search --project-id &#x3C;sha256> \
1257
+ [--step-name &#x3C;name>] [--agent-excerpt "&#x3C;text>"]
1258
+ </code></pre>
1259
+ <p><code>finding_acknowledged</code> is normally written by <code>scaffold observe ack</code>, not by
1260
+ hand.</p>
1261
+ <div class="callout callout-note"><p><strong>Value coercion</strong> (<code>src/cli/commands/observe.ts</code>): <code>--pr-number</code> is parsed
1262
+ numeric (a non-number is dropped); <code>--unplanned</code> is boolean (<code>true</code> ⇒ true,
1263
+ anything else ⇒ false); <code>--affects</code>, <code>--links</code>, <code>--references</code> are comma-split
1264
+ into arrays; everything else stays a string. Exit codes: <code>0</code> written · <code>2</code>
1265
+ validation failed / payload > 4 KiB · <code>3</code> other error.</p></div>
1266
+ <h3 id="redaction-write-time-and-render-time">Redaction — write time and render time</h3>
1267
+ <p>Redaction runs twice. <strong>Write-time</strong> redaction (recursive over the payload tree)
1268
+ scrubs the event before it lands on disk; <strong>render-time</strong> redaction
1269
+ (<code>redactEngineOutput</code> / <code>redactRendered</code>) scrubs string values again before any
1270
+ report is shown, catching anything synthesized after the fact.</p>
1271
+ <p>Order matters: a bare <code>ghp_…</code> matches the GitHub-token pattern, but inside a
1272
+ <code>token=…</code> pair the key/value rule wins first — so <code>token=ghp_…</code> redacts to
1273
+ <code>[REDACTED:kv-secret]</code>, not <code>[REDACTED:github-token]</code> (leftmost-match
1274
+ alternation, <span class="fp" data-path="src/observability/engine/redact.ts:16">src/observability/engine/redact.ts:16</span>).</p>
1275
+
1276
+
1277
+
1278
+
1279
+
1280
+
1281
+
1282
+
1283
+
1284
+
1285
+
1286
+
1287
+
1288
+
1289
+
1290
+
1291
+
1292
+
1293
+
1294
+
1295
+
1296
+
1297
+
1298
+
1299
+
1300
+
1301
+
1302
+
1303
+
1304
+
1305
+
1306
+
1307
+
1308
+
1309
+
1310
+ <table><thead><tr><th>pattern</th><th>matches</th><th>replacement</th></tr></thead><tbody><tr><td>AWS key</td><td><code>AKIA[0-9A-Z]{16}</code></td><td><code>[REDACTED:aws-key]</code></td></tr><tr><td>GitHub token</td><td><code>gh[pousr]_[A-Za-z0-9]{36,}</code></td><td><code>[REDACTED:github-token]</code></td></tr><tr><td>key/value secret</td><td>key matches <code>secret|token|password|api[_-]?key</code></td><td><code>[REDACTED:kv-secret]</code> (key + separator preserved)</td></tr><tr><td>sensitive object key</td><td>object key matches the same pattern</td><td>all descendant primitive values masked</td></tr><tr><td>home path</td><td><code>/Users/&#x3C;name></code>, <code>/home/&#x3C;name></code>, <code>C:\Users\&#x3C;name></code></td><td><code>~</code> (Windows keeps the drive: <code>C:\~</code>)</td></tr></tbody></table>
1311
+ <p>A redacted <code>decision_recorded</code> event on disk (one line, shown pretty-printed):</p>
1312
+ <pre><code class="language-json">{
1313
+ "event_id": "01H5ZABCDEFGHJKMNPQRSTVWX",
1314
+ "worktree_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
1315
+ "actor_label": "agent-alice",
1316
+ "branch": "alice-feat/cache",
1317
+ "task_id": "T-014",
1318
+ "type": "decision_recorded",
1319
+ "ts": "2026-05-04T12:00:00Z",
1320
+ "payload": {
1321
+ "key": "cache-backend",
1322
+ "summary": "Switched to Redis; token=[REDACTED:kv-secret]",
1323
+ "affects": ["~/repo/src/cache/**"],
1324
+ "links": ["ADR-021"]
1325
+ }
1326
+ }
1327
+ </code></pre>
1328
+ <h3 id="worktree-identity">Worktree identity</h3>
1329
+ <p>Each worktree gets a stable identity on first write — <code>.scaffold/identity.json</code>
1330
+ holds <code>worktree_id</code> (a UUID), <code>worktree_label</code> (derived from the directory name,
1331
+ or <code>primary</code>), and <code>created_at</code> (<code>src/observability/engine/identity.ts</code>). The id
1332
+ is what lets the harvester tell one worktree's events from another's, and what
1333
+ stamps every event's <code>worktree_id</code>.</p>
1334
+ <h2 id="adapters">Adapters</h2>
1335
+ <p>The ledger only holds what agents <em>chose</em> to record. Adapters fill in the rest by
1336
+ synthesizing events from the surrounding tools — commits, PRs, MMR jobs, pipeline
1337
+ state, test runs. There are eight adapters (<code>AdapterId</code> at
1338
+ <span class="fp" data-path="src/observability/engine/types.ts:69">src/observability/engine/types.ts:69</span>), but only five emit events into the
1339
+ timeline; the other three are availability probes / trend helpers.</p>
1340
+ <div class="callout callout-note"><p><strong>Five emit, three probe.</strong> <code>git</code>, <code>gh</code>, <code>mmr</code>, <code>state</code>, <code>tests</code> each implement
1341
+ <code>replayEvents()</code> and appear in the source-priority chain. <code>pipeline_docs</code>
1342
+ (probes 9 planning-doc roles; 5 of them are the canonical-required set that gates
1343
+ available vs degraded), <code>beads</code> (task-tracker linkage), and <code>audit_history</code>
1344
+ (reads <code>docs/audits/*.json</code> for trends + lens-skip streaks) do <em>not</em> emit replay
1345
+ events.</p></div>
1346
+
1347
+
1348
+
1349
+
1350
+
1351
+
1352
+
1353
+
1354
+
1355
+
1356
+
1357
+
1358
+
1359
+
1360
+
1361
+
1362
+
1363
+
1364
+
1365
+
1366
+
1367
+
1368
+
1369
+
1370
+
1371
+
1372
+
1373
+
1374
+
1375
+
1376
+
1377
+
1378
+
1379
+
1380
+
1381
+
1382
+
1383
+
1384
+
1385
+
1386
+
1387
+
1388
+
1389
+
1390
+
1391
+
1392
+
1393
+
1394
+
1395
+
1396
+
1397
+
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+
1404
+
1405
+ <table><thead><tr><th>adapter</th><th>source of truth</th><th>emits</th><th>correlation_id / dedup</th></tr></thead><tbody><tr><td><code>git</code></td><td><code>git log</code>, <code>git worktree list</code> (30 s timeout)</td><td><code>commit</code></td><td>none (<code>null</code>); per-SHA seen-set within the window</td></tr><tr><td><code>gh</code></td><td><code>gh pr list --state {open,merged,closed}</code> (needs auth)</td><td><code>pr_opened</code>, <code>pr_merged</code>, <code>pr_closed</code></td><td><code>pr:&#x3C;n>:&#x3C;state></code> — dedups against the ledger's own PR events</td></tr><tr><td><code>mmr</code></td><td><code>.mmr/jobs/*/result.json</code> (replay keyed on each job's <code>completed_at</code>)</td><td><code>job_completed</code></td><td>none</td></tr><tr><td><code>state</code></td><td><code>.scaffold/state.json</code> + <code>services/*/state.json</code></td><td><code>step_completed</code>, <code>step_in_progress</code></td><td>none; per-step timestamp, mtime fallback</td></tr><tr><td><code>tests</code></td><td><code>.scaffold/last-test-run.json</code></td><td><code>test_run_completed</code>, <code>test_run_failed</code></td><td>none</td></tr><tr><td><code>pipeline_docs</code></td><td>candidate <code>docs/*.md</code> artifact paths</td><td>— (probe: available / degraded / unavailable)</td><td>n/a</td></tr><tr><td><code>beads</code></td><td><code>.beads/</code> + <code>bd</code> CLI (≥ v1.0.0)</td><td>— (task claim ↔ event-id linkage)</td><td>n/a</td></tr><tr><td><code>audit_history</code></td><td><code>docs/audits/*.json</code> (≤ 100 scanned)</td><td>— (trends + <code>lensSkippedStreaks</code>)</td><td>n/a</td></tr></tbody></table>
1406
+ <h3 id="source-priority">Source priority</h3>
1407
+ <p>When two events describe the same thing (same <code>correlation_id</code>), the synthesizer
1408
+ keeps the one from the most authoritative source. The order is fixed in
1409
+ <code>SOURCE_PRIORITY</code> at <span class="fp" data-path="src/observability/engine/synthesizer.ts:254">src/observability/engine/synthesizer.ts:254</span> — lower
1410
+ number wins, ties broken by earliest <code>ts</code>:</p>
1411
+
1412
+
1413
+
1414
+
1415
+
1416
+
1417
+
1418
+
1419
+
1420
+
1421
+
1422
+
1423
+
1424
+
1425
+
1426
+
1427
+
1428
+
1429
+
1430
+
1431
+
1432
+
1433
+
1434
+
1435
+
1436
+
1437
+
1438
+
1439
+
1440
+
1441
+
1442
+
1443
+
1444
+
1445
+
1446
+
1447
+
1448
+
1449
+
1450
+
1451
+ <table><thead><tr><th>rank</th><th>source</th><th>why it wins</th></tr></thead><tbody><tr><td><code>0</code></td><td><code>ledger</code></td><td>the agent said so — first-party intent</td></tr><tr><td><code>1</code></td><td><code>mmr</code></td><td>authoritative build verdict</td></tr><tr><td><code>2</code></td><td><code>gh</code></td><td>GitHub PR state of record</td></tr><tr><td><code>3</code></td><td><code>git</code></td><td>local commit history</td></tr><tr><td><code>4</code></td><td><code>state</code></td><td>pipeline step status</td></tr><tr><td><code>5</code></td><td><code>tests</code></td><td>cached test run</td></tr></tbody></table>
1452
+ <p>The three probe-only adapters are intentionally absent from this table — they
1453
+ never produce a replay event to dedup.</p>
1454
+ <h2 id="the-nine-lens-audit">The nine-lens audit</h2>
1455
+ <p>The audit reads the planning docs (PRD, stories, tech-stack, standards, design
1456
+ system, plan, playbook) into a document graph, then runs a set of independent
1457
+ <em>lenses</em> over the graph, the code, the ledger and adapter data. The design spec
1458
+ calls it the "eight-lens audit" (A–H); a ninth lens — <code>I-knowledge-gaps</code> —
1459
+ joined the suite after v3.26.0.</p>
1460
+ <div class="callout callout-note"><p><strong>8 in the spec, 9 in the code.</strong>
1461
+ <code>docs/superpowers/specs/2026-04-30-build-observability-design.md</code> titles §3 "The
1462
+ Eight Audit Lenses" and its <code>LENS_REGISTRY</code> stops at H. The shipped
1463
+ <span class="fp" data-path="src/observability/engine/checks/registry.ts:44">src/observability/engine/checks/registry.ts:44</span> adds <code>I-knowledge-gaps</code>.
1464
+ Treat Lens I as the ninth lens that joined the suite — documented here as a
1465
+ first-class member.</p></div>
1466
+ <h3 id="scope-order-and-skips">Scope, order and skips</h3>
1467
+ <ul>
1468
+ <li><strong>Scope → lenses</strong> (<span class="fp" data-path="src/observability/engine/api.ts:72">src/observability/engine/api.ts:72</span>): <code>--scope code</code>
1469
+ runs <strong>A–G</strong>; <code>--scope docs</code> runs <strong>H, I</strong>; <code>--scope all</code> runs all nine. An
1470
+ explicit <code>--lens &#x3C;id></code> overrides the scope selection.</li>
1471
+ <li><strong>Registry order</strong>: A → B → C → D → E → F → G → H → I. <code>G-decisions</code> declares
1472
+ <code>depends_on: ['D-stack']</code> so it runs after D and can consume D's
1473
+ unsanctioned-dependency findings.</li>
1474
+ <li><strong>Missing adapter → skip</strong>: if a lens's <em>required</em> adapters are unavailable it
1475
+ is skipped and emits a <span class="sev sev-p3">P3</span> finding with
1476
+ <code>evidence.kind = lens_skipped</code>. Skipped lenses (not blocking findings) are what
1477
+ turn a <code>pass</code> into a <code>degraded-pass</code>.</li>
1478
+ </ul>
1479
+ <h3 id="what-feeds-the-doc-graph">What feeds the doc-graph</h3>
1480
+ <p>Before any lens runs, the planning docs and the working tree are parsed into a
1481
+ single graph (<code>src/observability/engine/doc-graph/index.ts</code>). Every lens's
1482
+ "Reads:" line refers to nodes and edges built here:</p>
1483
+
1484
+
1485
+
1486
+
1487
+
1488
+
1489
+
1490
+
1491
+
1492
+
1493
+
1494
+
1495
+
1496
+
1497
+
1498
+
1499
+
1500
+
1501
+
1502
+
1503
+
1504
+
1505
+
1506
+
1507
+
1508
+
1509
+
1510
+
1511
+
1512
+
1513
+
1514
+
1515
+
1516
+
1517
+
1518
+
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+
1525
+
1526
+
1527
+
1528
+
1529
+
1530
+
1531
+
1532
+
1533
+
1534
+
1535
+
1536
+
1537
+
1538
+
1539
+
1540
+
1541
+
1542
+
1543
+ <table><thead><tr><th>source</th><th>parser / detector</th><th>graph nodes &#x26; edges</th></tr></thead><tbody><tr><td>PRD</td><td><code>parseFeatures</code></td><td><code>features</code> (with priority)</td></tr><tr><td>user-stories</td><td><code>parseStories</code></td><td><code>stories</code>, <code>acceptance_criteria</code>, <code>feature_to_story</code></td></tr><tr><td>tech-stack</td><td><code>parseSanctionedComponents</code></td><td><code>components</code> (with <code>layer</code>)</td></tr><tr><td>coding-standards + tdd-standards</td><td><code>parseRules</code></td><td><code>rules</code></td></tr><tr><td>design-system</td><td><code>parseDesignTokens</code></td><td><code>tokens</code> (priority, category)</td></tr><tr><td>implementation-plan</td><td><code>parsePlanTasks</code></td><td><code>plan_tasks</code>, <code>story_to_plan_task</code></td></tr><tr><td>implementation-playbook</td><td><code>parsePlaybookTasks</code></td><td><code>playbook_tasks</code>, <code>playbook_task_to_story</code></td></tr><tr><td>decision docs (<code>decisions.jsonl</code>, <code>docs/decisions/*.md</code>)</td><td><code>parseDecisions</code></td><td><code>decisions</code></td></tr><tr><td>working tree</td><td><code>discoverTests</code>, <code>discoverFiles</code></td><td><code>tests</code> (with <code>last_status</code>), <code>files</code>, <code>ac_to_test</code></td></tr><tr><td>source files</td><td><code>detectCss/JsxTokenUses</code>, <code>detectComponentUses</code></td><td><code>file_to_token_use</code>, <code>file_to_component_use</code></td></tr></tbody></table>
1544
+ <h3 id="all-nine-at-a-glance">All nine at a glance</h3>
1545
+ <div class="filter-table"><input type="text" class="filter-input" placeholder="Filter…" aria-label="Filter table rows" disabled>
1546
+
1547
+
1548
+
1549
+
1550
+
1551
+
1552
+
1553
+
1554
+
1555
+
1556
+
1557
+
1558
+
1559
+
1560
+
1561
+
1562
+
1563
+
1564
+
1565
+
1566
+
1567
+
1568
+
1569
+
1570
+
1571
+
1572
+
1573
+
1574
+
1575
+
1576
+
1577
+
1578
+
1579
+
1580
+
1581
+
1582
+
1583
+
1584
+
1585
+
1586
+
1587
+
1588
+
1589
+
1590
+
1591
+
1592
+
1593
+
1594
+
1595
+
1596
+
1597
+
1598
+
1599
+
1600
+
1601
+
1602
+
1603
+
1604
+
1605
+
1606
+
1607
+
1608
+
1609
+
1610
+
1611
+
1612
+
1613
+
1614
+
1615
+
1616
+
1617
+
1618
+
1619
+
1620
+ <table><thead><tr><th>lens</th><th>question</th><th>scope</th><th>severities</th><th>ships in</th></tr></thead><tbody><tr><td><code>A-tdd</code></td><td>Are tests being skipped?</td><td>code</td><td>P0 P1</td><td>#331</td></tr><tr><td><code>B-ac-coverage</code></td><td>Is every AC covered by a passing test?</td><td>code</td><td>P0 P1</td><td>#331</td></tr><tr><td><code>C-standards</code></td><td>Are coding standards being violated?</td><td>code</td><td>P0–P3</td><td>#332</td></tr><tr><td><code>D-stack</code></td><td>Unsanctioned deps / out-of-layer use?</td><td>code</td><td>P0 P1</td><td>#332</td></tr><tr><td><code>E-design</code></td><td>Ad-hoc values bypassing design tokens?</td><td>code</td><td>P0 P1</td><td>#332</td></tr><tr><td><code>F-scope</code></td><td>High-priority work without a story/plan?</td><td>code</td><td>P0 P1 P2</td><td>#332</td></tr><tr><td><code>G-decisions</code></td><td>Decisions made but not documented?</td><td>code</td><td>P0 P1 P2</td><td>#332</td></tr><tr><td><code>H-cross-doc</code></td><td>Are the docs internally consistent?</td><td>docs</td><td>P0 P1 P2</td><td>#331 · #336</td></tr><tr><td><code>I-knowledge-gaps</code></td><td>Repeated needs with no KB entry?</td><td>docs</td><td>P1 P2</td><td>#397 · #406</td></tr></tbody></table></div>
1621
+ <h3 id="lens-by-lens">Lens by lens</h3>
1622
+ <p><strong>A-tdd — TDD violations</strong> (<code>src/observability/checks/lens-a-tdd.ts</code>). <em>Are there
1623
+ skipped tests?</em> <strong>Reads:</strong> the doc-graph's discovered tests (with <code>last_status</code>),
1624
+ ACs, stories, and <code>ac_to_test</code> edges. <strong>Tunes:</strong> none. <strong>Severity:</strong>
1625
+ <span class="sev sev-p0">P0</span> skipped test on a must-priority story; <span class="sev sev-p1">P1</span>
1626
+ otherwise.</p>
1627
+ <p><strong>B-ac-coverage — AC completion</strong>
1628
+ (<code>src/observability/checks/lens-b-ac-coverage.ts</code>). <em>Is every acceptance
1629
+ criterion covered by a passing test?</em> <strong>Reads:</strong> ACs, tests, <code>ac_to_test</code> edges;
1630
+ optional <code>tests</code> adapter for live status. <strong>Tunes:</strong> none. <strong>Severity:</strong>
1631
+ <span class="sev sev-p0">P0</span> AC's test is failing; <span class="sev sev-p1">P1</span> AC has no test edge
1632
+ or unknown status.</p>
1633
+ <p><strong>C-standards — coding-standards drift</strong>
1634
+ (<code>src/observability/checks/lens-c-standards.ts</code>). <em>Are coding standards being
1635
+ violated across files?</em> <strong>Reads:</strong> graph <code>rules</code> + <code>files</code>. <strong>Tunes:</strong>
1636
+ <code>lenses.C-standards.rule_overrides</code> (per-rule severity), <code>escalation_threshold</code>
1637
+ (default 5 — more than N violations of a rule escalates to P1).
1638
+ (<code>enforce_via_linter</code> is accepted in config but not read by the lens.)
1639
+ <strong>Severity:</strong> rule-specific <span class="sev sev-p0">P0</span>–<span class="sev sev-p3">P3</span>.</p>
1640
+ <p><strong>D-stack — tech-stack drift</strong>
1641
+ (<code>src/observability/checks/lens-d-stack.ts</code>). <em>Unsanctioned dependencies, or
1642
+ components used outside their layer?</em> <strong>Reads:</strong> <code>file_to_component_use</code> edges,
1643
+ components (with <code>layer</code>), and <code>decision_recorded</code> ledger events (which can
1644
+ sanction a dependency). <strong>Tunes:</strong> <code>lenses.D-stack.path_to_layer</code> (default 7
1645
+ glob→layer mappings). <strong>Severity:</strong> <span class="sev sev-p0">P0</span> unsanctioned dependency;
1646
+ <span class="sev sev-p1">P1</span> out-of-layer use.</p>
1647
+ <p><strong>E-design — design-system drift</strong>
1648
+ (<code>src/observability/checks/lens-e-design.ts</code>). <em>Are ad-hoc design values
1649
+ bypassing tokens?</em> <strong>Reads:</strong> <code>file_to_token_use</code> edges, tokens (with priority +
1650
+ category). <strong>Tunes:</strong> <code>lenses.E-design.ad_hoc_token_threshold</code> (default 3),
1651
+ <code>ui_glob</code> (default <code>src/components/**/*.tsx</code>, consumed by the doc-graph builder).
1652
+ <strong>Severity:</strong> <span class="sev sev-p1">P1</span> ad-hoc count over threshold; <span class="sev sev-p0">P0</span>
1653
+ must-priority token category bypassed.</p>
1654
+ <p><strong>F-scope — missing scope</strong> (<code>src/observability/checks/lens-f-scope.ts</code>). <em>Is
1655
+ high-priority work missing stories/plans, or are planned stories untouched?</em>
1656
+ <strong>Reads:</strong> features, stories, plan-tasks and their edges; <code>task_claimed</code> ledger
1657
+ events; optional <code>state</code> adapter. <strong>Tunes:</strong>
1658
+ <code>lenses.F-scope.untouched_story_grace_hours</code> (default 168 = 7 days). <strong>Severity:</strong>
1659
+ <span class="sev sev-p0">P0</span> must-feature without story/plan; <span class="sev sev-p1">P1</span>
1660
+ should-feature; <span class="sev sev-p2">P2</span> planned-but-untouched story past grace.</p>
1661
+ <p><strong>G-decisions — undocumented decisions</strong>
1662
+ (<code>src/observability/checks/lens-g-decisions.ts</code>). <em>Decisions made in the
1663
+ ledger/commits but not documented (or vice versa)?</em> <strong>Reads:</strong> graph decisions,
1664
+ <code>decision_recorded</code> events, recent git commits (keyword scan), and D-stack's
1665
+ findings. <strong>Tunes:</strong> none effective (see note). <strong>Severity:</strong> <span class="sev sev-p0">P0</span>
1666
+ unsanctioned dep from D with no decision; <span class="sev sev-p1">P1</span> ledger↔doc mismatch;
1667
+ <span class="sev sev-p2">P2</span> decision-keyword commit with no event/doc.</p>
1668
+ <div class="callout callout-warning"><p><strong>Inert override.</strong> The spec advertises a <code>lenses.G-decisions.keywords_file</code>
1669
+ override and ships <code>src/observability/checks/data/decision-keywords.txt</code>, but
1670
+ <code>loadKeywords()</code> hard-codes its keyword list and never reads the config key or
1671
+ the file. The override and the bundled file are currently inert.</p></div>
1672
+ <p><strong>H-cross-doc — cross-doc inconsistency</strong>
1673
+ (<code>src/observability/checks/lens-h-cross-doc.ts</code>). <em>Are features, stories, plans,
1674
+ playbook and decisions internally consistent?</em> <strong>Reads:</strong> the full structural
1675
+ doc-graph + unresolved globs. <strong>Severity:</strong> <span class="sev sev-p0">P0</span> decision supersedes
1676
+ a non-existent decision, or a <em>must</em>-priority story uncovered by plan/playbook;
1677
+ <span class="sev sev-p1">P1</span> feature with no story / orphan story / <em>should</em>-priority
1678
+ uncovered story; <span class="sev sev-p2">P2</span> plan task not in playbook, unresolved glob.</p>
1679
+ <p>Under <code>--profile=full</code> only, Lens H runs three extra checks via an LLM dispatcher
1680
+ (all silently return <code>[]</code> if the dispatcher fails, so the audit never breaks):</p>
1681
+ <ul>
1682
+ <li><strong>tech-stack ↔ PRD</strong> — contradictions (<span class="sev sev-p0">P0</span>) or tensions
1683
+ (<span class="sev sev-p2">P2</span>) between PRD prose and the chosen stack.</li>
1684
+ <li><strong>PRD → stories coverage</strong> — a prose feature with no covering story
1685
+ (<span class="sev sev-p1">P1</span>).</li>
1686
+ <li><strong>terminology drift</strong> — one concept named inconsistently across docs
1687
+ (<span class="sev sev-p2">P2</span>).</li>
1688
+ </ul>
1689
+ <div class="callout callout-danger"><p><strong>Security — the LLM dispatcher command is hard-coded.</strong> Lens H passes the
1690
+ literal <code>claude -p</code> (<span class="fp" data-path="src/observability/checks/lens-h-cross-doc.ts:164">src/observability/checks/lens-h-cross-doc.ts:164</span>) into
1691
+ the shared dispatcher (<code>src/observability/engine/llm-dispatcher.ts</code>, which runs
1692
+ it through a fixed subprocess). The command is <em>deliberately not</em>
1693
+ project-config-overridable, because the audit may run against untrusted
1694
+ third-party repos and a repo-controlled command would be arbitrary code
1695
+ execution. Only <code>llm.timeout_s</code> (and <code>llm.parallel_checks</code>) are configurable.
1696
+ Contrast the <code>--fix</code> dispatcher, which <em>is</em> configurable — see
1697
+ <a href="#the---fix-flow">The --fix flow</a>.</p></div>
1698
+ <p><strong>I-knowledge-gaps — the ninth lens</strong>
1699
+ (<code>src/observability/checks/lens-i-knowledge-gaps.ts</code>). <em>What topics have agents
1700
+ repeatedly needed (over a 90-day window) with no knowledge-base entry?</em> <strong>Reads:</strong>
1701
+ <code>knowledge_gap_signal</code> ledger events + synthetic signals scanned from
1702
+ <code>tasks/lessons.md</code> (<code>src/observability/checks/lens-i-lessons-scanner.ts</code>).
1703
+ <strong>Severity:</strong> <span class="sev sev-p1">P1</span> ≥5 signals from ≥3 projects; <span class="sev sev-p2">P2</span>
1704
+ ≥3 signals from ≥2 projects.</p>
1705
+ <div class="callout callout-note"><p><strong>Lens I — <code>--knowledge-root</code> and existing-entry suppression.</strong> The lens resolves
1706
+ the knowledge directory in three tiers (<code>src/observability/knowledge-index.ts</code>):
1707
+ the <code>--knowledge-root</code> CLI flag → <code>lenses.I-knowledge-gaps.knowledge_root</code> in
1708
+ config → auto-detect by walking up for <code>content/knowledge/</code>. If a gap's topic
1709
+ slug already exists in the resolved knowledge index, the finding is
1710
+ <strong>suppressed</strong> (no P1/P2). If no root resolves, suppression is disabled and the
1711
+ lens warns once per audit. Shipped in #397 (lens + lessons scanner) and #406
1712
+ (<code>--knowledge-root</code> + suppression).</p></div>
1713
+ <h2 id="the-observe-audit-cli">The <code>observe audit</code> CLI</h2>
1714
+ <p>One command runs the lenses and reports findings. Its exit code is the verdict
1715
+ gate: <code>0</code> on a non-blocked verdict (or any <code>mmr-findings</code> run), <code>1</code> when blocked
1716
+ or when a <code>--fix</code> pass still failed, <code>3</code> on an audit error.</p>
1717
+ <h3 id="every-flag">Every flag</h3>
1718
+
1719
+
1720
+
1721
+
1722
+
1723
+
1724
+
1725
+
1726
+
1727
+
1728
+
1729
+
1730
+
1731
+
1732
+
1733
+
1734
+
1735
+
1736
+
1737
+
1738
+
1739
+
1740
+
1741
+
1742
+
1743
+
1744
+
1745
+
1746
+
1747
+
1748
+
1749
+
1750
+
1751
+
1752
+
1753
+
1754
+
1755
+
1756
+
1757
+
1758
+
1759
+
1760
+
1761
+
1762
+
1763
+
1764
+
1765
+
1766
+
1767
+
1768
+
1769
+
1770
+
1771
+
1772
+
1773
+
1774
+
1775
+
1776
+
1777
+
1778
+ <table><thead><tr><th>flag</th><th>values / default</th><th>effect</th></tr></thead><tbody><tr><td><code>--scope</code></td><td><code>code</code> · <code>docs</code> · <code>all</code> (default <code>all</code>)</td><td>which lens set runs</td></tr><tr><td><code>--lens &#x3C;id></code></td><td>repeatable</td><td>run exactly these lenses, overriding scope</td></tr><tr><td><code>--profile</code></td><td><code>fast</code> (default) · <code>full</code></td><td><code>full</code> enables Lens H's LLM-graded sub-checks</td></tr><tr><td><code>--fix</code></td><td>flag</td><td>after auditing, dispatch the fix loop for blocking findings</td></tr><tr><td><code>--fix-threshold</code></td><td><code>P0</code>–<code>P3</code></td><td>override the blocking cutoff for this run</td></tr><tr><td><code>--output=&#x3C;path></code></td><td>path</td><td>override markdown destination (sidecar unaffected)</td></tr><tr><td><code>--render</code></td><td><code>dashboard-fragment-audit</code></td><td>emit HTML to stdout; skip markdown + sidecar</td></tr><tr><td><code>--output-mode</code></td><td><code>mmr-findings</code></td><td>emit MMR Finding JSON to stdout (always exit 0); skip markdown + sidecar</td></tr><tr><td><code>--knowledge-root=&#x3C;path></code></td><td>path</td><td>tier-1 knowledge dir for Lens I suppression</td></tr><tr><td><code>--json</code> · <code>--mask-paths</code> · <code>--show-acknowledged</code> · <code>--since-hours</code></td><td>flags / N</td><td>raw JSON output · redact paths · include acked findings · activity window</td></tr></tbody></table>
1779
+ <pre><code class="language-bash"># A docs-only full audit including LLM-graded checks
1780
+ scaffold observe audit --scope docs --profile full
1781
+
1782
+ # Audit, then dispatch a fix agent for each blocking finding
1783
+ scaffold observe audit --fix --fix-threshold P1
1784
+
1785
+ # Lens I only, with an explicit knowledge root for suppression
1786
+ scaffold observe audit --lens I-knowledge-gaps --knowledge-root content/knowledge
1787
+ </code></pre>
1788
+ <h3 id="verdict-taxonomy">Verdict taxonomy</h3>
1789
+ <p>The engine computes exactly three verdicts (<code>Verdict</code> at
1790
+ <span class="fp" data-path="src/observability/engine/types.ts:6">src/observability/engine/types.ts:6</span>):</p>
1791
+
1792
+
1793
+
1794
+
1795
+
1796
+
1797
+
1798
+
1799
+
1800
+
1801
+
1802
+
1803
+
1804
+
1805
+
1806
+
1807
+
1808
+
1809
+
1810
+
1811
+
1812
+
1813
+
1814
+
1815
+
1816
+ <table><thead><tr><th>verdict</th><th>when</th><th>exit</th></tr></thead><tbody><tr><td><span class="sev sev-pass">pass</span></td><td>no blocking findings, no skipped lenses</td><td><code>0</code></td></tr><tr><td><span class="sev sev-p2">degraded-pass</span></td><td>no blocking findings, but ≥1 lens skipped (missing required adapter)</td><td><code>0</code></td></tr><tr><td><span class="sev sev-p0">blocked</span></td><td>≥1 <em>open</em> finding at or above <code>fix_threshold</code></td><td><code>1</code></td></tr></tbody></table>
1817
+ <div class="callout callout-note"><p><strong>"needs-user-decision" is not an engine verdict.</strong> The code-review workflow has
1818
+ a <code>needs-user-decision</code> outcome, but that is an <em>MMR review</em> verdict, not one the
1819
+ audit engine emits. The audit engine's universe is exactly the three above.</p></div>
1820
+ <h4><code>fix_threshold</code></h4>
1821
+ <p>A finding is <em>blocking</em> when its status is <code>open</code> and its severity is at or above
1822
+ the threshold (<code>src/observability/engine/checks/fix-threshold.ts</code>). Resolution
1823
+ order: <code>--fix-threshold</code> flag → <code>.mmr.yaml</code>
1824
+ (<code>audit_fix_threshold</code> / <code>fix_threshold</code>) → default <strong>P2</strong>. The threshold never
1825
+ <em>hides</em> findings — every finding is still reported; it only decides which ones
1826
+ count toward <code>blocked</code>.</p>
1827
+ <div class="callout callout-note"><p><strong>How the counts are computed</strong>
1828
+ (<code>src/observability/engine/checks/findings-aggregator.ts</code>): <code>blocking</code> counts a
1829
+ finding only when <code>status === 'open'</code> <em>and</em> its severity is at or above
1830
+ <code>fix_threshold</code>; <code>acknowledged</code> counts only findings silenced by a
1831
+ <code>finding_acknowledged</code> ledger event (latest event per id wins); <code>skipped</code>
1832
+ findings (missing-adapter lenses) ignore acks entirely. The renderers'
1833
+ <code>(blocking · ack · total)</code> line is this tally.</p></div>
1834
+ <h3 id="companion-verb-scaffold-observe-ack">Companion verb — <code>scaffold observe ack</code></h3>
1835
+ <p>Acknowledge (or reopen) a finding by ID prefix; it writes a
1836
+ <code>finding_acknowledged</code> event the aggregator reads on the next audit.</p>
1837
+
1838
+
1839
+
1840
+
1841
+
1842
+
1843
+
1844
+
1845
+
1846
+
1847
+
1848
+
1849
+
1850
+
1851
+
1852
+
1853
+
1854
+
1855
+
1856
+
1857
+
1858
+ <table><thead><tr><th>arg / flag</th><th>effect</th></tr></thead><tbody><tr><td><code>&#x3C;prefix-or-id></code></td><td>matched against all finding IDs in <code>docs/audits/*.json</code>; an ambiguous prefix exits <code>2</code></td></tr><tr><td><code>--status acknowledged|open</code></td><td><code>acknowledged</code> (default) silences it; <code>open</code> reopens it</td></tr><tr><td><code>--note "…"</code></td><td>optional rationale recorded on the event</td></tr></tbody></table>
1859
+ <p>Branch is auto-derived (<code>git rev-parse --abbrev-ref HEAD</code>, fallback <code>main</code>); the
1860
+ event's <code>task_id</code> is null.</p>
1861
+ <h3 id="exit-codes-across-observe-verbs">Exit codes across <code>observe</code> verbs</h3>
1862
+
1863
+
1864
+
1865
+
1866
+
1867
+
1868
+
1869
+
1870
+
1871
+
1872
+
1873
+
1874
+
1875
+
1876
+
1877
+
1878
+
1879
+
1880
+
1881
+
1882
+
1883
+
1884
+
1885
+
1886
+
1887
+
1888
+
1889
+
1890
+
1891
+
1892
+
1893
+
1894
+
1895
+
1896
+
1897
+
1898
+
1899
+
1900
+
1901
+
1902
+
1903
+
1904
+
1905
+
1906
+
1907
+
1908
+
1909
+ <table><thead><tr><th>verb</th><th>0</th><th>1</th><th>2</th><th>3</th></tr></thead><tbody><tr><td><code>audit</code></td><td>not blocked, or any <code>mmr-findings</code> run</td><td>blocked, or <code>--fix</code> left a blocker</td><td>—</td><td>audit error (130 on SIGINT mid-<code>--fix</code>, snapshot restored)</td></tr><tr><td><code>event</code></td><td>written</td><td>—</td><td>validation failed / payload > 4 KiB</td><td>other error</td></tr><tr><td><code>ack</code></td><td>written</td><td>—</td><td>no match / ambiguous prefix</td><td>no audit sidecars / write error</td></tr><tr><td><code>progress</code></td><td>ok</td><td>—</td><td>—</td><td>markdown/sidecar write error (with <code>--output</code>)</td></tr><tr><td><code>harvest</code></td><td>harvested / recovered</td><td>—</td><td>—</td><td>no <code>identity.json</code>, or primary root is itself a worktree</td></tr></tbody></table>
1910
+ <h2 id="progress-replay-stall">Progress, replay &#x26; stall</h2>
1911
+ <p><code>scaffold observe progress</code> shows what's in flight. With <code>--replay</code> it fuses the
1912
+ ledger with synthesized git/gh/mmr/state/tests events into one timeline; when
1913
+ <code>scaffold observe progress</code> runs it also evaluates stall detection and surfaces a
1914
+ "Needs Attention" panel (suppress with <code>--no-stall-check</code>).</p>
1915
+ <h3 id="fusion-how-the-timeline-is-built">Fusion — how the timeline is built</h3>
1916
+ <p>The synthesizer's <code>composeReplay</code>
1917
+ (<span class="fp" data-path="src/observability/engine/synthesizer.ts:294">src/observability/engine/synthesizer.ts:294</span>) converts each ledger <code>Event</code>
1918
+ to a <code>ReplayEvent</code>, concatenates the adapter events, filters to the time window,
1919
+ then dedups:</p>
1920
+ <ul>
1921
+ <li>Events with a <code>null</code> <code>correlation_id</code> pass through untouched.</li>
1922
+ <li>Events that share a <code>correlation_id</code> collapse to one — the synthesizer keeps
1923
+ the lowest <code>SOURCE_PRIORITY</code> number (<code>ledger</code> 0 → <code>tests</code> 5), breaking ties by
1924
+ earliest <code>ts</code>.</li>
1925
+ <li>The surviving events sort by <code>(ts, source-priority, sort_id)</code>.</li>
1926
+ </ul>
1927
+ <p>So a PR that the agent recorded (<code>pr_opened</code> in the ledger) and that the <code>gh</code>
1928
+ adapter also reports collapses to the single ledger event, because <code>ledger</code> (0)
1929
+ outranks <code>gh</code> (2).</p>
1930
+ <h3 id="stall-detection-the-six-signals">Stall detection — the six signals</h3>
1931
+ <p>When <code>scaffold observe progress</code> runs (unless <code>--no-stall-check</code>) the engine
1932
+ checks for staleness and builds <code>needs_attention</code>
1933
+ (<code>src/observability/engine/stall.ts</code>). The type declares six
1934
+ signals; <strong>five fire today</strong> — <code>pr_review_stale</code> is reserved (deferred pending a
1935
+ per-PR correlation_id on the mmr adapter,
1936
+ <span class="fp" data-path="src/observability/engine/stall.ts:119">src/observability/engine/stall.ts:119</span>). Thresholds accept <code>'Nh'</code>, <code>'Nd'</code>,
1937
+ or <code>'off'</code> under <code>stall.*</code>:</p>
1938
+
1939
+
1940
+
1941
+
1942
+
1943
+
1944
+
1945
+
1946
+
1947
+
1948
+
1949
+
1950
+
1951
+
1952
+
1953
+
1954
+
1955
+
1956
+
1957
+
1958
+
1959
+
1960
+
1961
+
1962
+
1963
+
1964
+
1965
+
1966
+
1967
+
1968
+
1969
+
1970
+
1971
+
1972
+
1973
+
1974
+
1975
+
1976
+
1977
+
1978
+ <table><thead><tr><th>signal</th><th>fires when</th><th>config key · default</th></tr></thead><tbody><tr><td><code>task_stale</code></td><td>task claimed, no commit/heartbeat/completion since</td><td><code>stall.task_stale</code> · <code>4h</code></td></tr><tr><td><code>pr_stale</code></td><td>ledger <code>pr_opened</code> recorded, PR not merged/closed since</td><td><code>stall.pr_stale</code> · <code>48h</code></td></tr><tr><td><code>blocker_unaddressed</code></td><td><code>blocker_hit</code> with no <code>blocker_resolved</code></td><td><code>stall.blocker_unaddressed</code> · <code>2h</code></td></tr><tr><td><code>audit_findings_unresolved</code></td><td>open finding past threshold age, severity at or above <code>fix_threshold</code></td><td><code>stall.audit_findings_unresolved</code> · <code>24h</code></td></tr><tr><td><code>lens_skipped_repeatedly</code></td><td>a lens skipped on ≥3 consecutive audits</td><td>hard-coded streak ≥ 3 (no config key)</td></tr><tr><td><code>pr_review_stale</code> (reserved)</td><td>— not yet emitted —</td><td><code>stall.pr_review_stale</code> · <code>24h</code> (defined, unused)</td></tr></tbody></table>
1979
+ <h3 id="flags">Flags</h3>
1980
+
1981
+
1982
+
1983
+
1984
+
1985
+
1986
+
1987
+
1988
+
1989
+
1990
+
1991
+
1992
+
1993
+
1994
+
1995
+
1996
+
1997
+
1998
+
1999
+
2000
+
2001
+
2002
+
2003
+
2004
+
2005
+ <table><thead><tr><th>flag</th><th>effect</th></tr></thead><tbody><tr><td><code>--replay</code></td><td>include the fused timeline in the output</td></tr><tr><td><code>--no-stall-check</code></td><td>suppress the "Needs Attention" surface (empty <code>needs_attention</code>)</td></tr><tr><td><code>--render=dashboard-fragment</code></td><td>emit the progress HTML panel to stdout; skip markdown + sidecar</td></tr><tr><td><code>--output=&#x3C;path></code> · <code>--json</code> · <code>--since-hours N</code></td><td>override markdown path · raw JSON · activity window (default 24)</td></tr></tbody></table>
2006
+ <p>Stall thresholds are configurable under <code>stall:</code> in
2007
+ <code>.scaffold/observability.yaml</code> — see <a href="#config-reference">Config reference</a>.</p>
2008
+ <h2 id="phase-boundary-triggers">Phase-boundary triggers</h2>
2009
+ <p>Completing a planning document should re-check the docs for drift —
2010
+ automatically, but never blocking the build. When a pipeline step at a phase
2011
+ boundary is marked complete, the state manager fires a docs audit and prints a
2012
+ one-line summary.</p>
2013
+ <h3 id="the-hook">The hook</h3>
2014
+ <p><code>StateManager.markCompleted()</code> is <strong>async</strong>
2015
+ (<span class="fp" data-path="src/state/state-manager.ts:175">src/state/state-manager.ts:175</span>): it saves state, then awaits
2016
+ <code>runPhaseAudit({ primaryRoot, step })</code> for <em>every</em> completed step — the
2017
+ phase-boundary gate lives inside <code>runPhaseAudit</code>, which returns a no-op result
2018
+ unless the step is one of the six boundaries
2019
+ (<code>src/observability/engine/phase-subsets.ts</code>): <code>user-stories</code>, <code>tech-stack</code>,
2020
+ <code>coding-standards</code>, <code>design-system</code>, <code>implementation-plan</code>,
2021
+ <code>implementation-playbook</code>.</p>
2022
+ <figure class="mermaid"><svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1273.34px; background-color: transparent;" viewBox="0 0 1273.34375 190.39999389648438" role="graphics-document document">#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#666;stroke:#666;}#my-svg .marker.cross{stroke:#666;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg p{margin:0;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span{color:#333;}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#000000;color:#000000;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#eee;stroke:#999;stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#my-svg .arrowheadPath{fill:#333333;}#my-svg .edgePath .path{stroke:#666;stroke-width:1px;}#my-svg .flowchart-link{stroke:#666;fill:none;}#my-svg .edgeLabel{background-color:white;text-align:center;}#my-svg .edgeLabel p{background-color:white;}#my-svg .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#my-svg .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#my-svg .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:white;text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:white;padding:2px;}#my-svg .icon-shape .label rect,#my-svg .image-shape .label rect{opacity:0.5;background-color:white;fill:white;}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg .node .neo-node{stroke:#999;}#my-svg [data-look="neo"].node rect,#my-svg [data-look="neo"].cluster rect,#my-svg [data-look="neo"].node polygon{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node path{stroke:url(#my-svg-gradient);stroke-width:1px;}#my-svg [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node .neo-line path{stroke:#999;filter:none;}#my-svg [data-look="neo"].node circle{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node circle .state-start{fill:#000000;}#my-svg [data-look="neo"].icon-shape .icon{fill:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].icon-shape .icon-neo path{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}<g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></polygon></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"></path></marker><marker id="my-svg_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path d="M196.906,104L201.073,104C205.24,104,213.573,104,221.24,104C228.906,104,235.906,104,239.406,104L242.906,104" id="my-svg-L_CLI_MC_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M476.688,78.291L497.685,73.593C518.682,68.894,560.677,59.497,602.005,54.799C643.333,50.1,683.995,50.1,704.326,50.1L724.656,50.1" id="my-svg-L_MC_RPA_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M962.078,50.1L966.245,50.1C970.411,50.1,978.745,50.1,986.411,50.1C994.078,50.1,1001.078,50.1,1004.578,50.1L1008.078,50.1" id="my-svg-L_RPA_OUT_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M476.688,129.709L497.685,134.407C518.682,139.106,560.677,148.503,610.118,153.201C659.56,157.9,716.448,157.9,744.892,157.9L773.336,157.9" id="my-svg-L_MC_NG_0" class="edge-thickness-normal edge-pattern-dotted edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel" transform="translate(602.671875, 157.89999771118164)"><g class="label" transform="translate(0, -19.299999237060547)"><g><rect class="background" style="" x="-100.984375" y="-1" width="201.96875" height="40.599998474121094"></rect><text y="-10.1" text-anchor="middle" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"><tspan class="text-inner-tspan">state</tspan><tspan class="text-inner-tspan"> already</tspan><tspan class="text-inner-tspan"> saved</tspan><tspan class="text-inner-tspan"> —</tspan><tspan class="text-inner-tspan"> audit</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" text-anchor="middle"><tspan class="text-inner-tspan">error</tspan><tspan class="text-inner-tspan"> never</tspan><tspan class="text-inner-tspan"> rolls</tspan><tspan class="text-inner-tspan"> it</tspan><tspan class="text-inner-tspan"> back</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default" id="my-svg-flowchart-CLI-0" transform="translate(102.453125, 103.99999809265137)"><rect class="basic label-container" style="" x="-94.453125" y="-24.5" width="188.90625" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">scaffold</tspan><tspan class="text-inner-tspan"> complete</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-MC-1" transform="translate(361.796875, 103.99999809265137)"><rect class="basic label-container" style="" x="-114.890625" y="-42.099998474121094" width="229.78125" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">markCompleted</tspan><tspan class="text-inner-tspan"> (async)</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">saveState</tspan><tspan class="text-inner-tspan"> →</tspan><tspan class="text-inner-tspan"> set</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">completed_at</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-RPA-3" transform="translate(845.3671875, 50.099998474121094)"><rect class="basic label-container" style="" x="-116.7109375" y="-33.29999923706055" width="233.421875" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">runPhaseAudit</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">H-cross-doc</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> docs</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> fast</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-OUT-5" transform="translate(1138.7109375, 50.099998474121094)"><rect class="basic label-container" style="" x="-126.6328125" y="-42.099998474121094" width="253.265625" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">docs/audits/id.md</tspan><tspan class="text-inner-tspan"> +</tspan><tspan class="text-inner-tspan"> .json</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">PhaseAuditResult</tspan><tspan class="text-inner-tspan"> →</tspan><tspan class="text-inner-tspan"> stdout</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">line</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-NG-7" transform="translate(845.3671875, 157.89999771118164)"><rect class="basic label-container" style="" x="-68.03125" y="-24.5" width="136.0625" height="49"></rect><g class="label" style="" transform="translate(0, -9.5)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">non-gating</tspan></tspan></text></g></g></g></g></g></g><defs></defs><defs></defs></svg></figure>
2023
+ <p>The audit is fixed to <code>H-cross-doc</code>, <code>scope=docs</code>, <code>profile=fast</code>
2024
+ (<code>src/observability/engine/phase-audit.ts</code>). It is <strong>non-gating</strong>:
2025
+ <code>markCompleted</code> catches any audit error and returns a result object, so a state
2026
+ transition never fails because of the audit.</p>
2027
+ <p>The stdout line:</p>
2028
+ <pre><code class="language-text"># normal
2029
+ [audit] 3 findings (1 blocking, verdict=blocked) — see docs/audits/audit-…-fast-lens-H-cross-doc-….md
2030
+ # detached (fire-and-forget)
2031
+ [audit] dispatched in background (step: tech-stack)
2032
+ # timed out
2033
+ [audit] timed out after 60500ms — partial findings may not be written
2034
+ </code></pre>
2035
+ <h3 id="config-timestamps">Config &#x26; timestamps</h3>
2036
+
2037
+
2038
+
2039
+
2040
+
2041
+
2042
+
2043
+
2044
+
2045
+
2046
+
2047
+
2048
+
2049
+
2050
+
2051
+
2052
+
2053
+
2054
+
2055
+
2056
+
2057
+
2058
+
2059
+
2060
+
2061
+ <table><thead><tr><th>key</th><th>default</th><th>effect</th></tr></thead><tbody><tr><td><code>phase_audit.enabled</code></td><td><code>true</code></td><td>master switch</td></tr><tr><td><code>phase_audit.timeout_s</code></td><td><code>60</code></td><td>race budget; over → <code>timed_out</code></td></tr><tr><td><code>phase_audit.detached</code></td><td><code>false</code></td><td>fire-and-forget when true</td></tr></tbody></table>
2062
+ <p>Step state (<code>src/types/state.ts</code>) now carries <code>completed_at</code> (set once, on first
2063
+ completion) and <code>in_progress_started_at</code> (set on first entry to in-progress). The
2064
+ <code>state</code> adapter's <code>replayEvents</code> prefers these per-step timestamps and only falls
2065
+ back to the file's mtime when they're absent — so the timeline reflects when each
2066
+ step actually transitioned, not when the file was last written.</p>
2067
+ <h2 id="mmr-doc-conformance-channel">MMR <code>doc-conformance</code> channel</h2>
2068
+ <p>The audit plugs into multi-model review as a built-in channel.
2069
+ <code>scaffold observe audit --output-mode=mmr-findings</code> emits findings in MMR's
2070
+ <code>Finding</code> shape (<code>src/observability/renderers/mmr-findings.ts</code>), always exiting 0
2071
+ so the dispatcher captures stdout regardless of verdict. Only <code>open</code> findings are
2072
+ emitted (acknowledged and skipped are dropped).</p>
2073
+ <h3 id="finding-translation">Finding translation</h3>
2074
+ <p>The composite <code>location</code> — <code>&#x3C;source_doc>::&#x3C;lens_id>::&#x3C;short_id></code> — gives each
2075
+ finding a stable identity across audit runs, so MMR can track the same defect
2076
+ over time.</p>
2077
+
2078
+
2079
+
2080
+
2081
+
2082
+
2083
+
2084
+
2085
+
2086
+
2087
+
2088
+
2089
+
2090
+
2091
+
2092
+
2093
+
2094
+
2095
+
2096
+
2097
+
2098
+
2099
+
2100
+
2101
+
2102
+
2103
+
2104
+
2105
+
2106
+ <table><thead><tr><th>MMR field</th><th>derived from</th></tr></thead><tbody><tr><td><code>severity</code></td><td><code>finding.severity</code> (P0–P3)</td></tr><tr><td><code>location</code></td><td><code>${source_doc || '(no-source-doc)'}::${lens_id}::${id.slice(0,8)}</code></td></tr><tr><td><code>description</code></td><td><code>[doc-conformance/${lens_id}] ${title}</code> + optional <code>— ${description}</code></td></tr><tr><td><code>suggestion</code></td><td><code>fix_hint?.prompt ?? fix_hint?.target ?? ''</code></td></tr><tr><td><code>category</code></td><td>literal <code>'doc-conformance'</code></td></tr></tbody></table>
2107
+ <p>A Lens-B engine <code>Finding</code> like this:</p>
2108
+ <pre><code class="language-json">{
2109
+ "id": "3a8c1f0211223344", "lens_id": "B-ac-coverage", "severity": "P0",
2110
+ "title": "AC has failing test", "description": "Test refresh.spec.ts is failing.",
2111
+ "source_doc": "docs/user-stories.md#user-auth-1",
2112
+ "fix_hint": { "prompt": "Re-enable the test", "target": "src/auth/test.spec.ts" }
2113
+ }
2114
+ </code></pre>
2115
+ <p>translates to this MMR Finding:</p>
2116
+ <pre><code class="language-json">{
2117
+ "location": "docs/user-stories.md#user-auth-1::B-ac-coverage::3a8c1f02",
2118
+ "severity": "P0",
2119
+ "description": "[doc-conformance/B-ac-coverage] AC has failing test — Test refresh.spec.ts is failing.",
2120
+ "suggestion": "Re-enable the test",
2121
+ "category": "doc-conformance"
2122
+ }
2123
+ </code></pre>
2124
+ <h3 id="enabling-it">Enabling it</h3>
2125
+ <p>The <code>doc-conformance</code> channel is <strong>disabled by default</strong>. Enable it per-project in
2126
+ <code>.mmr.yaml</code> or for a single run with <code>--channels=doc-conformance</code>; if it's
2127
+ globally enabled you can opt a project out via
2128
+ <code>channels_disabled: ["doc-conformance"]</code>.</p>
2129
+ <pre><code class="language-bash">mmr review --channels=doc-conformance --diff &#x3C;(git diff main...HEAD) --sync --format json
2130
+ </code></pre>
2131
+ <p>Under the hood the built-in channel runs <code>scaffold observe audit --profile=full --scope=all --output-mode=mmr-findings</code> and parses its stdout. See the MMR guide
2132
+ (<a href="../mmr/index.md">../mmr/index.md</a>) for the channel architecture this plugs
2133
+ into.</p>
2134
+ <h2 id="the---fix-flow">The <code>--fix</code> flow</h2>
2135
+ <p><code>scaffold observe audit --fix</code> doesn't just report blocking findings — it
2136
+ dispatches an agent to fix each one, verifies the fix with a single-lens
2137
+ re-audit, and writes a post-fix report. The loop is abort-aware: an interrupt
2138
+ un-stages what the loop staged and re-applies the original stash (see the
2139
+ limitation in <a href="#abort-safety">Abort safety</a>).</p>
2140
+ <figure class="mermaid"><svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1433.47px; background-color: transparent;" viewBox="0 0 1433.46875 100.19999694824219" role="graphics-document document">#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#666;stroke:#666;}#my-svg .marker.cross{stroke:#666;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg p{margin:0;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span{color:#333;}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#000000;color:#000000;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#eee;stroke:#999;stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#my-svg .arrowheadPath{fill:#333333;}#my-svg .edgePath .path{stroke:#666;stroke-width:1px;}#my-svg .flowchart-link{stroke:#666;fill:none;}#my-svg .edgeLabel{background-color:white;text-align:center;}#my-svg .edgeLabel p{background-color:white;}#my-svg .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#my-svg .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#my-svg .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:white;text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:white;padding:2px;}#my-svg .icon-shape .label rect,#my-svg .image-shape .label rect{opacity:0.5;background-color:white;fill:white;}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg .node .neo-node{stroke:#999;}#my-svg [data-look="neo"].node rect,#my-svg [data-look="neo"].cluster rect,#my-svg [data-look="neo"].node polygon{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node path{stroke:url(#my-svg-gradient);stroke-width:1px;}#my-svg [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node .neo-line path{stroke:#999;filter:none;}#my-svg [data-look="neo"].node circle{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node circle .state-start{fill:#000000;}#my-svg [data-look="neo"].icon-shape .icon{fill:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].icon-shape .icon-neo path{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}<g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></polygon></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"></path></marker><marker id="my-svg_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path d="M246.266,50.1L250.432,50.1C254.599,50.1,262.932,50.1,270.599,50.1C278.266,50.1,285.266,50.1,288.766,50.1L292.266,50.1" id="my-svg-L_P1_P2_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M549.031,50.1L553.198,50.1C557.365,50.1,565.698,50.1,573.365,50.1C581.031,50.1,588.031,50.1,591.531,50.1L595.031,50.1" id="my-svg-L_P2_P3_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M821.594,50.1L825.76,50.1C829.927,50.1,838.26,50.1,845.927,50.1C853.594,50.1,860.594,50.1,864.094,50.1L867.594,50.1" id="my-svg-L_P3_P4_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M1121.234,50.1L1125.401,50.1C1129.568,50.1,1137.901,50.1,1145.568,50.1C1153.234,50.1,1160.234,50.1,1163.734,50.1L1167.234,50.1" id="my-svg-L_P4_P5_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" transform="translate(0, 0)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g></g><g class="nodes"><g class="node default" id="my-svg-flowchart-P1-0" transform="translate(127.1328125, 50.099998474121094)"><rect class="basic label-container" style="" x="-119.1328125" y="-33.29999923706055" width="238.265625" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">1</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> audit</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">collect</tspan><tspan class="text-inner-tspan"> findings</tspan><tspan class="text-inner-tspan"> +</tspan><tspan class="text-inner-tspan"> verdict</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-P2-1" transform="translate(422.6484375, 50.099998474121094)"><rect class="basic label-container" style="" x="-126.3828125" y="-42.099998474121094" width="252.765625" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">2</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> buildFixPlan</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">keep</tspan><tspan class="text-inner-tspan"> open</tspan><tspan class="text-inner-tspan"> &#x26;</tspan><tspan class="text-inner-tspan"> ≥</tspan><tspan class="text-inner-tspan"> threshold;</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">order</tspan><tspan class="text-inner-tspan"> by</tspan><tspan class="text-inner-tspan"> severity</tspan><tspan class="text-inner-tspan"> then</tspan><tspan class="text-inner-tspan"> lens</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-P3-3" transform="translate(710.3125, 50.099998474121094)"><rect class="basic label-container" style="" x="-111.28125" y="-42.099998474121094" width="222.5625" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">3</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> dispatchFixAgent</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">subprocess,</tspan><tspan class="text-inner-tspan"> prompt</tspan><tspan class="text-inner-tspan"> on</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">stdin</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-P4-5" transform="translate(996.4140625, 50.099998474121094)"><rect class="basic label-container" style="" x="-124.8203125" y="-42.099998474121094" width="249.640625" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">4</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> verify</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">re-run</tspan><tspan class="text-inner-tspan"> that</tspan><tspan class="text-inner-tspan"> one</tspan><tspan class="text-inner-tspan"> lens;</tspan><tspan class="text-inner-tspan"> retry</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">≤</tspan><tspan class="text-inner-tspan"> 3</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-P5-7" transform="translate(1298.3515625, 50.099998474121094)"><rect class="basic label-container" style="" x="-127.1171875" y="-42.099998474121094" width="254.234375" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">5</tspan><tspan class="text-inner-tspan"> ·</tspan><tspan class="text-inner-tspan"> post-fix</tspan><tspan class="text-inner-tspan"> report</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">docs/audits/id-postfix.{md</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">,json}</tspan></tspan></text></g></g></g></g></g></g><defs></defs><defs></defs></svg></figure>
2141
+ <h3 id="the-five-phases">The five phases</h3>
2142
+ <ul>
2143
+ <li><strong>1 · audit</strong> — run the audit to get findings and the verdict
2144
+ (<code>src/observability/engine/fix-flow.ts</code>).</li>
2145
+ <li><strong>2 · <code>buildFixPlan</code></strong> (<code>src/observability/engine/fix-plan.ts</code>) — keep findings
2146
+ that are <code>open</code> and at/above <code>fix_threshold</code>, ordered by severity ascending (P0
2147
+ first) then <code>lens_id</code>.</li>
2148
+ <li><strong>3 · <code>dispatchFixAgent</code></strong> (<code>src/observability/engine/fix-agent-dispatcher.ts</code>)
2149
+ — spawn the dispatcher command, write the per-finding prompt to stdin, stream
2150
+ stdout/stderr to the user.</li>
2151
+ <li><strong>4 · single-lens verify</strong> — re-run only the finding's lens; if the finding is
2152
+ gone, mark fixed; else retry, up to <code>per_finding_max_attempts</code> (default 3).</li>
2153
+ <li><strong>5 · post-fix report</strong> — a final <code>scope=all</code> audit, written to
2154
+ <code>docs/audits/&#x3C;id>-postfix.{md,json}</code>.</li>
2155
+ </ul>
2156
+ <h3 id="abort-safety">Abort safety</h3>
2157
+ <p>Before dispatching, <code>captureSnapshot</code>
2158
+ (<code>src/observability/engine/abort-snapshot.ts</code>) records a <code>git stash create</code> SHA
2159
+ plus the set of pre-existing staged files. Newly staged paths are tracked per
2160
+ attempt. On <code>SIGINT</code>, <code>restoreSnapshot</code> un-stages the paths the loop recorded as
2161
+ newly staged and re-applies the original stash tree. <strong>Limitation:</strong> edits the
2162
+ dispatcher made but that the loop did not record as staged are not actively
2163
+ reverted and may need manual cleanup — recovery is scoped to the recorded
2164
+ staged paths plus the stash, not a guaranteed full reset.</p>
2165
+ <h3 id="config-the-fix-block">Config — the <code>fix:</code> block</h3>
2166
+
2167
+
2168
+
2169
+
2170
+
2171
+
2172
+
2173
+
2174
+
2175
+
2176
+
2177
+
2178
+
2179
+
2180
+
2181
+
2182
+
2183
+
2184
+
2185
+
2186
+
2187
+
2188
+
2189
+
2190
+
2191
+ <table><thead><tr><th>key</th><th>default</th><th>effect</th></tr></thead><tbody><tr><td><code>fix.dispatcher_command</code></td><td><code>claude -p</code></td><td>the agent command (configurable — see note)</td></tr><tr><td><code>fix.timeout_s</code></td><td><code>300</code></td><td>per-finding subprocess budget</td></tr><tr><td><code>fix.per_finding_max_attempts</code></td><td><code>3</code></td><td>verify retries before giving up</td></tr></tbody></table>
2192
+ <div class="callout callout-note"><p><strong>The fix dispatcher IS configurable — unlike Lens H's.</strong>
2193
+ <code>fix.dispatcher_command</code> is read from project config because the fix loop only
2194
+ runs against the maintainer's own repo, not untrusted third-party code
2195
+ (<code>src/observability/engine/fix-agent-dispatcher.ts</code>). This is the opposite of the
2196
+ Lens H LLM dispatcher, which hard-codes its command for untrusted-repo safety.
2197
+ CLAUDE.md's <code>--fix</code> note describing the dispatcher as "not
2198
+ project-config-overridable" conflates the two.</p></div>
2199
+ <p>Exit code: <code>1</code> if any blocking finding survived the loop, else <code>0</code>.</p>
2200
+ <h2 id="harvest-recover-teardown">Harvest, recover &#x26; teardown</h2>
2201
+ <p>A worktree's ledger is local to that worktree. Harvest copies it into the primary
2202
+ repo's archive before the worktree is removed; recover sweeps up ledgers whose
2203
+ worktrees vanished without being harvested; teardown does the whole dance in one
2204
+ command.</p>
2205
+ <figure class="mermaid"><svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1087.94px; background-color: transparent;" viewBox="0 0 1087.9375 100.19999694824219" role="graphics-document document">#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#666;stroke:#666;}#my-svg .marker.cross{stroke:#666;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg p{margin:0;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span{color:#333;}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#000000;color:#000000;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#eee;stroke:#999;stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#my-svg .arrowheadPath{fill:#333333;}#my-svg .edgePath .path{stroke:#666;stroke-width:1px;}#my-svg .flowchart-link{stroke:#666;fill:none;}#my-svg .edgeLabel{background-color:white;text-align:center;}#my-svg .edgeLabel p{background-color:white;}#my-svg .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#my-svg .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#my-svg .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:white;text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:white;padding:2px;}#my-svg .icon-shape .label rect,#my-svg .image-shape .label rect{opacity:0.5;background-color:white;fill:white;}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg .node .neo-node{stroke:#999;}#my-svg [data-look="neo"].node rect,#my-svg [data-look="neo"].cluster rect,#my-svg [data-look="neo"].node polygon{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node path{stroke:url(#my-svg-gradient);stroke-width:1px;}#my-svg [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node .neo-line path{stroke:#999;filter:none;}#my-svg [data-look="neo"].node circle{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node circle .state-start{fill:#000000;}#my-svg [data-look="neo"].icon-shape .icon{fill:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].icon-shape .icon-neo path{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}<g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></polygon></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"></circle></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"></path></marker><marker id="my-svg_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"></path></marker><marker id="my-svg_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path d="M231.297,50.1L240.193,50.1C249.089,50.1,266.88,50.1,284.005,50.1C301.13,50.1,317.589,50.1,325.818,50.1L334.047,50.1" id="my-svg-L_WT_AA_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path><path d="M596,50.1L615.043,50.1C634.086,50.1,672.172,50.1,709.591,50.1C747.01,50.1,783.763,50.1,802.139,50.1L820.516,50.1" id="my-svg-L_AA_MA_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" marker-end="url(#my-svg_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(284.671875, 50.099998474121094)"><g class="label" transform="translate(0, -10.5)"><g><rect class="background" style="" x="-28.375" y="-1" width="56.75" height="23"></rect><text y="-10.1" text-anchor="middle" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"><tspan class="text-inner-tspan">harvest</tspan></tspan></text></g></g></g><g class="edgeLabel" transform="translate(710.2578125, 50.099998474121094)"><g class="label" transform="translate(0, -10.5)"><g><rect class="background" style="" x="-89.2578125" y="-1" width="178.515625" height="23"></rect><text y="-10.1" text-anchor="middle" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em" text-anchor="middle"><tspan class="text-inner-tspan">recover</tspan><tspan class="text-inner-tspan"> (worktree</tspan><tspan class="text-inner-tspan"> gone)</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default" id="my-svg-flowchart-WT-0" transform="translate(119.6484375, 50.099998474121094)"><rect class="basic label-container" style="" x="-111.6484375" y="-33.29999923706055" width="223.296875" height="66.5999984741211"></rect><g class="label" style="" transform="translate(0, -18.299999237060547)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">worktree</tspan><tspan class="text-inner-tspan"> ledger</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">.scaffold/activity.jsonl</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-AA-1" transform="translate(467.0234375, 50.099998474121094)"><rect class="basic label-container" style="" x="-128.9765625" y="-42.099998474121094" width="257.953125" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">active-archive</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">activity-archive/active/id.j</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">sonl</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-MA-3" transform="translate(952.2265625, 50.099998474121094)"><rect class="basic label-container" style="" x="-127.7109375" y="-42.099998474121094" width="255.421875" height="84.19999694824219"></rect><g class="label" style="" transform="translate(0, -27.099998474121094)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1" style=""><tspan class="text-outer-tspan row" x="0" y="-0.1em"><tspan class="text-inner-tspan">monthly</tspan><tspan class="text-inner-tspan"> archive</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">activity-archive/YYYY-MM.j</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">sonl</tspan></tspan></text></g></g></g></g></g></g><defs></defs><defs></defs></svg></figure>
2206
+ <h3 id="commands">Commands</h3>
2207
+ <ul>
2208
+ <li><strong><code>scaffold observe harvest --worktree=&#x3C;path></code></strong> — read the worktree's
2209
+ <code>identity.json</code>, then copy its <code>activity.jsonl</code> to
2210
+ <code>.scaffold/activity-archive/active/&#x3C;worktree_id>.jsonl</code> in the primary repo
2211
+ (atomic temp-file + rename under a lock; keyed by <code>worktree_id</code>, idempotent
2212
+ full-file replace). Requires the primary root to be a real repo, not itself a
2213
+ worktree.</li>
2214
+ <li><strong><code>scaffold observe harvest --recover</code></strong> — list live worktrees
2215
+ (<code>git worktree list --porcelain</code>), read each one's <code>worktree_id</code>, then scan
2216
+ <code>active/</code> for entries whose worktree is no longer live and rotate each into
2217
+ <code>activity-archive/&#x3C;YYYY-MM>.jsonl</code> (bucketed by the file's mtime), deleting the
2218
+ active entry. Returns the rotated ids.</li>
2219
+ <li><strong><code>scripts/teardown-agent-worktree.sh &#x3C;path></code></strong> — derives the primary repo via
2220
+ <code>git -C &#x3C;path> rev-parse --git-common-dir</code> (so it works from anywhere),
2221
+ harvests (fail-soft), runs <code>git worktree remove</code>, then deletes the workspace
2222
+ branch if it differs from the primary HEAD.</li>
2223
+ </ul>
2224
+ <div class="callout callout-note"><p><strong>Empty ledgers are fine.</strong> If a worktree never wrote an <code>activity.jsonl</code>,
2225
+ harvest is a clean no-op and teardown proceeds.</p></div>
2226
+ <h2 id="renderers">Renderers</h2>
2227
+ <p>One <code>EngineOutput</code>, several surfaces. The terminal renderer is for humans at a
2228
+ prompt; the markdown renderer writes a durable report plus a machine-readable
2229
+ JSON sidecar; the dashboard-fragment renderer emits an HTML panel that
2230
+ <code>scripts/generate-dashboard.sh</code> injects at named anchors.</p>
2231
+ <p>Default audit/progress runs write both a markdown report and a JSON sidecar:
2232
+ audits to <code>docs/audits/&#x3C;id>.{md,json}</code>, progress to
2233
+ <code>docs/build-status/&#x3C;id>.{md,json}</code> (<code>src/observability/renderers/sidecar.ts</code>).
2234
+ <code>--render</code> and <code>--output-mode</code> bypass the files and print to stdout instead.</p>
2235
+ <p>The same blocked audit renders three ways. As terminal ANSI:</p>
2236
+ <pre><code class="language-text">build observability — audit
2237
+ (profile: fast · scope: all)
2238
+
2239
+ [P0] AC has failing test
2240
+ lens: B-ac-coverage
2241
+ fix: Re-enable the test
2242
+
2243
+ verdict: blocked
2244
+ (blocking 1 · ack 0 · total 1)
2245
+ </code></pre>
2246
+ <p>As markdown (<code>docs/audits/&#x3C;id>.md</code>):</p>
2247
+ <pre><code class="language-text"># Build Observability — Audit
2248
+
2249
+ **Verdict:** blocked
2250
+
2251
+ ## Summary
2252
+ 1 findings · 1 blocking (at or above P2) · 0 acknowledged · 0 skipped lenses
2253
+
2254
+ ### [P0] B-ac-coverage — AC has failing test
2255
+ `3a8c1f02` · *source:* `…#user-auth-1` · *confidence:* high
2256
+ </code></pre>
2257
+ <p>As a dashboard fragment, an HTML <code>&#x3C;section id="build-audit" data-verdict="blocked"></code>
2258
+ panel injected into the dashboard.</p>
2259
+
2260
+
2261
+
2262
+
2263
+
2264
+
2265
+
2266
+
2267
+
2268
+
2269
+
2270
+
2271
+
2272
+
2273
+
2274
+
2275
+
2276
+
2277
+
2278
+
2279
+
2280
+
2281
+
2282
+
2283
+
2284
+
2285
+
2286
+
2287
+
2288
+
2289
+
2290
+
2291
+
2292
+
2293
+
2294
+
2295
+
2296
+
2297
+
2298
+
2299
+
2300
+ <table><thead><tr><th>renderer</th><th>file</th><th>destination</th><th>notes</th></tr></thead><tbody><tr><td>terminal</td><td><code>src/observability/renderers/terminal.ts</code></td><td>stdout</td><td>ANSI, "Needs Attention" banner + timeline</td></tr><tr><td>markdown</td><td><code>src/observability/renderers/markdown.ts</code></td><td><code>docs/audits/</code> · <code>docs/build-status/</code></td><td>full findings, evidence, fix hints</td></tr><tr><td>sidecar JSON</td><td><code>src/observability/renderers/sidecar.ts</code></td><td>alongside the markdown</td><td>redacted <code>EngineOutput</code>; read by the <code>audit_history</code> adapter</td></tr><tr><td>dashboard fragment</td><td><code>src/observability/renderers/dashboard.ts</code></td><td>stdout → dashboard</td><td>sections <code>build-progress</code> / <code>build-audit</code> injected by <code>scripts/generate-dashboard.sh</code></td></tr><tr><td>mmr-findings</td><td><code>src/observability/renderers/mmr-findings.ts</code></td><td>stdout</td><td>MMR Finding shape</td></tr></tbody></table>
2301
+ <h2 id="config-reference">Config reference</h2>
2302
+ <p>All tunables live in <code>.scaffold/observability.yaml</code>. The file is optional — when
2303
+ absent, <code>DEFAULT_CONFIG</code> applies wholesale
2304
+ (<code>src/observability/engine/checks/observability-config.ts</code>). A malformed or
2305
+ unreadable file <em>silently</em> falls back to defaults. Merging is a deep merge where
2306
+ objects merge key-by-key but <strong>arrays and scalars replace wholesale</strong> — so
2307
+ setting <code>disabled_lenses</code> or <code>rule_overrides</code> overwrites the default rather than
2308
+ extending it.</p>
2309
+ <div class="callout callout-warning"><p><strong>The shipped example file is misleading.</strong> The repo's
2310
+ <code>.scaffold/observability.yaml</code> contains only <code>knowledge_freshness.link_check.skip</code>
2311
+ and a comment claiming "other observability features read configuration from
2312
+ elsewhere." In fact the code reads many more keys — the example file simply omits
2313
+ them because they all have working defaults. The defaults documented here come
2314
+ from the code, which is ground truth.</p></div>
2315
+ <div class="filter-table"><input type="text" class="filter-input" placeholder="Filter…" aria-label="Filter table rows" disabled>
2316
+
2317
+
2318
+
2319
+
2320
+
2321
+
2322
+
2323
+
2324
+
2325
+
2326
+
2327
+
2328
+
2329
+
2330
+
2331
+
2332
+
2333
+
2334
+
2335
+
2336
+
2337
+
2338
+
2339
+
2340
+
2341
+
2342
+
2343
+
2344
+
2345
+
2346
+
2347
+
2348
+
2349
+
2350
+
2351
+
2352
+
2353
+
2354
+
2355
+
2356
+
2357
+
2358
+
2359
+
2360
+
2361
+
2362
+
2363
+
2364
+
2365
+
2366
+
2367
+
2368
+
2369
+
2370
+
2371
+
2372
+
2373
+
2374
+
2375
+
2376
+
2377
+
2378
+
2379
+
2380
+
2381
+
2382
+
2383
+
2384
+
2385
+
2386
+
2387
+
2388
+
2389
+
2390
+
2391
+
2392
+
2393
+
2394
+
2395
+
2396
+
2397
+
2398
+
2399
+
2400
+
2401
+
2402
+
2403
+
2404
+
2405
+
2406
+
2407
+
2408
+
2409
+
2410
+
2411
+
2412
+
2413
+
2414
+
2415
+
2416
+
2417
+
2418
+
2419
+
2420
+
2421
+
2422
+
2423
+
2424
+
2425
+
2426
+
2427
+
2428
+
2429
+
2430
+
2431
+
2432
+
2433
+
2434
+
2435
+
2436
+
2437
+
2438
+
2439
+
2440
+
2441
+
2442
+
2443
+
2444
+
2445
+
2446
+
2447
+
2448
+
2449
+
2450
+
2451
+
2452
+
2453
+
2454
+
2455
+
2456
+
2457
+
2458
+
2459
+
2460
+
2461
+
2462
+
2463
+
2464
+
2465
+
2466
+
2467
+
2468
+
2469
+
2470
+
2471
+
2472
+
2473
+
2474
+
2475
+
2476
+ <table><thead><tr><th>key</th><th>type</th><th>default</th><th>read by</th></tr></thead><tbody><tr><td><code>lenses.C-standards.enforce_via_linter</code></td><td>boolean</td><td><code>true</code></td><td>(accepted, not read)</td></tr><tr><td><code>lenses.C-standards.rule_overrides</code></td><td>map&#x3C;rule, P0–P3></td><td><code>{}</code></td><td>lenses · Lens C</td></tr><tr><td><code>lenses.C-standards.escalation_threshold</code></td><td>number</td><td><code>5</code></td><td>lenses · Lens C</td></tr><tr><td><code>lenses.D-stack.path_to_layer</code></td><td>{glob,layer}[]</td><td>7 default mappings</td><td>lenses · Lens D</td></tr><tr><td><code>lenses.E-design.ad_hoc_token_threshold</code></td><td>number</td><td><code>3</code></td><td>lenses · Lens E</td></tr><tr><td><code>lenses.E-design.ui_glob</code></td><td>glob</td><td><code>src/components/**/*.tsx</code></td><td>lenses · doc-graph builder</td></tr><tr><td><code>lenses.F-scope.untouched_story_grace_hours</code></td><td>number</td><td><code>168</code></td><td>lenses · Lens F</td></tr><tr><td><code>lenses.G-decisions.keywords_file</code></td><td>path</td><td>— (inert)</td><td>lenses · (no reader)</td></tr><tr><td><code>lenses.H-cross-doc.skip_phase_subsets</code></td><td>string[]</td><td>— (inert)</td><td>lenses · (no reader)</td></tr><tr><td><code>lenses.I-knowledge-gaps.knowledge_root</code></td><td>path</td><td>auto-detect</td><td>lenses · Lens I</td></tr><tr><td><code>disabled_lenses</code></td><td>string[]</td><td><code>[]</code></td><td>lenses · runner</td></tr><tr><td><code>phase_audit.enabled</code></td><td>boolean</td><td><code>true</code></td><td>phase_audit</td></tr><tr><td><code>phase_audit.timeout_s</code></td><td>number</td><td><code>60</code></td><td>phase_audit</td></tr><tr><td><code>phase_audit.detached</code></td><td>boolean</td><td><code>false</code></td><td>phase_audit</td></tr><tr><td><code>stall.task_stale</code></td><td>'Nh'·'Nd'·'off'</td><td><code>4h</code></td><td>stall</td></tr><tr><td><code>stall.pr_stale</code></td><td>'Nh'·'Nd'·'off'</td><td><code>48h</code></td><td>stall</td></tr><tr><td><code>stall.pr_review_stale</code></td><td>'Nh'·'Nd'·'off'</td><td><code>24h</code> (reserved)</td><td>stall (signal not yet emitted)</td></tr><tr><td><code>stall.blocker_unaddressed</code></td><td>'Nh'·'Nd'·'off'</td><td><code>2h</code></td><td>stall</td></tr><tr><td><code>stall.audit_findings_unresolved</code></td><td>'Nh'·'Nd'·'off'</td><td><code>24h</code></td><td>stall</td></tr><tr><td><code>llm.timeout_s</code></td><td>number</td><td><code>60</code></td><td>llm · Lens H dispatcher</td></tr><tr><td><code>llm.parallel_checks</code></td><td>boolean</td><td><code>false</code></td><td>llm · Lens H</td></tr><tr><td><code>fix.dispatcher_command</code></td><td>shell command</td><td><code>claude -p</code></td><td>fix · dispatcher</td></tr><tr><td><code>fix.timeout_s</code></td><td>number</td><td><code>300</code></td><td>fix</td></tr><tr><td><code>fix.per_finding_max_attempts</code></td><td>number</td><td><code>3</code></td><td>fix</td></tr><tr><td><code>knowledge_freshness.link_check.skip</code></td><td>string[]</td><td><code>[]</code></td><td>knowledge-freshness (separate loader)</td></tr></tbody></table></div>
2477
+ <div class="callout callout-note"><p><strong>No <code>llm.dispatcher_command</code>, no <code>F-scope.wave_budget</code>.</strong> Two keys you might
2478
+ expect are deliberately absent. The Lens H LLM command is hard-coded (security) —
2479
+ there is no <code>llm.dispatcher_command</code> key. And there is no wave/phase-budget key at
2480
+ all: <code>F-scope</code> reads only <code>untouched_story_grace_hours</code> from this file, so the
2481
+ "wave budget" CLAUDE.md implies does not exist in the config.</p></div>
2482
+ <p>Example:</p>
2483
+ <pre><code class="language-yaml">lenses:
2484
+ C-standards:
2485
+ enforce_via_linter: true
2486
+ rule_overrides:
2487
+ no-console: P1
2488
+ E-design:
2489
+ ad_hoc_token_threshold: 5
2490
+ disabled_lenses: []
2491
+ phase_audit:
2492
+ enabled: true
2493
+ timeout_s: 60
2494
+ stall:
2495
+ task_stale: '6h'
2496
+ fix:
2497
+ per_finding_max_attempts: 3
2498
+ # shared with the knowledge-freshness subsystem
2499
+ knowledge_freshness:
2500
+ link_check:
2501
+ skip:
2502
+ - platform.openai.com
2503
+ </code></pre>
2504
+ <h2 id="where-it-lives">Where it lives</h2>
2505
+ <p>The whole system lives under <code>src/observability/</code>, plus the state-manager hook
2506
+ and two shell scripts.</p>
2507
+
2508
+
2509
+
2510
+
2511
+
2512
+
2513
+
2514
+
2515
+
2516
+
2517
+
2518
+
2519
+
2520
+
2521
+
2522
+
2523
+
2524
+
2525
+
2526
+
2527
+
2528
+
2529
+
2530
+
2531
+
2532
+
2533
+
2534
+
2535
+
2536
+
2537
+
2538
+
2539
+
2540
+
2541
+
2542
+
2543
+
2544
+
2545
+
2546
+
2547
+
2548
+
2549
+
2550
+
2551
+
2552
+
2553
+
2554
+
2555
+
2556
+
2557
+
2558
+
2559
+
2560
+
2561
+
2562
+
2563
+
2564
+
2565
+
2566
+
2567
+
2568
+
2569
+
2570
+
2571
+
2572
+
2573
+
2574
+
2575
+
2576
+
2577
+
2578
+
2579
+
2580
+
2581
+
2582
+
2583
+
2584
+
2585
+
2586
+
2587
+
2588
+
2589
+
2590
+
2591
+
2592
+
2593
+
2594
+
2595
+
2596
+
2597
+
2598
+
2599
+
2600
+
2601
+
2602
+
2603
+
2604
+
2605
+
2606
+
2607
+
2608
+
2609
+
2610
+
2611
+
2612
+
2613
+
2614
+
2615
+
2616
+
2617
+
2618
+
2619
+
2620
+
2621
+
2622
+
2623
+
2624
+
2625
+
2626
+
2627
+
2628
+
2629
+
2630
+
2631
+
2632
+
2633
+
2634
+
2635
+
2636
+
2637
+
2638
+
2639
+
2640
+
2641
+
2642
+
2643
+
2644
+
2645
+
2646
+
2647
+
2648
+
2649
+
2650
+
2651
+
2652
+
2653
+
2654
+
2655
+
2656
+
2657
+
2658
+
2659
+
2660
+
2661
+
2662
+
2663
+
2664
+
2665
+
2666
+
2667
+
2668
+
2669
+
2670
+
2671
+
2672
+
2673
+
2674
+
2675
+
2676
+ <table><thead><tr><th>file</th><th>purpose</th></tr></thead><tbody><tr><td><code>src/observability/engine/ledger-writer.ts</code></td><td>validate → redact → 4 KiB check → locked append</td></tr><tr><td><code>src/observability/engine/event-schemas.ts</code></td><td>per-type payload allow-lists + validation</td></tr><tr><td><code>src/observability/engine/redact.ts</code></td><td>write-time + render-time secret/path scrubbing</td></tr><tr><td><code>src/observability/engine/identity.ts</code></td><td>worktree identity.json</td></tr><tr><td><code>src/observability/engine/harvester.ts</code></td><td>harvest worktree ledger + <code>--recover</code> rotation</td></tr><tr><td><code>src/observability/engine/synthesizer.ts</code></td><td>composeReplay: fuse + correlation_id dedup + source priority</td></tr><tr><td><code>src/observability/engine/api.ts</code></td><td>runAudit / runProgress; scope→lens; verdict</td></tr><tr><td><code>src/observability/engine/stall.ts</code></td><td>the stall signals + Needs-Attention surface</td></tr><tr><td><code>src/observability/engine/phase-audit.ts</code></td><td>runPhaseAudit + formatPhaseAuditLine</td></tr><tr><td><code>src/observability/engine/phase-subsets.ts</code></td><td>PHASE_BOUNDARY_STEPS + isPhaseBoundary</td></tr><tr><td><code>src/observability/engine/fix-flow.ts</code></td><td>the five-phase <code>--fix</code> loop</td></tr><tr><td><code>src/observability/engine/fix-plan.ts</code></td><td>buildFixPlan: filter + order findings</td></tr><tr><td><code>src/observability/engine/fix-agent-dispatcher.ts</code></td><td>configurable subprocess fix dispatcher</td></tr><tr><td><code>src/observability/engine/abort-snapshot.ts</code></td><td>capture/restore git stash on SIGINT</td></tr><tr><td><code>src/observability/engine/llm-dispatcher.ts</code></td><td>hard-coded <code>claude -p</code> for Lens H full profile</td></tr><tr><td><code>src/observability/engine/types.ts</code></td><td>Verdict, AdapterId, EngineOutput, Finding</td></tr><tr><td><code>src/observability/engine/checks/registry.ts</code></td><td>LENS_REGISTRY (A–I) + implementations</td></tr><tr><td><code>src/observability/engine/checks/fix-threshold.ts</code></td><td>resolve fix_threshold (default P2)</td></tr><tr><td><code>src/observability/engine/checks/findings-aggregator.ts</code></td><td>tally blocking/acked; compute verdict</td></tr><tr><td><code>src/observability/engine/checks/observability-config.ts</code></td><td>DEFAULT_CONFIG + deep-merge loader</td></tr><tr><td><code>src/observability/engine/doc-graph/index.ts</code></td><td>build the doc graph from planning docs</td></tr><tr><td><code>src/observability/checks/lens-a-tdd.ts</code></td><td>A · skipped tests</td></tr><tr><td><code>src/observability/checks/lens-b-ac-coverage.ts</code></td><td>B · AC ↔ test coverage</td></tr><tr><td><code>src/observability/checks/lens-c-standards.ts</code></td><td>C · coding-standards drift</td></tr><tr><td><code>src/observability/checks/lens-d-stack.ts</code></td><td>D · tech-stack drift</td></tr><tr><td><code>src/observability/checks/lens-e-design.ts</code></td><td>E · design-system drift</td></tr><tr><td><code>src/observability/checks/lens-f-scope.ts</code></td><td>F · missing scope</td></tr><tr><td><code>src/observability/checks/lens-g-decisions.ts</code></td><td>G · undocumented decisions</td></tr><tr><td><code>src/observability/checks/lens-h-cross-doc.ts</code></td><td>H · cross-doc consistency + LLM checks</td></tr><tr><td><code>src/observability/checks/lens-i-knowledge-gaps.ts</code></td><td>I · knowledge gaps</td></tr><tr><td><code>src/observability/checks/lens-i-lessons-scanner.ts</code></td><td>scan tasks/lessons.md for gap signals</td></tr><tr><td><code>src/observability/knowledge-index.ts</code></td><td>resolveKnowledgeRoot (3 tiers) + index for Lens I</td></tr><tr><td><code>src/observability/renderers/terminal.ts</code></td><td>ANSI output</td></tr><tr><td><code>src/observability/renderers/markdown.ts</code></td><td>markdown report</td></tr><tr><td><code>src/observability/renderers/dashboard.ts</code></td><td>build-progress / build-audit HTML fragments</td></tr><tr><td><code>src/observability/renderers/sidecar.ts</code></td><td>JSON sidecar + report-id derivation</td></tr><tr><td><code>src/observability/renderers/mmr-findings.ts</code></td><td>MMR Finding shape</td></tr><tr><td><code>src/state/state-manager.ts</code></td><td>async markCompleted → runPhaseAudit hook</td></tr><tr><td><code>scripts/teardown-agent-worktree.sh</code></td><td>harvest → remove → delete branch</td></tr><tr><td><code>scripts/generate-dashboard.sh</code></td><td>injects the dashboard fragments</td></tr></tbody></table>
2677
+ <p>The full design lives in
2678
+ <code>docs/superpowers/specs/2026-04-30-build-observability-design.md</code>.</p>
2679
+ <h2 id="shipping-history">Shipping history</h2>
2680
+ <p>Build Observability shipped as eight plans (Foundation → Fix-flow), released
2681
+ together as <code>v3.26.0 — Build Observability</code> on 2026-05-07, with Lens I and
2682
+ refinements following after.</p>
2683
+ <div class="filter-table"><input type="text" class="filter-input" placeholder="Filter…" aria-label="Filter table rows" disabled>
2684
+
2685
+
2686
+
2687
+
2688
+
2689
+
2690
+
2691
+
2692
+
2693
+
2694
+
2695
+
2696
+
2697
+
2698
+
2699
+
2700
+
2701
+
2702
+
2703
+
2704
+
2705
+
2706
+
2707
+
2708
+
2709
+
2710
+
2711
+
2712
+
2713
+
2714
+
2715
+
2716
+
2717
+
2718
+
2719
+
2720
+
2721
+
2722
+
2723
+
2724
+
2725
+
2726
+
2727
+
2728
+
2729
+
2730
+
2731
+
2732
+
2733
+
2734
+
2735
+
2736
+
2737
+
2738
+
2739
+
2740
+
2741
+
2742
+
2743
+
2744
+
2745
+
2746
+
2747
+
2748
+
2749
+
2750
+
2751
+
2752
+
2753
+
2754
+
2755
+
2756
+
2757
+
2758
+
2759
+
2760
+
2761
+
2762
+
2763
+
2764
+
2765
+
2766
+
2767
+
2768
+
2769
+
2770
+
2771
+
2772
+
2773
+
2774
+
2775
+
2776
+
2777
+
2778
+
2779
+ <table><thead><tr><th>plan</th><th>subsystem</th><th>squash</th><th>PR</th><th>added to the on-disk surface</th></tr></thead><tbody><tr><td>spec + plans</td><td>design doc + 8 plans</td><td><code>5cf689c4</code></td><td>#319</td><td><code>docs/superpowers/specs</code> + 8 plan files</td></tr><tr><td>1 · Foundation</td><td>identity, validation, redaction, ledger, harvester, adapters, synthesizer, API, CLI, renderers</td><td><code>21124010</code> (+ #320–328)</td><td>#320–329</td><td>most of <code>src/observability/engine</code>, <code>adapters</code>, <code>renderers</code>; <code>scaffold observe</code></td></tr><tr><td>2 · Audit MVP</td><td>doc-graph, checks framework, lenses A/B/H, <code>audit</code>+<code>ack</code></td><td><code>8eb70986</code></td><td>#331</td><td><code>engine/doc-graph</code>, <code>engine/checks</code>, <code>lens-a/b/h</code></td></tr><tr><td>3 · Full lens suite</td><td>lenses C/D/E/F/G + registry + config</td><td><code>bd014634</code></td><td>#332</td><td><code>lens-c…g</code>, <code>registry.ts</code>, <code>.scaffold/observability.yaml</code></td></tr><tr><td>4 · Renderers + history</td><td>markdown, JSON sidecars, dashboard fragments, audit-history</td><td><code>37f63ae4</code></td><td>#333</td><td><code>docs/build-status</code>, <code>docs/audits</code>, <code>audit-history</code> adapter</td></tr><tr><td>5 · Replay + stall</td><td>replay timeline, stall detection, Lens G keyword scan</td><td><code>cdbfd1de</code></td><td>#334</td><td><code>synthesizer.composeReplay</code>, <code>stall.ts</code>, Needs-Attention surfaces</td></tr><tr><td>6 · Phase triggers</td><td><code>markCompleted</code> async → <code>runPhaseAudit</code></td><td><code>ff04a1a6</code></td><td>#335</td><td><code>phase-audit.ts</code>, <code>phase-subsets.ts</code>, step timestamps</td></tr><tr><td>7 · MMR channel</td><td><code>doc-conformance</code> channel + Lens H full-profile LLM checks</td><td><code>8b723617</code></td><td>#336</td><td><code>mmr-findings.ts</code>, <code>llm-dispatcher.ts</code>, Lens H sub-checks</td></tr><tr><td>8 · Fix flow + teardown</td><td><code>--fix</code> loop, <code>harvest --recover</code>, teardown script</td><td><code>06fca3ef</code> (v3.26.0)</td><td>#337</td><td><code>fix-flow.ts</code>, <code>fix-plan.ts</code>, <code>fix-agent-dispatcher.ts</code>, <code>abort-snapshot.ts</code>, <code>teardown-agent-worktree.sh</code></td></tr><tr><td>follow-on</td><td>Lens I — knowledge gaps + lessons scanner</td><td><code>31e45f03</code></td><td>#397</td><td><code>lens-i-knowledge-gaps.ts</code>, <code>lens-i-lessons-scanner.ts</code></td></tr><tr><td>follow-on</td><td>Lens I — <code>--knowledge-root</code> + existing-entry suppression</td><td><code>46a47037</code></td><td>#406</td><td><code>knowledge-index.ts</code> resolver tiers</td></tr><tr><td>follow-on</td><td>TypeScript cleanup (<code>as never</code> → typed <code>isOneOf</code>)</td><td><code>362c7f93</code></td><td>#411</td><td>type-safety refactor across observability</td></tr></tbody></table></div>
2780
+ <h2 id="known-divergences">Known divergences</h2>
2781
+ <p>The audit's own rule — "code is ground truth where it disagrees with the spec or
2782
+ the docs" — surfaced these mismatches. They are deferred findings to resolve in
2783
+ the code/docs:</p>
2784
+ <ul>
2785
+ <li><strong>9 lenses, not 8.</strong> The design spec and CLAUDE.md enumerate A–H;
2786
+ <code>I-knowledge-gaps</code> shipped and is registered.</li>
2787
+ <li><strong><code>pr_opened</code> vs <code>pr_open</code>.</strong> CLAUDE.md abbreviates the event type as
2788
+ <code>pr_open</code>; the code only emits <code>pr_opened</code>.</li>
2789
+ <li><strong>Three engine verdicts, not four.</strong> <code>needs-user-decision</code> is an MMR review
2790
+ verdict; the audit engine emits only <code>pass</code> / <code>degraded-pass</code> / <code>blocked</code>.</li>
2791
+ <li><strong>Two dispatchers — don't conflate them.</strong> CLAUDE.md's "not
2792
+ project-config-overridable" note is about the <strong>Lens H LLM dispatcher</strong>
2793
+ (<code>llm-dispatcher.ts</code>, hard-coded <code>claude -p</code> by design). It does <strong>not</strong> apply
2794
+ to the separate <strong><code>--fix</code> agent dispatcher</strong>, whose <code>fix.dispatcher_command</code>
2795
+ <em>is</em> project-configurable. The divergence is that the two are easy to conflate.</li>
2796
+ <li><strong><code>pr_review_stale</code> reserved.</strong> The sixth stall signal has a config default but
2797
+ is not yet emitted (deferred pending a per-PR correlation_id on the mmr
2798
+ adapter).</li>
2799
+ <li><strong>Inert config keys.</strong> <code>lenses.G-decisions.keywords_file</code> and
2800
+ <code>lenses.H-cross-doc.skip_phase_subsets</code> are typed but unread; the bundled
2801
+ <code>decision-keywords.txt</code> is not loaded.</li>
2802
+ <li><strong>No <code>F-scope.wave_budget</code> key.</strong> CLAUDE.md implies a wave budget in
2803
+ <code>observability.yaml</code>; in fact no wave/phase budget is read anywhere — Lens F
2804
+ reads only <code>untouched_story_grace_hours</code>.</li>
2805
+ <li><strong><code>ui_glob</code> default narrower than spec.</strong> Spec default covers
2806
+ <code>{tsx,jsx,vue,svelte}</code> + styles; shipped default is just
2807
+ <code>src/components/**/*.tsx</code>.</li>
2808
+ </ul></main>
2809
+ </div>
2810
+ <script>(function(){
2811
+ var LS_KEY = 'guide-theme';
2812
+ function applyTheme(t) {
2813
+ document.documentElement.setAttribute('data-theme', t);
2814
+ }
2815
+
2816
+ document.addEventListener('DOMContentLoaded', function() {
2817
+ // ─── Theme toggle ────────────────────────────────────────────────────────
2818
+ document.querySelectorAll('[data-action="theme"]').forEach(function(btn) {
2819
+ btn.addEventListener('click', function() {
2820
+ var current = document.documentElement.getAttribute('data-theme');
2821
+ var next = current === 'dark' ? 'light' : 'dark';
2822
+ applyTheme(next);
2823
+ try { localStorage.setItem(LS_KEY, next); } catch(e) {}
2824
+ });
2825
+ });
2826
+
2827
+ // ─── Mobile nav ──────────────────────────────────────────────────────────
2828
+ document.querySelectorAll('[data-action="nav"]').forEach(function(btn) {
2829
+ btn.addEventListener('click', function() {
2830
+ var rail = document.querySelector('.rail');
2831
+ if (rail) rail.classList.toggle('open');
2832
+ });
2833
+ });
2834
+
2835
+ // ─── Copy buttons ─────────────────────────────────────────────────────────
2836
+ document.querySelectorAll('pre').forEach(function(pre) {
2837
+ if (!pre.parentNode) return;
2838
+ var wrapper = document.createElement('div');
2839
+ wrapper.className = 'code';
2840
+ pre.parentNode.insertBefore(wrapper, pre);
2841
+ wrapper.appendChild(pre);
2842
+ var btn = document.createElement('button');
2843
+ btn.className = 'copy-btn';
2844
+ btn.textContent = 'Copy';
2845
+ btn.addEventListener('click', function() {
2846
+ var text = pre.textContent || '';
2847
+ if (navigator.clipboard && navigator.clipboard.writeText) {
2848
+ navigator.clipboard.writeText(text).then(function() {
2849
+ btn.textContent = 'Copied';
2850
+ setTimeout(function() { btn.textContent = 'Copy'; }, 1200);
2851
+ }, function() {
2852
+ btn.textContent = 'Copy';
2853
+ });
2854
+ }
2855
+ });
2856
+ wrapper.insertBefore(btn, pre);
2857
+ });
2858
+
2859
+ // ─── Tabs ─────────────────────────────────────────────────────────────────
2860
+ document.querySelectorAll('.tabs').forEach(function(group) {
2861
+ group.querySelectorAll('.tab-btn').forEach(function(btn) {
2862
+ btn.addEventListener('click', function() {
2863
+ var idx = btn.getAttribute('data-tab');
2864
+ group.querySelectorAll('.tab-btn').forEach(function(b) {
2865
+ b.classList.toggle('active', b === btn);
2866
+ });
2867
+ group.querySelectorAll('.tabpane').forEach(function(pane) {
2868
+ pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
2869
+ });
2870
+ });
2871
+ });
2872
+ });
2873
+
2874
+ // ─── Filter tables ────────────────────────────────────────────────────────
2875
+ document.querySelectorAll('.filter-input').forEach(function(input) {
2876
+ input.addEventListener('input', function() {
2877
+ var q = input.value.toLowerCase();
2878
+ var container = input.closest('.filter-table');
2879
+ if (!container) return;
2880
+ container.querySelectorAll('tbody tr').forEach(function(row) {
2881
+ var text = (row.textContent || '').toLowerCase();
2882
+ row.style.display = text.includes(q) ? '' : 'none';
2883
+ });
2884
+ });
2885
+ });
2886
+
2887
+ // ─── Scrollspy ────────────────────────────────────────────────────────────
2888
+ if (typeof IntersectionObserver === 'undefined') return;
2889
+ var headings = document.querySelectorAll('h2[id],h3[id]');
2890
+ if (!headings.length) return;
2891
+ var observer = new IntersectionObserver(function(entries) {
2892
+ entries.forEach(function(entry) {
2893
+ if (!entry.isIntersecting) return;
2894
+ var id = entry.target.getAttribute('id');
2895
+ document.querySelectorAll('.toc a').forEach(function(a) {
2896
+ a.classList.toggle('active', a.getAttribute('href') === '#' + id);
2897
+ });
2898
+ });
2899
+ }, { rootMargin: '0px 0px -70% 0px', threshold: 0 });
2900
+ headings.forEach(function(h) { observer.observe(h); });
2901
+ });
2902
+ })();</script>
2903
+ </body>
2904
+ </html>