@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,2442 @@
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>Knowledge Freshness</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>Knowledge Freshness</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="#what-this-system-does">What this system does</a></li><li class="toc-3"><a href="#how-a-gap-closes">How a gap closes</a></li><li class="toc-2"><a href="#system-map">System map</a></li><li class="toc-2"><a href="#frontmatter-signals-and-resolution">Frontmatter, signals, and resolution</a></li><li class="toc-3"><a href="#frontmatter-schema">Frontmatter schema</a></li><li class="toc-3"><a href="#cadence-model">Cadence model</a></li><li class="toc-3"><a href="#adding-a-new-entry-to-the-kb">Adding a new entry to the KB</a></li><li class="toc-3"><a href="#gap-signal-payload">Gap-signal payload</a></li><li class="toc-3"><a href="#knowledgerootresolution-shape">KnowledgeRootResolution shape</a></li><li class="toc-2"><a href="#from-candidate-to-merged-pr">From candidate to merged PR</a></li><li class="toc-3"><a href="#prefilter">Prefilter</a></li><li class="toc-3"><a href="#audit-verdicts">Audit verdicts</a></li><li class="toc-3"><a href="#pr-generation">PR generation</a></li><li class="toc-3"><a href="#mmr-corroboration-manual">MMR corroboration (manual)</a></li><li class="toc-2"><a href="#the-five-pr-gates">The five PR gates</a></li><li class="toc-2"><a href="#lens-i-gap-detection-suppression">Lens I — gap detection + suppression</a></li><li class="toc-3"><a href="#threshold-matrix">Threshold matrix</a></li><li class="toc-3"><a href="#topic-normalization">Topic normalization</a></li><li class="toc-3"><a href="#what-the-lessonsmd-scanner-sees">What the lessons.md scanner sees</a></li><li class="toc-3"><a href="#3-tier---knowledge-root-resolution">3-tier --knowledge-root resolution</a></li><li class="toc-3"><a href="#warning-policy">Warning policy</a></li><li class="toc-3"><a href="#what-a-lens-i-finding-looks-like">What a Lens I finding looks like</a></li><li class="toc-2"><a href="#the-allowlist">The allowlist</a></li><li class="toc-3"><a href="#most-cited-hosts">Most-cited hosts</a></li><li class="toc-3"><a href="#the-full-allowlist">The full allowlist</a></li><li class="toc-3"><a href="#kb-inventory">KB inventory</a></li><li class="toc-3"><a href="#how-to-expand-the-allowlist">How to expand the allowlist</a></li><li class="toc-2"><a href="#anthropic-vs-deepseek-cron-uses-deepseek">Anthropic vs DeepSeek (cron uses DeepSeek)</a></li><li class="toc-2"><a href="#every-command-that-touches-the-system">Every command that touches the system</a></li><li class="toc-3"><a href="#refresh-arm-commands">Refresh-arm commands</a></li><li class="toc-3"><a href="#gap-arm-commands">Gap-arm commands</a></li><li class="toc-3"><a href="#gate-side-subcommands-also-runnable-locally-for-triage">Gate-side subcommands (also runnable locally for triage)</a></li><li class="toc-2"><a href="#operations-cheat-sheet">Operations cheat sheet</a></li><li class="toc-3"><a href="#an-entrys-audit-failed-in-the-cron">An entry's audit failed in the cron</a></li><li class="toc-3"><a href="#lens-i-keeps-surfacing-a-topic-the-kb-already-covers">Lens I keeps surfacing a topic the KB already covers</a></li><li class="toc-3"><a href="#downstream-auto-detect-cant-find-the-kb">Downstream auto-detect can't find the KB</a></li><li class="toc-3"><a href="#yaml-knowledge-root-stops-working-after-an-upgrade">Yaml knowledge_root stops working after an upgrade</a></li><li class="toc-3"><a href="#a-source-url-fetches-in-curl-but-the-cron-rejects-it">A source URL fetches in curl but the cron rejects it</a></li><li class="toc-3"><a href="#--knowledge-root-resolves-to-a-path-you-didnt-expect">--knowledge-root resolves to a path you didn't expect</a></li><li class="toc-2"><a href="#config-reference">Config reference</a></li><li class="toc-2"><a href="#roadmap-and-known-divergences">Roadmap and known divergences</a></li><li class="toc-3"><a href="#phase-5-planned">Phase 5 (planned)</a></li><li class="toc-3"><a href="#known-divergences">Known divergences</a></li></ul></nav></aside>
1092
+ <main class="content"><h2 id="what-this-system-does">What this system does</h2>
1093
+ <p>Knowledge entries under <code>content/knowledge/</code> declare a <code>volatility</code> tier and a
1094
+ list of <code>sources</code>. A daily cron prefilters at most ten entries that are <em>due</em> —
1095
+ by cadence or by a changed source hash — runs a grounded LLM audit against the
1096
+ prefetched source bodies, opens one PR per drifted entry, and gates that PR on
1097
+ five checks. In parallel, downstream agents emit <code>knowledge_gap_signal</code> events
1098
+ when they hit a topic the KB does not cover; <strong>Lens I</strong> aggregates those signals
1099
+ into P1/P2 audit findings, suppressing any topic an entry already covers.</p>
1100
+ <p>Two arms, two outcomes:</p>
1101
+ <ul>
1102
+ <li>The <strong>refresh arm</strong> chases <em>known</em> sources for drift. It ends in a PR that
1103
+ <em>updates</em> an entry.</li>
1104
+ <li>The <strong>gap arm</strong> surfaces <em>unknown</em> topics. It ends in a PR that <em>creates</em> an
1105
+ entry.</li>
1106
+ </ul>
1107
+ <p>Both terminate in a human-merged PR.</p>
1108
+
1109
+
1110
+
1111
+
1112
+
1113
+
1114
+
1115
+
1116
+
1117
+
1118
+
1119
+
1120
+
1121
+
1122
+
1123
+
1124
+
1125
+
1126
+
1127
+
1128
+
1129
+
1130
+
1131
+
1132
+
1133
+
1134
+
1135
+
1136
+
1137
+
1138
+
1139
+
1140
+
1141
+
1142
+
1143
+ <table><thead><tr><th>Surface</th><th>Value</th><th>Notes</th></tr></thead><tbody><tr><td>Volatility tiers</td><td>3</td><td><code>fast-moving</code> / <code>evolving</code> / <code>stable</code></td></tr><tr><td>Audit verdicts</td><td>4</td><td><code>current</code> / <code>minor-drift</code> / <code>major-drift</code> / <code>superseded</code></td></tr><tr><td>Daily audit ceiling</td><td>10</td><td>set by <code>--max=10</code> in the cron workflow; not a yaml knob</td></tr><tr><td>PR gates</td><td>5</td><td>4 blocking + 1 advisory</td></tr><tr><td>Signal window</td><td>90 days</td><td>rolling; drives Lens I aggregation</td></tr></tbody></table>
1144
+ <div class="callout callout-note"><p><strong>Two subsystems, one config file.</strong> Knowledge Freshness and the separate
1145
+ <a href="../observability/index.md">Build Observability</a> system both read
1146
+ <code>.scaffold/observability.yaml</code>. This guide documents Knowledge Freshness;
1147
+ Lens I is the seam where the two meet (it lives in the observability audit but
1148
+ reasons about the KB).</p></div>
1149
+ <h3 id="how-a-gap-closes">How a gap closes</h3>
1150
+ <p>The full lifecycle, end to end:</p>
1151
+ <ol>
1152
+ <li>Downstream agents emit signals; they accumulate in the rolling 90-day window.</li>
1153
+ <li>A topic's signal count and distinct-project count cross the threshold.</li>
1154
+ <li>Lens I emits a P1/P2 finding.</li>
1155
+ <li>An operator adds <code>content/knowledge/&#x3C;category>/&#x3C;slug>.md</code>.</li>
1156
+ <li>The next audit's knowledge index covers the slug and Lens I <strong>suppresses</strong> the
1157
+ bucket — the finding disappears.</li>
1158
+ </ol>
1159
+ <p>Signals are <em>not</em> purged when the entry is added. The window is rolling, so
1160
+ yesterday's signals still aggregate tomorrow; suppression filters the <em>emit</em>
1161
+ step, not the aggregation step (<span class="fp" data-path="src/observability/checks/lens-i-knowledge-gaps.ts:155">src/observability/checks/lens-i-knowledge-gaps.ts:155</span>).
1162
+ Signals only fade as they age out of the 90-day window naturally.</p>
1163
+ <h2 id="system-map">System map</h2>
1164
+ <figure class="mermaid"><svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 951.219px; background-color: transparent;" viewBox="0 0 951.21875 940.3999633789062" 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-gap"><rect style="" x="390.40625" y="8" width="552.8125" height="625.5999908447266"></rect><g class="cluster-label" transform="translate(636.375, 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">Gap</tspan><tspan class="text-inner-tspan"> arm</tspan></tspan></text></g></g></g><g class="cluster" id="my-svg-refresh"><rect style="" x="8" y="149.5999984741211" width="362.40625" height="782.7999877929688"></rect><g class="cluster-label" transform="translate(146.1015625, 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">Refresh</tspan><tspan class="text-inner-tspan"> arm</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M132.684,241.2L132.684,245.367C132.684,249.533,132.684,257.867,132.684,267C132.684,276.133,132.684,286.067,132.684,291.033L132.684,296" id="my-svg-L_CRON_PF_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="M132.684,366.6L132.684,372.233C132.684,377.867,132.684,389.133,132.684,398.267C132.684,407.4,132.684,414.4,132.684,417.9L132.684,421.4" id="my-svg-L_PF_RUN_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="M132.684,492L132.684,496.167C132.684,500.333,132.684,508.667,132.684,516.333C132.684,524,132.684,531,132.684,534.5L132.684,538" id="my-svg-L_RUN_APPLY_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="M132.684,608.6L132.684,612.767C132.684,616.933,132.684,625.267,132.684,638.45C132.684,651.633,132.684,669.667,138.932,687.163C145.18,704.66,157.676,721.62,163.925,730.1L170.173,738.58" id="my-svg-L_APPLY_GATES_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="M190.598,790.8L190.598,794.967C190.598,799.133,190.598,807.467,190.598,815.133C190.598,822.8,190.598,829.8,190.598,833.3L190.598,836.8" id="my-svg-L_GATES_MERGE_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="M536.594,99.6L536.594,103.767C536.594,107.933,536.594,116.267,536.594,124.6C536.594,132.933,536.594,141.267,536.594,148.933C536.594,156.6,536.594,163.6,536.594,167.1L536.594,170.6" id="my-svg-L_TAIL_EVENT_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="M536.594,241.2L536.594,245.367C536.594,249.533,536.594,257.867,536.594,267C536.594,276.133,536.594,286.067,536.594,291.033L536.594,296" id="my-svg-L_EVENT_LEDGER_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="M536.594,366.6L536.594,372.233C536.594,377.867,536.594,389.133,544.883,398.65C553.172,408.168,569.751,415.935,578.04,419.819L586.329,423.703" id="my-svg-L_LEDGER_LENSI_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="M661.023,492L661.023,496.167C661.023,500.333,661.023,508.667,661.023,516.333C661.023,524,661.023,531,661.023,534.5L661.023,538" id="my-svg-L_LENSI_FINDING_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="M785.453,375.4L785.453,379.567C785.453,383.733,785.453,392.067,777.164,400.117C768.875,408.168,752.296,415.935,744.007,419.819L735.718,423.703" id="my-svg-L_RESOLVER_LENSI_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="M661.023,608.6L661.023,612.767C661.023,616.933,661.023,625.267,592.271,638.45C523.52,651.633,386.016,669.667,311.015,687.163C236.015,704.66,223.519,721.62,217.271,730.1L211.023,738.58" id="my-svg-L_FINDING_GATES_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"><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" transform="translate(433.84693, 663.39369)"><g class="label" transform="translate(0, -28.099998474121094)"><g><rect class="background" style="" x="-95.828125" y="-1" width="191.65625" height="58.19999694824219"></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">operator</tspan><tspan class="text-inner-tspan"> adds</tspan><tspan class="text-inner-tspan"> an</tspan><tspan class="text-inner-tspan"> entry</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" text-anchor="middle"><tspan class="text-inner-tspan">whose</tspan><tspan class="text-inner-tspan"> name:</tspan><tspan class="text-inner-tspan"> matches</tspan><tspan class="text-inner-tspan"> the</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em" text-anchor="middle"><tspan class="text-inner-tspan">bucket</tspan><tspan class="text-inner-tspan"> →</tspan><tspan class="text-inner-tspan"> PR</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default" id="my-svg-flowchart-CRON-0" transform="translate(132.68359375, 207.89999771118164)"><rect class="basic label-container" style="" x="-86.40625" y="-33.29999923706055" width="172.8125" 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">cron</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">09:00</tspan><tspan class="text-inner-tspan"> UTC</tspan><tspan class="text-inner-tspan"> daily</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-PF-1" transform="translate(132.68359375, 333.2999954223633)"><rect class="basic label-container" style="" x="-81.59375" y="-33.29999923706055" width="163.1875" 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">audit-prefilter</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">--max=10</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-RUN-3" transform="translate(132.68359375, 458.6999931335449)"><rect class="basic label-container" style="" x="-85.171875" y="-33.29999923706055" width="170.34375" 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">audit-run-entry</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">grounded</tspan><tspan class="text-inner-tspan"> LLM</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-APPLY-5" transform="translate(132.68359375, 575.299991607666)"><rect class="basic label-container" style="" x="-70.84375" y="-33.29999923706055" width="141.6875" 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">audit-apply</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">--open-pr</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-GATES-7" transform="translate(190.59765625, 766.2999877929688)"><rect class="basic label-container" style="" x="-67.1328125" y="-24.5" width="134.265625" 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">5</tspan><tspan class="text-inner-tspan"> PR</tspan><tspan class="text-inner-tspan"> gates</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-MERGE-9" transform="translate(190.59765625, 874.0999870300293)"><rect class="basic label-container" style="" x="-92.9609375" y="-33.29999923706055" width="185.921875" 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">human</tspan><tspan class="text-inner-tspan"> merge</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">→</tspan><tspan class="text-inner-tspan"> VERSION</tspan><tspan class="text-inner-tspan"> bump</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-TAIL-10" transform="translate(536.59375, 66.29999923706055)"><rect class="basic label-container" style="" x="-90.6171875" y="-33.29999923706055" width="181.234375" 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">gap-signal-tail</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">89</tspan><tspan class="text-inner-tspan"> pipeline</tspan><tspan class="text-inner-tspan"> steps</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-EVENT-11" transform="translate(536.59375, 207.89999771118164)"><rect class="basic label-container" style="" x="-111.1875" y="-33.29999923706055" width="222.375" 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</tspan><tspan class="text-inner-tspan"> observe</tspan><tspan class="text-inner-tspan"> event</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">knowledge_gap_signal</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-LEDGER-13" transform="translate(536.59375, 333.2999954223633)"><rect class="basic label-container" style="" x="-76.09375" y="-33.29999923706055" width="152.1875" 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><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">activity.jsonl</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-LENSI-15" transform="translate(661.0234375, 458.6999931335449)"><rect class="basic label-container" style="" x="-83.65625" y="-33.29999923706055" width="167.3125" 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">Lens</tspan><tspan class="text-inner-tspan"> I</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">90-day</tspan><tspan class="text-inner-tspan"> window</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-FINDING-17" transform="translate(661.0234375, 575.299991607666)"><rect class="basic label-container" style="" x="-56.3359375" y="-33.29999923706055" width="112.671875" 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">finding</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">P1</tspan><tspan class="text-inner-tspan"> /</tspan><tspan class="text-inner-tspan"> P2</tspan></tspan></text></g></g></g><g class="node default" id="my-svg-flowchart-RESOLVER-18" transform="translate(785.453125, 333.2999954223633)"><rect class="basic label-container" style="" x="-122.765625" y="-42.099998474121094" width="245.53125" 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-tier</tspan><tspan class="text-inner-tspan"> --knowledge-root</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em"><tspan class="text-inner-tspan">resolver</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em"><tspan class="text-inner-tspan">suppresses</tspan><tspan class="text-inner-tspan"> covered</tspan><tspan class="text-inner-tspan"> topics</tspan></tspan></text></g></g></g></g></g></g><defs></defs><defs></defs></svg></figure>
1165
+ <p>Three real hooks sit beside the two arms: the <strong>phase-audit hook</strong> (runs Lens H
1166
+ only, never Lens I), the <strong>doc-conformance MMR channel</strong> (routes Lens I findings
1167
+ into MMR), and the <strong><code>--fix</code> flow</strong> (initial + verifier + postfix audit). They
1168
+ are covered below.</p>
1169
+ <div class="callout callout-warning"><p><strong>Doc drift on MMR-in-cron.</strong> Three docs frame MMR-in-cron differently. The
1170
+ parent spec's locked decision #3 is authoritative: a native
1171
+ <code>knowledge-freshness</code> MMR channel is deferred to Phase 5. The cron today runs
1172
+ only inline gates. Two interim paths give reviewers MMR signal on a freshness
1173
+ PR: (1) the built-in <code>doc-conformance</code> MMR channel (disabled by default; enable
1174
+ with <code>mmr review --channels=doc-conformance</code>); (2) the manual <code>mmr review --diff -</code> command in <a href="#from-candidate-to-merged-pr">From candidate to merged PR</a>.</p></div>
1175
+ <h2 id="frontmatter-signals-and-resolution">Frontmatter, signals, and resolution</h2>
1176
+ <h3 id="frontmatter-schema">Frontmatter schema</h3>
1177
+ <p>Every knowledge entry's frontmatter is a Zod-validated object with four
1178
+ freshness-relevant fields. The schema is the source of truth and runs as Gate 1
1179
+ of the PR CI (<span class="fp" data-path="src/validation/knowledge-frontmatter-validator.ts:42-50">src/validation/knowledge-frontmatter-validator.ts:42-50</span>);
1180
+ runtime readers tolerate missing optional fields.</p>
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
+
1230
+
1231
+
1232
+
1233
+
1234
+
1235
+
1236
+
1237
+
1238
+
1239
+
1240
+
1241
+
1242
+ <table><thead><tr><th>Field</th><th>Type</th><th>Default</th><th>Validation</th><th>Read by</th></tr></thead><tbody><tr><td><code>name</code></td><td>string</td><td>required</td><td>regex <code>/^[a-z][a-z0-9-]*$/</code></td><td>assembly-loader, Lens I suppression</td></tr><tr><td><code>description</code></td><td>string</td><td>required</td><td>warns if > 200 chars</td><td>assembly-loader (TOC), audit prompt</td></tr><tr><td><code>topics</code></td><td>string[]</td><td><code>[]</code></td><td>any string</td><td>assembly-loader (auto-selection)</td></tr><tr><td><code>volatility</code></td><td>enum</td><td><code>evolving</code></td><td><code>stable|evolving|fast-moving</code></td><td>prefilter cadence</td></tr><tr><td><code>last-reviewed</code></td><td>ISO date</td><td><code>null</code></td><td><code>YYYY-MM-DD</code> &#x26; real calendar date</td><td>prefilter cadence</td></tr><tr><td><code>version-pin</code></td><td>string</td><td><code>null</code></td><td>any string (e.g. <code>"OWASP Top 10 2021"</code>)</td><td>audit prompt; <code>superseded</code> verdict signals it must advance manually</td></tr><tr><td><code>sources[]</code></td><td>object[]</td><td><code>[]</code></td><td>each: <code>url</code> (SSRF-checked at fetch), <code>anchor</code> (optional, starts with <code>#</code>), <code>retrieved</code> (ISO date), <code>hash</code> (sha256)</td><td>prefilter (hash + cadence), audit runner (prefetch)</td></tr></tbody></table>
1243
+ <div class="callout callout-warning"><p><strong><code>name</code> vs. gap-topic regex.</strong> An entry <code>name</code> must start with a letter
1244
+ (<code>/^[a-z][a-z0-9-]*$/</code>), but Lens I gap <em>topics</em> allow a leading digit
1245
+ (<code>/^[a-z0-9]+(-[a-z0-9]+)*$/</code>). So a gap signalled for a topic like <code>3d-rendering</code>
1246
+ cannot be suppressed by an entry of the same name — pick a letter-leading <code>name</code>
1247
+ (and list the numeric form under <code>topics</code>) when closing such a gap.</p></div>
1248
+ <div class="callout callout-note"><p><strong>Anchor semantics.</strong> Put fragments in <code>anchor</code>, never inside <code>url</code>. The audit
1249
+ fetches <code>url + (anchor ?? '')</code> and hashes that body; the coverage check
1250
+ (<span class="fp" data-path="src/knowledge-freshness/audit-apply.ts:82-101">src/knowledge-freshness/audit-apply.ts:82-101</span>) matches the same combined
1251
+ string. Splitting prevents hash drift from spurious URL re-encodings and lets two
1252
+ sources at the same base URL with different <code>#anchor</code>s be tracked independently.</p></div>
1253
+ <h3 id="cadence-model">Cadence model</h3>
1254
+ <p>Three tiers, three windows — <strong>14 / 60 / 180</strong> days for <code>fast-moving</code> /
1255
+ <code>evolving</code> / <code>stable</code> (<span class="fp" data-path="src/knowledge-freshness/audit-prefilter.ts:5-7">src/knowledge-freshness/audit-prefilter.ts:5-7</span>).
1256
+ An entry with no <code>last-reviewed</code> always counts as due. Sources with a changed
1257
+ hash also become candidates regardless of age, but the hash check only runs for
1258
+ entries still <em>inside</em> their cadence window.</p>
1259
+ <h4>Which tier does an entry belong in?</h4>
1260
+
1261
+
1262
+
1263
+
1264
+
1265
+
1266
+
1267
+
1268
+
1269
+
1270
+
1271
+
1272
+
1273
+
1274
+
1275
+
1276
+
1277
+
1278
+
1279
+
1280
+
1281
+
1282
+
1283
+
1284
+
1285
+ <table><thead><tr><th>Provenance</th><th>Change frequency</th><th>Recommended tier</th></tr></thead><tbody><tr><td>vendor SDK / API docs</td><td>quarterly or faster</td><td><code>fast-moving</code></td></tr><tr><td>standards / RFCs, vendor docs</td><td>yearly-ish</td><td><code>evolving</code></td></tr><tr><td>canonical pattern reference</td><td>multi-year</td><td><code>stable</code></td></tr></tbody></table>
1286
+ <p>Rule of thumb: if a version bump <em>often breaks</em> downstream guidance, lean
1287
+ <code>fast-moving</code>; if drift is <em>extremely rare</em>, <code>stable</code>; otherwise <code>evolving</code>
1288
+ (the default).</p>
1289
+ <h3 id="adding-a-new-entry-to-the-kb">Adding a new entry to the KB</h3>
1290
+ <ol>
1291
+ <li><strong>Choose a category directory</strong> under <code>content/knowledge/&#x3C;category>/</code>. Many
1292
+ categories exist today (<code>backend</code>, <code>core</code>, <code>cli</code>, <code>research</code>, <code>web-app</code>,
1293
+ <code>web3</code>, …); prefer placing into an existing one. Creating a new category is a
1294
+ separate PR.</li>
1295
+ <li><strong>File name = entry slug + <code>.md</code>.</strong> The basename must match the <code>name:</code> field
1296
+ (e.g. <code>retry-with-jitter.md</code> ↔ <code>name: retry-with-jitter</code>). Lens I's
1297
+ suppression match reads <code>name:</code> only, not the filename — a mismatch silently
1298
+ breaks suppression.</li>
1299
+ <li><strong>Required frontmatter:</strong> <code>name</code>, <code>description</code>. Add <code>volatility</code> + <code>sources[]</code>
1300
+ if you want the cron to audit it — an entry with no <code>sources[]</code> is skipped by
1301
+ the prefilter (<span class="fp" data-path="src/knowledge-freshness/audit-prefilter.ts:17">src/knowledge-freshness/audit-prefilter.ts:17</span>).</li>
1302
+ <li><strong>Validate locally:</strong> <code>make validate-knowledge</code>.</li>
1303
+ <li><strong>Confirm the prefilter will pick it up.</strong> A fresh entry has no
1304
+ <code>last-reviewed</code>, so it should appear at priority 100:
1305
+ <pre><code class="language-bash">node dist/index.js knowledge-freshness audit-prefilter --max=10 \
1306
+ | jq '.[] | select(.name=="&#x3C;your-new-slug>")'
1307
+ </code></pre>
1308
+ The daily ceiling is 10, so a flood of new entries may queue past the first day.</li>
1309
+ </ol>
1310
+ <h3 id="gap-signal-payload">Gap-signal payload</h3>
1311
+ <p>A gap signal is a ledger event validated by
1312
+ <span class="fp" data-path="src/observability/engine/event-schemas.ts:191-220">src/observability/engine/event-schemas.ts:191-220</span> (payload allow-list at
1313
+ <span class="fp" data-path="src/observability/engine/event-schemas.ts:12">src/observability/engine/event-schemas.ts:12</span>):</p>
1314
+ <pre><code class="language-json">{
1315
+ "event_id": "&#x3C;uuid>",
1316
+ "worktree_id": "&#x3C;sha>",
1317
+ "actor_label": "agent | bot | …",
1318
+ "branch": "&#x3C;branch>",
1319
+ "task_id": null,
1320
+ "type": "knowledge_gap_signal",
1321
+ "ts": "&#x3C;ISO-8601>",
1322
+ "payload": {
1323
+ "topic": "&#x3C;kebab-slug>",
1324
+ "source": "agent_search",
1325
+ "project_id": "&#x3C;sha256-hex>",
1326
+ "step_name": "tech-stack",
1327
+ "agent_excerpt": "…"
1328
+ }
1329
+ }
1330
+ </code></pre>
1331
+ <p><code>topic</code> is ≤80 chars matching <code>/^[a-z0-9]+(-[a-z0-9]+)*$/</code>; <code>source</code> ∈
1332
+ {<code>agent_search</code>, <code>lessons</code>, <code>manual</code>}; <code>project_id</code> is 64-char sha256 hex (or the
1333
+ literal <code>lessons</code> when <code>source=lessons</code>); <code>step_name</code> and <code>agent_excerpt</code> (≤200
1334
+ chars) are optional.</p>
1335
+ <div class="callout callout-tip"><p><strong>Suppressing emission in tests/CI.</strong> Set <code>SCAFFOLD_GAP_SIGNAL_QUIET=1</code>. The
1336
+ assembly-time tail (<code>src/core/assembly/gap-signal-tail.ts</code>) then renders no
1337
+ emission template into the pipeline step. Default is always-on (locked decision
1338
+ #9) — catch gaps everywhere they occur.</p></div>
1339
+ <h3 id="knowledgerootresolution-shape">KnowledgeRootResolution shape</h3>
1340
+ <p>The resolver returns a three-field record that threads through the audit run
1341
+ (<span class="fp" data-path="src/observability/knowledge-index.ts:275-291">src/observability/knowledge-index.ts:275-291</span>):</p>
1342
+ <pre><code class="language-ts">export interface KnowledgeRootResolution {
1343
+ /** Validated absolute path to a knowledge directory, or null. */
1344
+ root: string | null
1345
+ /** Pre-loaded index Set, populated by the validator. Null when root is null.
1346
+ Lens I reads this directly — no re-walk. */
1347
+ index: Set&#x3C;string> | null
1348
+ /** Audit trail of what was tried. Lens I uses this to compose a precise
1349
+ warn-once message when root is null. */
1350
+ attempts: KnowledgeRootAttempt[]
1351
+ }
1352
+ </code></pre>
1353
+ <h2 id="from-candidate-to-merged-pr">From candidate to merged PR</h2>
1354
+ <p>The cron is a thin bash loop — the brains live in three CLI subcommands and a
1355
+ meta-prompt that runs a grounded LLM against pre-fetched source bodies.</p>
1356
+ <h3 id="prefilter">Prefilter</h3>
1357
+ <p>An entry becomes a candidate when (1) it has at least one source, AND (2) either
1358
+ its <code>last-reviewed</code> is older than the cadence window, OR a source's prefetched
1359
+ hash differs from the stored one. Priority orders highest-score first: unreviewed
1360
+ entries (100), then overdue entries (<code>50 + ageDays</code>, so the oldest rank highest),
1361
+ with in-window hash changes at 75; the top <code>--max</code>
1362
+ win (<span class="fp" data-path="src/knowledge-freshness/audit-prefilter.ts:14-72">src/knowledge-freshness/audit-prefilter.ts:14-72</span>):</p>
1363
+ <pre><code class="language-ts">for (const e of entries) {
1364
+ if (e.sources.length === 0) continue // no sources = no audit
1365
+ if (!e.lastReviewed) { select = true; priority = 100 }
1366
+ else if (ageDays > window) { select = true; priority = 50 + ageDays }
1367
+ else {
1368
+ // hash check — Promise.all over a small per-entry list (1-3 sources)
1369
+ if (anyHashChanged) { select = true; priority = 75 }
1370
+ }
1371
+ }
1372
+ candidates.sort((a, b) => b.priority - a.priority)
1373
+ return candidates.slice(0, max)
1374
+ </code></pre>
1375
+ <p>The hash check is a tiebreaker, not a baseline. Entries already <em>past</em> their
1376
+ window are selected immediately at priority <code>50 + ageDays</code> — no network cost.
1377
+ The hash check only runs in the <code>else</code> branch (still <em>inside</em> the window), runs
1378
+ <code>Promise.all</code> over the entry's 1–3 sources, and swallows fetch errors so a slow
1379
+ upstream doesn't crash the cron.</p>
1380
+ <h3 id="audit-verdicts">Audit verdicts</h3>
1381
+ <p>The meta-prompt at <code>content/tools/knowledge-audit-entry.md</code> instructs the LLM to
1382
+ read pre-fetched source bodies (no web tool available) and emit one of four
1383
+ verdicts. <strong>Every verdict opens a PR</strong> — the dry-run apply runs first so gates
1384
+ can inspect the proposed diff, then <code>--open-pr</code> creates the branch.</p>
1385
+
1386
+
1387
+
1388
+
1389
+
1390
+
1391
+
1392
+
1393
+
1394
+
1395
+
1396
+
1397
+
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+
1404
+
1405
+
1406
+
1407
+
1408
+
1409
+
1410
+ <table><thead><tr><th>Verdict</th><th>What the PR contains</th></tr></thead><tbody><tr><td><code>current</code></td><td>Frontmatter-only: bumps <code>last-reviewed</code>, <code>sources[*].hash</code>, <code>sources[*].retrieved</code> so the entry exits the queue.</td></tr><tr><td><code>minor-drift</code></td><td>Frontmatter persistence + findings table as commentary. <code>applyVerdictToEntry</code> refuses any <code>proposed_changes</code> on this verdict (<span class="fp" data-path="src/knowledge-freshness/audit-apply.ts:54-58">src/knowledge-freshness/audit-apply.ts:54-58</span>); no body edits.</td></tr><tr><td><code>major-drift</code></td><td>Body edits land via <code>proposed_changes</code> (H2-heading-anchored splices). Gate 4 blocks if a stable entry's diff exceeds 20% churn without the override label.</td></tr><tr><td><code>superseded</code></td><td>A new edition shipped; <code>version-pin</code> must advance. <code>last-reviewed</code> does <strong>not</strong> advance (<span class="fp" data-path="src/knowledge-freshness/audit-apply.ts:103-118">src/knowledge-freshness/audit-apply.ts:103-118</span>) — only <code>hash</code>/<code>retrieved</code> update, so the entry stays due until a human re-audits. Prevents a known-stale entry from looking fresh.</td></tr></tbody></table>
1411
+ <h3 id="pr-generation">PR generation</h3>
1412
+ <p>Branch: <code>knowledge-freshness/&#x3C;entry>-&#x3C;YYYY-MM-DD></code>. <code>renderPrBody</code> renders a
1413
+ summary, the verdict fields, a findings table, the sources, and any preserve
1414
+ warnings (it does not embed the raw verdict JSON). Each candidate gets
1415
+ its own PR off <code>origin/main</code> — the cron <code>git checkout main</code> between iterations
1416
+ and restores the entry between the dry-run apply (for gates) and the final
1417
+ <code>--open-pr</code> call. PRs do not stack; failures isolate per-candidate.</p>
1418
+ <h4>VERSION bump on merge</h4>
1419
+ <p>A dedicated workflow
1420
+ (<span class="fp" data-path=".github/workflows/knowledge-freshness-version-bump.yml:16">.github/workflows/knowledge-freshness-version-bump.yml:16</span>) fires on PR
1421
+ <code>closed</code> (merged-only) when the source branch starts with <code>knowledge-freshness/</code>
1422
+ <em>or</em> the PR carries the <code>knowledge-freshness</code> label. It computes the next SemVer
1423
+ from the PR title and body, writes <code>content/knowledge/VERSION</code>, commits with the
1424
+ prefix <code>chore(knowledge):</code> (deliberately not <code>knowledge-freshness/*</code>) so the
1425
+ commit doesn't re-trigger itself, then <code>git pull --rebase</code> before pushing. Bump
1426
+ rules (<span class="fp" data-path="src/knowledge-freshness/bump-version.ts:26-45">src/knowledge-freshness/bump-version.ts:26-45</span>):</p>
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
+
1452
+
1453
+
1454
+
1455
+
1456
+
1457
+ <table><thead><tr><th>Match</th><th>Bump</th><th>Notes</th></tr></thead><tbody><tr><td><code>BREAKING CHANGE:</code> anywhere in title, or start-of-line in body</td><td>major</td><td>Wins over every other prefix</td></tr><tr><td><code>feat(knowledge):</code> / <code>feat(knowledge-freshness):</code> title prefix</td><td>minor</td><td>Case-sensitive</td></tr><tr><td><code>chore(knowledge):</code> / <code>chore(knowledge-freshness):</code> title prefix</td><td>patch</td><td>Used by the bump commit itself</td></tr><tr><td>Anything else (including <code>fix(knowledge):</code>)</td><td>patch</td><td>Logs a <code>::notice::</code> for unrecognized prefixes</td></tr></tbody></table>
1458
+ <p>The start-of-line anchor on the BREAKING CHANGE body match (<code>/^BREAKING CHANGE:/m</code>) is deliberate — a freshness PR's body embeds an LLM-generated
1459
+ findings table whose evidence excerpts could otherwise mention "BREAKING CHANGE:"
1460
+ and trigger an accidental major bump.</p>
1461
+ <h3 id="mmr-corroboration-manual">MMR corroboration (manual)</h3>
1462
+ <p>The cron does <em>not</em> dispatch MMR today — the workflow only runs inline gates. To
1463
+ corroborate a freshness PR locally:</p>
1464
+ <pre><code class="language-bash">git diff origin/main...HEAD -- 'content/knowledge/**/*.md' \
1465
+ | mmr review --diff - --focus knowledge-freshness --sync --format json
1466
+ </code></pre>
1467
+ <p>A native <code>knowledge-freshness</code> MMR channel is the Phase 5 plan. See the
1468
+ <a href="../mmr/index.md">MMR guide</a> for the channel architecture.</p>
1469
+ <h2 id="the-five-pr-gates">The five PR gates</h2>
1470
+ <p>The cron's <code>GITHUB_TOKEN</code>-opened PRs don't fire downstream workflows, so the
1471
+ cron also runs the gate code inline (same CLI surface). Human-opened freshness
1472
+ PRs get gated by the workflow at
1473
+ <span class="fp" data-path=".github/workflows/knowledge-freshness-gates.yml:17">.github/workflows/knowledge-freshness-gates.yml:17</span>.</p>
1474
+ <div class="filter-table"><input type="text" class="filter-input" placeholder="Filter…" aria-label="Filter table rows" disabled>
1475
+
1476
+
1477
+
1478
+
1479
+
1480
+
1481
+
1482
+
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
+ <table><thead><tr><th>#</th><th>Gate</th><th>What it checks</th><th>Mode</th><th>Source</th></tr></thead><tbody><tr><td>1</td><td>Frontmatter validator</td><td>Zod schema parse over every entry (excludes README). Strict calendar-date refinement; SSRF guard on source URLs.</td><td><span class="sev sev-p0">blocking</span></td><td><span class="fp" data-path="src/validation/knowledge-frontmatter-validator.ts:42-50">src/validation/knowledge-frontmatter-validator.ts:42-50</span></td></tr><tr><td>2</td><td>Source link-check</td><td>Every <code>sources[*].url</code> returns 2xx. Operates on the changed-files list via <code>--files-from</code>.</td><td><span class="sev sev-p0">blocking</span></td><td><span class="fp" data-path=".github/workflows/knowledge-freshness-gates.yml:117-123">.github/workflows/knowledge-freshness-gates.yml:117-123</span></td></tr><tr><td>3</td><td>Unsourced-claims lint</td><td>New normative claims must have a <code>sources[]</code> entry. Runs even when 1/2 failed.</td><td><span class="sev sev-p3">advisory</span></td><td><span class="fp" data-path=".github/workflows/knowledge-freshness-gates.yml:126-135">.github/workflows/knowledge-freshness-gates.yml:126-135</span></td></tr><tr><td>4</td><td>Anti-over-rewrite</td><td>Stable entries reject diffs deleting >20% of lines unless the <code>override:anti-over-rewrite</code> label is applied. Cron-opened <code>knowledge-freshness/*</code> branches only.</td><td><span class="sev sev-p1">blocking</span></td><td><span class="fp" data-path=".github/workflows/knowledge-freshness-gates.yml:137-152">.github/workflows/knowledge-freshness-gates.yml:137-152</span></td></tr><tr><td>5</td><td>Deep Guidance preserved</td><td>Literal <code>## Deep Guidance</code> heading must survive — the assembly engine pulls just that section.</td><td><span class="sev sev-p0">blocking</span></td><td><span class="fp" data-path=".github/workflows/knowledge-freshness-gates.yml:154-160">.github/workflows/knowledge-freshness-gates.yml:154-160</span></td></tr></tbody></table></div>
1522
+ <div class="callout callout-warning"><p><strong>Spec drift on the Gate 4 override.</strong> The parent spec describes the override as
1523
+ a marker in the PR <em>description</em>; the shipped mechanism
1524
+ (<span class="fp" data-path=".github/workflows/knowledge-freshness-gates.yml:148-152">.github/workflows/knowledge-freshness-gates.yml:148-152</span>) reads a
1525
+ maintainer-applied PR <em>label</em> (<code>override:anti-over-rewrite</code>) via <code>--pr-labels</code>.
1526
+ The shipped behavior is authoritative; the spec text is stale.</p></div>
1527
+ <div class="callout callout-note"><p><strong>Anti-tamper checkout (known gap).</strong> The gate workflow builds the gate code from
1528
+ HEAD, not from <code>origin/main</code>
1529
+ (<span class="fp" data-path=".github/workflows/knowledge-freshness-gates.yml:42-53">.github/workflows/knowledge-freshness-gates.yml:42-53</span>). The hardening —
1530
+ build from base, overlay only PR HEAD's <code>content/knowledge/</code> — is deferred
1531
+ because the bootstrap PR introduced the gate code itself. Risk is mitigated by
1532
+ mandatory PR review until a follow-up flips the checkout strategy.</p></div>
1533
+ <h2 id="lens-i-gap-detection-suppression">Lens I — gap detection + suppression</h2>
1534
+ <p>Lens I runs under <code>--scope=docs</code> and <code>--scope=all</code>
1535
+ (<span class="fp" data-path="src/observability/checks/lens-i-knowledge-gaps.ts:43">src/observability/checks/lens-i-knowledge-gaps.ts:43</span>). It collects
1536
+ signals from the ledger (rolling 90-day window,
1537
+ <span class="fp" data-path="src/observability/checks/lens-i-knowledge-gaps.ts:52">src/observability/checks/lens-i-knowledge-gaps.ts:52</span>) plus synthetic
1538
+ signals from <code>tasks/lessons.md</code>, buckets them by normalized topic, applies the
1539
+ threshold matrix, and suppresses buckets whose topic an entry already covers.</p>
1540
+ <div class="callout callout-note"><p><strong>Where Lens I sits in the taxonomy.</strong> "Lens" is scaffold's name for an audit
1541
+ check function inside <code>scaffold observe audit</code>. The full set is A–I; Lens I
1542
+ (<code>I-knowledge-gaps</code>) is this one. The other seven plus Lens H are documented in
1543
+ the <a href="../observability/index.md">Build Observability guide</a>.</p></div>
1544
+ <h3 id="threshold-matrix">Threshold matrix</h3>
1545
+ <p>The rules (<span class="fp" data-path="src/observability/checks/lens-i-knowledge-gaps.ts:148-149">src/observability/checks/lens-i-knowledge-gaps.ts:148-149</span>):</p>
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
+ <table><thead><tr><th>signal_count</th><th>distinct_projects</th><th>Severity</th></tr></thead><tbody><tr><td>≥ 5</td><td>≥ 3</td><td><span class="sev sev-p1">P1</span></td></tr><tr><td>≥ 3</td><td>≥ 2</td><td><span class="sev sev-p2">P2</span></td></tr><tr><td>below both</td><td>—</td><td>no finding</td></tr></tbody></table>
1572
+ <h3 id="topic-normalization">Topic normalization</h3>
1573
+ <p>Lens I normalizes the raw topic before bucketing, then validates the result.
1574
+ Two distinct steps: <code>normalizeTopic</code>
1575
+ (<span class="fp" data-path="src/observability/checks/lens-i-lessons-scanner.ts:32-38">src/observability/checks/lens-i-lessons-scanner.ts:32-38</span>) always
1576
+ produces a (possibly empty) string; <code>isValidTopic</code>
1577
+ (<span class="fp" data-path="src/observability/checks/lens-i-lessons-scanner.ts:114-116">src/observability/checks/lens-i-lessons-scanner.ts:114-116</span>) decides
1578
+ whether to accept it. Normalization lowercases, strips apostrophes, replaces
1579
+ every other non-slug run with a single hyphen, collapses repeats, and trims. The
1580
+ validator additionally enforces ≤ 80 chars and <code>/^[a-z0-9]+(-[a-z0-9]+)*$/</code>.
1581
+ So <code>Agent Eval Harnesses!</code> → <code>agent-eval-harnesses</code> (valid), but <code>!!!</code> → `` (rejected).</p>
1582
+ <h3 id="what-the-lessonsmd-scanner-sees">What the lessons.md scanner sees</h3>
1583
+ <p>Lens I synthesizes signals from <code>tasks/lessons.md</code> at audit time (read inline, no
1584
+ ledger writes) via two passes per non-fenced line — code-fenced blocks are
1585
+ skipped (<span class="fp" data-path="src/observability/checks/lens-i-lessons-scanner.ts:4">src/observability/checks/lens-i-lessons-scanner.ts:4</span>):</p>
1586
+ <ol>
1587
+ <li><strong>Explicit marker</strong> — <code>&#x3C;!-- gap-topic: &#x3C;slug> --></code> (slug must already be
1588
+ kebab-case; the marker regex enforces it).</li>
1589
+ <li><strong>Heuristic phrases</strong> (case-insensitive): <em>"would have helped to have a guide
1590
+ on X"</em>, <em>"missing knowledge entry for X"</em>, <em>"no knowledge entry for X"</em> /
1591
+ <em>"no kb entry for X"</em>, <em>"missing knowledge: X"</em>.</li>
1592
+ </ol>
1593
+ <p>Captured topics run through the same <code>normalizeTopic</code> / <code>isValidTopic</code>.
1594
+ Synthetic signals carry <code>project_id: "lessons"</code> and are <strong>excluded</strong> from the
1595
+ distinct-projects count by the aggregator's <code>delete('lessons')</code> rule (decision
1596
+ #6) — they corroborate but don't independently satisfy the threshold.</p>
1597
+ <h3 id="3-tier---knowledge-root-resolution">3-tier <code>--knowledge-root</code> resolution</h3>
1598
+ <p>Lens I must know where the KB lives to skip already-covered topics. The resolver
1599
+ (<code>resolveKnowledgeRoot</code> at <span class="fp" data-path="src/observability/knowledge-index.ts:326-379">src/observability/knowledge-index.ts:326-379</span>)
1600
+ tries three tiers in order:</p>
1601
+
1602
+
1603
+
1604
+
1605
+
1606
+
1607
+
1608
+
1609
+
1610
+
1611
+
1612
+
1613
+
1614
+
1615
+
1616
+
1617
+
1618
+
1619
+
1620
+
1621
+
1622
+
1623
+
1624
+
1625
+
1626
+ <table><thead><tr><th>Tier</th><th>Source</th><th>On failure</th></tr></thead><tbody><tr><td>1</td><td><code>--knowledge-root</code> CLI flag (resolved against <code>process.cwd()</code>)</td><td><strong>hard error</strong> before any lens runs (<code>KnowledgeRootCliInvalidError</code>)</td></tr><tr><td>2</td><td><code>lenses.I-knowledge-gaps.knowledge_root</code> in yaml (resolved against cwd)</td><td>soft-fail; records <code>{outcome: 'invalid', reason}</code> in the attempts trail</td></tr><tr><td>3</td><td>auto-detect — <code>findScaffoldKnowledgeRoot</code> walks parents for <code>package.json#name === '@zigrivers/scaffold'</code> (<span class="fp" data-path="src/observability/knowledge-index.ts:164-178">src/observability/knowledge-index.ts:164-178</span>)</td><td>returns <code>null</code> if no install is found</td></tr></tbody></table>
1627
+ <p>The sharp asymmetry is intentional: an operator who <em>typed</em> a <code>--knowledge-root</code>
1628
+ gets a hard error on a bad path; yaml and auto-detect soft-fail so suppression
1629
+ degrades gracefully. The most instructive case: yaml invalid + auto-detect found
1630
+ — the trail records the yaml failure <em>and</em> the auto-detect success, root is the
1631
+ auto-detect path, and a one-line stderr note points at the stale yaml. This is
1632
+ what an operator sees when <code>npm update -g @zigrivers/scaffold</code> moved the install
1633
+ out from under a pinned yaml path.</p>
1634
+ <h3 id="warning-policy">Warning policy</h3>
1635
+
1636
+
1637
+
1638
+
1639
+
1640
+
1641
+
1642
+
1643
+
1644
+
1645
+
1646
+
1647
+
1648
+
1649
+
1650
+
1651
+
1652
+
1653
+
1654
+
1655
+
1656
+
1657
+
1658
+
1659
+
1660
+ <table><thead><tr><th>Key</th><th>Status</th><th>When emitted</th></tr></thead><tbody><tr><td><code>lens-i:no-root</code></td><td>active</td><td>Lens I runs, no root resolved, lens enabled. Per-audit deduped via <code>warnedKeys: Set&#x3C;string></code>. If yaml failed validation, the message gains a clause quoting the bad path + reason.</td></tr><tr><td><code>lens-i:index-load-failed</code></td><td>reserved</td><td>Never emitted today — <code>validateKnowledgeRoot</code> exercises the loader at resolution time, foreclosing this path.</td></tr><tr><td>(none)</td><td>no-warn</td><td>Lens I disabled — resolver runs but no warning surfaces (decisions #4 / #11).</td></tr></tbody></table>
1661
+ <p><code>emitOnceForAudit</code> (<span class="fp" data-path="src/observability/knowledge-index.ts:251-259">src/observability/knowledge-index.ts:251-259</span>) reads a
1662
+ caller-provided <code>Set</code> created fresh in each <code>runAudit</code>
1663
+ (<span class="fp" data-path="src/observability/engine/api.ts:114">src/observability/engine/api.ts:114</span>), so the <code>--fix</code> flow's three
1664
+ internal audits each get their own dedup scope.</p>
1665
+ <h3 id="what-a-lens-i-finding-looks-like">What a Lens I finding looks like</h3>
1666
+ <p>A single finding excerpt from the audit sidecar (<code>docs/audits/&#x3C;id>.json</code>):</p>
1667
+ <pre><code class="language-json">{
1668
+ "id": "a3f2c1d4...",
1669
+ "lens_id": "I-knowledge-gaps",
1670
+ "severity": "P2",
1671
+ "title": "Knowledge base lacks coverage for \"agent-eval-harnesses\" — 4 signals across 2 projects",
1672
+ "source_doc": "",
1673
+ "evidence": {
1674
+ "kind": "knowledge_gap",
1675
+ "topic": "agent-eval-harnesses",
1676
+ "signal_count": 4,
1677
+ "distinct_project_count": 2,
1678
+ "distinct_projects": ["a3f2...", "1c4e..."],
1679
+ "first_seen": "2026-04-12T09:00:00Z",
1680
+ "last_seen": "2026-05-21T14:30:00Z",
1681
+ "example_excerpts": ["No knowledge entry for agent eval harnesses"]
1682
+ },
1683
+ "confidence": "medium",
1684
+ "fix_hint": {
1685
+ "kind": "edit_doc",
1686
+ "target": "content/knowledge/&#x3C;category>/agent-eval-harnesses.md",
1687
+ "prompt": "Propose a new knowledge entry for \"agent-eval-harnesses\". Evidence: 4 signals from 2 projects in the last 90 days."
1688
+ }
1689
+ }
1690
+ </code></pre>
1691
+ <div class="callout callout-warning"><p><strong>Phase audits don't trigger Lens I.</strong> The phase-boundary hook
1692
+ (<code>StateManager.markCompleted</code> → <code>runPhaseAudit</code> at
1693
+ <span class="fp" data-path="src/observability/engine/phase-audit.ts:63">src/observability/engine/phase-audit.ts:63</span>) fires only Lens H-cross-doc
1694
+ (<code>lensIds: ['H-cross-doc']</code> at <span class="fp" data-path="src/observability/engine/phase-audit.ts:77">src/observability/engine/phase-audit.ts:77</span>).
1695
+ Lens I never runs at phase boundaries. A phase-audit run that surfaces zero
1696
+ findings does <strong>not</strong> mean Lens I is happy — it means Lens I never ran. To see
1697
+ Lens I findings, invoke <code>scaffold observe audit --scope=docs</code> (or <code>--scope=all</code>)
1698
+ explicitly, or run it through <code>--fix</code>.</p></div>
1699
+ <h2 id="the-allowlist">The allowlist</h2>
1700
+ <p>Out-of-allowlist sources warn but don't block (decision #4). Bare hostnames
1701
+ match subdomains; <code>host/path</code> entries additionally require the URL path to start
1702
+ with the prefix; <code>github_repos</code> is locked to specific <code>owner/repo</code>.</p>
1703
+ <p>The off-allowlist warning is <strong>advisory</strong> and is surfaced by the
1704
+ frontmatter-validation path (<code>validateKnowledgeFile</code>), not by a gate. Gate 3
1705
+ (<code>lint-unsourced</code>) is a separate advisory check that flags nearby links not
1706
+ covered by the entry's declared <code>sources[]</code> domains. Off-allowlist sources still
1707
+ get fetched, hashed, and audited — they just warn.
1708
+ It is <strong>not</strong> a security boundary: the SSRF guard
1709
+ (<code>src/knowledge-freshness/source-url-validator.ts</code>) runs independently, so a new
1710
+ host never unlocks private-IP fetches. The editorial bar is: "would the
1711
+ maintainers want this URL to be the verbatim grounding for a P0/P1 finding?"</p>
1712
+ <h3 id="most-cited-hosts">Most-cited hosts</h3>
1713
+ <p>Counted live from every entry's <code>sources[*].url</code> at build time.</p>
1714
+
1715
+ <div class="chart-block"><div class="chart chart-bar"><div class="chart-row" aria-label="martinfowler.com: 37"><span class="chart-label">martinfowler.com</span><div class="chart-bar" style="width:100%"></div></div><div class="chart-row" aria-label="developer.mozilla.org: 24"><span class="chart-label">developer.mozilla.org</span><div class="chart-bar" style="width:65%"></div></div><div class="chart-row" aria-label="owasp.org: 17"><span class="chart-label">owasp.org</span><div class="chart-bar" style="width:46%"></div></div><div class="chart-row" aria-label="developer.android.com: 15"><span class="chart-label">developer.android.com</span><div class="chart-bar" style="width:41%"></div></div><div class="chart-row" aria-label="the-turing-way.netlify.app: 15"><span class="chart-label">the-turing-way.netlify.app</span><div class="chart-bar" style="width:41%"></div></div><div class="chart-row" aria-label="developer.apple.com: 14"><span class="chart-label">developer.apple.com</span><div class="chart-bar" style="width:38%"></div></div><div class="chart-row" aria-label="developer.chrome.com: 14"><span class="chart-label">developer.chrome.com</span><div class="chart-bar" style="width:38%"></div></div><div class="chart-row" aria-label="sre.google: 12"><span class="chart-label">sre.google</span><div class="chart-bar" style="width:32%"></div></div><div class="chart-row" aria-label="w3.org: 12"><span class="chart-label">w3.org</span><div class="chart-bar" style="width:32%"></div></div><div class="chart-row" aria-label="microservices.io: 11"><span class="chart-label">microservices.io</span><div class="chart-bar" style="width:30%"></div></div><div class="chart-row" aria-label="ethereum.org: 10"><span class="chart-label">ethereum.org</span><div class="chart-bar" style="width:27%"></div></div><div class="chart-row" aria-label="rfc-editor.org: 10"><span class="chart-label">rfc-editor.org</span><div class="chart-bar" style="width:27%"></div></div><div class="chart-row" aria-label="consensys.github.io: 9"><span class="chart-label">consensys.github.io</span><div class="chart-bar" style="width:24%"></div></div><div class="chart-row" aria-label="docs.openzeppelin.com: 9"><span class="chart-label">docs.openzeppelin.com</span><div class="chart-bar" style="width:24%"></div></div><div class="chart-row" aria-label="opentelemetry.io: 9"><span class="chart-label">opentelemetry.io</span><div class="chart-bar" style="width:24%"></div></div></div>
1716
+
1717
+
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
+
1779
+
1780
+
1781
+
1782
+
1783
+
1784
+ <table><thead><tr><th>Host</th><th>Citations</th></tr></thead><tbody><tr><td>martinfowler.com</td><td>37</td></tr><tr><td>developer.mozilla.org</td><td>24</td></tr><tr><td>owasp.org</td><td>17</td></tr><tr><td>developer.android.com</td><td>15</td></tr><tr><td>the-turing-way.netlify.app</td><td>15</td></tr><tr><td>developer.apple.com</td><td>14</td></tr><tr><td>developer.chrome.com</td><td>14</td></tr><tr><td>sre.google</td><td>12</td></tr><tr><td>w3.org</td><td>12</td></tr><tr><td>microservices.io</td><td>11</td></tr><tr><td>ethereum.org</td><td>10</td></tr><tr><td>rfc-editor.org</td><td>10</td></tr><tr><td>consensys.github.io</td><td>9</td></tr><tr><td>docs.openzeppelin.com</td><td>9</td></tr><tr><td>opentelemetry.io</td><td>9</td></tr></tbody></table></div>
1785
+
1786
+ <h3 id="the-full-allowlist">The full allowlist</h3>
1787
+ <p>Every host plus its category, and the pinned GitHub repos.</p>
1788
+
1789
+ <p>47 allowlisted hosts and 3 GitHub repos. Out-of-list sources warn (they do not block).</p>
1790
+ <div class="filter-table"><input type="text" class="filter-input" placeholder="Filter…" aria-label="Filter table rows" disabled>
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
+
1817
+
1818
+
1819
+
1820
+
1821
+
1822
+
1823
+
1824
+
1825
+
1826
+
1827
+
1828
+
1829
+
1830
+
1831
+
1832
+
1833
+
1834
+
1835
+
1836
+
1837
+
1838
+
1839
+
1840
+
1841
+
1842
+
1843
+
1844
+
1845
+
1846
+
1847
+
1848
+
1849
+
1850
+
1851
+
1852
+
1853
+
1854
+
1855
+
1856
+
1857
+
1858
+
1859
+
1860
+
1861
+
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
+
1910
+
1911
+
1912
+
1913
+
1914
+
1915
+
1916
+
1917
+
1918
+
1919
+
1920
+
1921
+
1922
+
1923
+
1924
+
1925
+
1926
+
1927
+
1928
+
1929
+
1930
+
1931
+
1932
+
1933
+
1934
+
1935
+
1936
+
1937
+
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
+
1979
+
1980
+
1981
+
1982
+
1983
+
1984
+
1985
+
1986
+
1987
+ <table><thead><tr><th>Host</th><th>Category</th></tr></thead><tbody><tr><td><code>ai.google.dev</code></td><td>ai-ml</td></tr><tr><td><code>anthropic.com</code></td><td>ai-ml</td></tr><tr><td><code>docs.wandb.ai</code></td><td>ai-ml</td></tr><tr><td><code>mlflow.org</code></td><td>ai-ml</td></tr><tr><td><code>modelcontextprotocol.io</code></td><td>ai-ml</td></tr><tr><td><code>platform.openai.com</code></td><td>ai-ml</td></tr><tr><td><code>spec.graphql.org</code></td><td>api</td></tr><tr><td><code>spec.openapis.org</code></td><td>api</td></tr><tr><td><code>developer.chrome.com</code></td><td>browser-ext</td></tr><tr><td><code>docs.aws.amazon.com</code></td><td>cloud-ops</td></tr><tr><td><code>opentelemetry.io</code></td><td>cloud-ops</td></tr><tr><td><code>sre.google</code></td><td>cloud-ops</td></tr><tr><td><code>aicpa-cima.com</code></td><td>compliance</td></tr><tr><td><code>aicpa.org</code></td><td>compliance</td></tr><tr><td><code>eur-lex.europa.eu</code></td><td>compliance</td></tr><tr><td><code>pcisecuritystandards.org</code></td><td>compliance</td></tr><tr><td><code>www.finra.org</code></td><td>compliance</td></tr><tr><td><code>www.sec.gov</code></td><td>compliance</td></tr><tr><td><code>developer.android.com</code></td><td>mobile</td></tr><tr><td><code>developer.apple.com</code></td><td>mobile</td></tr><tr><td><code>adr.github.io</code></td><td>patterns</td></tr><tr><td><code>agilealliance.org</code></td><td>patterns</td></tr><tr><td><code>conventionalcommits.org</code></td><td>patterns</td></tr><tr><td><code>google.github.io</code></td><td>patterns</td></tr><tr><td><code>martinfowler.com</code></td><td>patterns</td></tr><tr><td><code>microservices.io</code></td><td>patterns</td></tr><tr><td><code>thoughtworks.com</code></td><td>patterns</td></tr><tr><td><code>the-turing-way.netlify.app</code></td><td>research</td></tr><tr><td><code>nist.gov</code></td><td>security</td></tr><tr><td><code>openid.net</code></td><td>security</td></tr><tr><td><code>owasp.org</code></td><td>security</td></tr><tr><td><code>consensys.github.io</code></td><td>smart-contracts</td></tr><tr><td><code>docs.openzeppelin.com</code></td><td>smart-contracts</td></tr><tr><td><code>docs.safe.global</code></td><td>smart-contracts</td></tr><tr><td><code>ethereum.org</code></td><td>smart-contracts</td></tr><tr><td><code>swcregistry.io</code></td><td>smart-contracts</td></tr><tr><td><code>ietf.org/rfc</code></td><td>standards</td></tr><tr><td><code>www.iso.org</code></td><td>standards</td></tr><tr><td><code>www.rfc-editor.org</code></td><td>standards</td></tr><tr><td><code>docs.pact.io</code></td><td>testing</td></tr><tr><td><code>docs.astral.sh</code></td><td>tooling</td></tr><tr><td><code>git-scm.com</code></td><td>tooling</td></tr><tr><td><code>peps.python.org</code></td><td>tooling</td></tr><tr><td><code>www.postgresql.org</code></td><td>tooling</td></tr><tr><td><code>developer.mozilla.org</code></td><td>web-standards</td></tr><tr><td><code>tr.designtokens.org</code></td><td>web-standards</td></tr><tr><td><code>www.w3.org</code></td><td>web-standards</td></tr></tbody></table></div>
1988
+ <p><strong>GitHub repos:</strong> <code>modelcontextprotocol/specification</code>, <code>steveyegge/beads</code>, <code>joelparkerhenderson/architecture-decision-record</code></p>
1989
+
1990
+ <h3 id="kb-inventory">KB inventory</h3>
1991
+ <p>Totals over <code>content/knowledge/</code>, broken down per category.</p>
1992
+
1993
+ <p><strong>266 entries</strong> across 19 categories:</p>
1994
+
1995
+
1996
+
1997
+
1998
+
1999
+
2000
+
2001
+
2002
+
2003
+
2004
+
2005
+
2006
+
2007
+
2008
+
2009
+
2010
+
2011
+
2012
+
2013
+
2014
+
2015
+
2016
+
2017
+
2018
+
2019
+
2020
+
2021
+
2022
+
2023
+
2024
+
2025
+
2026
+
2027
+
2028
+
2029
+
2030
+
2031
+
2032
+
2033
+
2034
+
2035
+
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
+
2062
+
2063
+
2064
+
2065
+
2066
+
2067
+
2068
+
2069
+
2070
+
2071
+
2072
+
2073
+
2074
+
2075
+
2076
+
2077
+
2078
+
2079
+ <table><thead><tr><th>Category</th><th>Entries</th></tr></thead><tbody><tr><td>core</td><td>35</td></tr><tr><td>game</td><td>25</td></tr><tr><td>research</td><td>25</td></tr><tr><td>backend</td><td>22</td></tr><tr><td>review</td><td>20</td></tr><tr><td>web-app</td><td>17</td></tr><tr><td>web3</td><td>14</td></tr><tr><td>data-science</td><td>13</td></tr><tr><td>browser-extension</td><td>12</td></tr><tr><td>data-pipeline</td><td>12</td></tr><tr><td>library</td><td>12</td></tr><tr><td>ml</td><td>12</td></tr><tr><td>mobile-app</td><td>12</td></tr><tr><td>cli</td><td>10</td></tr><tr><td>validation</td><td>7</td></tr><tr><td>product</td><td>6</td></tr><tr><td>execution</td><td>5</td></tr><tr><td>tools</td><td>4</td></tr><tr><td>finalization</td><td>3</td></tr></tbody></table>
2080
+
2081
+ <h3 id="how-to-expand-the-allowlist">How to expand the allowlist</h3>
2082
+ <p>Adding a host is a one-line PR to
2083
+ <code>docs/knowledge-freshness/authoritative-sources.yaml</code>:</p>
2084
+ <pre><code class="language-diff"> hosts:
2085
+ - owasp.org
2086
+ + - developers.cloudflare.com
2087
+ - nist.gov
2088
+ </code></pre>
2089
+ <ol>
2090
+ <li><strong>Pick the form.</strong> Bare hostname for vendor docs whose path layout changes;
2091
+ <code>host/path</code> prefix for shared-tenancy hosts where you only trust a sub-path;
2092
+ <code>owner/repo</code> under <code>github_repos:</code> for specific GitHub repos. Skip <code>www.</code>
2093
+ (bare entries auto-match subdomains).</li>
2094
+ <li><strong>Verify the host is live</strong> — <code>curl -sI https://&#x3C;host>/&#x3C;path></code> should return
2095
+ 2xx (or a 3xx that ultimately resolves).</li>
2096
+ <li><strong>Mirror the category</strong> in <code>CATEGORY_MAP</code> in
2097
+ <code>scripts/build-freshness-reference.mjs</code> — otherwise the regenerated allowlist
2098
+ table shows the new host as <code>other</code>.</li>
2099
+ <li><strong>Open a normal PR.</strong> Allowlist additions are not a separate trust delegation;
2100
+ any maintainer can review.</li>
2101
+ </ol>
2102
+ <h2 id="anthropic-vs-deepseek-cron-uses-deepseek">Anthropic vs DeepSeek (cron uses DeepSeek)</h2>
2103
+ <p>The cron switched to DeepSeek HTTP to remove the local <code>claude</code> CLI dependency
2104
+ from CI. Local audits keep using whichever provider is configured. Precedence is
2105
+ resolved by <code>resolveProvider</code> (<span class="fp" data-path="src/knowledge-freshness/providers/index.ts:36">src/knowledge-freshness/providers/index.ts:36</span>):</p>
2106
+ <ol>
2107
+ <li><code>--provider &#x3C;name></code> — explicit flag, operator override</li>
2108
+ <li><code>KNOWLEDGE_FRESHNESS_PROVIDER</code> env var</li>
2109
+ <li>A single API key in env — inferred</li>
2110
+ <li>Both API keys present → error (ambiguous)</li>
2111
+ <li>No env, <code>claude</code> on PATH → anthropic (subprocess uses keychain)</li>
2112
+ <li>Nothing → error (no provider configured)</li>
2113
+ </ol>
2114
+ <div class="tabs"><div class="tablist" role="tablist"><button class="tab-btn active" role="tab" data-tab="0">Anthropic</button><button class="tab-btn" role="tab" data-tab="1">DeepSeek</button></div><div class="tabpane active" data-tab="0"><p>Subprocess: <code>claude -p --tools ""</code> (empty-tools disables WebFetch so the model
2115
+ can only read the prefetched bodies). <strong>Requires the <code>claude</code> CLI on PATH
2116
+ regardless of how the provider was chosen</strong> — the resolver throws
2117
+ (<span class="fp" data-path="src/knowledge-freshness/providers/index.ts:44-56">src/knowledge-freshness/providers/index.ts:44-56</span>) if anthropic is
2118
+ picked via flag, env, or API-key inference and <code>claude</code> isn't installed.
2119
+ <code>ANTHROPIC_API_KEY</code> alone is <em>not</em> sufficient. Source:
2120
+ <code>src/knowledge-freshness/providers/anthropic.ts</code>.</p></div><div class="tabpane" data-tab="1"><p>HTTP. No subprocess; works in CI without the Claude CLI.</p><ul>
2121
+ <li><strong>Auth:</strong> requires <code>DEEPSEEK_API_KEY</code>.</li>
2122
+ <li><strong>Default model:</strong> <code>deepseek-v4-flash</code>.</li>
2123
+ <li><strong>Override:</strong> set <code>KNOWLEDGE_FRESHNESS_DEEPSEEK_MODEL</code> to <code>deepseek-v4-pro</code>.
2124
+ Other values throw at dispatcher-build time
2125
+ (<span class="fp" data-path="src/knowledge-freshness/providers/deepseek.ts:54-58">src/knowledge-freshness/providers/deepseek.ts:54-58</span>).</li>
2126
+ <li><strong>Thinking mode:</strong> hardcoded <code>thinking: { type: 'disabled' }</code>.</li>
2127
+ <li><strong>URL:</strong> hardcoded to <code>https://api.deepseek.com/chat/completions</code>;
2128
+ project-local config cannot redirect (decision #7 invariant).</li>
2129
+ </ul></div></div>
2130
+ <div class="callout callout-danger"><p><strong>Why the DeepSeek URL is hardcoded.</strong> An untrusted project's
2131
+ <code>.scaffold/observability.yaml</code> could otherwise redirect the LLM dispatcher at an
2132
+ attacker-controlled host that captures <code>DEEPSEEK_API_KEY</code> from request headers.
2133
+ Hardcoding closes that exfiltration path — the same threat model that hardcodes
2134
+ Lens H's <code>claude -p</code> command in the Build Observability audit.</p></div>
2135
+ <p>The cron wires DeepSeek explicitly
2136
+ (<span class="fp" data-path=".github/workflows/knowledge-freshness-audit.yml:70">.github/workflows/knowledge-freshness-audit.yml:70</span>):</p>
2137
+ <pre><code class="language-yaml">env:
2138
+ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
2139
+ KNOWLEDGE_FRESHNESS_PROVIDER: deepseek
2140
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2141
+ </code></pre>
2142
+ <p>A missing <code>DEEPSEEK_API_KEY</code> fails the run loudly at preflight rather than
2143
+ silently exiting 0 with zero PRs.</p>
2144
+ <h2 id="every-command-that-touches-the-system">Every command that touches the system</h2>
2145
+ <p>All commands ship in the published CLI.</p>
2146
+ <h3 id="refresh-arm-commands">Refresh-arm commands</h3>
2147
+
2148
+
2149
+
2150
+
2151
+
2152
+
2153
+
2154
+
2155
+
2156
+
2157
+
2158
+
2159
+
2160
+
2161
+
2162
+
2163
+
2164
+
2165
+
2166
+
2167
+
2168
+
2169
+
2170
+
2171
+
2172
+ <table><thead><tr><th>Command</th><th>Purpose</th></tr></thead><tbody><tr><td><code>scaffold knowledge-freshness audit-prefilter [--max=N]</code></td><td>Walk <code>content/knowledge/</code>, apply cadence + hash check, print a JSON candidate array. <code>--max</code> default 10 (<span class="fp" data-path="src/cli/commands/knowledge-freshness-audit-prefilter.ts:18">src/cli/commands/knowledge-freshness-audit-prefilter.ts:18</span>); the CLI emits only <code>{ name, path }</code> per candidate (<span class="fp" data-path="src/cli/commands/knowledge-freshness-audit-prefilter.ts:43">src/cli/commands/knowledge-freshness-audit-prefilter.ts:43</span>).</td></tr><tr><td><code>scaffold knowledge-freshness audit-run-entry &#x3C;path></code></td><td>Pre-fetch each source through SSRF guards, dispatch the grounded audit, print verdict JSON. <code>--provider anthropic|deepseek</code> overrides env precedence.</td></tr><tr><td><code>scaffold knowledge-freshness audit-apply &#x3C;path> &#x3C;verdict.json> [--open-pr]</code></td><td>Patch frontmatter + apply <code>proposed_changes</code> by H2 heading. The wrapper re-fetches every checked URL and computes its own sha256 (<span class="fp" data-path="src/knowledge-freshness/audit-apply.ts:82-101">src/knowledge-freshness/audit-apply.ts:82-101</span>), so persisted hashes are deterministic, not the LLM's claim. Refuses to advance <code>last-reviewed</code> unless every declared source is covered.</td></tr><tr><td><code>make validate-knowledge</code></td><td>Gate 1 — runs the Zod validator over every entry (README excluded).</td></tr></tbody></table>
2173
+ <h3 id="gap-arm-commands">Gap-arm commands</h3>
2174
+
2175
+
2176
+
2177
+
2178
+
2179
+
2180
+
2181
+
2182
+
2183
+
2184
+
2185
+
2186
+
2187
+
2188
+
2189
+
2190
+
2191
+
2192
+
2193
+
2194
+
2195
+ <table><thead><tr><th>Command</th><th>Purpose</th></tr></thead><tbody><tr><td><code>scaffold observe audit --lens I-knowledge-gaps [--knowledge-root &#x3C;path>] [--fix]</code></td><td>Run the gap-detection lens against the local ledger + <code>tasks/lessons.md</code>. <code>--knowledge-root</code> overrides yaml + auto-detect for suppression. <code>--fix</code> dispatches the fix flow; the override threads through all three audits.</td></tr><tr><td><code>scaffold observe event knowledge_gap_signal --topic=&#x3C;slug> --source=&#x3C;…> --project-id=&#x3C;sha> …</code></td><td>Write one validated gap signal to the ledger. Used by the assembly-time tail and by operators backfilling synthetic signals.</td></tr><tr><td><code>scaffold observe ack &#x3C;prefix-or-id></code></td><td>Acknowledge (or reopen) a finding so it stops surfacing. Use when a Lens I topic is deliberately out of scope.</td></tr></tbody></table>
2196
+ <p>The <code>--fix</code> flow (<code>runFixFlow</code> at <span class="fp" data-path="src/observability/engine/fix-flow.ts:71">src/observability/engine/fix-flow.ts:71</span>)
2197
+ runs a three-audit loop: (1) the initial audit produces a fix plan; (2) for each
2198
+ blocking finding, dispatch a fix agent then re-audit just that finding (the
2199
+ verifier); (3) one postfix audit runs everything for the final report. The
2200
+ <code>--knowledge-root</code> override threads into all three (decision #20) so suppression
2201
+ is consistent throughout.</p>
2202
+ <h3 id="gate-side-subcommands-also-runnable-locally-for-triage">Gate-side subcommands (also runnable locally for triage)</h3>
2203
+
2204
+
2205
+
2206
+
2207
+
2208
+
2209
+
2210
+
2211
+
2212
+
2213
+
2214
+
2215
+
2216
+
2217
+
2218
+
2219
+
2220
+
2221
+
2222
+
2223
+
2224
+
2225
+
2226
+
2227
+
2228
+
2229
+
2230
+
2231
+
2232
+
2233
+
2234
+
2235
+
2236
+
2237
+
2238
+ <table><thead><tr><th>Command</th><th>Gate</th><th>Purpose</th></tr></thead><tbody><tr><td><code>knowledge-freshness link-check [&#x3C;path>] [--files-from &#x3C;json>]</code></td><td>2</td><td>HTTP-HEAD every <code>sources[*].url</code>; 2xx passes, else exit 1.</td></tr><tr><td><code>knowledge-freshness lint-unsourced [&#x3C;path>] [--files-from &#x3C;json>] [--diff &#x3C;patch>]</code></td><td>3</td><td>Heuristic scan for normative language in new lines without a <code>sources[]</code> reference. Advisory: prints findings but always exits 0.</td></tr><tr><td><code>knowledge-freshness anti-over-rewrite [--files-from &#x3C;json>] [--diff &#x3C;patch>] [--pr-labels &#x3C;csv>]</code></td><td>4</td><td>For each changed <code>stable</code> entry, compare deleted-line count to 20% of the body; exit 1 if crossed without <code>override:anti-over-rewrite</code>. The cron passes <code>--pr-labels ""</code> (it can't self-apply labels).</td></tr><tr><td><code>knowledge-freshness deep-guidance-check [&#x3C;path>] [--files-from &#x3C;json>]</code></td><td>5</td><td>Assert each changed entry still contains a <code>## Deep Guidance</code> heading (case-sensitive).</td></tr><tr><td><code>knowledge-freshness bump-version --title &#x3C;str> --body &#x3C;str></code></td><td>—</td><td>Pure-function dry-run of <code>deriveBumpKind</code> + <code>bumpSemver</code>; prints <code>bump:</code> and <code>next:</code> lines parsed by the version-bump workflow.</td></tr></tbody></table>
2239
+ <h2 id="operations-cheat-sheet">Operations cheat sheet</h2>
2240
+ <h3 id="an-entrys-audit-failed-in-the-cron">An entry's audit failed in the cron</h3>
2241
+ <p>The cron logs <code>audit failed for &#x3C;name> — moving on</code> and continues; the entry
2242
+ stays in tomorrow's queue. Causes: provider auth (key rotated), source URL now
2243
+ 404s, a fetch/HTTP error or the 5 MiB fetch-and-hash cap, dispatcher error, or
2244
+ LLM timeout. (A source body over the 96 KiB embed cap is <strong>truncated</strong> and
2245
+ flagged <code>truncated: true</code> — it does not fail the audit.) Reproduce locally:</p>
2246
+ <pre><code class="language-bash">DEEPSEEK_API_KEY=sk-… node dist/index.js knowledge-freshness \
2247
+ audit-run-entry content/knowledge/&#x3C;cat>/&#x3C;name>.md
2248
+ # read stderr to see if it's a URL issue or a provider issue
2249
+ </code></pre>
2250
+ <h3 id="lens-i-keeps-surfacing-a-topic-the-kb-already-covers">Lens I keeps surfacing a topic the KB already covers</h3>
2251
+ <p>Suppression didn't match. Either the resolver returned <code>root: null</code> (look for
2252
+ <code>[Lens I] knowledge-root not located</code> in stderr) or the entry's <code>name:</code> doesn't
2253
+ normalize to the same slug as the bucket topic — the match is exact and
2254
+ post-normalize.</p>
2255
+ <pre><code class="language-bash">scaffold observe audit --lens I-knowledge-gaps --json \
2256
+ --knowledge-root /path/to/content/knowledge \
2257
+ | jq '.findings[] | select(.lens_id=="I-knowledge-gaps")'
2258
+ grep -A1 "^---" content/knowledge/&#x3C;cat>/&#x3C;slug>.md | grep "^name:"
2259
+ </code></pre>
2260
+ <h3 id="downstream-auto-detect-cant-find-the-kb">Downstream auto-detect can't find the KB</h3>
2261
+ <p><code>findScaffoldKnowledgeRoot</code> walks parents from the CLI install's module location
2262
+ looking for <code>package.json#name === '@zigrivers/scaffold'</code>. Symlinked or
2263
+ repackaged installs may miss. Pin it via the tier-2 yaml:</p>
2264
+ <pre><code class="language-yaml">lenses:
2265
+ I-knowledge-gaps:
2266
+ knowledge_root: /opt/homebrew/lib/node_modules/@zigrivers/scaffold/content/knowledge
2267
+ </code></pre>
2268
+ <h3 id="yaml-knowledge-root-stops-working-after-an-upgrade">Yaml <code>knowledge_root</code> stops working after an upgrade</h3>
2269
+ <p>The yaml tier soft-fails and records the reason in the attempts trail; Lens I
2270
+ appends it to the warning. Validation requires all four: the path exists, is a
2271
+ directory, contains a <code>&#x3C;path>/VERSION</code> marker, and <code>loadKnowledgeIndex</code> runs
2272
+ without throwing (an empty index is OK). The usual cause after an upgrade is a
2273
+ moved install path:</p>
2274
+ <pre><code class="language-bash">find / -name VERSION -path '*content/knowledge*' 2>/dev/null
2275
+ </code></pre>
2276
+ <p>Then update <code>lenses.I-knowledge-gaps.knowledge_root</code> to the new path.</p>
2277
+ <h3 id="a-source-url-fetches-in-curl-but-the-cron-rejects-it">A source URL fetches in <code>curl</code> but the cron rejects it</h3>
2278
+ <p>The SSRF guard re-resolves the hostname at fetch time and rejects any IP in a
2279
+ non-globally-routable range (RFC1918, link-local, loopback, CGNAT, ULA,
2280
+ IPv4-mapped IPv6, …). Common cause: an internal DNS view returning a private IP
2281
+ for an outwardly-public hostname.</p>
2282
+ <pre><code class="language-bash">node -e 'require("node:dns").promises.lookup("&#x3C;host>", { all: true }).then(console.log)'
2283
+ </code></pre>
2284
+ <p>Fix: move the source to a globally-routable host, or remove it. Allowlisting does
2285
+ <strong>not</strong> bypass the SSRF guard.</p>
2286
+ <h3 id="--knowledge-root-resolves-to-a-path-you-didnt-expect"><code>--knowledge-root</code> resolves to a path you didn't expect</h3>
2287
+ <p>Auto-detect may pick a stale npm-global install. The successful-resolution path
2288
+ doesn't log its <code>attempts</code> trail today (only the failure path warns), so pin and
2289
+ compare:</p>
2290
+ <pre><code class="language-bash">scaffold observe audit --lens I-knowledge-gaps --json \
2291
+ --knowledge-root /path/you/expected/content/knowledge \
2292
+ | jq '.findings[] | select(.lens_id=="I-knowledge-gaps") | .evidence.topic'
2293
+ # compare against the unset behavior; if the lists differ, auto-detect picked a different KB
2294
+ </code></pre>
2295
+ <p>Fix: pin <code>lenses.I-knowledge-gaps.knowledge_root</code> in
2296
+ <code>.scaffold/observability.yaml</code>. A pinned yaml path takes precedence over
2297
+ auto-detect.</p>
2298
+ <h2 id="config-reference">Config reference</h2>
2299
+ <p>Everything operator-tunable lives in <code>.scaffold/observability.yaml</code>. Anything
2300
+ outside this list is hardcoded (decision #7 invariant) so an untrusted project
2301
+ can't redirect dispatch commands or LLM URLs.</p>
2302
+ <pre><code class="language-yaml">lenses:
2303
+ I-knowledge-gaps:
2304
+ knowledge_root: /path/to/content/knowledge # tier-2 resolver override
2305
+
2306
+ disabled_lenses: [I-knowledge-gaps] # opt-out
2307
+
2308
+ phase_audit:
2309
+ enabled: true # default
2310
+ timeout_s: 60
2311
+ detached: false # fire-and-forget when true
2312
+
2313
+ fix:
2314
+ dispatcher_command: "claude -p" # default
2315
+ timeout_s: 300
2316
+ per_finding_max_attempts: 3
2317
+ </code></pre>
2318
+ <div class="callout callout-warning"><p><strong>The daily audit ceiling is NOT in yaml.</strong> The parent spec's decision #8 reads
2319
+ "10 grounded audits per day; configurable via <code>.scaffold/observability.yaml</code>",
2320
+ but the yaml knob was never implemented. The ceiling is the <code>--max=10</code> flag in
2321
+ <span class="fp" data-path=".github/workflows/knowledge-freshness-audit.yml:67">.github/workflows/knowledge-freshness-audit.yml:67</span>; the CLI default at
2322
+ <span class="fp" data-path="src/cli/commands/knowledge-freshness-audit-prefilter.ts:18">src/cli/commands/knowledge-freshness-audit-prefilter.ts:18</span> is the only
2323
+ fallback. To lower it for your fork, edit the workflow — nothing in yaml will help.</p></div>
2324
+ <h2 id="roadmap-and-known-divergences">Roadmap and known divergences</h2>
2325
+ <h3 id="phase-5-planned">Phase 5 (planned)</h3>
2326
+ <ul>
2327
+ <li><strong>Native MMR <code>knowledge-freshness</code> channel</strong> — runs automatically on freshness
2328
+ PRs (today the cron dispatches no MMR).</li>
2329
+ <li><strong>Frontier scan</strong> — augment cadence/hash triggers with a model-driven check:
2330
+ "has the underlying technology meaningfully changed since <code>last-reviewed</code>?"</li>
2331
+ <li><strong>Taxonomy cross-reference</strong> — detect when two entries assert contradictory
2332
+ facts and route to a reviewer.</li>
2333
+ </ul>
2334
+ <h3 id="known-divergences">Known divergences</h3>
2335
+ <p>The reference page's own audits surfaced these doc-vs-code mismatches; the code
2336
+ is ground truth:</p>
2337
+ <ul>
2338
+ <li><strong>MMR-in-cron framing</strong> — three docs describe it differently; decision #3
2339
+ (Phase 5 deferral) is authoritative.</li>
2340
+ <li><strong>Gate 4 override</strong> — spec says PR-description marker; code reads a PR <em>label</em>.</li>
2341
+ <li><strong>Daily ceiling</strong> — spec implies a yaml knob; only the <code>--max</code> flag exists.</li>
2342
+ <li><strong><code>operations.md</code> lags</strong> — labels the native MMR channel "Phase 4" and
2343
+ describes suppression in future tense; both have shipped.</li>
2344
+ <li><strong><code>www.</code> prefix inconsistency</strong> (P3) — mixed <code>www.</code> use in the allowlist; bare
2345
+ entries already auto-match subdomains, so the prefix is redundant.</li>
2346
+ </ul></main>
2347
+ </div>
2348
+ <script>(function(){
2349
+ var LS_KEY = 'guide-theme';
2350
+ function applyTheme(t) {
2351
+ document.documentElement.setAttribute('data-theme', t);
2352
+ }
2353
+
2354
+ document.addEventListener('DOMContentLoaded', function() {
2355
+ // ─── Theme toggle ────────────────────────────────────────────────────────
2356
+ document.querySelectorAll('[data-action="theme"]').forEach(function(btn) {
2357
+ btn.addEventListener('click', function() {
2358
+ var current = document.documentElement.getAttribute('data-theme');
2359
+ var next = current === 'dark' ? 'light' : 'dark';
2360
+ applyTheme(next);
2361
+ try { localStorage.setItem(LS_KEY, next); } catch(e) {}
2362
+ });
2363
+ });
2364
+
2365
+ // ─── Mobile nav ──────────────────────────────────────────────────────────
2366
+ document.querySelectorAll('[data-action="nav"]').forEach(function(btn) {
2367
+ btn.addEventListener('click', function() {
2368
+ var rail = document.querySelector('.rail');
2369
+ if (rail) rail.classList.toggle('open');
2370
+ });
2371
+ });
2372
+
2373
+ // ─── Copy buttons ─────────────────────────────────────────────────────────
2374
+ document.querySelectorAll('pre').forEach(function(pre) {
2375
+ if (!pre.parentNode) return;
2376
+ var wrapper = document.createElement('div');
2377
+ wrapper.className = 'code';
2378
+ pre.parentNode.insertBefore(wrapper, pre);
2379
+ wrapper.appendChild(pre);
2380
+ var btn = document.createElement('button');
2381
+ btn.className = 'copy-btn';
2382
+ btn.textContent = 'Copy';
2383
+ btn.addEventListener('click', function() {
2384
+ var text = pre.textContent || '';
2385
+ if (navigator.clipboard && navigator.clipboard.writeText) {
2386
+ navigator.clipboard.writeText(text).then(function() {
2387
+ btn.textContent = 'Copied';
2388
+ setTimeout(function() { btn.textContent = 'Copy'; }, 1200);
2389
+ }, function() {
2390
+ btn.textContent = 'Copy';
2391
+ });
2392
+ }
2393
+ });
2394
+ wrapper.insertBefore(btn, pre);
2395
+ });
2396
+
2397
+ // ─── Tabs ─────────────────────────────────────────────────────────────────
2398
+ document.querySelectorAll('.tabs').forEach(function(group) {
2399
+ group.querySelectorAll('.tab-btn').forEach(function(btn) {
2400
+ btn.addEventListener('click', function() {
2401
+ var idx = btn.getAttribute('data-tab');
2402
+ group.querySelectorAll('.tab-btn').forEach(function(b) {
2403
+ b.classList.toggle('active', b === btn);
2404
+ });
2405
+ group.querySelectorAll('.tabpane').forEach(function(pane) {
2406
+ pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
2407
+ });
2408
+ });
2409
+ });
2410
+ });
2411
+
2412
+ // ─── Filter tables ────────────────────────────────────────────────────────
2413
+ document.querySelectorAll('.filter-input').forEach(function(input) {
2414
+ input.addEventListener('input', function() {
2415
+ var q = input.value.toLowerCase();
2416
+ var container = input.closest('.filter-table');
2417
+ if (!container) return;
2418
+ container.querySelectorAll('tbody tr').forEach(function(row) {
2419
+ var text = (row.textContent || '').toLowerCase();
2420
+ row.style.display = text.includes(q) ? '' : 'none';
2421
+ });
2422
+ });
2423
+ });
2424
+
2425
+ // ─── Scrollspy ────────────────────────────────────────────────────────────
2426
+ if (typeof IntersectionObserver === 'undefined') return;
2427
+ var headings = document.querySelectorAll('h2[id],h3[id]');
2428
+ if (!headings.length) return;
2429
+ var observer = new IntersectionObserver(function(entries) {
2430
+ entries.forEach(function(entry) {
2431
+ if (!entry.isIntersecting) return;
2432
+ var id = entry.target.getAttribute('id');
2433
+ document.querySelectorAll('.toc a').forEach(function(a) {
2434
+ a.classList.toggle('active', a.getAttribute('href') === '#' + id);
2435
+ });
2436
+ });
2437
+ }, { rootMargin: '0px 0px -70% 0px', threshold: 0 });
2438
+ headings.forEach(function(h) { observer.observe(h); });
2439
+ });
2440
+ })();</script>
2441
+ </body>
2442
+ </html>