productfoo 0.0.0-stage → 1.0.1

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 (498) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +338 -2
  3. package/bin/foo +3 -0
  4. package/bin/foo.mjs +42 -0
  5. package/commands/bug.md +31 -0
  6. package/commands/discovery.md +28 -0
  7. package/commands/ideate.md +27 -0
  8. package/commands/init.md +28 -0
  9. package/commands/pitch.md +27 -0
  10. package/commands/preview.md +25 -0
  11. package/commands/research.md +31 -0
  12. package/commands/roast.md +27 -0
  13. package/commands/spec.md +30 -0
  14. package/config/pricing.yaml +198 -0
  15. package/config/routing-policy.yaml +171 -0
  16. package/dist-public/src/activeModel.d.ts +83 -0
  17. package/dist-public/src/activeModel.js +331 -0
  18. package/dist-public/src/agentAdapters.d.ts +122 -0
  19. package/dist-public/src/agentAdapters.js +178 -0
  20. package/dist-public/src/agentState.d.ts +43 -0
  21. package/dist-public/src/agentState.js +177 -0
  22. package/dist-public/src/bugCapsule.d.ts +94 -0
  23. package/dist-public/src/bugCapsule.js +227 -0
  24. package/dist-public/src/bugCapture.d.ts +26 -0
  25. package/dist-public/src/bugCapture.js +251 -0
  26. package/dist-public/src/bugOwnerLog.d.ts +93 -0
  27. package/dist-public/src/bugOwnerLog.js +304 -0
  28. package/dist-public/src/bugReportStore.d.ts +48 -0
  29. package/dist-public/src/bugReportStore.js +860 -0
  30. package/dist-public/src/bugSpecContext.d.ts +7 -0
  31. package/dist-public/src/bugSpecContext.js +129 -0
  32. package/dist-public/src/channelFreshness.d.ts +25 -0
  33. package/dist-public/src/channelFreshness.js +194 -0
  34. package/dist-public/src/cli.d.ts +30 -0
  35. package/dist-public/src/cli.js +1667 -0
  36. package/dist-public/src/cliArgs.d.ts +7 -0
  37. package/dist-public/src/cliArgs.js +44 -0
  38. package/dist-public/src/codeCatalog.d.ts +41 -0
  39. package/dist-public/src/codeCatalog.js +197 -0
  40. package/dist-public/src/commandContract.d.ts +84 -0
  41. package/dist-public/src/commandContract.js +390 -0
  42. package/dist-public/src/commandUsage.d.ts +3 -0
  43. package/dist-public/src/commandUsage.js +30 -0
  44. package/dist-public/src/commands/agent.d.ts +60 -0
  45. package/dist-public/src/commands/agent.js +1353 -0
  46. package/dist-public/src/commands/asp.d.ts +8 -0
  47. package/dist-public/src/commands/asp.js +669 -0
  48. package/dist-public/src/commands/evalCmd.d.ts +4 -0
  49. package/dist-public/src/commands/evalCmd.js +405 -0
  50. package/dist-public/src/commands/login.d.ts +25 -0
  51. package/dist-public/src/commands/login.js +94 -0
  52. package/dist-public/src/commands/loop.d.ts +16 -0
  53. package/dist-public/src/commands/loop.js +619 -0
  54. package/dist-public/src/commands/reconcile.d.ts +41 -0
  55. package/dist-public/src/commands/reconcile.js +292 -0
  56. package/dist-public/src/commands/runtime.d.ts +1 -0
  57. package/dist-public/src/commands/runtime.js +36 -0
  58. package/dist-public/src/commands/scanRedact.d.ts +8 -0
  59. package/dist-public/src/commands/scanRedact.js +266 -0
  60. package/dist-public/src/commands/setup.d.ts +28 -0
  61. package/dist-public/src/commands/setup.js +398 -0
  62. package/dist-public/src/commands/shared.d.ts +17 -0
  63. package/dist-public/src/commands/shared.js +147 -0
  64. package/dist-public/src/commands/source.d.ts +1 -0
  65. package/dist-public/src/commands/source.js +134 -0
  66. package/dist-public/src/commands/specCmd.d.ts +24 -0
  67. package/dist-public/src/commands/specCmd.js +794 -0
  68. package/dist-public/src/commands/update.d.ts +61 -0
  69. package/dist-public/src/commands/update.js +244 -0
  70. package/dist-public/src/comprehensionSignal.d.ts +20 -0
  71. package/dist-public/src/comprehensionSignal.js +166 -0
  72. package/dist-public/src/conductor/diversity.d.ts +48 -0
  73. package/dist-public/src/conductor/diversity.js +423 -0
  74. package/dist-public/src/conductor/engine.d.ts +7 -0
  75. package/dist-public/src/conductor/engine.js +295 -0
  76. package/dist-public/src/conductor/families/ideate.d.ts +2 -0
  77. package/dist-public/src/conductor/families/ideate.js +255 -0
  78. package/dist-public/src/conductor/families/interview.d.ts +2 -0
  79. package/dist-public/src/conductor/families/interview.js +39 -0
  80. package/dist-public/src/conductor/families/specSwarm.d.ts +2 -0
  81. package/dist-public/src/conductor/families/specSwarm.js +146 -0
  82. package/dist-public/src/conductor/familyDelivery.d.ts +6 -0
  83. package/dist-public/src/conductor/familyDelivery.js +37 -0
  84. package/dist-public/src/conductor/familyPackArtifact.d.ts +4 -0
  85. package/dist-public/src/conductor/familyPackArtifact.js +260 -0
  86. package/dist-public/src/conductor/ideateDecision.d.ts +6 -0
  87. package/dist-public/src/conductor/ideateDecision.js +237 -0
  88. package/dist-public/src/conductor/pack.d.ts +26 -0
  89. package/dist-public/src/conductor/pack.js +75 -0
  90. package/dist-public/src/conductor/types.d.ts +111 -0
  91. package/dist-public/src/conductor/types.js +7 -0
  92. package/dist-public/src/configPath.d.ts +2 -0
  93. package/dist-public/src/configPath.js +7 -0
  94. package/dist-public/src/discoveryArtifacts.d.ts +54 -0
  95. package/dist-public/src/discoveryArtifacts.js +467 -0
  96. package/dist-public/src/discoveryCommand.d.ts +127 -0
  97. package/dist-public/src/discoveryCommand.js +2148 -0
  98. package/dist-public/src/discoveryConnections.d.ts +339 -0
  99. package/dist-public/src/discoveryConnections.js +613 -0
  100. package/dist-public/src/discoveryDocs.d.ts +129 -0
  101. package/dist-public/src/discoveryDocs.js +577 -0
  102. package/dist-public/src/discoveryEvidence.d.ts +59 -0
  103. package/dist-public/src/discoveryEvidence.js +198 -0
  104. package/dist-public/src/discoveryFireflies.d.ts +25 -0
  105. package/dist-public/src/discoveryFireflies.js +551 -0
  106. package/dist-public/src/discoveryGmail.d.ts +12 -0
  107. package/dist-public/src/discoveryGmail.js +466 -0
  108. package/dist-public/src/discoveryGong.d.ts +12 -0
  109. package/dist-public/src/discoveryGong.js +491 -0
  110. package/dist-public/src/discoveryLearningRouting.d.ts +90 -0
  111. package/dist-public/src/discoveryLearningRouting.js +405 -0
  112. package/dist-public/src/discoveryMatching.d.ts +26 -0
  113. package/dist-public/src/discoveryMatching.js +73 -0
  114. package/dist-public/src/discoveryOutlook.d.ts +12 -0
  115. package/dist-public/src/discoveryOutlook.js +492 -0
  116. package/dist-public/src/discoveryProcess.d.ts +221 -0
  117. package/dist-public/src/discoveryProcess.js +703 -0
  118. package/dist-public/src/discoveryProof.d.ts +143 -0
  119. package/dist-public/src/discoveryProof.js +529 -0
  120. package/dist-public/src/discoveryProviders.d.ts +152 -0
  121. package/dist-public/src/discoveryProviders.js +756 -0
  122. package/dist-public/src/discoveryReadiness.d.ts +219 -0
  123. package/dist-public/src/discoveryReadiness.js +672 -0
  124. package/dist-public/src/discoveryReconcile.d.ts +57 -0
  125. package/dist-public/src/discoveryReconcile.js +424 -0
  126. package/dist-public/src/discoverySlack.d.ts +12 -0
  127. package/dist-public/src/discoverySlack.js +452 -0
  128. package/dist-public/src/discoverySources.d.ts +56 -0
  129. package/dist-public/src/discoverySources.js +322 -0
  130. package/dist-public/src/discoverySync.d.ts +52 -0
  131. package/dist-public/src/discoverySync.js +151 -0
  132. package/dist-public/src/discoverySynthesis.d.ts +57 -0
  133. package/dist-public/src/discoverySynthesis.js +215 -0
  134. package/dist-public/src/discoveryTeams.d.ts +12 -0
  135. package/dist-public/src/discoveryTeams.js +516 -0
  136. package/dist-public/src/discoveryZoom.d.ts +12 -0
  137. package/dist-public/src/discoveryZoom.js +523 -0
  138. package/dist-public/src/elicitationSurface.d.ts +13 -0
  139. package/dist-public/src/elicitationSurface.js +77 -0
  140. package/dist-public/src/errorReport.d.ts +99 -0
  141. package/dist-public/src/errorReport.js +443 -0
  142. package/dist-public/src/errorTaxonomy.d.ts +84 -0
  143. package/dist-public/src/errorTaxonomy.js +189 -0
  144. package/dist-public/src/evalCalibration.d.ts +82 -0
  145. package/dist-public/src/evalCalibration.js +169 -0
  146. package/dist-public/src/evalChecks.d.ts +21 -0
  147. package/dist-public/src/evalChecks.js +295 -0
  148. package/dist-public/src/evalCompile.d.ts +56 -0
  149. package/dist-public/src/evalCompile.js +58 -0
  150. package/dist-public/src/evalImprove.d.ts +60 -0
  151. package/dist-public/src/evalImprove.js +175 -0
  152. package/dist-public/src/evalJudge.d.ts +47 -0
  153. package/dist-public/src/evalJudge.js +143 -0
  154. package/dist-public/src/evalLabels.d.ts +100 -0
  155. package/dist-public/src/evalLabels.js +287 -0
  156. package/dist-public/src/evalRecord.d.ts +68 -0
  157. package/dist-public/src/evalRecord.js +165 -0
  158. package/dist-public/src/evalRoast.d.ts +88 -0
  159. package/dist-public/src/evalRoast.js +249 -0
  160. package/dist-public/src/evidenceIntake.d.ts +66 -0
  161. package/dist-public/src/evidenceIntake.js +356 -0
  162. package/dist-public/src/feedback.d.ts +78 -0
  163. package/dist-public/src/feedback.js +294 -0
  164. package/dist-public/src/firefliesClient.d.ts +106 -0
  165. package/dist-public/src/firefliesClient.js +258 -0
  166. package/dist-public/src/firstMoveTensions.d.ts +10 -0
  167. package/dist-public/src/firstMoveTensions.js +93 -0
  168. package/dist-public/src/gitRun.d.ts +21 -0
  169. package/dist-public/src/gitRun.js +50 -0
  170. package/dist-public/src/githubReconcile.d.ts +94 -0
  171. package/dist-public/src/githubReconcile.js +205 -0
  172. package/dist-public/src/gmailNango.d.ts +131 -0
  173. package/dist-public/src/gmailNango.js +329 -0
  174. package/dist-public/src/gongNango.d.ts +118 -0
  175. package/dist-public/src/gongNango.js +308 -0
  176. package/dist-public/src/grammarGate.d.ts +13 -0
  177. package/dist-public/src/grammarGate.js +72 -0
  178. package/dist-public/src/harnessGates.d.ts +65 -0
  179. package/dist-public/src/harnessGates.js +168 -0
  180. package/dist-public/src/humanSurfaceEval.d.ts +19 -0
  181. package/dist-public/src/humanSurfaceEval.js +156 -0
  182. package/dist-public/src/ideate.d.ts +43 -0
  183. package/dist-public/src/ideate.js +255 -0
  184. package/dist-public/src/ideateContext.d.ts +11 -0
  185. package/dist-public/src/ideateContext.js +83 -0
  186. package/dist-public/src/index.d.ts +2 -0
  187. package/dist-public/src/index.js +4 -0
  188. package/dist-public/src/initCommand.d.ts +25 -0
  189. package/dist-public/src/initCommand.js +558 -0
  190. package/dist-public/src/interviewGate.d.ts +25 -0
  191. package/dist-public/src/interviewGate.js +126 -0
  192. package/dist-public/src/jsonFile.d.ts +2 -0
  193. package/dist-public/src/jsonFile.js +22 -0
  194. package/dist-public/src/loopBudget.d.ts +182 -0
  195. package/dist-public/src/loopBudget.js +494 -0
  196. package/dist-public/src/loopContracts.d.ts +35 -0
  197. package/dist-public/src/loopContracts.js +364 -0
  198. package/dist-public/src/machineState.d.ts +1 -0
  199. package/dist-public/src/machineState.js +9 -0
  200. package/dist-public/src/notionNango.d.ts +93 -0
  201. package/dist-public/src/notionNango.js +279 -0
  202. package/dist-public/src/orchestrationResult.d.ts +11 -0
  203. package/dist-public/src/orchestrationResult.js +73 -0
  204. package/dist-public/src/outlookNango.d.ts +124 -0
  205. package/dist-public/src/outlookNango.js +327 -0
  206. package/dist-public/src/packetCorrections.d.ts +40 -0
  207. package/dist-public/src/packetCorrections.js +172 -0
  208. package/dist-public/src/packetGlossary.d.ts +6 -0
  209. package/dist-public/src/packetGlossary.js +42 -0
  210. package/dist-public/src/packetIndex.d.ts +54 -0
  211. package/dist-public/src/packetIndex.js +744 -0
  212. package/dist-public/src/packetIndexEntry.d.ts +3 -0
  213. package/dist-public/src/packetIndexEntry.js +128 -0
  214. package/dist-public/src/packetIndexServe.d.ts +13 -0
  215. package/dist-public/src/packetIndexServe.js +113 -0
  216. package/dist-public/src/packetIndexWorker.d.ts +7 -0
  217. package/dist-public/src/packetIndexWorker.js +45 -0
  218. package/dist-public/src/packetLifecycle.d.ts +30 -0
  219. package/dist-public/src/packetLifecycle.js +127 -0
  220. package/dist-public/src/packetReconcile.d.ts +73 -0
  221. package/dist-public/src/packetReconcile.js +386 -0
  222. package/dist-public/src/packetStatus.d.ts +125 -0
  223. package/dist-public/src/packetStatus.js +770 -0
  224. package/dist-public/src/packetValidation.d.ts +17 -0
  225. package/dist-public/src/packetValidation.js +333 -0
  226. package/dist-public/src/packetVerify.d.ts +73 -0
  227. package/dist-public/src/packetVerify.js +303 -0
  228. package/dist-public/src/packetWrite.d.ts +52 -0
  229. package/dist-public/src/packetWrite.js +448 -0
  230. package/dist-public/src/pitch.d.ts +38 -0
  231. package/dist-public/src/pitch.js +154 -0
  232. package/dist-public/src/processMemo.d.ts +8 -0
  233. package/dist-public/src/processMemo.js +23 -0
  234. package/dist-public/src/productDetect.d.ts +30 -0
  235. package/dist-public/src/productDetect.js +376 -0
  236. package/dist-public/src/profile.d.ts +62 -0
  237. package/dist-public/src/profile.js +334 -0
  238. package/dist-public/src/questionModel.d.ts +46 -0
  239. package/dist-public/src/questionModel.js +74 -0
  240. package/dist-public/src/questionText.d.ts +7 -0
  241. package/dist-public/src/questionText.js +38 -0
  242. package/dist-public/src/redactConfig.d.ts +2 -0
  243. package/dist-public/src/redactConfig.js +11 -0
  244. package/dist-public/src/redactEngine.d.ts +59 -0
  245. package/dist-public/src/redactEngine.js +509 -0
  246. package/dist-public/src/redactGate.d.ts +63 -0
  247. package/dist-public/src/redactGate.js +209 -0
  248. package/dist-public/src/repoRead.d.ts +29 -0
  249. package/dist-public/src/repoRead.js +160 -0
  250. package/dist-public/src/research.d.ts +242 -0
  251. package/dist-public/src/research.js +1314 -0
  252. package/dist-public/src/researchCompletion.d.ts +34 -0
  253. package/dist-public/src/researchCompletion.js +123 -0
  254. package/dist-public/src/researchExecution.d.ts +47 -0
  255. package/dist-public/src/researchExecution.js +309 -0
  256. package/dist-public/src/researchFormat.d.ts +21 -0
  257. package/dist-public/src/researchFormat.js +77 -0
  258. package/dist-public/src/researchGate.d.ts +86 -0
  259. package/dist-public/src/researchGate.js +1036 -0
  260. package/dist-public/src/researchReceipt.d.ts +76 -0
  261. package/dist-public/src/researchReceipt.js +148 -0
  262. package/dist-public/src/researchRender.d.ts +2 -0
  263. package/dist-public/src/researchRender.js +267 -0
  264. package/dist-public/src/researchSigningKey.d.ts +19 -0
  265. package/dist-public/src/researchSigningKey.js +95 -0
  266. package/dist-public/src/riskClassify.d.ts +42 -0
  267. package/dist-public/src/riskClassify.js +149 -0
  268. package/dist-public/src/routines.d.ts +42 -0
  269. package/dist-public/src/routines.js +269 -0
  270. package/dist-public/src/routingPolicy.d.ts +172 -0
  271. package/dist-public/src/routingPolicy.js +646 -0
  272. package/dist-public/src/secretInput.d.ts +8 -0
  273. package/dist-public/src/secretInput.js +89 -0
  274. package/dist-public/src/selfCheck.d.ts +7 -0
  275. package/dist-public/src/selfCheck.js +473 -0
  276. package/dist-public/src/selfCheckCache.d.ts +29 -0
  277. package/dist-public/src/selfCheckCache.js +147 -0
  278. package/dist-public/src/severityClass.d.ts +15 -0
  279. package/dist-public/src/severityClass.js +22 -0
  280. package/dist-public/src/skillClientPolicy.d.ts +8 -0
  281. package/dist-public/src/skillClientPolicy.js +16 -0
  282. package/dist-public/src/skillLocal.d.ts +98 -0
  283. package/dist-public/src/skillLocal.js +463 -0
  284. package/dist-public/src/skillTreeDigest.d.ts +9 -0
  285. package/dist-public/src/skillTreeDigest.js +64 -0
  286. package/dist-public/src/slackNango.d.ts +62 -0
  287. package/dist-public/src/slackNango.js +208 -0
  288. package/dist-public/src/social/arxiv.d.ts +19 -0
  289. package/dist-public/src/social/arxiv.js +96 -0
  290. package/dist-public/src/social/github.d.ts +2 -0
  291. package/dist-public/src/social/github.js +78 -0
  292. package/dist-public/src/social/hackernews.d.ts +2 -0
  293. package/dist-public/src/social/hackernews.js +67 -0
  294. package/dist-public/src/social/index.d.ts +10 -0
  295. package/dist-public/src/social/index.js +79 -0
  296. package/dist-public/src/social/reddit.d.ts +2 -0
  297. package/dist-public/src/social/reddit.js +125 -0
  298. package/dist-public/src/social/types.d.ts +73 -0
  299. package/dist-public/src/social/types.js +61 -0
  300. package/dist-public/src/social/xml.d.ts +10 -0
  301. package/dist-public/src/social/xml.js +49 -0
  302. package/dist-public/src/social/youtube.d.ts +7 -0
  303. package/dist-public/src/social/youtube.js +217 -0
  304. package/dist-public/src/sourceIngress.d.ts +488 -0
  305. package/dist-public/src/sourceIngress.js +1651 -0
  306. package/dist-public/src/specAfter.d.ts +187 -0
  307. package/dist-public/src/specAfter.js +814 -0
  308. package/dist-public/src/specBudget.d.ts +124 -0
  309. package/dist-public/src/specBudget.js +424 -0
  310. package/dist-public/src/specDecisionSignals.d.ts +38 -0
  311. package/dist-public/src/specDecisionSignals.js +121 -0
  312. package/dist-public/src/specLinkedCommits.d.ts +7 -0
  313. package/dist-public/src/specLinkedCommits.js +107 -0
  314. package/dist-public/src/specSections.d.ts +105 -0
  315. package/dist-public/src/specSections.js +718 -0
  316. package/dist-public/src/statusGlyphs.d.ts +18 -0
  317. package/dist-public/src/statusGlyphs.js +75 -0
  318. package/dist-public/src/swarmCommand.d.ts +10 -0
  319. package/dist-public/src/swarmCommand.js +96 -0
  320. package/dist-public/src/teamsNango.d.ts +114 -0
  321. package/dist-public/src/teamsNango.js +301 -0
  322. package/dist-public/src/tokenStore.d.ts +10 -0
  323. package/dist-public/src/tokenStore.js +77 -0
  324. package/dist-public/src/trustEnvelope.d.ts +78 -0
  325. package/dist-public/src/trustEnvelope.js +392 -0
  326. package/dist-public/src/untrustedText.d.ts +6 -0
  327. package/dist-public/src/untrustedText.js +20 -0
  328. package/dist-public/src/updateNotice.d.ts +41 -0
  329. package/dist-public/src/updateNotice.js +290 -0
  330. package/dist-public/src/vendorYaml.d.ts +1 -0
  331. package/dist-public/src/vendorYaml.js +7374 -0
  332. package/dist-public/src/workflowRunActuals.d.ts +67 -0
  333. package/dist-public/src/workflowRunActuals.js +184 -0
  334. package/dist-public/src/zendeskDiscovery.d.ts +12 -0
  335. package/dist-public/src/zendeskDiscovery.js +513 -0
  336. package/dist-public/src/zendeskNango.d.ts +131 -0
  337. package/dist-public/src/zendeskNango.js +344 -0
  338. package/dist-public/src/zoomNango.d.ts +106 -0
  339. package/dist-public/src/zoomNango.js +351 -0
  340. package/docs/CHANGELOG.md +32 -0
  341. package/docs/THIRD_PARTY_NOTICES.md +36 -0
  342. package/package.json +71 -4
  343. package/schemas/bug-capsule-receipt.schema.json +54 -0
  344. package/schemas/bug-capsule.schema.json +189 -0
  345. package/schemas/discovery-evidence-envelope.schema.json +79 -0
  346. package/schemas/discovery-processing-receipt.schema.json +71 -0
  347. package/schemas/discovery-service-contract.schema.json +189 -0
  348. package/skills/AGENTS.md +32 -0
  349. package/skills/README.md +287 -0
  350. package/skills/bug/SKILL.md +49 -0
  351. package/skills/build-preview-spec/SKILL.md +103 -0
  352. package/skills/capture-external-artifact/SKILL.md +23 -0
  353. package/skills/capture-external-artifact/agents/openai.yaml +4 -0
  354. package/skills/capture-external-artifact/references/extractor-libraries.md +27 -0
  355. package/skills/check-spec-health/SKILL.md +45 -0
  356. package/skills/check-spec-health/agents/openai.yaml +4 -0
  357. package/skills/competitive-positioning/SKILL.md +86 -0
  358. package/skills/competitive-positioning/agents/openai.yaml +4 -0
  359. package/skills/cpo-lens/SKILL.md +398 -0
  360. package/skills/cpo-lens/agents/openai.yaml +4 -0
  361. package/skills/create-prd/SKILL.md +26 -0
  362. package/skills/create-prd/agents/openai.yaml +4 -0
  363. package/skills/customer-discovery/SKILL.md +192 -0
  364. package/skills/design-system/SKILL.md +49 -0
  365. package/skills/design-system/agents/openai.yaml +4 -0
  366. package/skills/discussion-guide/SKILL.md +75 -0
  367. package/skills/discussion-guide/agents/openai.yaml +4 -0
  368. package/skills/firecrawl-research-papers/SKILL.md +96 -0
  369. package/skills/firecrawl-research-papers/agents/openai.yaml +4 -0
  370. package/skills/foo-roast/SKILL.md +325 -0
  371. package/skills/generate-gtm-brief/SKILL.md +41 -0
  372. package/skills/generate-gtm-brief/agents/openai.yaml +4 -0
  373. package/skills/generate-gtm-brief/references/gtm-brief-template.md +56 -0
  374. package/skills/gong-quotes/SKILL.md +43 -0
  375. package/skills/gong-quotes/agents/openai.yaml +4 -0
  376. package/skills/grill-me/SKILL.md +27 -0
  377. package/skills/grill-me/agents/openai.yaml +4 -0
  378. package/skills/ideate/SKILL.md +180 -0
  379. package/skills/ideate/references/family-packs.md +5 -0
  380. package/skills/initiative-tracker/SKILL.md +23 -0
  381. package/skills/initiative-tracker/agents/openai.yaml +4 -0
  382. package/skills/interop-review/SKILL.md +25 -0
  383. package/skills/interop-review/agents/openai.yaml +4 -0
  384. package/skills/interview-me/SKILL.md +404 -0
  385. package/skills/interview-me/agents/openai.yaml +4 -0
  386. package/skills/loop-library-audit/SKILL.md +123 -0
  387. package/skills/pitch/SKILL.md +110 -0
  388. package/skills/pitch/references/opportunity-assessment.md +136 -0
  389. package/skills/pitch/references/prfaq-template.md +54 -0
  390. package/skills/pmm-release-briefing/SKILL.md +27 -0
  391. package/skills/pmm-release-briefing/agents/openai.yaml +4 -0
  392. package/skills/pmm-release-briefing/references/variant-generalized.md +16 -0
  393. package/skills/positioning-messaging/SKILL.md +70 -0
  394. package/skills/positioning-messaging/agents/openai.yaml +4 -0
  395. package/skills/positioning-messaging/references/guest-insights.md +1400 -0
  396. package/skills/preview-swarm/SKILL.md +418 -0
  397. package/skills/preview-swarm/agents/openai.yaml +4 -0
  398. package/skills/preview-swarm/references/html-effectiveness-build-previews.md +168 -0
  399. package/skills/product-copywriting/SKILL.md +127 -0
  400. package/skills/product-copywriting/agents/openai.yaml +4 -0
  401. package/skills/product-copywriting/references/foo-voice.md +26 -0
  402. package/skills/product-shape-interview/SKILL.md +219 -0
  403. package/skills/product-shape-interview/agents/openai.yaml +4 -0
  404. package/skills/ready-for-release/SKILL.md +39 -0
  405. package/skills/ready-for-release/agents/openai.yaml +4 -0
  406. package/skills/redact-transcripts/SKILL.md +23 -0
  407. package/skills/redact-transcripts/agents/openai.yaml +4 -0
  408. package/skills/research-brief/SKILL.md +48 -0
  409. package/skills/research-brief/agents/openai.yaml +4 -0
  410. package/skills/research-refresh/SKILL.md +63 -0
  411. package/skills/research-refresh/references/worked-cases.md +44 -0
  412. package/skills/review-loop/SKILL.md +200 -0
  413. package/skills/review-spec/SKILL.md +32 -0
  414. package/skills/review-spec/agents/openai.yaml +4 -0
  415. package/skills/rfc-from-research/SKILL.md +42 -0
  416. package/skills/rfc-from-research/agents/openai.yaml +4 -0
  417. package/skills/rfc-from-research/references/rfc-pattern.md +69 -0
  418. package/skills/skill-registry.yaml +810 -0
  419. package/skills/spec-cleanup/SKILL.md +103 -0
  420. package/skills/spec-cleanup/agents/openai.yaml +4 -0
  421. package/skills/spec-swarm/SKILL.md +87 -0
  422. package/skills/spec-swarm/agents/openai.yaml +4 -0
  423. package/skills/spec-swarm/template/SKILL.template.md +94 -0
  424. package/skills/spec-swarm/template/personas/accessibility-inclusion.md +40 -0
  425. package/skills/spec-swarm/template/personas/ai-technical-pm.md +69 -0
  426. package/skills/spec-swarm/template/personas/analytics-engineering.md +39 -0
  427. package/skills/spec-swarm/template/personas/applied-ai-eval.md +64 -0
  428. package/skills/spec-swarm/template/personas/business-gm-pm.md +67 -0
  429. package/skills/spec-swarm/template/personas/consumer-craft-pm.md +68 -0
  430. package/skills/spec-swarm/template/personas/content-design.md +40 -0
  431. package/skills/spec-swarm/template/personas/cpo.md +42 -0
  432. package/skills/spec-swarm/template/personas/data-science.md +42 -0
  433. package/skills/spec-swarm/template/personas/developer-experience.md +65 -0
  434. package/skills/spec-swarm/template/personas/dx-product.md +70 -0
  435. package/skills/spec-swarm/template/personas/field.md +43 -0
  436. package/skills/spec-swarm/template/personas/finance-cfo.md +40 -0
  437. package/skills/spec-swarm/template/personas/growth-pm.md +67 -0
  438. package/skills/spec-swarm/template/personas/platform-pm.md +67 -0
  439. package/skills/spec-swarm/template/personas/pmm.md +43 -0
  440. package/skills/spec-swarm/template/personas/principal-product-designer.md +42 -0
  441. package/skills/spec-swarm/template/personas/product-ops-program.md +41 -0
  442. package/skills/spec-swarm/template/personas/qa-release.md +41 -0
  443. package/skills/spec-swarm/template/personas/responsible-ai-safety.md +64 -0
  444. package/skills/spec-swarm/template/personas/security-privacy-legal.md +40 -0
  445. package/skills/spec-swarm/template/personas/strategy-competitive.md +41 -0
  446. package/skills/spec-swarm/template/personas/technical-architect.md +42 -0
  447. package/skills/spec-swarm/template/personas/technical-lead.md +43 -0
  448. package/skills/spec-swarm/template/personas/user-research.md +41 -0
  449. package/skills/spec-swarm-accessibility-inclusion/SKILL.md +95 -0
  450. package/skills/spec-swarm-accessibility-inclusion/agents/openai.yaml +4 -0
  451. package/skills/spec-swarm-ai-technical-pm/SKILL.md +124 -0
  452. package/skills/spec-swarm-analytics-engineering/SKILL.md +94 -0
  453. package/skills/spec-swarm-analytics-engineering/agents/openai.yaml +4 -0
  454. package/skills/spec-swarm-applied-ai-eval/SKILL.md +120 -0
  455. package/skills/spec-swarm-business-gm-pm/SKILL.md +122 -0
  456. package/skills/spec-swarm-consumer-craft-pm/SKILL.md +122 -0
  457. package/skills/spec-swarm-content-design/SKILL.md +95 -0
  458. package/skills/spec-swarm-content-design/agents/openai.yaml +4 -0
  459. package/skills/spec-swarm-cpo/SKILL.md +94 -0
  460. package/skills/spec-swarm-cpo/agents/openai.yaml +4 -0
  461. package/skills/spec-swarm-data-science/SKILL.md +95 -0
  462. package/skills/spec-swarm-data-science/agents/openai.yaml +4 -0
  463. package/skills/spec-swarm-developer-experience/SKILL.md +121 -0
  464. package/skills/spec-swarm-dx-product/SKILL.md +126 -0
  465. package/skills/spec-swarm-field/SKILL.md +96 -0
  466. package/skills/spec-swarm-field/agents/openai.yaml +4 -0
  467. package/skills/spec-swarm-finance-cfo/SKILL.md +95 -0
  468. package/skills/spec-swarm-finance-cfo/agents/openai.yaml +4 -0
  469. package/skills/spec-swarm-growth-pm/SKILL.md +122 -0
  470. package/skills/spec-swarm-platform-pm/SKILL.md +122 -0
  471. package/skills/spec-swarm-pmm/SKILL.md +96 -0
  472. package/skills/spec-swarm-pmm/agents/openai.yaml +4 -0
  473. package/skills/spec-swarm-principal-product-designer/SKILL.md +95 -0
  474. package/skills/spec-swarm-principal-product-designer/agents/openai.yaml +4 -0
  475. package/skills/spec-swarm-product-ops-program/SKILL.md +96 -0
  476. package/skills/spec-swarm-product-ops-program/agents/openai.yaml +4 -0
  477. package/skills/spec-swarm-qa-release/SKILL.md +96 -0
  478. package/skills/spec-swarm-qa-release/agents/openai.yaml +4 -0
  479. package/skills/spec-swarm-responsible-ai-safety/SKILL.md +120 -0
  480. package/skills/spec-swarm-security-privacy-legal/SKILL.md +95 -0
  481. package/skills/spec-swarm-security-privacy-legal/agents/openai.yaml +4 -0
  482. package/skills/spec-swarm-strategy-competitive/SKILL.md +96 -0
  483. package/skills/spec-swarm-strategy-competitive/agents/openai.yaml +4 -0
  484. package/skills/spec-swarm-technical-architect/SKILL.md +95 -0
  485. package/skills/spec-swarm-technical-architect/agents/openai.yaml +4 -0
  486. package/skills/spec-swarm-technical-lead/SKILL.md +96 -0
  487. package/skills/spec-swarm-technical-lead/agents/openai.yaml +4 -0
  488. package/skills/spec-swarm-user-research/SKILL.md +95 -0
  489. package/skills/spec-swarm-user-research/agents/openai.yaml +4 -0
  490. package/skills/strategy-check/SKILL.md +170 -0
  491. package/skills/strategy-check/agents/openai.yaml +4 -0
  492. package/skills/study-digest/SKILL.md +41 -0
  493. package/skills/synthetic-customer/SKILL.md +68 -0
  494. package/skills/update-cli-spec/SKILL.md +69 -0
  495. package/skills/update-cli-spec/agents/openai.yaml +4 -0
  496. package/skills/voc-researcher/SKILL.md +45 -0
  497. package/skills/voc-researcher/agents/openai.yaml +4 -0
  498. package/skills-lock.json +35 -0
@@ -0,0 +1,744 @@
1
+ // Cross-packet packet index (packet_index_v0).
2
+ //
3
+ // A birds-eye view over every spec packet: enumerate specs/*, derive each
4
+ // through the shared derivePacketStatus reader (R-019), and render the result
5
+ // as text (default), a versioned --json array, or a self-contained Plain Packet
6
+ // HTML artifact. This is a renderer, not a second reader (D-002). It is
7
+ // read-only — the only write is the --html artifact, and only when asked.
8
+ import { existsSync, readdirSync, writeFileSync } from "node:fs";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
+ import { Worker } from "node:worker_threads";
12
+ import { STATUS_GLYPHS, withGlyph } from "./statusGlyphs.js";
13
+ import { NotFoundError } from "./errorTaxonomy.js";
14
+ import { derivePacketEntry } from "./packetIndexEntry.js";
15
+ export const PACKET_INDEX_SCHEMA_VERSION = "1.2";
16
+ // Discover every specs/* directory that is actually a packet (spec.md +
17
+ // tasks.yaml). Non-packets like rfcs/ are skipped because they lack tasks.yaml.
18
+ export function listPackets(root) {
19
+ const specsDir = path.join(root, "specs");
20
+ if (!existsSync(specsDir))
21
+ throw new NotFoundError(`no specs directory at ${specsDir}`, "E_PACKET_NOT_FOUND");
22
+ return readdirSync(specsDir, { withFileTypes: true })
23
+ .filter((entry) => entry.isDirectory())
24
+ .map((entry) => path.join(specsDir, entry.name))
25
+ .filter((dir) => existsSync(path.join(dir, "spec.md")) && existsSync(path.join(dir, "tasks.yaml")))
26
+ .sort();
27
+ }
28
+ // Parallel per-packet derivation (cli-performance-v0 R-001): the per-packet
29
+ // read/parse/derive work is CPU-bound and independent, so at scale it runs on
30
+ // worker threads (packetIndexWorker.ts). The API stays synchronous — the
31
+ // parent waits on atomically-set per-worker flags — and any failure (worker
32
+ // boot, oversized slot, crash) falls back to the serial loop, so output never
33
+ // depends on which path ran. Below PARALLEL_MIN_PACKETS the serial loop wins:
34
+ // worker bootstrap would cost more than it saves (and keeps unit-test
35
+ // fixtures on the simple path).
36
+ const PARALLEL_MIN_PACKETS = 24;
37
+ const PARALLEL_MAX_WORKERS = 8;
38
+ // Per-worker slot: JSON-encoded entries. Sized for ~300 packets at today's
39
+ // ~6KB entry JSON; an overflowing slot falls back to serial, never truncates.
40
+ const WORKER_REGION_BYTES = 2 * 1024 * 1024;
41
+ const WORKER_SLOT_HEADER = 8; // Int32 header cell + Int32 byte-length cell
42
+ const WORKER_DONE = 1;
43
+ const WORKER_FAILED = 2;
44
+ function derivePacketEntries(root, dirs) {
45
+ if (dirs.length >= PARALLEL_MIN_PACKETS) {
46
+ const parallel = derivePacketEntriesParallel(root, dirs);
47
+ if (parallel)
48
+ return parallel;
49
+ }
50
+ return dirs.map((dir) => derivePacketEntry(root, dir));
51
+ }
52
+ function derivePacketEntriesParallel(root, dirs) {
53
+ const workerCount = Math.max(2, Math.min(PARALLEL_MAX_WORKERS, os.cpus().length - 1));
54
+ const slotBytes = WORKER_REGION_BYTES + WORKER_SLOT_HEADER;
55
+ const workers = [];
56
+ let failed = false;
57
+ try {
58
+ const sab = new SharedArrayBuffer(workerCount * slotBytes);
59
+ const batch = Math.ceil(dirs.length / workerCount);
60
+ const cells = [];
61
+ for (let slot = 0; slot < workerCount; slot++) {
62
+ const slotBase = slot * slotBytes;
63
+ const cell = new Int32Array(sab, slotBase, 1);
64
+ cells.push(cell);
65
+ const chunk = dirs.slice(slot * batch, (slot + 1) * batch);
66
+ if (chunk.length === 0) {
67
+ // Nothing to derive in this slot — pre-set done so the wait skips it.
68
+ Atomics.store(cell, 0, WORKER_DONE);
69
+ continue;
70
+ }
71
+ const worker = new Worker(new URL("./packetIndexWorker.js", import.meta.url), {
72
+ workerData: { root, dirs: chunk, sab, slotBase, regionBytes: WORKER_REGION_BYTES },
73
+ });
74
+ worker.on("error", () => markWorkerFailed(cell, () => (failed = true)));
75
+ worker.on("exit", (code) => {
76
+ if (code !== 0)
77
+ markWorkerFailed(cell, () => (failed = true));
78
+ });
79
+ workers.push(worker);
80
+ }
81
+ // Synchronous bridge: workers write their result slots and store+notify
82
+ // their header cell; Atomics.wait wakes on the write (a store that lands
83
+ // before the wait is not lost — wait re-checks the expected value).
84
+ for (const cell of cells) {
85
+ if (Atomics.load(cell, 0) === 0)
86
+ Atomics.wait(cell, 0, 0);
87
+ }
88
+ if (failed)
89
+ return null;
90
+ const entries = [];
91
+ const decoder = new TextDecoder();
92
+ for (let slot = 0; slot < cells.length; slot++) {
93
+ if (Atomics.load(cells[slot], 0) !== WORKER_DONE)
94
+ return null;
95
+ const slotBase = slot * slotBytes;
96
+ const byteLength = Atomics.load(new Int32Array(sab, slotBase + 4, 1), 0);
97
+ const chunk = JSON.parse(decoder.decode(new Uint8Array(sab, slotBase + WORKER_SLOT_HEADER, byteLength)));
98
+ entries.push(...chunk);
99
+ }
100
+ return entries;
101
+ }
102
+ catch {
103
+ failed = true;
104
+ return null;
105
+ }
106
+ finally {
107
+ // Finished workers exit on their own right after storing their slot;
108
+ // terminate() is only cleanup for a failed run's stragglers.
109
+ if (failed)
110
+ for (const worker of workers)
111
+ void worker.terminate();
112
+ }
113
+ }
114
+ function markWorkerFailed(cell, fail) {
115
+ fail();
116
+ // A finished slot keeps its result; only a still-pending slot is failed, so
117
+ // a late error after DONE can never discard good entries.
118
+ if (Atomics.load(cell, 0) === 0) {
119
+ Atomics.store(cell, 0, WORKER_FAILED);
120
+ Atomics.notify(cell, 0);
121
+ }
122
+ }
123
+ export function derivePacketIndex(root) {
124
+ const entries = derivePacketEntries(root, listPackets(root));
125
+ const rank = { progress: 0, proven: 1, open: 2, retired: 3 };
126
+ return entries.sort((a, b) => {
127
+ if (rank[a.bucket] !== rank[b.bucket])
128
+ return rank[a.bucket] - rank[b.bucket];
129
+ const ra = a.tasks.total ? a.tasks.done / a.tasks.total : 0;
130
+ const rb = b.tasks.total ? b.tasks.done / b.tasks.total : 0;
131
+ if (rb !== ra)
132
+ return rb - ra;
133
+ return a.spec_id.localeCompare(b.spec_id);
134
+ });
135
+ }
136
+ function pad(value, width) {
137
+ return value.length >= width ? value : value + " ".repeat(width - value.length);
138
+ }
139
+ function renderIndexText(index, glyphs = false) {
140
+ // Headline totals count only live work: a retired packet's undone tasks are
141
+ // covered by its verdict, not open commitments (packet-lifecycle-v0 R-003).
142
+ const totals = index.reduce((acc, e) => {
143
+ if (e.bucket === "retired") {
144
+ acc.retired += 1;
145
+ return acc;
146
+ }
147
+ acc.tasksTotal += e.tasks.total;
148
+ acc.tasksDone += e.tasks.done;
149
+ acc.blocked += e.tasks.blocked;
150
+ acc.proven += e.bucket === "proven" ? 1 : 0;
151
+ acc.drift += e.lifecycle_drift ? 1 : 0;
152
+ return acc;
153
+ }, { tasksTotal: 0, tasksDone: 0, blocked: 0, proven: 0, retired: 0, drift: 0 });
154
+ const lines = [];
155
+ lines.push("foo packet index");
156
+ const retiredNote = totals.retired > 0 ? ` · ${totals.retired} retired` : "";
157
+ // Lifecycle drift (packet-lifecycle-truth-v0 R-002): contradicted packets are
158
+ // a headline number, not a per-row easter egg — the index exists to say what
159
+ // is actually done, and these rows are where it currently lies.
160
+ const driftNote = totals.drift > 0 ? ` · ${totals.drift} lifecycle drift` : "";
161
+ lines.push(`${index.length - totals.retired} packets · ${totals.tasksDone} of ${totals.tasksTotal} tasks done · ${totals.proven} complete · ${totals.blocked} blocked${driftNote}${retiredNote}`);
162
+ lines.push("");
163
+ let retiredDivider = false;
164
+ for (const e of index) {
165
+ if (e.error) {
166
+ lines.push(`${pad(e.spec_id, 30)} error: ${e.error}`);
167
+ continue;
168
+ }
169
+ if (e.bucket === "retired" && !retiredDivider) {
170
+ if (lines[lines.length - 1] !== "")
171
+ lines.push("");
172
+ lines.push("retired (parked/killed — not open work):");
173
+ retiredDivider = true;
174
+ }
175
+ const counts = `${e.tasks.done}/${e.tasks.total} done`;
176
+ const proofs = `${e.proof.checks_done}/${e.proof.checks_total} proven`;
177
+ const flags = e.tasks.blocked > 0 && e.bucket !== "retired" ? ` ! ${e.tasks.blocked} blocked` : "";
178
+ const nextNote = e.next ? ` → ${e.next.id}` : "";
179
+ const reason = e.bucket === "retired" && e.verdict?.reason ? ` — ${e.verdict.reason}` : "";
180
+ // The ⚠️ glyph is TTY-only (statusGlyphs R-008); the word "drift" always
181
+ // travels, so piped output and greps still carry the contradiction.
182
+ const drift = e.lifecycle_drift
183
+ ? ` ${withGlyph("drift", e.lifecycle_effective === "done" ? `drift: work proven done, header says ${e.lifecycle ?? "(unset)"}` : `drift: header says ${e.lifecycle}, work incomplete`, glyphs)}`
184
+ : "";
185
+ lines.push(`${pad(e.spec_id, 30)} ${pad(counts, 11)} ${pad(proofs, 13)} ${pad(e.lifecycle ?? "-", 8)}${nextNote}${flags}${drift}${reason}`);
186
+ }
187
+ lines.push("");
188
+ return lines.join("\n");
189
+ }
190
+ const LANE_ORDER = { needs_you: 0, in_flight: 1, ready: 2, blocked: 3 };
191
+ // One lane rule shared by the per-task triage and the packet-level portfolio
192
+ // rollup (packet-index-portfolio-v0 R-001), so the two surfaces can never
193
+ // disagree about what lane a task is in.
194
+ function laneForOpenTask(t, unmet) {
195
+ if (unmet.length > 0)
196
+ return "blocked";
197
+ const humanOwned = (t.owner ?? "").replace(/"/g, "").startsWith("human") || (t.owner ?? "") === "product";
198
+ if (humanOwned || t.agreement === "suggested" || t.agreement === "discovered" || t.status === "blocked")
199
+ return "needs_you";
200
+ if (t.status === "doing")
201
+ return "in_flight";
202
+ return "ready";
203
+ }
204
+ export function deriveTriage(index) {
205
+ const rows = [];
206
+ for (const e of index) {
207
+ // A retired packet is not work. Its lifecycle already says killed/parked —
208
+ // that IS the status, and triage's job is to honor it, not to re-surface the
209
+ // packet's open tasks under another name.
210
+ //
211
+ // This skip is worth 34 of 172 triage rows at the time it was written: six
212
+ // packets (personal-brain-stash-v0, build-preview-mini-apps-v0,
213
+ // build-preview-annotation-branches-v0, build-preview-integrated-loop-v1,
214
+ // code-catalog-rip-v0, release-channel-v0), every one carrying an explicit
215
+ // kill/park event with a reason — one of them recording that its scope had
216
+ // already SHIPPED into `foo preview`. They were being read as a fifth of the
217
+ // open board. The rest of the index already got this right (headline totals
218
+ // exclude retired; the render prints "retired (parked/killed — not open
219
+ // work)"); only triage asked a different question and got a different answer.
220
+ //
221
+ // Nothing is deleted: the packet keeps its tasks, its ledger, and its place
222
+ // in the `packets` array with its lifecycle intact. It simply stops being
223
+ // offered as something to pick up.
224
+ if (e.bucket === "retired")
225
+ continue;
226
+ const done = new Set(e.task_list.filter((t) => t.status === "done").map((t) => t.id));
227
+ for (const t of e.task_list) {
228
+ if (t.status === "done" || t.status === "dropped")
229
+ continue;
230
+ const unmet = t.deps.filter((d) => !done.has(d));
231
+ const unlocks = e.task_list.filter((o) => o.status !== "done" && o.status !== "dropped" && o.deps.includes(t.id)).length;
232
+ const prios = t.refs.map((r) => e.requirement_priorities[r]).filter((x) => Boolean(x));
233
+ const prio = prios.length ? prios.sort()[0] : null;
234
+ const lane = laneForOpenTask(t, unmet);
235
+ rows.push({ packet: e.packet, task: t.id, title: t.title, lane, prio, unlocks, unmet, owner: t.owner, agreement: t.agreement, updated: e.updated });
236
+ }
237
+ }
238
+ return rows.sort((a, b) => {
239
+ if (LANE_ORDER[a.lane] !== LANE_ORDER[b.lane])
240
+ return LANE_ORDER[a.lane] - LANE_ORDER[b.lane];
241
+ const pa = a.prio ?? "P8"; // unknown priority ranks below every stated one
242
+ const pb = b.prio ?? "P8";
243
+ if (pa !== pb)
244
+ return pa.localeCompare(pb);
245
+ if (b.unlocks !== a.unlocks)
246
+ return b.unlocks - a.unlocks;
247
+ return (b.updated ?? "").localeCompare(a.updated ?? "");
248
+ });
249
+ }
250
+ // --- Packet-level portfolio rollup (packet-index-portfolio-v0) ---------------
251
+ // The data deriveTriage already reads, asked one level up: not "what do I pick
252
+ // up next" but "where should portfolio attention and decisions go". Triage maps
253
+ // unknown priority to "P8", so a packet stating no priority sinks below every
254
+ // labeled one no matter how much work it would unlock — `mispriced` flags that
255
+ // as a review queue (missing signal is not low signal, D-002), never as a
256
+ // priority value.
257
+ // Threshold calibration (packet-index-portfolio-v0 D-004): the observed
258
+ // unlabeled tier of the 2026-09-24 stack rank sat at 13/10/8/7/7/4/2 unlocks;
259
+ // 7 catches the heavy unlabeled packets without firing on single-task ones
260
+ // (whose sum is 0). An assumption on record — recalibrate if it fires on
261
+ // obviously small packets or misses a known mispriced one at review.
262
+ export const MISPRICED_UNLOCK_THRESHOLD = 7;
263
+ export function derivePacketPortfolio(index) {
264
+ const rows = [];
265
+ for (const e of index) {
266
+ // Rows are for live work only (packet-index-portfolio-v0 R-001, carrying
267
+ // packet-index-triage-v0 D-003's retirement exclusion): a retired packet is
268
+ // not work, and a packet with zero open tasks (proven or otherwise) has
269
+ // nothing to roll up.
270
+ if (e.bucket === "retired")
271
+ continue;
272
+ // An unreadable packet produces no row — there is nothing to roll up — but
273
+ // never silently disappears: its error row stays in `packets` (R-005).
274
+ if (e.error)
275
+ continue;
276
+ const open = e.task_list.filter((t) => t.status !== "done" && t.status !== "dropped");
277
+ if (open.length === 0)
278
+ continue;
279
+ // Unlocks are re-derived from task_list[].deps, never summed from the
280
+ // triage rows (D-003): a per-task one-hop count is correct per-task but not
281
+ // additive — several open tasks sharing one downstream dependent each count
282
+ // it, so the sum is an upper bound. unlocks_max is the no-double-count view.
283
+ const done = new Set(e.task_list.filter((t) => t.status === "done").map((t) => t.id));
284
+ const laneSet = new Set();
285
+ let unlocksSum = 0;
286
+ let unlocksMax = 0;
287
+ for (const t of open) {
288
+ laneSet.add(laneForOpenTask(t, t.deps.filter((d) => !done.has(d))));
289
+ const unlocks = e.task_list.filter((o) => o.status !== "done" && o.status !== "dropped" && o.deps.includes(t.id)).length;
290
+ unlocksSum += unlocks;
291
+ if (unlocks > unlocksMax)
292
+ unlocksMax = unlocks;
293
+ }
294
+ const lanes = Object.keys(LANE_ORDER).filter((l) => laneSet.has(l));
295
+ // Packet-level signal (R-001/R-002): the best value stated on any
296
+ // requirement, even where individual tasks reference none.
297
+ const stated = Object.values(e.requirement_priorities);
298
+ const priority = stated.length ? stated.sort()[0] : null;
299
+ rows.push({
300
+ packet: e.packet,
301
+ spec_id: e.spec_id,
302
+ open_tasks: e.tasks.total - e.tasks.done - e.tasks.dropped,
303
+ total_tasks: e.tasks.total,
304
+ lanes,
305
+ priority,
306
+ unlocks_sum: unlocksSum,
307
+ unlocks_max: unlocksMax,
308
+ mispriced: priority === null && unlocksSum >= MISPRICED_UNLOCK_THRESHOLD,
309
+ lifecycle_drift: e.lifecycle_drift,
310
+ updated: e.updated,
311
+ });
312
+ }
313
+ // Sort (R-004): mispriced first (the review queue), then priority with the
314
+ // unstated fallback below every stated one ("P8", carrying
315
+ // packet-index-triage-v0 D-001), then unlocks_sum descending, then packet
316
+ // name ascending. Deterministic, zero model calls.
317
+ return rows.sort((a, b) => {
318
+ if (a.mispriced !== b.mispriced)
319
+ return a.mispriced ? -1 : 1;
320
+ const pa = a.priority ?? "P8";
321
+ const pb = b.priority ?? "P8";
322
+ if (pa !== pb)
323
+ return pa.localeCompare(pb);
324
+ if (b.unlocks_sum !== a.unlocks_sum)
325
+ return b.unlocks_sum - a.unlocks_sum;
326
+ return a.packet.localeCompare(b.packet);
327
+ });
328
+ }
329
+ function renderIndexJson(index) {
330
+ // portfolio is additive (packet-index-portfolio-v0 R-003): triage and packets
331
+ // keep shape, order, and byte content; only the key and the schema bump land.
332
+ return `${JSON.stringify({ schema_version: PACKET_INDEX_SCHEMA_VERSION, triage: deriveTriage(index), portfolio: derivePacketPortfolio(index), packets: index }, null, 2)}\n`;
333
+ }
334
+ // Per-task signifier. "Done is a claim. Proof is a receipt." —
335
+ // p proven status done AND every done_when check met (the receipt)
336
+ // d done status done without a full receipt (no checks, or not all met)
337
+ // w doing in progress
338
+ // b blocked
339
+ // o open todo / dropped
340
+ export function taskGlyph(t) {
341
+ if (t.status === "blocked")
342
+ return "b";
343
+ if (t.status === "done")
344
+ return t.checksTotal > 0 && t.checksDone === t.checksTotal ? "p" : "d";
345
+ if (t.status === "doing")
346
+ return "w";
347
+ return "o";
348
+ }
349
+ function htmlData(index) {
350
+ const shortDate = (iso) => {
351
+ if (!iso)
352
+ return "";
353
+ const d = new Date(iso);
354
+ if (Number.isNaN(d.getTime()))
355
+ return "";
356
+ const mon = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"][d.getUTCMonth()];
357
+ return `${d.getUTCDate()} ${mon}`;
358
+ };
359
+ const data = index.map((e) => {
360
+ const flags = [];
361
+ if (e.tasks.blocked > 0) {
362
+ const blockedTitle = e.blocked[0] ? `${e.blocked[0].id} ${e.blocked[0].title}` : "";
363
+ flags.push({ tone: "warn", label: `${e.tasks.blocked} blocked${blockedTitle ? ` — ${blockedTitle}` : ""}` });
364
+ }
365
+ // Lifecycle drift (packet-lifecycle-truth-v0 R-002) subsumes the older
366
+ // draft-restamp nudge; the draft-specific wording survives for the one
367
+ // case lifecycle_drift does not cover (all done, proofs not yet in).
368
+ if (e.lifecycle_drift) {
369
+ flags.push({
370
+ tone: "att",
371
+ label: e.lifecycle_effective === "done"
372
+ ? `drift — header says ${e.lifecycle ?? "(unset)"}, work proven; record the done verdict?`
373
+ : `drift — header says ${e.lifecycle}, work not proofs-complete`,
374
+ });
375
+ }
376
+ else if (e.lifecycle === "draft" && e.tasks.total > 0 && e.tasks.done === e.tasks.total) {
377
+ flags.push({ tone: "att", label: "drift — header says draft, work proven; restamp?" });
378
+ }
379
+ return {
380
+ id: e.spec_id,
381
+ title: e.title ?? e.packet,
382
+ intent: e.intent ?? "",
383
+ mode: e.mode ?? "",
384
+ stage: e.stage ?? "",
385
+ life: e.lifecycle ?? "",
386
+ up: shortDate(e.updated),
387
+ bucket: e.bucket,
388
+ d: e.tasks.done,
389
+ t: e.tasks.total,
390
+ cD: e.proof.checks_done,
391
+ cT: e.proof.checks_total,
392
+ reqC: e.requirements.covered,
393
+ reqT: e.requirements.total,
394
+ next: e.next ? `${e.next.id} · ${e.next.title}` : "",
395
+ questions: e.open_questions,
396
+ flags,
397
+ tasks: e.task_list.map((task) => [task.id, taskGlyph(task), task.title]),
398
+ };
399
+ });
400
+ // Escape "<" so a packet title containing </script> cannot break out of the tag.
401
+ return JSON.stringify(data).replace(/</g, "\\u003c");
402
+ }
403
+ // The HTML legend is generated from the status-glyph vocabulary (R-001) so the
404
+ // web key, the CLI, and the spec read the same glyph+label; only the swatch color
405
+ // is web-specific. Emoji are decorative (aria-hidden); the word is the accessible
406
+ // carrier (R-002). Emoji appear ONLY here in the legend — never on data rows
407
+ // (design-swarm consensus: no badge pile, no color-only state).
408
+ const LEGEND_STATES = [
409
+ { state: "proven", swatch: "background:var(--proof)" },
410
+ { state: "claimed", swatch: "background:var(--proof);opacity:.42" },
411
+ { state: "doing", swatch: "background:var(--line2)" },
412
+ { state: "todo", swatch: "background:var(--line)" },
413
+ { state: "blocked", swatch: "background:var(--warn)" },
414
+ { state: "needs_input", swatch: "background:var(--att)" },
415
+ ];
416
+ function legendHtml() {
417
+ return LEGEND_STATES.map(({ state, swatch }) => {
418
+ const g = STATUS_GLYPHS[state];
419
+ return `<span><i class="em" aria-hidden="true">${g.glyph}</i><i class="sw" style="${swatch}" aria-hidden="true"></i>${g.label}</span>`;
420
+ }).join("\n ");
421
+ }
422
+ export function renderIndexHtml(index) {
423
+ // Numeric aggregates count live packets only; retired rows still render in
424
+ // the table (their lifecycle column carries the verdict) but their undone
425
+ // tasks are not open work (packet-lifecycle-v0 R-003). Retired-specific HTML
426
+ // treatment (tab/section) is a recorded follow-up, not silently absent.
427
+ const live = index.filter((e) => e.bucket !== "retired");
428
+ const generated = index.length;
429
+ const tasksDone = live.reduce((n, e) => n + e.tasks.done, 0);
430
+ const tasksTotal = live.reduce((n, e) => n + e.tasks.total, 0);
431
+ const checksDone = live.reduce((n, e) => n + e.proof.checks_done, 0);
432
+ const checksTotal = live.reduce((n, e) => n + e.proof.checks_total, 0);
433
+ const complete = index.filter((e) => e.bucket === "proven").length;
434
+ const progress = index.filter((e) => e.bucket === "progress").length;
435
+ const open = index.filter((e) => e.bucket === "open").length;
436
+ const blocked = live.filter((e) => e.tasks.blocked > 0).length;
437
+ // Task-state segments. proven ⊆ done; the remainder split by authoritative counts.
438
+ let segProven = 0;
439
+ for (const e of live)
440
+ for (const t of e.task_list)
441
+ if (taskGlyph(t) === "p")
442
+ segProven += 1;
443
+ const segBlocked = live.reduce((n, e) => n + e.tasks.blocked, 0);
444
+ const segDoing = live.reduce((n, e) => n + e.tasks.doing, 0);
445
+ const segDone = Math.max(0, tasksDone - segProven);
446
+ const segOpen = Math.max(0, tasksTotal - tasksDone - segDoing - segBlocked);
447
+ const data = htmlData(index);
448
+ const triage = JSON.stringify(deriveTriage(index).map((r) => [r.lane, r.packet, r.task, r.title, r.prio ?? "", r.unlocks, r.unmet.join(","), r.owner ?? "", r.agreement])).replace(/</g, "\\u003c");
449
+ // Tokens repeat in the template, so replace globally (string.replace hits only the first).
450
+ return INDEX_HTML.replace("__DATA__", data).replace("__TRIAGE__", triage)
451
+ .replace(/__N__/g, String(generated))
452
+ .replace(/__TASKS_DONE__/g, String(tasksDone))
453
+ .replace(/__TASKS_TOTAL__/g, String(tasksTotal))
454
+ .replace(/__TASKS_PROVEN__/g, String(segProven))
455
+ .replace(/__CHECKS_DONE__/g, String(checksDone))
456
+ .replace(/__CHECKS_TOTAL__/g, String(checksTotal))
457
+ .replace(/__COMPLETE__/g, String(complete))
458
+ .replace(/__PROGRESS__/g, String(progress))
459
+ .replace(/__OPEN__/g, String(open))
460
+ .replace(/__BLOCKED__/g, String(blocked))
461
+ .replace("__PROVEN_SEG__", String(segProven))
462
+ .replace("__DONE_SEG__", String(segDone))
463
+ .replace("__DOING_SEG__", String(segDoing))
464
+ .replace("__OPEN_SEG__", String(segOpen))
465
+ .replace("__BLOCKED_SEG__", String(segBlocked))
466
+ .replace("__LEGEND__", legendHtml());
467
+ }
468
+ export function runAspIndex(root, options) {
469
+ const index = derivePacketIndex(root);
470
+ if (options.json)
471
+ return renderIndexJson(index);
472
+ if (options.html) {
473
+ const outPath = path.resolve(root, options.htmlPath ?? "packet-index.html");
474
+ writeFileSync(outPath, renderIndexHtml(index));
475
+ return `wrote ${index.length}-packet index to ${outPath}\n`;
476
+ }
477
+ return renderIndexText(index, options.glyphs === true);
478
+ }
479
+ const INDEX_HTML = `<!doctype html>
480
+ <html lang="en">
481
+ <head>
482
+ <meta charset="utf-8" />
483
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
484
+ <title>foo · packet index</title>
485
+ <style>
486
+ @import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:wght@400;700&display=swap');
487
+ :root{--ink:oklch(0.205 0.012 25);--muted:oklch(0.455 0.014 25);--line:oklch(0.875 0.006 25);--line2:oklch(0.705 0.012 25);--bg:oklch(1 0 0);--surf:oklch(0.985 0.001 25);--surf2:oklch(0.972 0.002 25);--att:oklch(0.36 0.12 0);--att-soft:oklch(0.945 0.022 8);--proof:oklch(0.47 0.095 190);--warn:oklch(0.62 0.1 70);--warn-soft:oklch(0.955 0.035 82);--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;--ui:"Atkinson Hyperlegible",ui-sans-serif,system-ui,sans-serif;}
488
+ *{box-sizing:border-box;}
489
+ body{margin:0;background:var(--surf);color:var(--ink);font-family:var(--ui);line-height:1.5;-webkit-font-smoothing:antialiased;}
490
+ .wrap{max-width:920px;margin:0 auto;padding:40px 24px 64px;}
491
+ .sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);}
492
+ .mast{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;flex-wrap:wrap;}
493
+ .wm{font-size:27px;font-weight:700;letter-spacing:-0.01em;}
494
+ .wm b{color:var(--att);}
495
+ .sub{margin:4px 0 0;font-size:13px;color:var(--muted);}
496
+ .asof{font-family:var(--mono);font-size:12px;color:var(--muted);text-align:right;}
497
+ .chips{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin:20px 0 14px;}
498
+ .chip{background:var(--bg);border:0.5px solid var(--line);border-radius:8px;padding:11px 13px;}
499
+ .chip .k{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--muted);}
500
+ .chip .v{font-size:21px;font-weight:700;margin-top:3px;}
501
+ .chip .v small{font-size:12px;font-weight:400;color:var(--muted);}
502
+ .pbar{display:flex;height:10px;border-radius:3px;overflow:hidden;background:var(--line);margin:2px 0 9px;}
503
+ .pbar i{display:block;}
504
+ .lg{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--muted);margin-bottom:18px;}
505
+ .lg span{display:inline-flex;align-items:center;gap:5px;}
506
+ .sw{width:11px;height:11px;border-radius:3px;display:inline-block;}
507
+ .filters{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px;}
508
+ .ft{font-family:var(--mono);font-size:12px;color:var(--muted);background:transparent;border:0.5px solid var(--line2);border-radius:20px;padding:5px 11px;cursor:pointer;}
509
+ .ft[aria-pressed="true"]{background:var(--ink);color:var(--bg);border-color:var(--ink);}
510
+ .ft b{font-weight:700;opacity:.6;margin-left:4px;}
511
+ .list{display:flex;flex-direction:column;gap:8px;}
512
+ .row{position:relative;text-align:left;width:100%;background:var(--bg);border:0.5px solid var(--line);border-radius:9px;padding:13px 15px 13px 17px;cursor:pointer;font-family:var(--ui);color:var(--ink);overflow:hidden;}
513
+ .row:hover,.row.open{background:var(--surf2);border-color:var(--line2);}
514
+ .row::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:transparent;}
515
+ .row.open::before{background:var(--att);}
516
+ .r1{display:flex;align-items:center;gap:8px;}
517
+ .sid{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--muted);}
518
+ .tag{font-family:var(--mono);font-size:12px;font-weight:700;border:0.5px solid var(--line2);border-radius:4px;padding:2px 6px;color:var(--muted);}
519
+ .tag.life{color:var(--ink);}
520
+ .cnt{margin-left:auto;font-family:var(--mono);font-size:12px;font-weight:700;white-space:nowrap;}
521
+ .cnt small{color:var(--muted);font-weight:400;}
522
+ .ttl{font-size:15px;margin:7px 0 9px;}
523
+ .segs{display:flex;gap:2px;height:7px;}
524
+ .segs i{flex:1;border-radius:1.5px;min-width:3px;}
525
+ .segs i.p{background:var(--proof);}
526
+ .segs i.d{background:var(--proof);opacity:.42;}
527
+ .segs i.w{background:var(--line2);}
528
+ .segs i.o{background:var(--line);}
529
+ .segs i.b{background:var(--warn);}
530
+ .flag{display:inline-flex;align-items:center;gap:5px;font-size:12px;border-radius:5px;padding:3px 8px;margin-top:9px;}
531
+ .flag.warn{background:var(--warn-soft);color:oklch(0.42 0.07 60);}
532
+ .flag.att{background:var(--att-soft);color:var(--att);}
533
+ .det{max-height:0;overflow:hidden;transition:max-height .22s ease;}
534
+ .det-in{padding-top:13px;margin-top:13px;border-top:0.5px solid var(--line);}
535
+ .det .intent{font-size:13px;color:var(--muted);margin:0 0 11px;}
536
+ .det .meta{font-family:var(--mono);font-size:12px;color:var(--muted);margin-bottom:11px;display:flex;flex-wrap:wrap;gap:5px 14px;}
537
+ .tk{display:flex;align-items:flex-start;gap:9px;padding:4px 0;font-size:13px;}
538
+ .g{flex:none;width:17px;height:17px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:12px;font-weight:700;margin-top:1px;}
539
+ .g.p{background:var(--proof);color:oklch(0.99 0.004 190);}
540
+ .g.d{background:transparent;border:1.25px solid var(--proof);color:var(--proof);}
541
+ .g.w{background:transparent;border:1.25px solid var(--ink);color:var(--ink);}
542
+ .g.o{border:1px solid var(--line2);color:var(--muted);}
543
+ .g.b{background:var(--warn);color:oklch(0.99 0.004 80);}
544
+ .tk .tid{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--muted);flex:none;width:38px;margin-top:2px;}
545
+ .tk.dn .tt{color:var(--muted);}
546
+ .q{font-size:12.5px;color:var(--muted);padding:3px 0 3px 12px;}
547
+ .seclabel{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--muted);margin:14px 0 6px;}
548
+ .seclabel.sep{border-top:0.5px solid var(--line);padding-top:12px;}
549
+ .next{font-family:var(--mono);font-size:12px;color:var(--att);margin-top:10px;}
550
+ .hint{font-size:12px;color:var(--muted);margin:16px 0 0;font-style:italic;}
551
+ .foot{margin-top:14px;font-family:var(--mono);font-size:12px;color:var(--muted);}
552
+ .lg .em{font-size:12px;line-height:1;}
553
+ .row:focus-visible,.ft:focus-visible,.search:focus-visible{outline:none;box-shadow:0 0 0 3px oklch(0.82 0.07 190);}
554
+ .search{width:100%;font-family:var(--ui);font-size:13px;color:var(--ink);background:var(--bg);border:0.5px solid var(--line2);border-radius:8px;padding:9px 12px;margin:0 0 10px;}
555
+ .search::placeholder{color:var(--muted);}
556
+ .empty{font-size:13px;color:var(--muted);padding:16px 2px;display:none;}
557
+ .tabs{display:flex;gap:2px;margin:22px 0 14px;border-bottom:0.5px solid var(--line2);}
558
+ .tab{font-family:var(--ui);font-size:14px;font-weight:700;color:var(--muted);background:transparent;border:none;border-bottom:2.5px solid transparent;padding:8px 14px 9px;cursor:pointer;}
559
+ .tab[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--att);}
560
+ .tab:focus-visible{outline:none;box-shadow:0 0 0 3px oklch(0.82 0.07 190);border-radius:6px 6px 0 0;}
561
+ .qsub{font-size:12px;color:var(--muted);margin:0 0 10px;}
562
+ .lgwrap{margin-bottom:14px;}
563
+ .lgwrap summary{font-family:var(--mono);font-size:12px;color:var(--muted);cursor:pointer;width:max-content;}
564
+ .lgwrap[open] summary{margin-bottom:8px;}
565
+ .qbar{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--muted);margin:0 0 10px;}
566
+ .qbar select{font-family:var(--mono);font-size:12px;color:var(--ink);background:var(--bg);border:0.5px solid var(--line2);border-radius:6px;padding:4px 8px;}
567
+ .qrow{display:block;background:var(--bg);border:0.5px solid var(--line);border-radius:9px;padding:12px 15px;margin-bottom:8px;}
568
+ .qr1{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
569
+ .qlane{font-family:var(--mono);font-size:12px;font-weight:700;white-space:nowrap;border:0.5px solid var(--line2);border-radius:4px;padding:2px 7px;}
570
+ .qlane.l-needs_you{color:var(--att);border-color:var(--att);background:var(--att-soft);}
571
+ .qlane.l-in_flight{color:var(--ink);}
572
+ .qlane.l-ready{color:var(--proof);border-color:var(--proof);}
573
+ .qlane.l-blocked{color:oklch(0.52 0.09 70);border-color:oklch(0.75 0.07 70);background:var(--warn-soft);}
574
+ .qlane.l-drift{color:var(--muted);}
575
+ .qpk{font-family:var(--mono);font-size:12px;color:var(--muted);}
576
+ .qtid{font-family:var(--mono);font-size:12px;font-weight:700;}
577
+ .qtt{font-size:15px;margin-top:7px;}
578
+ .qmeta{margin-left:auto;font-family:var(--mono);font-size:12px;color:var(--muted);white-space:nowrap;}
579
+ </style>
580
+ </head>
581
+ <body>
582
+ <div class="wrap">
583
+ <h2 class="sr">Birds-eye index of __N__ foo spec packets with task completion and rollover detail.</h2>
584
+ <div class="mast">
585
+ <div>
586
+ <div class="wm">foo<b>.</b> packet index</div>
587
+ <p class="sub">every spec, its proof, at a glance — derived live from each packet's tasks.yaml</p>
588
+ </div>
589
+ <div class="asof">foo asp index<br>__N__ packets</div>
590
+ </div>
591
+ <div class="chips">
592
+ <div class="chip"><div class="k">packets</div><div class="v">__N__</div></div>
593
+ <div class="chip"><div class="k">tasks done</div><div class="v">__TASKS_DONE__<small>/__TASKS_TOTAL__</small></div></div>
594
+ <div class="chip"><div class="k">proven</div><div class="v">__TASKS_PROVEN__<small>/__TASKS_TOTAL__ · checks __CHECKS_DONE__/__CHECKS_TOTAL__</small></div></div>
595
+ <div class="chip"><div class="k">state</div><div class="v">__COMPLETE__<small> complete · __PROGRESS__ wip · __OPEN__ open</small></div></div>
596
+ </div>
597
+ <div class="pbar" aria-hidden="true"><i style="flex:__PROVEN_SEG__;background:var(--proof)"></i><i style="flex:__DONE_SEG__;background:var(--proof);opacity:.42"></i><i style="flex:__DOING_SEG__;background:var(--line2)"></i><i style="flex:__BLOCKED_SEG__;background:var(--warn)"></i><i style="flex:__OPEN_SEG__"></i></div>
598
+ <details class="lgwrap"><summary>key</summary><div class="lg">__LEGEND__</div></details>
599
+ <div class="tabs" role="tablist" aria-label="Index views">
600
+ <button class="tab" id="tab-queue" role="tab" aria-selected="true" aria-controls="panel-queue">work queue</button>
601
+ <button class="tab" id="tab-packets" role="tab" aria-selected="false" aria-controls="panel-packets">packets</button>
602
+ </div>
603
+ <section id="panel-queue" role="tabpanel" aria-labelledby="tab-queue">
604
+ <p class="qsub">everything still to do across active specs, most important first — drift means tasks left behind in retired specs</p>
605
+ <div class="filters" id="qfilters"></div>
606
+ <div class="qbar"><label for="qsort">sort</label><select id="qsort" aria-label="Sort work queue"><option value="rank">most important first</option><option value="packet">by spec</option><option value="unlocks">unblocks the most work</option></select></div>
607
+ <div class="qlist" id="qlist"></div>
608
+ </section>
609
+ <section id="panel-packets" role="tabpanel" aria-labelledby="tab-packets" hidden>
610
+ <input class="search" id="search" type="search" placeholder="filter by id or title — press /" aria-label="Filter packets by id or title" autocomplete="off" />
611
+ <div class="filters" id="filters">
612
+ <button class="ft" data-f="all" aria-pressed="true">all<b>__N__</b></button>
613
+ <button class="ft" data-f="progress" aria-pressed="false">in progress<b>__PROGRESS__</b></button>
614
+ <button class="ft" data-f="proven" aria-pressed="false">complete<b>__COMPLETE__</b></button>
615
+ <button class="ft" data-f="open" aria-pressed="false">open<b>__OPEN__</b></button>
616
+ <button class="ft" data-f="blocked" aria-pressed="false">blocked<b>__BLOCKED__</b></button>
617
+ </div>
618
+ <div class="list" id="list"></div>
619
+ <p class="empty" id="empty"></p>
620
+ </section>
621
+ <p class="hint">click a packet to open its tasks · click again to close · / to search · esc to close all</p>
622
+ <p class="foot">generated by foo asp index — read-only, derived from tasks.yaml</p>
623
+ </div>
624
+ <script>
625
+ var P=__DATA__;
626
+ var T=__TRIAGE__;
627
+ var LANES={needs_you:["\u270B","needs your input"],in_flight:["\uD83D\uDD04","doing"],ready:["\u25B6","ready to start"],blocked:["\uD83D\uDEA7","blocked"]};
628
+ var LANE_SEQ=["needs_you","in_flight","ready","blocked"];
629
+ var CLAIMED_LABEL=${JSON.stringify(STATUS_GLYPHS.claimed.label)};
630
+ var GW={p:"proven",d:CLAIMED_LABEL,w:"doing",o:"todo",b:"blocked"};
631
+ function esc(s){return String(s).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;","\\"":"&quot;"}[c];});}
632
+ var list=document.getElementById("list");
633
+ P.forEach(function(p){
634
+ var segs=p.tasks.map(function(t){return '<i class="'+t[1]+'"></i>';}).join("");
635
+ if(!segs){segs='<i class="o"></i>';}
636
+ var sc={p:0,d:0,w:0,o:0,b:0};p.tasks.forEach(function(t){sc[t[1]]=(sc[t[1]]||0)+1;});
637
+ var segLabel=p.tasks.length?('tasks: '+sc.p+' proven, '+sc.d+' '+CLAIMED_LABEL+', '+sc.w+' doing, '+sc.b+' blocked, '+sc.o+' todo'):'no tasks yet';
638
+ var tags='<span class="tag life">'+esc(p.life||"-")+'</span>'+(p.mode?'<span class="tag">'+esc(p.mode)+'</span>':"");
639
+ var flags=p.flags.map(function(f){return '<div class="flag '+(f.tone==="att"?"att":"warn")+'">'+esc(f.label)+'</div>';}).join("");
640
+ var meta='<div class="meta"><span>mode · '+esc(p.mode||"-")+'</span><span>stage · '+esc(p.stage||"-")+'</span><span>lifecycle · '+esc(p.life||"-")+'</span><span>checks · '+p.cD+'/'+p.cT+'</span><span>requirements · '+p.reqC+'/'+p.reqT+'</span><span>updated · '+esc(p.up||"-")+'</span></div>';
641
+ var tks=(p.tasks.length?'<div class="seclabel">tasks</div>':"")+p.tasks.map(function(t){var g=t[1];var gl=(g==="p"||g==="d")?"\\u2713":(g==="b"?"!":(g==="w"?"\\u00b7":""));return '<div class="tk '+((g==="p"||g==="d")?"dn":"")+'"><span class="g '+g+'" aria-hidden="true">'+gl+'</span><span class="sr">'+(GW[g]||"")+' </span><span class="tid">'+esc(t[0])+'</span><span class="tt">'+esc(t[2])+'</span></div>';}).join("");
642
+ var qs=p.questions&&p.questions.length?'<div class="seclabel sep">open questions</div>'+p.questions.map(function(q){return '<div class="q">- '+esc(q)+'</div>';}).join(""):"";
643
+ var nx=p.next?'<div class="next">next · '+esc(p.next)+'</div>':"";
644
+ var row=document.createElement("button");
645
+ row.className="row";row.type="button";
646
+ row.setAttribute("data-bucket",p.bucket);
647
+ row.setAttribute("data-blocked",p.flags.some(function(f){return f.tone==="warn";})?"1":"0");
648
+ row.setAttribute("data-search",(p.id+" "+p.title).toLowerCase());
649
+ row.setAttribute("aria-expanded","false");
650
+ row.innerHTML='<div class="r1"><span class="sid">'+esc(p.id)+'</span>'+tags+'<span class="cnt">'+p.d+'<small>/'+p.t+' done</small></span></div>'+
651
+ '<div class="ttl">'+esc(p.title)+'</div>'+
652
+ '<div class="segs" role="img" aria-label="'+esc(segLabel)+'">'+segs+'</div>'+flags+
653
+ '<div class="det"><div class="det-in"><p class="intent">'+esc(p.intent||"")+'</p>'+meta+tks+qs+nx+'</div></div>';
654
+ var det=row.querySelector(".det");
655
+ function open(){det.style.maxHeight=det.scrollHeight+"px";row.classList.add("open");row.setAttribute("aria-expanded","true");}
656
+ function close(){det.style.maxHeight="0";row.classList.remove("open");row.setAttribute("aria-expanded","false");}
657
+ row.addEventListener("click",function(){if(row.classList.contains("open"))close();else open();});
658
+ list.appendChild(row);
659
+ });
660
+ (function(){
661
+ var tabs=[document.getElementById("tab-queue"),document.getElementById("tab-packets")];
662
+ var panels=[document.getElementById("panel-queue"),document.getElementById("panel-packets")];
663
+ function select(i){
664
+ tabs.forEach(function(t,j){t.setAttribute("aria-selected",j===i?"true":"false");});
665
+ panels.forEach(function(p,j){if(j===i)p.removeAttribute("hidden");else p.setAttribute("hidden","");});
666
+ try{location.hash=i===0?"queue":"packets";}catch(e){}
667
+ }
668
+ tabs.forEach(function(t,i){t.addEventListener("click",function(){select(i);});});
669
+ tabs.forEach(function(t,i){t.addEventListener("keydown",function(e){
670
+ if(e.key==="ArrowRight"||e.key==="ArrowLeft"){var n=(i+1)%2;select(n);tabs[n].focus();}
671
+ });});
672
+ if(location.hash==="#packets")select(1);
673
+ })();
674
+ (function(){
675
+ var qlist=document.getElementById("qlist");
676
+ var qfilters=document.getElementById("qfilters");
677
+ var qsort=document.getElementById("qsort");
678
+ var active={all:true};
679
+ var counts={};T.forEach(function(r){counts[r[0]]=(counts[r[0]]||0)+1;});
680
+ var chips=[["all","all",T.length]].concat(LANE_SEQ.map(function(l){return [l,LANES[l][0]+" "+LANES[l][1],counts[l]||0];}));
681
+ chips.forEach(function(c,i){
682
+ var b=document.createElement("button");b.className="ft";b.setAttribute("data-l",c[0]);
683
+ b.setAttribute("aria-pressed",i===0?"true":"false");
684
+ b.innerHTML=esc(c[1])+"<b>"+c[2]+"</b>";
685
+ qfilters.appendChild(b);
686
+ });
687
+ function laneRank(l){return LANE_SEQ.indexOf(l);}
688
+ function render(){
689
+ var mode=qsort.value;var rows=T.slice();
690
+ if(mode==="packet"){rows.sort(function(a,b){return a[1]===b[1]?laneRank(a[0])-laneRank(b[0]):(a[1]<b[1]?-1:1);});}
691
+ else if(mode==="unlocks"){rows.sort(function(a,b){return b[5]-a[5];});}
692
+ var f=qfilters.querySelector('[aria-pressed="true"]').getAttribute("data-l");
693
+ qlist.innerHTML="";
694
+ rows.forEach(function(r){
695
+ if(f!=="all"&&r[0]!==f)return;
696
+ var d=document.createElement("div");d.className="qrow";
697
+ var meta=[];if(r[4])meta.push(r[4]);if(r[5])meta.push("frees "+r[5]+(r[5]===1?" task":" tasks"));if(r[6])meta.push("waiting on "+r[6]);
698
+ if(r[0]==="needs_you"&&r[8]!=="agreed")meta.push(r[8]);
699
+ d.innerHTML='<div class="qr1"><span class="qlane l-'+r[0]+'">'+LANES[r[0]][0]+' '+LANES[r[0]][1]+'</span><span class="qpk">'+esc(r[1])+'</span><span class="qtid">'+esc(r[2])+'</span><span class="qmeta">'+esc(meta.join(" \u00b7 "))+'</span></div><div class="qtt">'+esc(r[3])+'</div>';
700
+ qlist.appendChild(d);
701
+ });
702
+ if(!qlist.children.length){qlist.innerHTML='<div class="qrow" style="color:var(--muted)">no open tasks in this lane</div>';}
703
+ }
704
+ qfilters.addEventListener("click",function(e){
705
+ var b=e.target.closest(".ft");if(!b)return;
706
+ [].forEach.call(qfilters.querySelectorAll(".ft"),function(x){x.setAttribute("aria-pressed",x===b?"true":"false");});
707
+ render();
708
+ });
709
+ qsort.addEventListener("change",render);
710
+ render();
711
+ })();
712
+ var filters=document.getElementById("filters");
713
+ var search=document.getElementById("search");
714
+ var emptyMsg=document.getElementById("empty");
715
+ var activeF="all";
716
+ function applyFilters(){
717
+ var q=(search.value||"").trim().toLowerCase();
718
+ var shown=0;
719
+ [].forEach.call(list.querySelectorAll(".row"),function(r){
720
+ var bucketOk=activeF==="all"||r.getAttribute("data-bucket")===activeF||(activeF==="blocked"&&r.getAttribute("data-blocked")==="1");
721
+ var qOk=!q||(r.getAttribute("data-search")||"").indexOf(q)!==-1;
722
+ var show=bucketOk&&qOk;r.style.display=show?"":"none";if(show)shown++;
723
+ });
724
+ emptyMsg.textContent=shown?"":(q?'no packets match "'+q+'"':"no packets");
725
+ emptyMsg.style.display=shown?"none":"block";
726
+ }
727
+ filters.addEventListener("click",function(e){
728
+ var b=e.target.closest(".ft");if(!b)return;
729
+ activeF=b.getAttribute("data-f");
730
+ [].forEach.call(filters.querySelectorAll(".ft"),function(x){x.setAttribute("aria-pressed",x===b?"true":"false");});
731
+ applyFilters();
732
+ });
733
+ search.addEventListener("input",applyFilters);
734
+ document.addEventListener("keydown",function(e){
735
+ if(e.key==="/"&&document.activeElement!==search){e.preventDefault();search.focus();return;}
736
+ if(e.key==="Escape"){
737
+ if(document.activeElement===search&&search.value){search.value="";applyFilters();return;}
738
+ [].forEach.call(list.querySelectorAll(".row"),function(r){r.classList.remove("open");r.setAttribute("aria-expanded","false");r.querySelector(".det").style.maxHeight="0";});
739
+ }
740
+ });
741
+ </script>
742
+ </body>
743
+ </html>
744
+ `;