@wenathlan/devthink 2.0.12 → 2.0.14

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 (444) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/accounts.ts +2087 -0
  3. package/agent.ts +2331 -0
  4. package/antigravity.ts +2139 -0
  5. package/apifreeze.ts +4146 -0
  6. package/auth.ts +642 -0
  7. package/background.ts +16837 -0
  8. package/bridge.ts +2080 -0
  9. package/build.ts +54 -0
  10. package/bun.ts +26 -0
  11. package/capture.ts +1614 -0
  12. package/checksecrets.ts +72 -0
  13. package/cjs.ts +9 -0
  14. package/cli.ts +3500 -0
  15. package/commands.ts +1101 -0
  16. package/companion.ts +450 -0
  17. package/compatibility.ts +37 -0
  18. package/config.ts +3137 -0
  19. package/constants.ts +943 -0
  20. package/core.ts +1473 -0
  21. package/crossbrowser.ts +1359 -0
  22. package/dashdone.ts +605 -0
  23. package/data.ts +943 -0
  24. package/database.ts +133 -0
  25. package/debug.ts +850 -0
  26. package/deno.ts +27 -0
  27. package/devthink.ts +707 -0
  28. package/dist/accounts.d.ts +409 -0
  29. package/dist/agent.d.ts +837 -0
  30. package/dist/antigravity.d.ts +380 -0
  31. package/dist/apifreeze.d.ts +113 -0
  32. package/dist/auth.d.ts +224 -0
  33. package/{bridge.d.ts → dist/bridge.d.ts} +1 -1
  34. package/dist/capture.d.ts +595 -0
  35. package/dist/cjs.d.ts +6 -0
  36. package/{cli.js → dist/cli.js} +2 -2
  37. package/dist/commands.d.ts +310 -0
  38. package/dist/compatibility.d.ts +17 -0
  39. package/dist/config.d.ts +1853 -0
  40. package/dist/constants.d.ts +637 -0
  41. package/dist/core.d.ts +361 -0
  42. package/dist/data.d.ts +330 -0
  43. package/dist/database.d.ts +24 -0
  44. package/dist/debug.d.ts +270 -0
  45. package/dist/engine.d.ts +74 -0
  46. package/dist/environments.d.ts +244 -0
  47. package/dist/evidence.d.ts +277 -0
  48. package/dist/export.d.ts +311 -0
  49. package/dist/fingerprint.d.ts +594 -0
  50. package/dist/flow.d.ts +300 -0
  51. package/dist/gates.d.ts +103 -0
  52. package/dist/identity.d.ts +51 -0
  53. package/dist/ids.d.ts +7 -0
  54. package/dist/llm.d.ts +652 -0
  55. package/dist/models.d.ts +1807 -0
  56. package/dist/net.d.ts +325 -0
  57. package/dist/neutral.d.ts +41 -0
  58. package/dist/oauth.d.ts +703 -0
  59. package/dist/page.d.ts +1166 -0
  60. package/dist/perf.d.ts +481 -0
  61. package/dist/plan.d.ts +134 -0
  62. package/dist/project.d.ts +395 -0
  63. package/dist/providers.d.ts +37 -0
  64. package/dist/quota.d.ts +453 -0
  65. package/dist/recovery.d.ts +621 -0
  66. package/dist/request.d.ts +1492 -0
  67. package/dist/run.d.ts +779 -0
  68. package/dist/runtime.d.ts +212 -0
  69. package/dist/search.d.ts +375 -0
  70. package/dist/security.d.ts +534 -0
  71. package/dist/serve.d.ts +136 -0
  72. package/dist/session.d.ts +296 -0
  73. package/dist/storage.d.ts +53 -0
  74. package/dist/streaming.d.ts +1119 -0
  75. package/dist/swarm.d.ts +328 -0
  76. package/dist/tools.d.ts +170 -0
  77. package/dist/types.d.ts +8357 -0
  78. package/dist/utils.d.ts +82 -0
  79. package/dist/version.d.ts +7 -0
  80. package/dist/versionregistry.d.ts +406 -0
  81. package/dist/views.d.ts +579 -0
  82. package/dist/workbenchsession.d.ts +69 -0
  83. package/dist/workflow.d.ts +649 -0
  84. package/docs/readme.md +92 -0
  85. package/docs/schemas/antigravity.json +192 -0
  86. package/engine.ts +1524 -0
  87. package/environments.ts +1005 -0
  88. package/evidence.ts +643 -0
  89. package/export.ts +889 -0
  90. package/fingerprint.ts +1458 -0
  91. package/flow.ts +1190 -0
  92. package/gates.ts +307 -0
  93. package/gateway.ts +34 -0
  94. package/hardening.ts +191 -0
  95. package/headless.ts +369 -0
  96. package/http.ts +2110 -0
  97. package/identity.ts +246 -0
  98. package/ids.ts +26 -0
  99. package/index.ts +369 -0
  100. package/llm.ts +2682 -0
  101. package/mcp.ts +1835 -0
  102. package/{memory.js → memory.ts} +3834 -2310
  103. package/models.ts +3805 -0
  104. package/modes.ts +43 -0
  105. package/net.ts +1214 -0
  106. package/neutral.ts +362 -0
  107. package/node.ts +26 -0
  108. package/oauth.ts +2216 -0
  109. package/offscreen.ts +170 -0
  110. package/pack.ts +956 -0
  111. package/package.json +175 -82
  112. package/page.ts +7824 -0
  113. package/pagebridge.ts +1116 -0
  114. package/perf.ts +957 -0
  115. package/plan.ts +672 -0
  116. package/plugin.ts +76 -0
  117. package/{policy.js → policy.ts} +5073 -4679
  118. package/prisma.config.ts +27 -0
  119. package/progress.ts +1308 -0
  120. package/project.ts +1291 -0
  121. package/protocol.ts +4187 -0
  122. package/providers.ts +246 -0
  123. package/quota.ts +1887 -0
  124. package/recovery.ts +2433 -0
  125. package/request.ts +4482 -0
  126. package/run.ts +2135 -0
  127. package/runtime.ts +526 -0
  128. package/search.ts +1760 -0
  129. package/security.ts +1673 -0
  130. package/serve.ts +419 -0
  131. package/server.ts +5538 -0
  132. package/session.ts +1162 -0
  133. package/storage.ts +182 -0
  134. package/streaming.ts +4101 -0
  135. package/swarm.ts +884 -0
  136. package/sync.ts +56 -0
  137. package/tests/agentcert.test.ts +128 -0
  138. package/tests/agentfleet.test.ts +214 -0
  139. package/tests/agentreview.test.ts +241 -0
  140. package/tests/agentstream.test.ts +335 -0
  141. package/tests/agentwork.test.ts +356 -0
  142. package/tests/apifreeze.test.ts +290 -0
  143. package/tests/apimap.test.ts +112 -0
  144. package/tests/approvalgate.test.ts +175 -0
  145. package/tests/artifactmanifest.test.ts +204 -0
  146. package/tests/attentionfeed.test.ts +163 -0
  147. package/tests/backgroundruns.test.ts +100 -0
  148. package/tests/batchscheduling.test.ts +131 -0
  149. package/tests/blackboard.test.ts +238 -0
  150. package/tests/bridge.test.ts +1088 -0
  151. package/tests/browsercoverage.test.ts +134 -0
  152. package/tests/browserpolyfills.test.ts +124 -0
  153. package/tests/build.mjs +1260 -0
  154. package/tests/clientauth.test.ts +223 -0
  155. package/tests/clitools.test.ts +578 -0
  156. package/tests/confirmgates.test.ts +190 -0
  157. package/tests/containerpack.test.ts +143 -0
  158. package/tests/controlflow.test.ts +2120 -0
  159. package/tests/coordination.test.ts +345 -0
  160. package/tests/copyscreen.test.ts +139 -0
  161. package/tests/core.test.ts +417 -0
  162. package/tests/costcert.test.ts +95 -0
  163. package/tests/crossbrowser.test.ts +115 -0
  164. package/tests/cspaudit.test.ts +79 -0
  165. package/tests/dashdone.test.ts +615 -0
  166. package/tests/datagrid.test.ts +137 -0
  167. package/tests/doccheck.test.ts +107 -0
  168. package/tests/dombatch.test.ts +109 -0
  169. package/tests/emulation.test.ts +282 -0
  170. package/tests/environments.test.ts +259 -0
  171. package/tests/evidenceviews.test.ts +88 -0
  172. package/tests/exporttools.test.ts +185 -0
  173. package/tests/firefoxprep.test.ts +134 -0
  174. package/tests/flowlibrary.test.ts +242 -0
  175. package/tests/flowrun.test.ts +303 -0
  176. package/tests/forensics.test.ts +439 -0
  177. package/tests/gateway.test.ts +939 -0
  178. package/tests/headlesslib.test.ts +171 -0
  179. package/tests/httpstream.test.ts +277 -0
  180. package/tests/immutablelog.test.ts +157 -0
  181. package/tests/inboundguard.test.ts +177 -0
  182. package/tests/lazyload.test.ts +72 -0
  183. package/tests/librarymodes.test.ts +623 -0
  184. package/tests/llm.test.ts +696 -0
  185. package/tests/manifest.test.ts +226 -0
  186. package/tests/maskinputs.test.ts +150 -0
  187. package/tests/matrixverify.test.ts +111 -0
  188. package/tests/mavenpack.test.ts +75 -0
  189. package/tests/mcpserver.test.ts +704 -0
  190. package/tests/memory.test.ts +4155 -0
  191. package/tests/memorycare.test.ts +230 -0
  192. package/tests/migrateplan.test.ts +349 -0
  193. package/tests/minimization.test.ts +414 -0
  194. package/tests/modelroute.test.ts +112 -0
  195. package/tests/multiagent.test.ts +440 -0
  196. package/tests/nativebridge.test.ts +807 -0
  197. package/tests/navigation.test.ts +284 -0
  198. package/tests/nugetpack.test.ts +83 -0
  199. package/tests/orchestration.test.ts +449 -0
  200. package/tests/originpolicy.test.ts +405 -0
  201. package/tests/outputcompare.test.ts +97 -0
  202. package/tests/package.test.ts +91 -0
  203. package/tests/pagebridge.test.ts +5909 -0
  204. package/tests/pentest.test.ts +75 -0
  205. package/tests/perfrecords.test.ts +257 -0
  206. package/tests/permdiff.test.ts +85 -0
  207. package/tests/phishguard.test.ts +128 -0
  208. package/tests/pickerviews.test.ts +155 -0
  209. package/tests/pipelines.test.ts +381 -0
  210. package/tests/planlint.test.ts +240 -0
  211. package/tests/planreview.test.ts +383 -0
  212. package/tests/platformtargets.test.ts +56 -0
  213. package/tests/policy.test.ts +8284 -0
  214. package/tests/portability.test.ts +96 -0
  215. package/tests/progress.test.ts +1036 -0
  216. package/tests/promptlibrary.test.ts +116 -0
  217. package/tests/protocol.test.ts +4793 -0
  218. package/tests/publishingpipeline.test.ts +314 -0
  219. package/tests/quickactions.test.ts +164 -0
  220. package/tests/readiness.test.ts +78 -0
  221. package/tests/recipes.test.ts +163 -0
  222. package/tests/redactshots.test.ts +216 -0
  223. package/tests/registernvidiakeys.ts +51 -0
  224. package/tests/relayserve.test.ts +304 -0
  225. package/tests/releasenotes.test.ts +276 -0
  226. package/tests/resilience.test.ts +467 -0
  227. package/tests/resourceaware.test.ts +187 -0
  228. package/tests/runbudget.test.ts +125 -0
  229. package/tests/runhistory.test.ts +299 -0
  230. package/tests/runreplay.test.ts +113 -0
  231. package/tests/runresume.test.ts +184 -0
  232. package/tests/runstate.test.ts +288 -0
  233. package/tests/runtimeadapters.test.ts +251 -0
  234. package/tests/safariskeleton.test.ts +115 -0
  235. package/tests/sandboxframe.test.ts +196 -0
  236. package/tests/sbom.test.ts +105 -0
  237. package/tests/secretvault.test.ts +188 -0
  238. package/tests/selcache.test.ts +111 -0
  239. package/tests/sessioninterface.test.ts +791 -0
  240. package/tests/sessions.test.ts +591 -0
  241. package/tests/siteprefs.test.ts +228 -0
  242. package/tests/snapshotdelta.test.ts +165 -0
  243. package/tests/statusviews.test.ts +237 -0
  244. package/tests/stepmeter.test.ts +200 -0
  245. package/tests/streamparse.test.ts +163 -0
  246. package/tests/stylepruning.test.ts +90 -0
  247. package/tests/surfaces.test.ts +282 -0
  248. package/tests/sweep.test.ts +78 -0
  249. package/tests/syncbridge.test.ts +164 -0
  250. package/tests/taskqueue.test.ts +206 -0
  251. package/tests/toolcalls.test.ts +336 -0
  252. package/tests/toolcatalog.test.ts +201 -0
  253. package/tests/torture-agent.test.ts +706 -0
  254. package/tests/torture-auth.test.ts +461 -0
  255. package/tests/torture-commands.test.ts +895 -0
  256. package/tests/torture-data.test.ts +923 -0
  257. package/tests/torture-environments.test.ts +285 -0
  258. package/tests/torture-export.test.ts +263 -0
  259. package/tests/torture-http.test.ts +1379 -0
  260. package/tests/torture-llm.test.ts +808 -0
  261. package/tests/torture-memory.test.ts +1084 -0
  262. package/tests/torture-plan.test.ts +829 -0
  263. package/tests/torture-run.test.ts +626 -0
  264. package/tests/torture-session.test.ts +928 -0
  265. package/tests/torture-swarm.test.ts +1245 -0
  266. package/tests/torture-tools.test.ts +964 -0
  267. package/tests/torture-workflow.test.ts +2354 -0
  268. package/tests/tourviews.test.ts +52 -0
  269. package/tests/transparency.test.ts +147 -0
  270. package/tests/trigger.test.ts +941 -0
  271. package/tests/v1sunset.test.ts +428 -0
  272. package/tests/virtlist.test.ts +94 -0
  273. package/tests/vision.test.ts +540 -0
  274. package/tests/vsixpack.test.ts +145 -0
  275. package/tests/webapi.test.ts +523 -0
  276. package/tests/workflow.test.ts +1785 -0
  277. package/tests/workfloweditor.test.ts +759 -0
  278. package/tests/xpipack.test.ts +128 -0
  279. package/tools.ts +734 -0
  280. package/types.ts +9908 -0
  281. package/umd.ts +9 -0
  282. package/utils.ts +249 -0
  283. package/version.ts +8 -0
  284. package/versionregistry.ts +1310 -0
  285. package/views.ts +2153 -0
  286. package/vitest.config.ts +12 -0
  287. package/web/App.tsx +78 -0
  288. package/web/README.md +47 -0
  289. package/web/capacitor.config.ts +46 -0
  290. package/web/const.ts +19 -0
  291. package/web/control.shell.tsx +66 -0
  292. package/web/dashboardpage.ts +554 -0
  293. package/web/db.ts +493 -0
  294. package/web/design.html +541 -0
  295. package/web/gatewayview/Gateway.tsx +345 -0
  296. package/web/gatewayview/config.ts +1042 -0
  297. package/web/gatewayview/definition.ts +174 -0
  298. package/web/icons.ts +15 -0
  299. package/{manifest.json → web/manifest.json} +1 -1
  300. package/web/optionspage.ts +691 -0
  301. package/web/popup.ts +1651 -0
  302. package/web/schema.prisma +176 -0
  303. package/web/sidepanel.ts +7941 -0
  304. package/web/sitemanifest.json +13 -0
  305. package/web/terminal-ui.tsx +622 -0
  306. package/web/transparencypage.ts +541 -0
  307. package/web/vite.config.ts +39 -0
  308. package/web/workspace.ts +23 -0
  309. package/workbenchmemory.ts +104 -0
  310. package/workbenchsession.ts +304 -0
  311. package/workflow.ts +4495 -0
  312. package/bridge.d.ts.map +0 -1
  313. package/bridge.js +0 -1877
  314. package/bun.d.ts.map +0 -1
  315. package/bun.js +0 -52329
  316. package/caps/background.json +0 -712
  317. package/caps/cli.json +0 -360
  318. package/caps/library.json +0 -3611
  319. package/caps/mcp.json +0 -126
  320. package/caps/pagebridge.json +0 -377
  321. package/caps/popup.json +0 -51
  322. package/caps/sidepanel.json +0 -522
  323. package/checksums.txt +0 -142
  324. package/cli.d.ts.map +0 -1
  325. package/companion.d.ts.map +0 -1
  326. package/companion.js +0 -389
  327. package/crossbrowser.d.ts.map +0 -1
  328. package/crossbrowser.js +0 -1191
  329. package/dashdone.d.ts.map +0 -1
  330. package/dashdone.js +0 -368
  331. package/deno.d.ts.map +0 -1
  332. package/deno.js +0 -52325
  333. package/devthink.umd.js +0 -52346
  334. package/fixtures/bun.ts +0 -7
  335. package/fixtures/cjs.js +0 -6
  336. package/fixtures/deno.ts +0 -6
  337. package/fixtures/example-org-pagestate.json +0 -22
  338. package/fixtures/importers/automa-workflow.json +0 -18
  339. package/fixtures/importers/selenium-side.json +0 -20
  340. package/fixtures/importers/tabular-plan.csv +0 -7
  341. package/fixtures/importers/uivision-macro.json +0 -10
  342. package/fixtures/importers/v1-plan.json +0 -13
  343. package/fixtures/mcp-client.mjs +0 -14
  344. package/fixtures/pages/checkout.html +0 -192
  345. package/fixtures/pages/dashboard.html +0 -110
  346. package/fixtures/pages/feed.html +0 -113
  347. package/fixtures/pages/product-grid.html +0 -150
  348. package/fixtures/plans/form-inventory-plan.json +0 -11
  349. package/fixtures/plans/release-notes-plan.json +0 -11
  350. package/fixtures/recipes/agentscompete.json +0 -11
  351. package/fixtures/recipes/agentsforms.json +0 -11
  352. package/fixtures/recipes/agentsmonitor.json +0 -11
  353. package/fixtures/recipes/agentsreview.json +0 -29
  354. package/fixtures/recipes/agentswarm.json +0 -11
  355. package/fixtures/recipes/fillcalendar.json +0 -12
  356. package/fixtures/recipes/fillcheckout.json +0 -13
  357. package/fixtures/recipes/filldependentdropdowns.json +0 -12
  358. package/fixtures/recipes/fillfromcsv.json +0 -13
  359. package/fixtures/recipes/fillloginform.json +0 -12
  360. package/fixtures/recipes/fillmultistepwizard.json +0 -13
  361. package/fixtures/recipes/fillregistration.json +0 -12
  362. package/fixtures/recipes/fillsearchfilters.json +0 -12
  363. package/fixtures/recipes/filltypeahead.json +0 -11
  364. package/fixtures/recipes/monitoralerts.json +0 -12
  365. package/fixtures/recipes/monitoravailability.json +0 -11
  366. package/fixtures/recipes/monitornetworkquiet.json +0 -12
  367. package/fixtures/recipes/monitorpagechanges.json +0 -11
  368. package/fixtures/recipes/monitorpricechange.json +0 -11
  369. package/fixtures/recipes/monitorschedule.json +0 -12
  370. package/fixtures/recipes/scrapedynamicprices.json +0 -13
  371. package/fixtures/recipes/scrapeimagegallery.json +0 -12
  372. package/fixtures/recipes/scrapeinfinitefeed.json +0 -13
  373. package/fixtures/recipes/scrapenestedlists.json +0 -12
  374. package/fixtures/recipes/scrapeproductgrid.json +0 -12
  375. package/fixtures/recipes/scrapereviews.json +0 -12
  376. package/fixtures/recipes/scrapesearchresults.json +0 -12
  377. package/fixtures/recipes/scrapetablepagination.json +0 -12
  378. package/fixtures/recipes/testaccessibility.json +0 -34
  379. package/fixtures/recipes/testconsoleerrors.json +0 -12
  380. package/fixtures/recipes/testdeeplinks.json +0 -12
  381. package/fixtures/recipes/testformvalidation.json +0 -13
  382. package/fixtures/recipes/testlayoutshift.json +0 -12
  383. package/fixtures/recipes/testlinkcheck.json +0 -12
  384. package/fixtures/recipes/testloadtiming.json +0 -12
  385. package/fixtures/recipes/testvisualdiff.json +0 -12
  386. package/fixtures/workflows/changelog-digest-workflow.json +0 -13
  387. package/fixtures/workflows/form-survey-workflow.json +0 -13
  388. package/gallery.json +0 -706
  389. package/hardening.d.ts.map +0 -1
  390. package/hardening.js +0 -188
  391. package/headless.d.ts.map +0 -1
  392. package/headless.js +0 -11522
  393. package/http.d.ts.map +0 -1
  394. package/http.js +0 -2165
  395. package/index.cjs +0 -54682
  396. package/index.d.ts.map +0 -1
  397. package/index.js +0 -52310
  398. package/index.neutral.js +0 -46343
  399. package/mcp.d.ts.map +0 -1
  400. package/mcp.js +0 -20515
  401. package/memory.d.ts.map +0 -1
  402. package/nativehost.template.json +0 -10
  403. package/node.cjs +0 -54686
  404. package/node.d.ts.map +0 -1
  405. package/pack.d.ts.map +0 -1
  406. package/pack.js +0 -805
  407. package/policy.d.ts.map +0 -1
  408. package/progress.d.ts.map +0 -1
  409. package/progress.js +0 -645
  410. package/protocol.d.ts.map +0 -1
  411. package/protocol.js +0 -8334
  412. package/schemas/audit.schema.json +0 -226
  413. package/schemas/capability.schema.json +0 -113
  414. package/schemas/envelope.schema.json +0 -162
  415. package/schemas/memory.schema.json +0 -51
  416. package/schemas/observation.schema.json +0 -113
  417. package/schemas/plan.schema.json +0 -208
  418. package/schemas/progress.schema.json +0 -126
  419. package/schemas/proposal.schema.json +0 -68
  420. package/schemas/session.schema.json +0 -82
  421. package/schemas/tool.schema.json +0 -94
  422. package/server.d.ts.map +0 -1
  423. package/server.js +0 -28877
  424. package/umd-example.html +0 -18
  425. package/umd.d.ts.map +0 -1
  426. /package/{bun.d.ts → dist/bun.d.ts} +0 -0
  427. /package/{cli.d.ts → dist/cli.d.ts} +0 -0
  428. /package/{companion.d.ts → dist/companion.d.ts} +0 -0
  429. /package/{crossbrowser.d.ts → dist/crossbrowser.d.ts} +0 -0
  430. /package/{dashdone.d.ts → dist/dashdone.d.ts} +0 -0
  431. /package/{deno.d.ts → dist/deno.d.ts} +0 -0
  432. /package/{hardening.d.ts → dist/hardening.d.ts} +0 -0
  433. /package/{headless.d.ts → dist/headless.d.ts} +0 -0
  434. /package/{http.d.ts → dist/http.d.ts} +0 -0
  435. /package/{index.d.ts → dist/index.d.ts} +0 -0
  436. /package/{mcp.d.ts → dist/mcp.d.ts} +0 -0
  437. /package/{memory.d.ts → dist/memory.d.ts} +0 -0
  438. /package/{node.d.ts → dist/node.d.ts} +0 -0
  439. /package/{pack.d.ts → dist/pack.d.ts} +0 -0
  440. /package/{policy.d.ts → dist/policy.d.ts} +0 -0
  441. /package/{progress.d.ts → dist/progress.d.ts} +0 -0
  442. /package/{protocol.d.ts → dist/protocol.d.ts} +0 -0
  443. /package/{server.d.ts → dist/server.d.ts} +0 -0
  444. /package/{umd.d.ts → dist/umd.d.ts} +0 -0
@@ -0,0 +1,314 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { existsSync } from "node:fs";
4
+ import { readdir, readFile, stat } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ import { describe, expect, it } from "vitest";
7
+ import { promisify } from "node:util";
8
+ import { parseDocument } from "yaml";
9
+ import { artifactchannelof, artifactmanifestcheck, artifactmanifestnameof } from "../pack.js";
10
+
11
+ const execute = promisify(execFile);
12
+
13
+ /** The byte budget of the npm tarball: an engineering bound on the packed unpacked size so the tarball never grows unbounded — the file allowlist keeps the shipped paths honest inside it. */
14
+ const tarballbudget = 80_000_000;
15
+
16
+ describe("the publishing pipeline", () => {
17
+ it("keeps zero version drift across every packaging file", async () => {
18
+ const packagejson = JSON.parse(await readFile("package.json", "utf8"));
19
+ const version = String(packagejson.version);
20
+ /* the extension manifest, the version module and the web and mobile package mirrors */
21
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8"));
22
+ expect(manifest.version).toBe(version);
23
+ const versionmodule = await readFile("version.ts", "utf8");
24
+ expect(versionmodule).toContain(`export const packageversion = "${version}" as const;`);
25
+ const webpackage = JSON.parse(await readFile("web/package.json", "utf8"));
26
+ expect(webpackage.version).toBe(version);
27
+ /* the mobile identity rides web/capacitor.config.ts — the single config design leaves no second version source to drift */
28
+ /* the browser overlays and the vsix overlay live inside the root manifest — the single manifest design of 1.1.93 leaves no second version source to drift */
29
+ expect(manifest.browsers?.firefox?.browser).toBe("firefox");
30
+ expect(manifest.browsers?.safari?.browser).toBe("safari");
31
+ expect(manifest.vsix?.name).toBe("devthink");
32
+ expect(Object.keys(manifest.browsers?.firefox ?? {})).not.toContain("version");
33
+ expect(Object.keys(manifest.browsers?.safari ?? {})).not.toContain("version");
34
+ expect(Object.keys(manifest.vsix ?? {})).not.toContain("version");
35
+ /* the registry envelopes of the merged repository: the maven pom carries the release in its revision property, the nuget csproj and the ruby gemspec beside it */
36
+ const pom = await readFile("pom.xml", "utf8");
37
+ expect(pom).toContain(`<revision>${version}</revision>`);
38
+ expect(pom).toContain("<packaging>jar</packaging>");
39
+ const csproj = await readFile("devthink.csproj", "utf8");
40
+ expect(/<Version>[^<]+<\/Version>/.exec(csproj)?.[0]).toBe(`<Version>${version}</Version>`);
41
+ const gemspec = await readFile("devthink.gemspec", "utf8");
42
+ expect(gemspec).toContain(`"${version}"`);
43
+ /* the java and csharp envelope carriers: the version constants answer the lockstep family the release sync propagates — the file names carry the lowercase technical name, the class names keep the presentation name, and the drift gate watches both stamps every bump (the 2.0.3 release stamped them by hand because no gate watched them) */
44
+ const javacarrier = await readFile("devthink.java", "utf8");
45
+ expect(javacarrier).toContain(`VERSION = "${version}"`);
46
+ const csharpcarrier = await readFile("devthinkcli.cs", "utf8");
47
+ expect(csharpcarrier).toContain(`Version = "${version}"`);
48
+ /* the version stamps of the container build defaults the build args ride */
49
+ const dockerfile = await readFile("Dockerfile", "utf8");
50
+ expect(dockerfile).toContain(`ARG DEVTHINK_VERSION=${version}`);
51
+ /* the changelog section the release notes render from */
52
+ const changelog = await readFile("CHANGELOG.md", "utf8");
53
+ const heading = new RegExp(`^##\\s+${version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`, "m");
54
+ expect(heading.test(changelog)).toBe(true);
55
+ /* the generated release-document family — the readme version line, the release gates table, the runtime version catalog, the release notes document and the popup eyebrow label — stamps through the release metadata synchronization (node tests/release.mjs sync, the restamp the release pass runs), so those carries join the drift family at the restamp instead of a second version source living in the test */
56
+ });
57
+
58
+ it("emits and validates the release workflow controls through the checked test scripts", async () => {
59
+ /* the workflowcheck asserts the channel jobs, the gates and the new release steps; the npmgate asserts the new bundles stay allowed in the tarball */
60
+ const workflowcheck = await execute("node", ["tests/workflowcheck.mjs"]);
61
+ expect(workflowcheck.stdout).toContain("verified");
62
+ const npmgate = await execute("node", ["tests/npmgate.mjs"]);
63
+ expect(npmgate.stdout).toContain('"valid": true');
64
+ });
65
+
66
+ it("coordinates the release lane with the standalone publish workflows of the merged repository", async () => {
67
+ /* the publish-lane coordination of the merged layout: the release workflow owns the assembly, the verification and the draft → verify → publish chain plus the npmjs publication job the npm gate demands (the flat distribution tarball with provenance and the bounded retry), while the standalone publish workflows keep the registry lanes on their release-published triggers — no channel ships through two racing lanes, and the two npm lanes answer idempotently (the first publish wins, the latecomer skips) */
68
+ const release = await readFile(".github/workflows/release.yml", "utf8");
69
+ const workflow = parseDocument(release, { version: "1.2" }).toJS() as { jobs?: Record<string, unknown> };
70
+ expect(Object.keys(workflow.jobs ?? {})).toEqual([
71
+ "auto-tag",
72
+ "metadata",
73
+ "verify",
74
+ "assemble",
75
+ "vsix",
76
+ "firefox",
77
+ "site",
78
+ "sbom",
79
+ "attest",
80
+ "releaseassets",
81
+ "npmjs",
82
+ "githubrelease",
83
+ ]);
84
+ /* the draft → assemble → verify → publish chain the readiness gate walks */
85
+ for (const control of ["--draft", "sha256sum --check SHA256SUMS.txt", "draft=false"])
86
+ expect(release).toContain(control);
87
+ /* the registry lanes the standalone publish workflows own stay release-triggered with their registry controls */
88
+ const lanes: Array<[string, string]> = [
89
+ [".github/workflows/publishgithubnpm.yml", "npm.pkg.github.com"],
90
+ [".github/workflows/publishmaven.yml", "mvn --batch-mode"],
91
+ [".github/workflows/publishnuget.yml", "dotnet nuget push"],
92
+ [".github/workflows/publishrubygems.yml", "gem push devthink-*.gem"],
93
+ [".github/workflows/publishghcr.yml", "ghcr.io"],
94
+ [".github/workflows/publishnpmjs.yml", "registry.npmjs.org"],
95
+ ];
96
+ for (const [lane, control] of lanes) {
97
+ const standalone = await readFile(lane, "utf8");
98
+ expect(standalone).toContain("types: [published]");
99
+ expect(standalone).toContain(control);
100
+ }
101
+ /* the npm channel stays idempotent across its two lanes: both answer the same existence check before any publish */
102
+ expect(release).toContain("npmjs version already exists; skipping publish.");
103
+ const npmjs = await readFile(".github/workflows/publishnpmjs.yml", "utf8");
104
+ expect(npmjs).toContain("already exists; skipping publish.");
105
+ });
106
+
107
+ it("covers every built artifact with names, sizes, checksums and channels in the local artifact manifest", async () => {
108
+ if (!existsSync("dist/checksums.txt"))
109
+ return; /* the validate chain runs the tests before the build; the built-set pass runs on the next pass and in the ci lanes that build first */
110
+ const packagejson = JSON.parse(await readFile("package.json", "utf8"));
111
+ const version = String(packagejson.version);
112
+ const manifest = JSON.parse(await readFile("dist/artifactmanifest.json", "utf8")) as {
113
+ name: string;
114
+ version: string;
115
+ banner: string;
116
+ artifacts: Array<{ name: string; size: number; checksum: string; channels: string[] }>;
117
+ };
118
+ expect(manifest.version).toBe(version);
119
+ expect(manifest.name).toBe("devthink artifact manifest");
120
+ expect(manifest.artifacts.length).toBeGreaterThan(10);
121
+ for (const entry of manifest.artifacts) {
122
+ /* the naming convention keeps every artifact name lowercase without underscores */
123
+ expect(entry.name).toMatch(/^[a-z0-9./-]+$/);
124
+ expect(entry.name).not.toContain("_");
125
+ expect(entry.channels.length).toBeGreaterThan(0);
126
+ expect(entry.channels).toEqual(artifactchannelof(entry.name, version));
127
+ /* the recorded size and sha256 checksum match the file on disk */
128
+ const bytes = await readFile(entry.name);
129
+ expect(entry.size).toBe(bytes.length);
130
+ expect(entry.checksum).toBe(createHash("sha256").update(bytes).digest("hex"));
131
+ }
132
+ /* the manifest covers the artifacts of every channel of the release */
133
+ const names = manifest.artifacts.map((entry) => entry.name);
134
+ expect(names).toContain(`dist/devthink${version}.zip`);
135
+ expect(names).toContain(`dist/devthink-firefox-${version}.xpi`);
136
+ expect(names).toContain(`dist/devthink-safari-${version}.zip`);
137
+ expect(names).toContain(`dist/devthink-vscode-${version}.vsix`);
138
+ expect(names).toContain(`dist/devthink-site-${version}.zip`);
139
+ expect(names).toContain(`dist/devthink-declarations-${version}.zip`);
140
+ /* the manifest never lists itself: a self entry would need its own checksum of itself, so the checksums file covers dist/artifactmanifest.json instead */
141
+ });
142
+
143
+ it("covers every dist artifact the checksums file records with its true sha256 digest", async () => {
144
+ if (!existsSync("dist/checksums.txt"))
145
+ return; /* the validate chain runs the tests before the build; the checksum pass runs on the next pass and in the ci lanes that build first */
146
+ const lines = (await readFile("dist/checksums.txt", "utf8")).split("\n").filter((line) => line.trim() !== "");
147
+ expect(lines.length).toBeGreaterThan(10);
148
+ for (const line of lines) {
149
+ const [digest, name] = line.split(/\s+/);
150
+ expect(digest).toMatch(/^[0-9a-f]{64}$/);
151
+ if (name === "manifest.json")
152
+ continue; /* the identity record of the published key: the build pins the manifest identity digest beside the bundle checksums so the pin rides the generated artifact, never a hard coded source constant */
153
+ if ((name ?? "").startsWith("site/"))
154
+ continue; /* the static site assets carry their own manifest and hashed names, not the reviewed naming rule */
155
+ expect(name ?? "").not.toContain("_");
156
+ const bytes = await readFile(`dist/${name}`);
157
+ expect(createHash("sha256").update(bytes).digest("hex")).toBe(digest);
158
+ }
159
+ /* the publishing pipeline bundles and the manifest itself join the covered set */
160
+ const names = lines.map((line) => line.split(/\s+/)[1] ?? "");
161
+ for (const bundle of ["bridge.js", "crossbrowser.js", "pack.js", "http.js", "artifactmanifest.json"])
162
+ expect(names).toContain(bundle);
163
+ });
164
+
165
+ it("answers the artifact manifest name of the release set", async () => {
166
+ const packagejson = JSON.parse(await readFile("package.json", "utf8"));
167
+ const version = String(packagejson.version);
168
+ expect(artifactmanifestnameof(version)).toBe(`devthink-artifactmanifest-${version}.json`);
169
+ if (!existsSync("dist/artifactmanifest.json"))
170
+ return; /* the validate chain runs the tests before the build; the manifest check pass runs on the next pass and in the ci lanes that build first */
171
+ /* the manifest check catches a drifted built set before the release ships */
172
+ const manifest = JSON.parse(await readFile("dist/artifactmanifest.json", "utf8")) as {
173
+ name: string;
174
+ version: string;
175
+ banner: string;
176
+ artifacts: Array<{ name: string; size: number; checksum: string; channels: string[] }>;
177
+ };
178
+ const files = manifest.artifacts.map((entry) => ({ name: entry.name, checksum: entry.checksum }));
179
+ expect(artifactmanifestcheck({ manifest, files }).ok).toBe(true);
180
+ const drifted = artifactmanifestcheck({ manifest, files: files.slice(0, Math.max(1, files.length - 1)) });
181
+ expect(drifted.ok).toBe(false);
182
+ });
183
+
184
+ it("keeps the npm tarball inside its size budget through the flat staged package", { timeout: 120_000 }, async () => {
185
+ if (!existsSync("dist/checksums.txt"))
186
+ return; /* the validate chain runs the tests before the build; the tarball pass runs on the next pass and in the ci lanes that build first */
187
+ const packagejson = JSON.parse(await readFile("package.json", "utf8"));
188
+ const stagedmanifest = JSON.parse(await readFile("distpackage/package.json", "utf8"));
189
+ const packed = await execute("npm", ["pack", "./distpackage", "--dry-run", "--json"]);
190
+ /* npm eleven answers a json array of tarball entries while npm twelve answers an object keyed by package name: the dry run reads the single entry of this package under either shape so the pinned toolchain majors both verify the tarball */
191
+ const parsed = JSON.parse(packed.stdout) as unknown;
192
+ const entry = (
193
+ Array.isArray(parsed)
194
+ ? parsed[0]
195
+ : (parsed as Record<string, { files: Array<{ path: string; size: number }>; size: number }>)[
196
+ String(stagedmanifest.name)
197
+ ]
198
+ ) as { files: Array<{ path: string; size: number }>; size: number } | undefined;
199
+ if (entry === undefined) throw new Error("The npm pack dry run reported no tarball entry.");
200
+ /* the unpacked size of the tarball stays inside the engineering budget */
201
+ expect(entry.size).toBeLessThan(tarballbudget);
202
+ /* the staged manifest carries no files allowlist: the staging directory IS the package set, so every shipped file must exist in the staged tree and every staged file must ship */
203
+ const stagedwalk = async (relative: string): Promise<string[]> => {
204
+ const found: string[] = [];
205
+ for (const item of await readdir(join("distpackage", relative), { withFileTypes: true })) {
206
+ const child = relative === "" ? item.name : `${relative}/${item.name}`;
207
+ if (item.isDirectory()) found.push(...(await stagedwalk(child)));
208
+ else found.push(child);
209
+ }
210
+ return found;
211
+ };
212
+ const stagedset = new Set(await stagedwalk(""));
213
+ const shippedpaths = entry.files.map((file) => file.path.replace(/^distpackage\//, ""));
214
+ for (const path of shippedpaths) expect(stagedset.has(path)).toBe(true);
215
+ for (const staged of stagedset) expect(shippedpaths).toContain(staged);
216
+ /* the flat organization: no shipped path nests past two directories */
217
+ for (const path of shippedpaths) expect(path.split("/").length).toBeLessThanOrEqual(3);
218
+ /* the surface, the data groups and the identity files ship at the package root */
219
+ for (const artifact of [
220
+ "index.js",
221
+ "index.cjs",
222
+ "index.neutral.js",
223
+ "devthink.umd.js",
224
+ "policy.js",
225
+ "protocol.js",
226
+ "memory.js",
227
+ "progress.js",
228
+ "hardening.js",
229
+ "dashdone.js",
230
+ "node.cjs",
231
+ "bun.js",
232
+ "deno.js",
233
+ "cli.js",
234
+ "headless.js",
235
+ "mcp.js",
236
+ "bridge.js",
237
+ "companion.js",
238
+ "nativehost.template.json",
239
+ "pack.js",
240
+ "http.js",
241
+ "server.js",
242
+ "crossbrowser.js",
243
+ "index.d.ts",
244
+ "umd.d.ts",
245
+ "cli.d.ts",
246
+ "checksums.txt",
247
+ "gallery.json",
248
+ "umd-example.html",
249
+ "manifest.json",
250
+ "README.md",
251
+ "LICENSE",
252
+ "CHANGELOG.md",
253
+ "package.json",
254
+ "caps/background.json",
255
+ "caps/library.json",
256
+ "schemas/plan.schema.json",
257
+ "fixtures/example-org-pagestate.json",
258
+ "fixtures/plans/release-notes-plan.json",
259
+ "fixtures/recipes/scrapeproductgrid.json",
260
+ "fixtures/pages/feed.html",
261
+ "fixtures/importers/v1-plan.json",
262
+ ])
263
+ expect(shippedpaths).toContain(artifact);
264
+ /* the minified variants and the release archives stay out of the package: the release assets own them */
265
+ for (const path of shippedpaths) expect(path.endsWith(".min.js") || path.endsWith(".min.cjs")).toBe(false);
266
+ /* the staged manifest mirrors the repository manifest with the entry paths remapped to the package root */
267
+ expect(stagedmanifest.name).toBe(packagejson.name);
268
+ expect(stagedmanifest.version).toBe(packagejson.version);
269
+ expect(stagedmanifest.main).toBe("./index.js");
270
+ expect(stagedmanifest.types).toBe("./index.d.ts");
271
+ expect(stagedmanifest.bin.devthink).toBe("./cli.js");
272
+ const exporttargets: string[] = [];
273
+ const collecttargets = (node: unknown) => {
274
+ if (typeof node === "string") exporttargets.push(node);
275
+ else if (Array.isArray(node)) node.forEach(collecttargets);
276
+ else if (node !== null && typeof node === "object") Object.values(node).forEach(collecttargets);
277
+ };
278
+ collecttargets(stagedmanifest.exports);
279
+ expect(exporttargets.length).toBeGreaterThan(20);
280
+ for (const target of exporttargets) {
281
+ expect(target.startsWith("./")).toBe(true);
282
+ expect(target.startsWith("./dist/")).toBe(false);
283
+ expect(existsSync(join("distpackage", target.replace(/^\.\//, "")))).toBe(true);
284
+ }
285
+ });
286
+
287
+ it("stamps the built vsix artifact with the manifest version and the consent fields", async () => {
288
+ if (!existsSync(`dist/devthink-vscode-${String(JSON.parse(await readFile("package.json", "utf8")).version)}.vsix`))
289
+ return; /* the validate chain runs the tests before the build; the vsix pass runs on the next pass and in the ci lanes that build first */
290
+ const packagejson = JSON.parse(await readFile("package.json", "utf8"));
291
+ const version = String(packagejson.version);
292
+ const bytes = await readFile(`dist/devthink-vscode-${version}.vsix`);
293
+ /* the vsix is a stored zip: the local file header magic and the end of central directory record both read back */
294
+ expect(bytes.readUInt32LE(0)).toBe(0x04034b50);
295
+ const end = bytes.subarray(bytes.length - 22);
296
+ expect(end.readUInt32LE(0)).toBe(0x06054b50);
297
+ expect(end.readUInt16LE(8)).toBe(6);
298
+ const text = bytes.toString("utf8");
299
+ expect(text).toContain(`"version": "${version}"`);
300
+ expect(text).toContain('"telemetry": "off"');
301
+ expect(text).not.toMatch(/marketplace\.visualstudio\.com|clients2\.google\.com/i);
302
+ });
303
+
304
+ it("keeps the site and declaration artifacts beside the browser builds", async () => {
305
+ if (!existsSync("dist/checksums.txt"))
306
+ return; /* the validate chain runs the tests before the build; the artifact pass runs on the next pass and in the ci lanes that build first */
307
+ const packagejson = JSON.parse(await readFile("package.json", "utf8"));
308
+ const version = String(packagejson.version);
309
+ const site = await stat(`dist/devthink-site-${version}.zip`);
310
+ expect(site.size).toBeGreaterThan(100);
311
+ const declarations = await stat(`dist/devthink-declarations-${version}.zip`);
312
+ expect(declarations.size).toBeGreaterThan(100);
313
+ });
314
+ });
@@ -0,0 +1,164 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ omniboxtasktotaskinput,
4
+ parseomniboxtask,
5
+ parseshortcut,
6
+ quickactioncatalog,
7
+ quickactionsfor,
8
+ shortcutbindingafter,
9
+ shortcutcommandof,
10
+ shortcutdefaults,
11
+ shortcutdispatchable,
12
+ shortcuttext,
13
+ } from "../views.js";
14
+ import { surfacepalette } from "../views.js";
15
+ import { omniboxtaskgate, quickactiongate, shortcutkeygate } from "../policy.js";
16
+
17
+ const now = 1_800_000_000_000;
18
+
19
+ describe("quickactions, shortcutkeys and omniboxtask", () => {
20
+ it("gates the quickactions behind the origin allowlist of the clicked tab", () => {
21
+ const catalog = quickactioncatalog();
22
+ expect(catalog.map((action) => action.id)).toEqual([
23
+ "extractpage",
24
+ "captureshot",
25
+ "runrecent",
26
+ "opendashboardpage",
27
+ ]);
28
+ const granted = ["https://example.com"];
29
+ const permitted = quickactionsfor(catalog, {
30
+ origin: "https://example.com",
31
+ granted,
32
+ sessionactive: true,
33
+ grantedcapabilities: ["activeTab", "storage", "scripting", "sidePanel", "downloads"],
34
+ });
35
+ expect(permitted.map((action) => action.id)).toEqual([
36
+ "extractpage",
37
+ "captureshot",
38
+ "runrecent",
39
+ "opendashboardpage",
40
+ ]);
41
+ const nosession = quickactionsfor(catalog, {
42
+ origin: "https://example.com",
43
+ granted,
44
+ sessionactive: false,
45
+ grantedcapabilities: ["activeTab", "storage", "scripting", "sidePanel", "downloads"],
46
+ });
47
+ expect(nosession.map((action) => action.id)).toEqual(["opendashboardpage"]);
48
+ const ungranted = quickactionsfor(catalog, {
49
+ origin: "https://other.example",
50
+ granted,
51
+ sessionactive: true,
52
+ grantedcapabilities: ["activeTab", "storage", "scripting", "sidePanel", "downloads"],
53
+ });
54
+ expect(ungranted).toHaveLength(0);
55
+ expect(
56
+ quickactiongate({
57
+ action: { command: "starttask", origin: "https://example.com", session: true },
58
+ granted,
59
+ sessionactive: false,
60
+ capabilities: ["activeTab"],
61
+ }).reason,
62
+ ).toMatch(/active browser session/);
63
+ expect(
64
+ quickactiongate({
65
+ action: { command: "captureshot", origin: "https://example.com", permission: "downloads" },
66
+ granted,
67
+ sessionactive: true,
68
+ capabilities: ["activeTab"],
69
+ }).allowed,
70
+ ).toBe(false);
71
+ });
72
+
73
+ it("binds run, pause, resume, cancelrun and the palette to user editable shortcuts", () => {
74
+ const defaults = shortcutdefaults();
75
+ expect(defaults.map((binding) => binding.command)).toEqual([
76
+ "starttask",
77
+ "pauserun",
78
+ "resumerun",
79
+ "cancelrun",
80
+ "commandpalette",
81
+ ]);
82
+ expect(defaults.every((binding) => binding.editable)).toBe(true);
83
+ expect(
84
+ shortcuttext(
85
+ defaults.find((binding) => binding.command === "commandpalette") ?? {
86
+ command: "",
87
+ key: "",
88
+ modifiers: [],
89
+ editable: true,
90
+ surface: "popup",
91
+ },
92
+ ),
93
+ ).toBe("ctrl+.");
94
+ expect(parseshortcut("Ctrl+Shift+P")).toEqual({ key: "p", modifiers: ["ctrl", "shift"] });
95
+ expect(() => parseshortcut("ctrl+")).toThrow(/key/);
96
+ const edited = shortcutbindingafter(defaults, "pauserun", "alt+q");
97
+ expect(edited.find((binding) => binding.command === "pauserun")?.key).toBe("q");
98
+ expect(() => shortcutbindingafter(defaults, "unknowncommand", "alt+q")).toThrow(/know no/);
99
+ });
100
+
101
+ it("matches a pressed key against the bindings with the palette command open from every surface", () => {
102
+ const bindings = shortcutdefaults();
103
+ expect(shortcutcommandof(bindings, { key: ".", modifiers: ["ctrl"], surface: "sidepanel" })).toBe("commandpalette");
104
+ expect(shortcutcommandof(bindings, { key: ".", modifiers: ["ctrl"], surface: "dashboardpage" })).toBe(
105
+ "commandpalette",
106
+ );
107
+ expect(shortcutcommandof(bindings, { key: "p", modifiers: ["ctrl", "shift"], surface: "sidepanel" })).toBe(
108
+ "pauserun",
109
+ );
110
+ expect(shortcutcommandof(bindings, { key: "p", modifiers: ["ctrl", "shift"], surface: "popup" })).toBeUndefined();
111
+ expect(shortcutcommandof(bindings, { key: "z", modifiers: [], surface: "popup" })).toBeUndefined();
112
+ });
113
+
114
+ it("keeps the palette gates intact behind every shortcut dispatch", () => {
115
+ const entries = surfacepalette();
116
+ expect(shortcutdispatchable("starttask", entries, { granted: ["activeTab", "storage"], sessionactive: true })).toBe(
117
+ true,
118
+ );
119
+ expect(
120
+ shortcutdispatchable("cancelrun", entries, { granted: ["activeTab", "storage"], sessionactive: false }),
121
+ ).toBe(false);
122
+ expect(shortcutdispatchable("cancelrun", entries, { granted: ["activeTab", "storage"], sessionactive: true })).toBe(
123
+ true,
124
+ );
125
+ expect(shortcutdispatchable("commandpalette", entries, { granted: [], sessionactive: false })).toBe(true);
126
+ expect(
127
+ shortcutkeygate({
128
+ command: "historysearch",
129
+ palettecommands: entries.map((entry) => entry.action.command),
130
+ granted: ["tabs"],
131
+ sessionactive: true,
132
+ action: { permission: "tabs" },
133
+ }).allowed,
134
+ ).toBe(true);
135
+ expect(
136
+ shortcutkeygate({
137
+ command: "unknowncommand",
138
+ palettecommands: entries.map((entry) => entry.action.command),
139
+ granted: [],
140
+ sessionactive: false,
141
+ }).reason,
142
+ ).toMatch(/commandpalette command/);
143
+ });
144
+
145
+ it("parses the omnibox keyword into a taskinput submission of the proposal flow", () => {
146
+ const submission = parseomniboxtask({
147
+ text: " Collect the pricing table ",
148
+ origin: "https://example.com",
149
+ at: now,
150
+ });
151
+ expect(submission.text).toBe("Collect the pricing table");
152
+ expect(submission.surface).toBe("omnibox");
153
+ expect(() => parseomniboxtask({ text: " ", origin: "https://example.com", at: now })).toThrow(/goal/);
154
+ expect(() => parseomniboxtask({ text: "goal", origin: " ", at: now })).toThrow(/origin/);
155
+ const taskinput = omniboxtasktotaskinput(submission);
156
+ expect(taskinput.origin).toBe("https://example.com");
157
+ expect(taskinput.surface).toBe("omnibox");
158
+ expect(omniboxtaskgate({ text: "goal", origin: "https://example.com", direct: false }).allowed).toBe(true);
159
+ expect(omniboxtaskgate({ text: "goal", origin: "https://example.com", direct: true }).reason).toMatch(
160
+ /never executes a goal directly/,
161
+ );
162
+ expect(omniboxtaskgate({ text: "", origin: "https://example.com", direct: false }).allowed).toBe(false);
163
+ });
164
+ });
@@ -0,0 +1,78 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { describe, expect, it } from "vitest";
4
+ import { runreadinesssuite } from "./readiness.mjs";
5
+
6
+ describe("the release readiness review of the platform release", () => {
7
+ it("answers a verdict row with its evidence link for every gate the review walks", async () => {
8
+ const report = await runreadinesssuite();
9
+ expect(report.verdicts.length).toBeGreaterThanOrEqual(20);
10
+ for (const verdict of report.verdicts) {
11
+ expect(verdict.gate.length).toBeGreaterThan(10);
12
+ expect(typeof verdict.ok).toBe("boolean");
13
+ expect(verdict.evidence.length).toBeGreaterThan(10);
14
+ }
15
+ });
16
+
17
+ it("walks every candidate gate the chain greened and answers the go decision the release stamps", async () => {
18
+ const report = await runreadinesssuite();
19
+ const names = report.verdicts.map((verdict) => verdict.gate);
20
+ for (const expected of [
21
+ "pool audit",
22
+ "api freeze",
23
+ "deprecation window",
24
+ "migrateplan",
25
+ "pentest",
26
+ "csp",
27
+ "permission diff",
28
+ "transparency",
29
+ "agent certification",
30
+ "cost certification",
31
+ "doc check",
32
+ "recipes",
33
+ "sweep",
34
+ "matrix",
35
+ "telemetry",
36
+ "changelog",
37
+ "migration guide",
38
+ "release notes",
39
+ "roadmap",
40
+ "release pipeline",
41
+ "capability manifests",
42
+ ]) {
43
+ expect(names.join("; ")).toContain(expected);
44
+ }
45
+ /* the go assertion runs where the lane recorded the candidate evidence: the validate chain runs the candidate gates before the suite, and the verify workflow runs the standalone readiness step after every gate wrote its artifact; a lane without the recorded artifacts (the bun matrix lane) still verifies the structure and the gate names above */
46
+ if (
47
+ existsSync("tests/artifacts/poolcoverage.json") &&
48
+ existsSync("tests/artifacts/sweep.json") &&
49
+ existsSync("tests/artifacts/telemetryfree.json") &&
50
+ existsSync("tests/artifacts/agentcert.json") &&
51
+ existsSync("tests/artifacts/doccheck.json") &&
52
+ existsSync("tests/artifacts/recipes.json")
53
+ ) {
54
+ expect(report.godecision).toBe("go");
55
+ expect(report.summary.blocked).toBe(0);
56
+ expect(report.summary.ok).toBe(report.summary.gates);
57
+ }
58
+ });
59
+
60
+ it("records the artifact and the go decision review the roadmap publishes", async () => {
61
+ if (!existsSync("tests/artifacts/readiness.json"))
62
+ return; /* the readiness lane runs after the candidate gates recorded their artifacts; the artifact pass covers the recorded state */
63
+ const artifact = JSON.parse(await readFile("tests/artifacts/readiness.json", "utf8")) as {
64
+ verdicts: Array<{ gate: string; ok: boolean }>;
65
+ summary: { gates: number; ok: number; blocked: number };
66
+ godecision: string;
67
+ };
68
+ if (artifact.summary.blocked === 0) {
69
+ expect(artifact.summary.gates).toBe(artifact.summary.ok);
70
+ expect(artifact.godecision).toBe("go");
71
+ for (const verdict of artifact.verdicts) expect(verdict.ok).toBe(true);
72
+ }
73
+ const review = await readFile("docs/readiness.md", "utf8");
74
+ expect(review).toContain("# Release readiness review");
75
+ expect(review).toContain("The go decision");
76
+ expect(review).toContain("| Gate | Verdict | Evidence |");
77
+ });
78
+ });