@xhulz/nina 0.50.3 → 0.52.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 (273) hide show
  1. package/README.md +11 -5
  2. package/bin/nina.mjs +1 -0
  3. package/package.json +1 -1
  4. package/releases/0.51.0/core/GAPS.md +125 -0
  5. package/releases/0.51.0/core/answered.json +68 -0
  6. package/releases/0.51.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  7. package/releases/0.51.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  8. package/releases/0.51.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  9. package/releases/0.51.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  10. package/releases/0.51.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  11. package/releases/0.51.0/core/defaults/tree/.claude/agents/qa.md +7 -0
  12. package/releases/0.51.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  13. package/releases/0.51.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  14. package/releases/0.51.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  15. package/releases/0.51.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  16. package/releases/0.51.0/core/tree/.claude/agents/architect.md +129 -0
  17. package/releases/0.51.0/core/tree/.claude/agents/dba.md +117 -0
  18. package/releases/0.51.0/core/tree/.claude/agents/devops.md +118 -0
  19. package/releases/0.51.0/core/tree/.claude/agents/implementer.md +163 -0
  20. package/releases/0.51.0/core/tree/.claude/agents/integration-tester.md +168 -0
  21. package/releases/0.51.0/core/tree/.claude/agents/planner.md +132 -0
  22. package/releases/0.51.0/core/tree/.claude/agents/qa.md +150 -0
  23. package/releases/0.51.0/core/tree/.claude/agents/reviewer.md +184 -0
  24. package/releases/0.51.0/core/tree/.claude/agents/secops.md +120 -0
  25. package/releases/0.51.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  26. package/releases/0.51.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  27. package/releases/0.51.0/core/tree/.claude/agents-overview.md +64 -0
  28. package/releases/0.51.0/core/tree/.claude/frontend.md +10 -0
  29. package/releases/0.51.0/core/tree/.claude/graph.md +75 -0
  30. package/releases/0.51.0/core/tree/.claude/patterns.md +251 -0
  31. package/releases/0.51.0/core/tree/.claude/pills/README.md +150 -0
  32. package/releases/0.51.0/core/tree/.claude/pipeline.md +101 -0
  33. package/releases/0.51.0/core/tree/.claude/retrieval.md +103 -0
  34. package/releases/0.51.0/core/tree/.claude/router.md +202 -0
  35. package/releases/0.51.0/core/tree/.claude/templates/integration.md +46 -0
  36. package/releases/0.51.0/core/tree/CLAUDE.md +164 -0
  37. package/releases/0.51.0/core/tree/scripts/cost-watch.mjs +25 -0
  38. package/releases/0.51.0/core/tree/scripts/edit-guard.mjs +24 -0
  39. package/releases/0.51.0/core/tree/scripts/harness-check.mjs +71 -0
  40. package/releases/0.51.0/core/tree/scripts/loop-gate.mjs +19 -0
  41. package/releases/0.51.0/core/vocabulary.json +13 -0
  42. package/releases/0.51.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  43. package/releases/0.51.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  44. package/releases/0.51.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  45. package/releases/0.51.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  46. package/releases/0.51.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  47. package/releases/0.51.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  48. package/releases/0.51.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  49. package/releases/0.51.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  50. package/releases/0.51.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  51. package/releases/0.51.0/surfaces/d1/tree/.claude/agents/dba.md +15 -0
  52. package/releases/0.51.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
  53. package/releases/0.51.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
  54. package/releases/0.51.0/surfaces/d1/tree/.claude/patterns.md +8 -0
  55. package/releases/0.51.0/surfaces/d1/tree/CLAUDE.md +2 -0
  56. package/releases/0.51.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  57. package/releases/0.51.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
  58. package/releases/0.51.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  59. package/releases/0.51.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  60. package/releases/0.51.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  61. package/releases/0.51.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  62. package/releases/0.51.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  63. package/releases/0.51.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  64. package/releases/0.51.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  65. package/releases/0.51.0/surfaces/db/tree/.claude/graph.md +8 -0
  66. package/releases/0.51.0/surfaces/db/tree/.claude/patterns.md +22 -0
  67. package/releases/0.51.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  68. package/releases/0.51.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  69. package/releases/0.51.0/surfaces/db/tree/.claude/router.md +4 -0
  70. package/releases/0.51.0/surfaces/db/tree/CLAUDE.md +17 -0
  71. package/releases/0.51.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
  72. package/releases/0.51.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
  73. package/releases/0.51.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
  74. package/releases/0.51.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
  75. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  76. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  77. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
  78. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  79. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  80. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  81. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  82. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  83. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  84. package/releases/0.51.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  85. package/releases/0.51.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  86. package/releases/0.51.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  87. package/releases/0.51.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  88. package/releases/0.51.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  89. package/releases/0.51.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  90. package/releases/0.51.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  91. package/releases/0.51.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  92. package/releases/0.51.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  93. package/releases/0.51.0/surfaces/frontend/tree/.claude/router.md +14 -0
  94. package/releases/0.51.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  95. package/releases/0.51.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  96. package/releases/0.51.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  97. package/releases/0.51.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  98. package/releases/0.51.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  99. package/releases/0.51.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  100. package/releases/0.51.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  101. package/releases/0.51.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  102. package/releases/0.51.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  103. package/releases/0.51.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  104. package/releases/0.51.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  105. package/releases/0.51.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  106. package/releases/0.51.0/surfaces/integrations/tree/.claude/router.md +6 -0
  107. package/releases/0.51.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  108. package/releases/0.51.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  109. package/releases/0.51.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  110. package/releases/0.51.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  111. package/releases/0.51.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  112. package/releases/0.51.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  113. package/releases/0.51.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  114. package/releases/0.51.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  115. package/releases/0.51.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  116. package/releases/0.51.0/surfaces/money/tree/.claude/graph.md +2 -0
  117. package/releases/0.51.0/surfaces/money/tree/.claude/patterns.md +49 -0
  118. package/releases/0.51.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  119. package/releases/0.51.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  120. package/releases/0.51.0/surfaces/money/tree/CLAUDE.md +11 -0
  121. package/releases/0.51.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  122. package/releases/0.51.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  123. package/releases/0.51.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  124. package/releases/0.51.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  125. package/releases/0.51.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  126. package/releases/0.51.0/surfaces/pii/tree/CLAUDE.md +5 -0
  127. package/releases/0.51.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  128. package/releases/0.51.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
  129. package/releases/0.51.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  130. package/releases/0.51.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  131. package/releases/0.51.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  132. package/releases/0.51.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  133. package/releases/0.51.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  134. package/releases/0.51.0/surfaces/prisma/tree/CLAUDE.md +5 -0
  135. package/releases/0.52.0/core/GAPS.md +125 -0
  136. package/releases/0.52.0/core/answered.json +68 -0
  137. package/releases/0.52.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  138. package/releases/0.52.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  139. package/releases/0.52.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  140. package/releases/0.52.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  141. package/releases/0.52.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  142. package/releases/0.52.0/core/defaults/tree/.claude/agents/qa.md +7 -0
  143. package/releases/0.52.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  144. package/releases/0.52.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  145. package/releases/0.52.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  146. package/releases/0.52.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  147. package/releases/0.52.0/core/tree/.claude/agents/architect.md +129 -0
  148. package/releases/0.52.0/core/tree/.claude/agents/dba.md +117 -0
  149. package/releases/0.52.0/core/tree/.claude/agents/devops.md +118 -0
  150. package/releases/0.52.0/core/tree/.claude/agents/implementer.md +163 -0
  151. package/releases/0.52.0/core/tree/.claude/agents/integration-tester.md +168 -0
  152. package/releases/0.52.0/core/tree/.claude/agents/planner.md +132 -0
  153. package/releases/0.52.0/core/tree/.claude/agents/qa.md +150 -0
  154. package/releases/0.52.0/core/tree/.claude/agents/reviewer.md +184 -0
  155. package/releases/0.52.0/core/tree/.claude/agents/secops.md +120 -0
  156. package/releases/0.52.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  157. package/releases/0.52.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  158. package/releases/0.52.0/core/tree/.claude/agents-overview.md +64 -0
  159. package/releases/0.52.0/core/tree/.claude/frontend.md +10 -0
  160. package/releases/0.52.0/core/tree/.claude/graph.md +75 -0
  161. package/releases/0.52.0/core/tree/.claude/patterns.md +251 -0
  162. package/releases/0.52.0/core/tree/.claude/pills/README.md +150 -0
  163. package/releases/0.52.0/core/tree/.claude/pipeline.md +101 -0
  164. package/releases/0.52.0/core/tree/.claude/retrieval.md +103 -0
  165. package/releases/0.52.0/core/tree/.claude/router.md +202 -0
  166. package/releases/0.52.0/core/tree/.claude/templates/integration.md +46 -0
  167. package/releases/0.52.0/core/tree/CLAUDE.md +164 -0
  168. package/releases/0.52.0/core/tree/scripts/cost-watch.mjs +25 -0
  169. package/releases/0.52.0/core/tree/scripts/edit-guard.mjs +24 -0
  170. package/releases/0.52.0/core/tree/scripts/harness-check.mjs +71 -0
  171. package/releases/0.52.0/core/tree/scripts/loop-gate.mjs +19 -0
  172. package/releases/0.52.0/core/vocabulary.json +13 -0
  173. package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  174. package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  175. package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  176. package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  177. package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  178. package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  179. package/releases/0.52.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  180. package/releases/0.52.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  181. package/releases/0.52.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  182. package/releases/0.52.0/surfaces/d1/tree/.claude/agents/dba.md +15 -0
  183. package/releases/0.52.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
  184. package/releases/0.52.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
  185. package/releases/0.52.0/surfaces/d1/tree/.claude/patterns.md +8 -0
  186. package/releases/0.52.0/surfaces/d1/tree/CLAUDE.md +2 -0
  187. package/releases/0.52.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  188. package/releases/0.52.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
  189. package/releases/0.52.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  190. package/releases/0.52.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  191. package/releases/0.52.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  192. package/releases/0.52.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  193. package/releases/0.52.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  194. package/releases/0.52.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  195. package/releases/0.52.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  196. package/releases/0.52.0/surfaces/db/tree/.claude/graph.md +8 -0
  197. package/releases/0.52.0/surfaces/db/tree/.claude/patterns.md +22 -0
  198. package/releases/0.52.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  199. package/releases/0.52.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  200. package/releases/0.52.0/surfaces/db/tree/.claude/router.md +4 -0
  201. package/releases/0.52.0/surfaces/db/tree/CLAUDE.md +17 -0
  202. package/releases/0.52.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
  203. package/releases/0.52.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
  204. package/releases/0.52.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
  205. package/releases/0.52.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
  206. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  207. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  208. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
  209. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  210. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  211. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  212. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  213. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  214. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  215. package/releases/0.52.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  216. package/releases/0.52.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  217. package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  218. package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  219. package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  220. package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  221. package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  222. package/releases/0.52.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  223. package/releases/0.52.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  224. package/releases/0.52.0/surfaces/frontend/tree/.claude/router.md +14 -0
  225. package/releases/0.52.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  226. package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  227. package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  228. package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  229. package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  230. package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  231. package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  232. package/releases/0.52.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  233. package/releases/0.52.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  234. package/releases/0.52.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  235. package/releases/0.52.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  236. package/releases/0.52.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  237. package/releases/0.52.0/surfaces/integrations/tree/.claude/router.md +6 -0
  238. package/releases/0.52.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  239. package/releases/0.52.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  240. package/releases/0.52.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  241. package/releases/0.52.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  242. package/releases/0.52.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  243. package/releases/0.52.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  244. package/releases/0.52.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  245. package/releases/0.52.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  246. package/releases/0.52.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  247. package/releases/0.52.0/surfaces/money/tree/.claude/graph.md +2 -0
  248. package/releases/0.52.0/surfaces/money/tree/.claude/patterns.md +49 -0
  249. package/releases/0.52.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  250. package/releases/0.52.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  251. package/releases/0.52.0/surfaces/money/tree/CLAUDE.md +11 -0
  252. package/releases/0.52.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  253. package/releases/0.52.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  254. package/releases/0.52.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  255. package/releases/0.52.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  256. package/releases/0.52.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  257. package/releases/0.52.0/surfaces/pii/tree/CLAUDE.md +5 -0
  258. package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  259. package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
  260. package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  261. package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  262. package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  263. package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  264. package/releases/0.52.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  265. package/releases/0.52.0/surfaces/prisma/tree/CLAUDE.md +5 -0
  266. package/src/commands/learn.mjs +302 -48
  267. package/src/commands/pills.mjs +11 -8
  268. package/src/commands/stats.mjs +28 -8
  269. package/src/commands/upgrade.mjs +1 -1
  270. package/src/gate.mjs +14 -2
  271. package/src/office.html +142 -4
  272. package/src/office.mjs +144 -21
  273. package/src/store.mjs +13 -1
@@ -6,7 +6,7 @@
6
6
  * observe its runs are recorded — the snapshot
7
7
  * capture a mistake that will recur is written down — a pill
8
8
  * apply the next run reads it — the role's spec tells it to
9
- * graduate a lesson that keeps recurring becomes a rule — a request to the harness
9
+ * graduate a lesson that keeps recurring becomes a rule — here, or by a request to the harness
10
10
  * verify the rule is seen to have helped — the loop-back rate, before and after
11
11
  *
12
12
  * An audit found every link but the first open. Capture ran at 3% of the rate the router asked
@@ -23,12 +23,12 @@
23
23
  */
24
24
 
25
25
  import { existsSync, readdirSync } from 'node:fs';
26
- import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
27
- import { join, relative, resolve } from 'node:path';
26
+ import { mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
27
+ import { isAbsolute, join, relative, resolve, sep } from 'node:path';
28
28
  import { HARNESS, readProfile, slugFor, snapshotsDir } from '../paths.mjs';
29
29
  import { localDay, projectRecords } from '../store.mjs';
30
30
  import { isLoopBack, runOf } from '../transcripts.mjs';
31
- import { byVersion, closedGate, layerRootFor } from './compose.mjs';
31
+ import { SLOT, byVersion, closedGate, layerRootFor, noticeOf } from './compose.mjs';
32
32
  import { GRADUATION_AT, RETIRED, frontmatter, graduationTarget, list, pillFiles, roleGates, tidyRetired } from './pills.mjs';
33
33
  import { decodeProjectDir } from './stats.mjs';
34
34
  import { snapshot } from './snapshot.mjs';
@@ -335,6 +335,8 @@ export function relapsed(known, filed) {
335
335
  }
336
336
  const out = [];
337
337
  for (const [pill, request] of newest) {
338
+ // Declined since: the harness left it to the project, which is asked for it as a rule of its own (`undecided`).
339
+ if (newestRecord(filed, pill)?.status === 'declined') continue;
338
340
  const shelved = pill.replace(/^\.claude\/pills\//, `.claude/pills/${RETIRED}/`);
339
341
  const lesson = known.find((l) => l.rel === pill || l.rel === shelved);
340
342
  const then = Number(request.occurrences);
@@ -346,20 +348,80 @@ export function relapsed(known, filed) {
346
348
  return out;
347
349
  }
348
350
 
351
+ /** A record's `target` when the lesson became a rule in its own project instead of going to the harness. */
352
+ export const HERE = 'project';
353
+
354
+ /** How far back the harness inbox lists the rules projects made of their own lessons. */
355
+ const MADE_HERE_DAYS = 30;
356
+
349
357
  /**
350
- * Lessons that have recurred often enough to graduate and have no request yet. Before this, the
351
- * threshold was a note in a report someone had to go and read; the per-turn detector now files
352
- * the request itself.
358
+ * Lessons owed a graduation, and so a decision only the project can make: where the rule goes. Each is active
359
+ * and either recurred often enough with nothing recorded for it, or was declined by the harness — which leaves
360
+ * the rule to its project — and not made one there yet.
361
+ *
362
+ * The detector used to file every lesson at three as a request to the harness. A convention of one codebase
363
+ * then went the whole way — request, decline, release, upgrade — and came back still a pill: the one step it
364
+ * needed, a rule in its own project, was the one nothing asked for.
353
365
  *
354
366
  * @param {Awaited<ReturnType<typeof lessons>>} known - The project's pills.
355
- * @param {{pill: string}[]} filed - Requests already written, open or closed.
356
- * @returns {Awaited<ReturnType<typeof lessons>>}
367
+ * @param {Awaited<ReturnType<typeof filedRequests>>} filed - What it has recorded about them.
368
+ * @returns {{lesson: Awaited<ReturnType<typeof lessons>>[number], declined?: Awaited<ReturnType<typeof filedRequests>>[number]}[]}
357
369
  */
358
- function unfiled(known, filed) {
359
- const asked = new Set(filed.map((r) => r.pill));
360
- return known.filter((l) => !l.retired && l.occurrences >= GRADUATION_AT && !asked.has(l.rel));
370
+ export function undecided(known, filed) {
371
+ const out = [];
372
+ for (const lesson of known) {
373
+ const last = newestRecord(filed, learnedAt(lesson.rel));
374
+ if (!last && !lesson.retired && lesson.occurrences >= GRADUATION_AT) out.push({ lesson });
375
+ // Retired, only when what was declined reopened it: a rule that did not hold, sent to the harness and declined
376
+ // there, is the project's to make again — otherwise nothing could settle it. One retired by hand after its
377
+ // first request was declined is settled.
378
+ else if (last?.status === 'declined' && (!lesson.retired || last.reopens)) out.push({ lesson, declined: last });
379
+ }
380
+ return out;
381
+ }
382
+
383
+ /** Where a pill was learned — the path its records name — whether or not it has since been retired. */
384
+ const learnedAt = (rel) => rel.replace(new RegExp(`^\\.claude/pills/${RETIRED}/`), '.claude/pills/');
385
+
386
+ /**
387
+ * The newest record about one pill: by date, and on one date the one that counted most, since what comes after
388
+ * — a reopening, a rule made again — is written with the count it saw. By the order a directory lists them, a
389
+ * record written the same day read as older.
390
+ *
391
+ * @param {Awaited<ReturnType<typeof filedRequests>>} filed - The project's records.
392
+ * @param {string} pill - Where the pill was learned.
393
+ * @returns {Awaited<ReturnType<typeof filedRequests>>[number]|undefined}
394
+ */
395
+ function newestRecord(filed, pill) {
396
+ let newest;
397
+ for (const r of filed) {
398
+ if (r.pill !== pill) continue;
399
+ const later = !newest || r.date > newest.date ||
400
+ (r.date === newest.date && (Number(r.occurrences) > Number(newest.occurrences) ||
401
+ (Number(r.occurrences) === Number(newest.occurrences) && generation(r.file) > generation(newest.file))));
402
+ if (later) newest = r;
403
+ }
404
+ return newest;
361
405
  }
362
406
 
407
+ /** Which record of one pill on one day a file name is: the first, `-again`, `-again-2`, … ({@link recordName}). */
408
+ const generation = (file) => {
409
+ const again = /-again(?:-(\d+))?\.md$/.exec(String(file ?? ''));
410
+ return again ? Number(again[1] ?? 1) : 0;
411
+ };
412
+
413
+ /**
414
+ * Where a graduated lesson's rule went, as a clause: "it became a rule here in …" or "its rule shipped in …".
415
+ *
416
+ * @param {{target: string, rule_in: string, answered_in: string}} request - The record of its graduation.
417
+ * @param {boolean} [layer] - Whether to name the layer a shipped rule went into.
418
+ * @returns {string}
419
+ */
420
+ const ruleWent = (request, layer = true) =>
421
+ request.target === HERE
422
+ ? `it became a rule here in ${request.rule_in}`
423
+ : `its rule shipped in ${request.answered_in || 'a release'}${layer ? ` (${request.rule_in || request.target})` : ''}`;
424
+
363
425
  /**
364
426
  * Every request this project has written, with its status.
365
427
  *
@@ -381,6 +443,8 @@ export async function filedRequests(target) {
381
443
  occurrences: fields.occurrences ?? '',
382
444
  rule_in: fields.rule_in ?? '',
383
445
  answered_in: fields.answered_in ?? '',
446
+ declined: fields.declined ?? '',
447
+ reopens: fields.reopens ?? '',
384
448
  });
385
449
  }
386
450
  return out;
@@ -409,13 +473,23 @@ async function answerRequest(target, file, answer) {
409
473
  // A declined lesson is still this project's lesson: the harness said it does not belong in every
410
474
  // project, not that it stopped being true in this one. So is one whose rule went into a layer this
411
475
  // project does not compose — retiring it there would drop the lesson with nothing in its place.
412
- if (!answer.retire || !fields.pill || !existsSync(join(target, fields.pill))) return { pill: null };
413
- const pill = await readFile(join(target, fields.pill), 'utf8');
476
+ return { pill: answer.retire ? await retirePill(target, fields.pill) : null };
477
+ }
478
+
479
+ /**
480
+ * Retires a pill, and moves it out of the way of the agents that open every pill to see which apply.
481
+ *
482
+ * @param {string} target - The project directory.
483
+ * @param {string} rel - The pill, relative to the project.
484
+ * @returns {Promise<string|null>} The pill retired, or null when there is none at that path.
485
+ */
486
+ async function retirePill(target, rel) {
487
+ if (!rel || !existsSync(join(target, rel))) return null;
488
+ const pill = await readFile(join(target, rel), 'utf8');
414
489
  const retired = /^status: .*$/m.test(pill) ? pill.replace(/^status: .*$/m, 'status: retired') : pill.replace(/^---\n/, '---\nstatus: retired\n');
415
- await writeFile(join(target, fields.pill), retired);
416
- // And out of the way of the agents that open every pill to see which apply.
490
+ await writeFile(join(target, rel), retired);
417
491
  await tidyRetired(target);
418
- return { pill: fields.pill };
492
+ return rel;
419
493
  }
420
494
 
421
495
  /**
@@ -609,18 +683,19 @@ async function fileRequest(target, profile, layerRoot, lesson, { reopens = null,
609
683
  const date = localDay(new Date().toISOString());
610
684
  // Asked about the pill where it was learned, a retired one included: its request names that path.
611
685
  const pill = reopens?.pill ?? lesson.rel;
612
- // The directory separator becomes `__`, so `reviewer/a.b.md` and `reviewer-a/b.md` stay two
613
- // names — flattening both `/` and `.` to `-` gave them the same one.
614
- const name = pill.replace(/^\.claude\/pills\//, '').replace(/\.md$/, '').replace(/\//g, '__');
615
686
  const dir = join(target, HARNESS, 'requests');
616
- // Never over a request already written: the same lesson can be sent more than once on one day.
617
- let file = `${date}-${name}.md`;
618
- for (let n = 1; existsSync(join(dir, file)); n += 1) file = `${date}-${name}-again${n > 1 ? `-${n}` : ''}.md`;
619
687
  await mkdir(dir, { recursive: true });
620
- const heading = reopens
621
- ? `**A lesson that graduated into \`${reopens.rule_in || reopens.target}\` in ${reopens.answered_in || 'a release'} came back ${recurred} time(s) since** — ` +
622
- `the rule did not hold here. It reopens ${HARNESS}/requests/${reopens.file}.`
623
- : `**A lesson this project has learned ${lesson.occurrences} time(s), proposed as a rule in \`${target_.layer}\`** — ${target_.why}.`;
688
+ const file = recordName(dir, pill, date);
689
+ // A rule this project made of its own that did not hold goes to the harness, as a rule for the layer its roles
690
+ // live in; one the harness shipped goes back to where it went.
691
+ const mine = reopens?.target === HERE;
692
+ const heading = mine
693
+ ? `**A lesson this project made a rule in \`${reopens.rule_in}\` came back ${recurred} time(s) since** — the rule did not hold here, ` +
694
+ `so it goes to the harness, proposed as a rule in \`${target_.layer}\`: ${target_.why}. It reopens ${HARNESS}/requests/${reopens.file}.`
695
+ : reopens
696
+ ? `**A lesson that graduated into \`${reopens.rule_in || reopens.target}\` in ${reopens.answered_in || 'a release'} came back ${recurred} time(s) since** — ` +
697
+ `the rule did not hold here. It reopens ${HARNESS}/requests/${reopens.file}.`
698
+ : `**A lesson this project has learned ${lesson.occurrences} time(s), proposed as a rule in \`${target_.layer}\`** — ${target_.why}.`;
624
699
  await writeFile(
625
700
  join(dir, file),
626
701
  [
@@ -629,7 +704,7 @@ async function fileRequest(target, profile, layerRoot, lesson, { reopens = null,
629
704
  'status: open',
630
705
  `date: ${date}`,
631
706
  `core: ${profile.core}`,
632
- `target: ${reopens?.target || target_.layer}`,
707
+ `target: ${reopens && !mine ? reopens.target || target_.layer : target_.layer}`,
633
708
  `pill: ${pill}`,
634
709
  `occurrences: ${lesson.occurrences}`,
635
710
  ...(reopens ? [`reopens: ${reopens.file}`, `rule_in: ${oneLine(reopens.rule_in)}`] : []),
@@ -649,17 +724,156 @@ async function fileRequest(target, profile, layerRoot, lesson, { reopens = null,
649
724
  }
650
725
 
651
726
  /**
652
- * `nina learn --graduate <pill>` — files a request by hand. The detector files one on its own
653
- * once a lesson reaches three occurrences; this is for sending one sooner.
727
+ * A file name under `.nina/requests/` for a record about one pill, never one already written: the same
728
+ * lesson can be recorded more than once on one day.
654
729
  *
730
+ * @param {string} dir - The project's `.nina/requests/`.
731
+ * @param {string} pill - The pill, relative to the project.
732
+ * @param {string} date - Today, as the owner's day.
733
+ * @returns {string}
734
+ */
735
+ function recordName(dir, pill, date) {
736
+ // The directory separator becomes `__`, so `reviewer/a.b.md` and `reviewer-a/b.md` stay two
737
+ // names — flattening both `/` and `.` to `-` gave them the same one.
738
+ const name = pill.replace(/^\.claude\/pills\//, '').replace(/\.md$/, '').replace(/\//g, '__');
739
+ let file = `${date}-${name}.md`;
740
+ for (let n = 1; existsSync(join(dir, file)); n += 1) file = `${date}-${name}-again${n > 1 ? `-${n}` : ''}.md`;
741
+ return file;
742
+ }
743
+
744
+ /**
745
+ * Why a file cannot hold the rule a project makes of its own lesson, or null when it can.
746
+ *
747
+ * The rule goes where the project writes and its agents read. A composed file is neither — the next compose
748
+ * overwrites it, and the edit guard refuses the edit — so its rule goes in the project layer's copy; a pill, a
749
+ * plan or NINA's own records are not where a rule lives.
750
+ *
751
+ * @param {string} target - The project directory.
752
+ * @param {string} file - The file named, as given.
753
+ * @returns {Promise<string|null>}
754
+ */
755
+ async function notHere(target, file, layerRoot, surfaces) {
756
+ const rel = relative(target, resolve(target, file));
757
+ if (!file || rel === '' || rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return `${file || 'no file'} is not a file inside the project`;
758
+ const path = join(target, rel);
759
+ if (!(await stat(path).catch(() => null))?.isFile()) return `${rel} is not a file — write the rule first, then graduate the lesson into it`;
760
+ const posix = rel.split(sep).join('/');
761
+ const text = await readFile(path, 'utf8');
762
+ if (noticeOf(text)) {
763
+ return `${posix} is composed from the harness, and the next compose overwrites it — write the rule in this project's layer, ` +
764
+ `${HARNESS}/project/tree/${posix} (\`nina where ${posix}\` lists its slots), and name that file`;
765
+ }
766
+ if (posix.startsWith('.claude/pills/')) return `${posix} is a pill — a rule lives where no agent has to remember to apply it`;
767
+ if (posix.startsWith('.claude/plans/')) return `${posix} is a plan, work in progress — the rule outlives it`;
768
+ const layer = `${HARNESS}/project/tree/`;
769
+ if (posix.startsWith(`${HARNESS}/`) && !posix.startsWith(layer)) {
770
+ return `${posix} is NINA's own record — the rule goes in ${layer} or a file the project owns`;
771
+ }
772
+ // A fragment composes only into a slot its core file opens for the project: anywhere else, no agent reads it.
773
+ if (posix.startsWith(layer)) {
774
+ const composed = posix.slice(layer.length);
775
+ const projectSlots = (lines) => new Set(lines.split('\n').map((l) => SLOT.exec(l.trim())?.[1]).filter((s) => s?.startsWith('project.')));
776
+ const core = await readFile(join(layerRoot, 'core', 'tree', composed), 'utf8').catch(() => '');
777
+ const gate = closedGate(core, surfaces);
778
+ if (gate) return `${posix} is a fragment of ${composed}, which this project does not compose without the ${gate} surface — no agent would read the rule`;
779
+ const open = projectSlots(core);
780
+ if (![...projectSlots(text)].some((s) => open.has(s))) {
781
+ return `${posix} fills no project slot of ${composed}, so nothing composes it and no agent would read the rule — \`nina where ${composed}\` lists the slots`;
782
+ }
783
+ }
784
+ return null;
785
+ }
786
+
787
+ /**
788
+ * Records a lesson this project made a rule of its own, and retires its pill.
789
+ *
790
+ * Written beside the requests and closed from the start, so the reader that watches a graduated lesson for
791
+ * coming back — {@link relapsed} — watches this one too, and the harness inbox lists it, where a rule every
792
+ * project needs can still be raised.
793
+ *
794
+ * @param {string} target - The project directory.
795
+ * @param {{core: string}} profile - The project's profile.
796
+ * @param {Awaited<ReturnType<typeof lessons>>[number]} lesson - The lesson.
797
+ * @param {string} rule - The file that now holds the rule, relative to the project.
798
+ * @returns {Promise<string>} The record's file name.
799
+ */
800
+ async function graduateHere(target, profile, lesson, rule) {
801
+ const date = localDay(new Date().toISOString());
802
+ const dir = join(target, HARNESS, 'requests');
803
+ await mkdir(dir, { recursive: true });
804
+ const pill = learnedAt(lesson.rel);
805
+ const file = recordName(dir, pill, date);
806
+ await writeFile(
807
+ join(dir, file),
808
+ [
809
+ '---',
810
+ 'kind: project-rule',
811
+ 'status: closed',
812
+ `date: ${date}`,
813
+ `core: ${profile.core}`,
814
+ `target: ${HERE}`,
815
+ `pill: ${pill}`,
816
+ `occurrences: ${lesson.occurrences}`,
817
+ `rule_in: ${rule}`,
818
+ '---',
819
+ '',
820
+ `**A lesson this project has learned ${lesson.occurrences} time(s), made a rule here in \`${rule}\`** — a convention of this codebase.`,
821
+ '',
822
+ `If it comes back ${CAPTURE_AT} more times, the rule did not hold, and \`harness:check\` sends it to the harness.`,
823
+ '',
824
+ lesson.body,
825
+ '',
826
+ ].join('\n'),
827
+ { flag: 'wx' },
828
+ );
829
+ // A record with the pill still active would stop the asking with the lesson nowhere: both, or neither.
830
+ try {
831
+ await retirePill(target, lesson.rel);
832
+ } catch (error) {
833
+ await rm(join(dir, file), { force: true });
834
+ throw error;
835
+ }
836
+ return file;
837
+ }
838
+
839
+ /**
840
+ * `nina learn --graduate <pill>` — sends a lesson to the harness as a request; with `--here <file>`, records
841
+ * that the project made it a rule of its own, in that file, and retires the pill. The detector asks for one or
842
+ * the other once a lesson reaches three occurrences, and either can be done sooner.
843
+ *
844
+ * @param {string|null} here - The file that holds the rule, for a lesson that graduates in its own project.
655
845
  * @returns {Promise<number>} Process exit code.
656
846
  */
657
- async function graduate(target, profile, layerRoot, known, which) {
847
+ async function graduate(target, profile, layerRoot, known, which, here) {
658
848
  const lesson = known.find((l) => l.rel === which || l.path === resolve(target, which));
659
849
  if (!lesson) {
660
850
  console.error(` no pill at ${which} — run \`nina learn\` to list them\n`);
661
851
  return 1;
662
852
  }
853
+ if (here !== null) {
854
+ // Declined is the one answer from the harness that leaves the rule to the project. A request still open is
855
+ // waiting on it, and a pill retired by any other record has graduated already.
856
+ const last = newestRecord(await filedRequests(target), learnedAt(lesson.rel));
857
+ if (last ? last.status !== 'declined' : lesson.retired) {
858
+ console.error(last ? ` ${lesson.rel} is recorded already, ${last.status}: ${HARNESS}/requests/${last.file}\n` : ` ${lesson.rel} is retired already\n`);
859
+ return 1;
860
+ }
861
+ const why = await notHere(target, here, layerRoot, profile.surfaces ?? []);
862
+ if (why) {
863
+ console.error(` ${why}\n`);
864
+ return 1;
865
+ }
866
+ const rule = relative(target, resolve(target, here)).split(sep).join('/');
867
+ const file = await graduateHere(target, profile, lesson, rule);
868
+ console.log(` made a rule here: ${lesson.rel} → ${rule}`);
869
+ console.log(` recorded in ${HARNESS}/requests/${file}; the pill is retired, under .claude/pills/${RETIRED}/`);
870
+ console.log(
871
+ rule.startsWith(`${HARNESS}/project/tree/`)
872
+ ? ' `nina compose` puts it where the agents read it; commit the rule, the record and the pill together\n'
873
+ : ' commit the rule, the record and the pill together\n',
874
+ );
875
+ return 0;
876
+ }
663
877
  if (lesson.roles.length === 0) {
664
878
  console.error(` ${lesson.rel} names no role in applies_to, so there is no layer to send it to — \`nina pills\` says what to fix\n`);
665
879
  return 1;
@@ -724,7 +938,8 @@ export async function learn(argv, ctx) {
724
938
  if (argv.includes('--deep')) return deepReport(target, records, known, argv);
725
939
 
726
940
  if (argv.includes('--graduate')) {
727
- return graduate(target, profile, resolved.dir, known, argv[argv.indexOf('--graduate') + 1] ?? '');
941
+ const here = argv.includes('--here') ? argv[argv.indexOf('--here') + 1] ?? '' : null;
942
+ return graduate(target, profile, resolved.dir, known, argv[argv.indexOf('--graduate') + 1] ?? '', here);
728
943
  }
729
944
  if (argv.includes('--close')) return close(target, argv[argv.indexOf('--close') + 1] ?? '');
730
945
 
@@ -734,7 +949,7 @@ export async function learn(argv, ctx) {
734
949
  const targets = loops ? new Set([...loops.values()].flatMap((byToken) => [...byToken.values()].flatMap((to) => [...to.keys()]))) : null;
735
950
  const late = overdue(records, known, new Date(), days, owners);
736
951
  const filed = await filedRequests(target);
737
- const owed = unfiled(known, filed);
952
+ const owed = undecided(known, filed);
738
953
 
739
954
  if (check) {
740
955
  // During `nina upgrade --apply`: this is about history, not the move — see upgrade.mjs.
@@ -751,14 +966,13 @@ export async function learn(argv, ctx) {
751
966
  } catch (error) {
752
967
  exported = `the export to Langfuse could not start — ${error.message}`;
753
968
  }
754
- // A lesson at three occurrences used to be reported here with the command that files it, and
755
- // then it waited for someone to type that command. Filing decides nothing — the request is a
756
- // proposal the harness maintainer accepts or declines — so the detector files it and says so
757
- // once. The per-line actions matter too: one hint for the whole detector told a project to
758
- // "write the lesson" when the lesson was written three times over and only needed sending.
969
+ // A lesson at three occurrences used to be filed here as a request to the harness, which decided where it
970
+ // went before anyone asked: a convention of one codebase went the whole way and came back still a pill. Where
971
+ // a lesson graduates is the project's judgment, so it is asked for, every turn until it is made (`undecided`).
972
+ // A lesson that came back after it graduated is no judgment — the rule did not hold — and goes to the
973
+ // harness on its own, said once.
759
974
  const sent = [];
760
- // A lesson that came back after its rule shipped goes back to the harness the way it went the first time.
761
- const outgoing = [...owed.map((lesson) => ({ lesson })), ...relapsed(known, filed).filter((r) => r.due).map(({ lesson, request, recurred }) => ({ lesson, reopens: request, recurred }))];
975
+ const outgoing = relapsed(known, filed).filter((r) => r.due).map(({ lesson, request, recurred }) => ({ lesson, reopens: request, recurred }));
762
976
  const stuck = outgoing
763
977
  .filter((o) => o.lesson.roles.length === 0)
764
978
  .map((o) => ({ ...o, why: 'names no role in applies_to, so no layer can take it', fix: 'fix its frontmatter — `nina pills` says what is wrong' }));
@@ -775,7 +989,7 @@ export async function learn(argv, ctx) {
775
989
  stuck.push({ ...o, why: `could not be written to ${HARNESS}/requests/ — ${error.message}`, fix: `make it writable, or send it by hand: \`nina learn --graduate ${o.lesson.rel}\`` });
776
990
  }
777
991
  }
778
- if (late.length === 0 && sent.length === 0 && due.length === 0 && stuck.length === 0 && !exported) {
992
+ if (late.length === 0 && owed.length === 0 && sent.length === 0 && due.length === 0 && stuck.length === 0 && !exported) {
779
993
  console.log('learn: current');
780
994
  return 0;
781
995
  }
@@ -788,12 +1002,27 @@ export async function learn(argv, ctx) {
788
1002
  for (const r of o.recent) console.log(` ${r.ts} ${r.desc || '(no description)'} session ${r.session}`);
789
1003
  console.log(' → ask of each whether it would happen again; if so, write the pill, or bump `occurrences` and `last_seen` on the one that already says it');
790
1004
  }
791
- const what = (o) =>
792
- o.reopens
793
- ? `${o.lesson.rel} came back ${o.recurred} time(s) after its rule shipped in ${o.reopens.answered_in || 'a release'} (${o.reopens.rule_in || o.reopens.target})`
794
- : `${o.lesson.rel} has recurred ${o.lesson.occurrences} times`;
1005
+ for (const { lesson, declined } of owed) {
1006
+ const here = `\`nina learn --graduate ${lesson.rel} --here <file>\``;
1007
+ if (declined) {
1008
+ console.log(` ${lesson.rel} was declined by the harness${declined.answered_in ? ` in ${declined.answered_in}` : ''}: ${declined.declined || 'no reason recorded'}`);
1009
+ console.log(
1010
+ lesson.retired
1011
+ ? ` → the rule this project made did not hold: make it again where its agents read it — in its layer under ${HARNESS}/project/tree/, or a file it owns — then ${here}; or, if the reason says a rule already covers it, \`nina learn --close ${declined.file}\``
1012
+ : ` → make it a rule of this project — in its layer under ${HARNESS}/project/tree/, or a file it owns — then ${here}; or, if the reason says a rule already covers it, set the pill's status to retired`,
1013
+ );
1014
+ continue;
1015
+ }
1016
+ console.log(` ${lesson.rel} has recurred ${lesson.occurrences} times — a rule, not a pill. Where it goes is this project's call:`);
1017
+ console.log(` → a convention of this codebase: write the rule in this project's layer under ${HARNESS}/project/tree/ (\`nina where <file>\` lists the slots) or in a file it owns, then ${here}`);
1018
+ console.log(` → a rule any project of this shape needs: \`nina learn --graduate ${lesson.rel}\` sends it to the harness; commit the request with the pill`);
1019
+ }
1020
+ const what = (o) => `${o.lesson.rel} came back ${o.recurred} time(s) after ${ruleWent(o.reopens)}`;
795
1021
  for (const s of sent) {
796
- console.log(` ${what(s)} — sent to the harness as ${HARNESS}/requests/${s.file}${s.reopens ? ', reopening the rule' : `, proposing a rule in ${s.layer}`}`);
1022
+ console.log(
1023
+ ` ${what(s)} — sent to the harness as ${HARNESS}/requests/${s.file}` +
1024
+ (s.reopens.target === HERE ? `, the rule this project made having not held: proposed for ${s.layer}` : ', reopening the rule'),
1025
+ );
797
1026
  console.log(' → commit it with the pill; nothing else is owed here. `nina upgrade` to the release that answers it closes it and retires the pill');
798
1027
  }
799
1028
  for (const o of due) {
@@ -807,6 +1036,7 @@ export async function learn(argv, ctx) {
807
1036
  console.log(
808
1037
  `learn: ${[
809
1038
  late.length && `${late.length} role(s) keep being sent back with nothing written down`,
1039
+ owed.length && `${owed.length} lesson(s) to graduate, here or in the harness`,
810
1040
  sent.length && `${sent.length} lesson(s) sent to the harness`,
811
1041
  due.length && `${due.length} lesson(s) to send to the harness`,
812
1042
  stuck.length && `${stuck.length} lesson(s) cannot be sent`,
@@ -844,16 +1074,24 @@ export async function learn(argv, ctx) {
844
1074
  `${use.listed} only listed the directory — measured since ${use.from}. Reading is not obeying; nothing here can see that.`
845
1075
  : ' apply not measured yet — runs recorded from now on say whether they read their pills',
846
1076
  );
1077
+ const madeHere = filed.filter((r) => r.target === HERE).length;
847
1078
  console.log(
848
1079
  ` graduate ${ready.length} lesson(s) at ${GRADUATION_AT}+ occurrences · ${open.length} request(s) open` +
1080
+ (madeHere ? ` · ${madeHere} made a rule here` : '') +
849
1081
  (Math.max(0, ...active.map((l) => l.occurrences)) < 2
850
1082
  ? ' — no lesson has ever been counted twice; bump `occurrences` when one recurs'
851
1083
  : ''),
852
1084
  );
853
- for (const l of ready.filter((l) => owed.includes(l))) console.log(` not sent yet: ${l.rel} — the next \`harness:check\` sends it`);
1085
+ for (const { lesson, declined } of owed) {
1086
+ console.log(
1087
+ declined
1088
+ ? ` declined by the harness: ${lesson.rel} — make it a rule here, \`nina learn --graduate ${lesson.rel} --here <file>\``
1089
+ : ` to graduate: ${lesson.rel} — here (\`--graduate ${lesson.rel} --here <file>\`) or in the harness (\`--graduate ${lesson.rel}\`)`,
1090
+ );
1091
+ }
854
1092
  for (const r of open) console.log(` open since ${r.date}: ${r.pill} → ${r.target}. \`nina upgrade\` to the release that answers it closes it`);
855
1093
  for (const r of relapsed(known, filed)) {
856
- const shipped = `came back ${r.recurred} time(s) after its rule shipped in ${r.request.answered_in || 'a release'}`;
1094
+ const shipped = `came back ${r.recurred} time(s) after ${ruleWent(r.request, false)}`;
857
1095
  if (r.due) console.log(` ✗ ${r.lesson.rel} ${shipped} — the rule did not hold; \`harness:check\` reopens it`);
858
1096
  else console.log(` · ${r.lesson.rel} ${shipped} — watched; it reopens at ${CAPTURE_AT}`);
859
1097
  }
@@ -990,6 +1228,10 @@ async function recordAnswer(argv, ctx, open) {
990
1228
  export async function requests(argv = [], ctx = { root: '.' }) {
991
1229
  const check = argv.includes('--check');
992
1230
  const found = [];
1231
+ // What projects made rules of their own lately: theirs to make, and listed so one every project needs can
1232
+ // still be raised into a layer here — a project can misjudge a rule as its own.
1233
+ const since = localDay(new Date(Date.now() - MADE_HERE_DAYS * 86_400_000).toISOString());
1234
+ const madeHere = [];
993
1235
  const files = (await readdir(snapshotsDir()).catch(() => [])).filter((f) => f.endsWith('.jsonl'));
994
1236
  for (const file of files) {
995
1237
  const dir = decodeProjectDir(file.replace(/\.jsonl$/, ''));
@@ -998,6 +1240,7 @@ export async function requests(argv = [], ctx = { root: '.' }) {
998
1240
  for (const f of (await readdir(reqDir).catch(() => [])).filter((f) => f.endsWith('.md'))) {
999
1241
  const fields = frontmatter(await readFile(join(reqDir, f), 'utf8'));
1000
1242
  if (fields?.status === 'open') found.push({ dir, f, id: f.replace(/\.md$/, ''), fields });
1243
+ else if (fields?.target === HERE && String(fields.date ?? '') >= since) madeHere.push({ dir, fields });
1001
1244
  }
1002
1245
  }
1003
1246
  if (argv.includes('--answer') || argv.includes('--decline')) return recordAnswer(argv, ctx, found);
@@ -1031,8 +1274,18 @@ export async function requests(argv = [], ctx = { root: '.' }) {
1031
1274
  );
1032
1275
  return 1;
1033
1276
  }
1277
+ const listMadeHere = () => {
1278
+ if (madeHere.length === 0) return;
1279
+ console.log(` ${madeHere.length} lesson(s) made a rule in their own project in the last ${MADE_HERE_DAYS} days — one every project needs goes into a layer here:\n`);
1280
+ for (const m of madeHere) {
1281
+ console.log(` ${m.fields.date} ${m.fields.pill} → ${m.fields.rule_in}`);
1282
+ console.log(` ${m.dir}`);
1283
+ }
1284
+ console.log('');
1285
+ };
1034
1286
  if (found.length === 0) {
1035
1287
  console.log(' no open requests — no project has graduated a lesson to the harness\n');
1288
+ listMadeHere();
1036
1289
  return 0;
1037
1290
  }
1038
1291
  console.log(`\n ${found.length} open request(s)\n`);
@@ -1047,5 +1300,6 @@ export async function requests(argv = [], ctx = { root: '.' }) {
1047
1300
  console.log(` ${state}`);
1048
1301
  }
1049
1302
  console.log('');
1303
+ listMadeHere();
1050
1304
  return 0;
1051
1305
  }
@@ -445,18 +445,21 @@ export async function pills(argv, ctx) {
445
445
  }
446
446
  if (cited > 0) notes.push(`${resolved} of ${cited} citation(s) still resolve`);
447
447
 
448
- // A lesson learned three times is not a correction any more; it is a rule nobody wrote down.
449
- // A project cannot write the harness, and a pill retired before the release that carries its rule arrives
450
- // leaves the lesson nowhere: it goes as a request, and waits.
448
+ // A lesson learned three times is not a correction any more; it is a rule nobody wrote down. A convention of
449
+ // this codebase becomes one here; one every project of its shape needs goes to the harness as a request, since
450
+ // a project cannot write the harness, and waits — a pill retired before its rule arrives leaves the lesson nowhere.
451
451
  for (const c of candidates) {
452
452
  const home = gates && c.applies.length > 0 ? graduationTarget(c.applies, gates) : null;
453
- const asked = requested.get(`.claude/pills/${c.label}`);
453
+ const pill = `.claude/pills/${c.label}`;
454
+ const asked = requested.get(pill);
454
455
  notes.push(
455
456
  `${c.label} has recurred ${c.seen} times — that is a rule, not a pill.` +
456
- (home ? ` Its home is ${home.layer}, since ${home.why}.` : '') +
457
- (asked
458
- ? ` It went to the harness as a request on ${asked.date} (${asked.status}); the \`nina upgrade\` to the release that answers it retires this pill.`
459
- : ' `harness:check` sends it to the harness as a request, or `nina learn --graduate` does now; keep it active until the release that answers it retires it.'),
457
+ (asked?.status === 'declined'
458
+ ? ` The harness declined it on ${asked.date}: make it a rule here, then \`nina learn --graduate ${pill} --here <file>\` retires it.`
459
+ : asked
460
+ ? ` It went to the harness as a request on ${asked.date} (${asked.status}); the \`nina upgrade\` to the release that answers it retires this pill.`
461
+ : ` A convention of this codebase becomes a rule here — \`nina learn --graduate ${pill} --here <file>\` retires it;` +
462
+ ` one any project of this shape needs goes to the harness with \`nina learn --graduate ${pill}\`${home ? `, and its home there is ${home.layer}, since ${home.why}` : ''}.`),
460
463
  );
461
464
  }
462
465
 
@@ -436,10 +436,17 @@ export const WRITERS = new Set(['implementer', 'solidity-dev']);
436
436
  /** The stages whose presence makes a cycle a designed one. */
437
437
  const DESIGNERS = new Set(['planner', 'architect']);
438
438
 
439
+ /**
440
+ * The verdict a run still at work stands in with, for a reader that cuts a session as it happens — the office's
441
+ * timeline: it closes nothing, being neither a closing verdict nor one that could not be read, and it lets no
442
+ * write through. A run with none at all would read as unreadable, and a qa still at work would close its cycle.
443
+ */
444
+ export const IN_FLIGHT = 'IN-FLIGHT';
445
+
439
446
  /**
440
447
  * A session's records cut into cycles: each up to the verdict that closes one, or to the next design stage
441
- * after code was written unless a loop-back sent the work there, or to a closing stage whose verdict cannot
442
- * be read (see `proportionReport`). What `stats` sizes and `nina runs` prices.
448
+ * after code was written and let through, or to a closing stage whose verdict cannot be read (see
449
+ * `proportionReport`). What `stats` sizes, `nina runs` prices and the office's timeline names.
443
450
  *
444
451
  * @param {object[]} records - Records of any number of sessions and projects.
445
452
  * @returns {{runs: object[], end: 'closed'|'unreadable'|'next'|'open'}[]} In the order they happened within
@@ -455,21 +462,33 @@ export function cyclesOf(records) {
455
462
  /** @type {{runs: object[], end: 'closed'|'unreadable'|'next'|'open'}[]} */
456
463
  const cycles = [];
457
464
  const unread = (verdict) => !verdict || verdict === 'UNCLEAR' || verdict === 'NONE';
465
+ // Code was written, and what reviewed its latest write let it through: a verdict read from a stage that reviews —
466
+ // neither one that writes nor one that designs — and nothing sent back since. Read off the one verdict dispatched
467
+ // before the design, the gates that run side by side made it a matter of which went out last — a rejection beside
468
+ // three approvals ended the change its own fix belonged to.
469
+ const letThrough = (cycle) => {
470
+ const write = cycle.findLastIndex((x) => WRITERS.has(x.role));
471
+ if (write < 0) return false;
472
+ const since = cycle
473
+ .slice(write + 1)
474
+ .filter((x) => !WRITERS.has(x.role) && !DESIGNERS.has(x.role))
475
+ .map((x) => x.verdict)
476
+ .filter((v) => !unread(v) && v !== IN_FLIGHT);
477
+ return since.length > 0 && !since.some(isLoopBack);
478
+ };
458
479
  for (const runs of bySession.values()) {
459
480
  let cycle = [];
460
- let previous = null;
461
481
  const end = (how) => {
462
482
  if (cycle.length > 0) cycles.push({ runs: cycle, end: how });
463
483
  cycle = [];
464
484
  };
465
485
  for (const r of runs) {
466
- if (DESIGNERS.has(r.role) && cycle.some((x) => WRITERS.has(x.role)) && !isLoopBack(previous)) end('next');
486
+ if (DESIGNERS.has(r.role) && letThrough(cycle)) end('next');
467
487
  cycle.push(r);
468
488
  if (r.role in CYCLE_ENDS) {
469
489
  if (r.verdict === CYCLE_ENDS[r.role]) end('closed');
470
490
  else if (unread(r.verdict)) end('unreadable');
471
491
  }
472
- if (!unread(r.verdict)) previous = r.verdict;
473
492
  }
474
493
  end('open');
475
494
  }
@@ -482,9 +501,10 @@ export function cyclesOf(records) {
482
501
  * (`CYCLE_ENDS`), so a loop-back's fix rounds stay with the design that preceded them — the owner's own
483
502
  * prompts were tried as the boundary first, and every "pode seguir" after a spec cut a pipeline in two.
484
503
  * Two more things end a cycle, both found in real sessions: a design stage dispatched after code was
485
- * written starts the next change — the light chain has no qa to close it, and it was absorbed into the
486
- * design that followed — unless the verdict before it sent work back, which the graph routes into both
487
- * designers; and a closing stage whose verdict cannot be read still closes it, counted as unreadable,
504
+ * written and let through starts the next change — the light chain has no qa to close it, and it was absorbed
505
+ * into the design that followed — but not while anything since the latest write sent work back, which the graph
506
+ * routes into both designers, or no stage that reviews has read that write yet, as when a fix and its spec's
507
+ * correction go out together; and a closing stage whose verdict cannot be read still closes it, counted as unreadable,
488
508
  * because a qa run with no verdict line otherwise let one cycle swallow a dozen pipelines. A cycle is
489
509
  * sized by the most files any one of its writers wrote, not their sum, so three fix rounds on two files
490
510
  * stay two files; and only edits through the edit tools count, so a file written from a shell is missed
@@ -618,7 +618,7 @@ export async function upgrade(argv, ctx) {
618
618
  } else if (a.status === 'closed') {
619
619
  console.log(` closed ${HARNESS}/requests/${a.file} — the rule is now in ${a.where}${a.pill ? `; retired ${a.pill}` : ''}`);
620
620
  } else {
621
- console.log(` declined ${HARNESS}/requests/${a.file} — ${a.why}. The pill stays active: it is this project's lesson.`);
621
+ console.log(` declined ${HARNESS}/requests/${a.file} — ${a.why}. The pill stays active until this project makes it a rule of its own.`);
622
622
  }
623
623
  }
624
624
  }