failproofai 1.0.4-beta.1 → 1.0.4-beta.4

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 (348) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +5 -5
  3. package/.next/standalone/.next/prerender-manifest.json +5 -5
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
  26. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/run/route_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/status/route_client-reference-manifest.js +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/login-request/route_client-reference-manifest.js +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-verify/route_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  38. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
  42. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +37 -4
  43. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  44. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/index.html +1 -1
  47. package/.next/standalone/.next/server/app/index.rsc +15 -15
  48. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  49. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  50. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  52. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  53. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
  56. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  57. package/.next/standalone/.next/server/app/policies/page.js +1 -1
  58. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
  63. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
  66. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  67. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  73. package/.next/standalone/.next/server/app/projects/page.js +1 -1
  74. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +2 -2
  77. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  78. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/chunks/{[externals]__1zndo1i._.js → [externals]__0v1hz15._.js} +1 -1
  81. package/.next/standalone/.next/server/chunks/{[externals]__0_gzk8q._.js → [externals]__1nb206a._.js} +1 -1
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cuho4x._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
  90. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/_0otft92._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +14 -21
  95. package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
  99. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
  100. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +1 -1
  102. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00-s7h8._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
  105. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  109. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wvusu0._.js → [root-of-the-server]__0n_lxhg._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__16gamwq._.js → [root-of-the-server]__0yl9wb5._.js} +2 -2
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__190won8._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1sc84bb._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zl_r0t._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +1 -1
  121. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  122. package/.next/standalone/.next/server/chunks/ssr/{_1byck5v._.js → _0guxnh-._.js} +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/_0wso5d3._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +61 -168
  128. package/.next/standalone/.next/server/chunks/ssr/{_08f0jd2._.js → _1if9ixi._.js} +1 -1
  129. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -69
  132. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
  137. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
  138. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  141. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  142. package/.next/standalone/.next/server/pages/404.html +1 -1
  143. package/.next/standalone/.next/server/pages/500.html +1 -1
  144. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  145. package/.next/standalone/.next/server/server-reference-manifest.json +54 -21
  146. package/.next/standalone/.next/static/chunks/04o5zfl7p5lhx.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{29-iqnp9v_axt.css → 0jpd8dv2vk930.css} +1 -1
  148. package/.next/standalone/.next/static/chunks/{1po3lirgk2riw.js → 0po8n4tyotjf2.js} +1 -1
  149. package/.next/standalone/.next/static/chunks/0qmsunv04x4na.css +2 -0
  150. package/.next/standalone/.next/static/chunks/0qx7gav1c2_vu.js +1 -0
  151. package/.next/standalone/.next/static/chunks/17tfki9x5rd_7.js +1 -0
  152. package/.next/standalone/.next/static/chunks/{1_bxotmf9qngs.js → 1i-i0pxg9q_8y.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/{1_2jsatfuthrw.js → 2bxr2_2h4kaqq.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{01hvwwrhvbzyh.js → 2zafgs90r_leq.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/{00gv0v91mycm-.js → 323cyb3a2s06y.js} +1 -1
  156. package/.next/standalone/.next/static/chunks/{0qn9khv0oo9ex.js → 364cqbpeg1y5i.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{2bvwsyu6x__ia.js → 38dqur43qthrl.js} +1 -1
  158. package/.next/standalone/.next/static/chunks/{3_--3ibia6ba1.js → 3l36d73kj18zw.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/{2k9f4tyv04809.css → 3uyhmm01g64k4.css} +1 -0
  160. package/.next/standalone/.next/static/chunks/{2bnf_4pgyk1rq.js → 40lpt2o2b283e.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/42gwu2bsz4ske.js +6 -0
  162. package/.next/standalone/.next/static/chunks/{turbopack-18qf4jodtft6b.js → turbopack-3s3c-u_u_l0u9.js} +1 -1
  163. package/.next/standalone/.opencode/plugins/failproofai.mjs +51 -27
  164. package/.next/standalone/app/actions/get-leaks.ts +109 -0
  165. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +140 -34
  166. package/.next/standalone/app/audit/_components/audit-poster.tsx +29 -10
  167. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +6 -1
  168. package/.next/standalone/app/audit/_components/empty-state.tsx +4 -1
  169. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +19 -5
  170. package/.next/standalone/app/audit/_components/leak-section.tsx +192 -0
  171. package/.next/standalone/app/audit/_components/share-templates.ts +110 -44
  172. package/.next/standalone/app/audit/audit-styles.css +132 -1
  173. package/.next/standalone/app/project/[name]/page.tsx +4 -24
  174. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +26 -3
  175. package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +31 -8
  176. package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +1 -0
  177. package/.next/standalone/fp-cloud-cli/pyproject.toml +9 -2
  178. package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +54 -0
  179. package/.next/standalone/fp-cloud-cli/uv.lock +9 -12
  180. package/.next/standalone/lib/auth/api-server-client.ts +28 -0
  181. package/.next/standalone/lib/claude-sessions.ts +101 -27
  182. package/.next/standalone/lib/cli-registry.ts +1 -33
  183. package/.next/standalone/lib/download-session.ts +0 -37
  184. package/.next/standalone/lib/projects.ts +3 -27
  185. package/.next/standalone/lib/sqlite-reader.ts +78 -1
  186. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  187. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  188. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
  189. package/.next/standalone/node_modules/next/dist/experimental/testmode/fetch.js +10 -2
  190. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  191. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +2 -2
  192. package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +3 -1
  193. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  194. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  195. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  196. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +2 -2
  197. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  198. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  199. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  200. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  201. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  202. package/.next/standalone/node_modules/next/package.json +11 -11
  203. package/.next/standalone/package.json +15 -14
  204. package/.next/standalone/sdk/python/CHANGELOG.md +155 -0
  205. package/.next/standalone/sdk/python/README.md +8 -0
  206. package/.next/standalone/sdk/python/examples/evaluator_worker.py +121 -0
  207. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
  208. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +101 -0
  209. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__main__.py +49 -0
  210. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/_sandbox_runner.py +63 -0
  211. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +404 -0
  212. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/client.py +299 -0
  213. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/protocol.py +754 -0
  214. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +936 -0
  215. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/source.py +684 -0
  216. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/README.md +28 -0
  217. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/contract.json +252 -0
  218. package/.next/standalone/sdk/python/tests/test_evaluator_authoring.py +124 -0
  219. package/.next/standalone/sdk/python/tests/test_evaluator_client.py +253 -0
  220. package/.next/standalone/sdk/python/tests/test_evaluator_example.py +35 -0
  221. package/.next/standalone/sdk/python/tests/test_evaluator_http_e2e.py +636 -0
  222. package/.next/standalone/sdk/python/tests/test_evaluator_main.py +47 -0
  223. package/.next/standalone/sdk/python/tests/test_evaluator_protocol.py +246 -0
  224. package/.next/standalone/sdk/python/tests/test_evaluator_review_fixes.py +164 -0
  225. package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +1103 -0
  226. package/.next/standalone/sdk/python/tests/test_evaluator_source.py +430 -0
  227. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +17 -0
  228. package/.next/standalone/sdk/python/uv.lock +18 -18
  229. package/.next/standalone/server.js +1 -1
  230. package/README.md +19 -95
  231. package/bin/failproofai.mjs +8 -25
  232. package/dist/cli.mjs +20091 -25864
  233. package/dist/worker.mjs +4881 -6361
  234. package/lib/auth/api-server-client.ts +28 -0
  235. package/lib/claude-sessions.ts +101 -27
  236. package/lib/cli-registry.ts +1 -33
  237. package/lib/download-session.ts +0 -37
  238. package/lib/projects.ts +3 -27
  239. package/lib/sqlite-reader.ts +78 -1
  240. package/package.json +15 -14
  241. package/pi-extension/index.ts +61 -8
  242. package/scripts/changelog-open.py +115 -0
  243. package/src/audit/cache.ts +6 -0
  244. package/src/audit/cli-adapters/claude.ts +13 -0
  245. package/src/audit/cli-adapters/index.ts +0 -24
  246. package/src/audit/cli-login.ts +4 -1
  247. package/src/audit/cli.ts +164 -5
  248. package/src/audit/desktop-notify.ts +420 -0
  249. package/src/audit/harm-report.ts +101 -0
  250. package/src/audit/index.ts +284 -15
  251. package/src/audit/leak-fingerprint.ts +200 -0
  252. package/src/audit/leak-notice.ts +259 -0
  253. package/src/audit/leak-record.ts +232 -0
  254. package/src/audit/leak-scan.ts +268 -0
  255. package/src/audit/leak-store.ts +265 -0
  256. package/src/audit/macos-notifier.ts +310 -0
  257. package/src/audit/redact-example.ts +322 -15
  258. package/src/audit/report-harm.ts +12 -1
  259. package/src/audit/report.ts +6 -3
  260. package/src/audit/schedule-cli.ts +37 -0
  261. package/src/audit/scoring.ts +49 -0
  262. package/src/audit/types.ts +47 -0
  263. package/src/hooks/builtin-policies.ts +71 -8
  264. package/src/hooks/configure-wizard.ts +20 -0
  265. package/src/hooks/enforcement-capability.ts +0 -97
  266. package/src/hooks/fp-config.ts +71 -11
  267. package/src/hooks/fp-home.ts +27 -0
  268. package/src/hooks/handler.ts +84 -62
  269. package/src/hooks/harness-cli.ts +1 -3
  270. package/src/hooks/integrations.ts +63 -769
  271. package/src/hooks/normalize-cli-payload.ts +0 -126
  272. package/src/hooks/notice.ts +191 -0
  273. package/src/hooks/policy-evaluator.ts +6 -297
  274. package/src/hooks/resolve-transcript-path.ts +0 -10
  275. package/src/hooks/tool-name-canonicalize.ts +0 -64
  276. package/src/hooks/types.ts +2 -533
  277. package/src/hooks/uninstall-cli.ts +15 -0
  278. package/.next/standalone/.grok/hooks/failproofai.json +0 -172
  279. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07hor7h._.js +0 -5
  280. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0elsuli._.js +0 -3
  281. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gnj_qa._.js +0 -3
  282. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljuzm_._.js +0 -3
  283. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w23bpg._.js +0 -3
  284. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0x5c7sh._.js +0 -3
  285. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yik9_w._.js +0 -3
  286. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1jg6s9m._.js +0 -3
  287. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1s6qci9._.js +0 -3
  288. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xfm05z._.js +0 -3
  289. package/.next/standalone/.next/server/chunks/_0tdy_6l._.js +0 -3
  290. package/.next/standalone/.next/server/chunks/lib_grok-projects_ts_1i2epl_._.js +0 -3
  291. package/.next/standalone/.next/server/chunks/lib_qwen-projects_ts_0wu_a8e._.js +0 -3
  292. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_4gcz8._.js +0 -3
  293. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d--wmy._.js +0 -3
  294. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qqrlf_._.js +0 -3
  295. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s11fx1._.js +0 -3
  296. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bbk-si._.js +0 -3
  297. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1c3ybxr._.js +0 -3
  298. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1izi7zn._.js +0 -3
  299. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__21bj7uw._.js +0 -3
  300. package/.next/standalone/.next/server/chunks/ssr/_00z5uuq._.js +0 -3
  301. package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +0 -3
  302. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +0 -3
  303. package/.next/standalone/.next/server/chunks/ssr/lib_cline-projects_ts_13_hbdd._.js +0 -3
  304. package/.next/standalone/.next/server/chunks/ssr/lib_grok-projects_ts_0rc-d0o._.js +0 -3
  305. package/.next/standalone/.next/server/chunks/ssr/lib_ori-projects_ts_04tofqo._.js +0 -3
  306. package/.next/standalone/.next/server/chunks/ssr/lib_qwen-projects_ts_0w3i9o7._.js +0 -3
  307. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +0 -3
  308. package/.next/standalone/.next/static/chunks/0hzmxwb2pvk5p.js +0 -6
  309. package/.next/standalone/.next/static/chunks/0j884dwnwtyrt.js +0 -1
  310. package/.next/standalone/.next/static/chunks/0mr9dg3bnloa1.js +0 -1
  311. package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +0 -1
  312. package/.next/standalone/.next/static/chunks/3gf1jg-5m-u1t.css +0 -2
  313. package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +0 -1
  314. package/.next/standalone/.next/static/chunks/3ws7eljkq25wg.js +0 -69
  315. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +0 -1
  316. package/.next/standalone/.qwen/settings.json +0 -251
  317. package/.next/standalone/assets/logos/cline.png +0 -0
  318. package/.next/standalone/assets/logos/grok-dark.svg +0 -1
  319. package/.next/standalone/assets/logos/grok-light.svg +0 -1
  320. package/.next/standalone/assets/logos/ori-dark.svg +0 -1
  321. package/.next/standalone/assets/logos/ori-light.svg +0 -1
  322. package/.next/standalone/assets/logos/qwen.svg +0 -1
  323. package/.next/standalone/lib/cline-projects.ts +0 -155
  324. package/.next/standalone/lib/cline-sessions.ts +0 -268
  325. package/.next/standalone/lib/grok-projects.ts +0 -123
  326. package/.next/standalone/lib/grok-sessions.ts +0 -355
  327. package/.next/standalone/lib/ori-projects.ts +0 -141
  328. package/.next/standalone/lib/ori-sessions.ts +0 -221
  329. package/.next/standalone/lib/qwen-projects.ts +0 -106
  330. package/.next/standalone/lib/qwen-sessions.ts +0 -313
  331. package/lib/cline-projects.ts +0 -155
  332. package/lib/cline-sessions.ts +0 -268
  333. package/lib/grok-projects.ts +0 -123
  334. package/lib/grok-sessions.ts +0 -355
  335. package/lib/ori-projects.ts +0 -141
  336. package/lib/ori-sessions.ts +0 -221
  337. package/lib/qwen-projects.ts +0 -106
  338. package/lib/qwen-sessions.ts +0 -313
  339. package/src/audit/cli-adapters/cline.ts +0 -50
  340. package/src/audit/cli-adapters/grok.ts +0 -70
  341. package/src/audit/cli-adapters/ori.ts +0 -55
  342. package/src/audit/cli-adapters/qwen.ts +0 -71
  343. package/src/hooks/batch-expand.ts +0 -380
  344. package/src/hooks/batch-fanout.ts +0 -142
  345. package/src/hooks/risk-patterns.ts +0 -26
  346. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_buildManifest.js +0 -0
  347. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_clientMiddlewareManifest.js +0 -0
  348. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_ssgManifest.js +0 -0
@@ -18,7 +18,7 @@
18
18
  // • src/hooks/integrations.ts (buildOpenCodePluginShim production template)
19
19
  // When #337 landed, this dev shim drifted and `block-read-outside-cwd`
20
20
  // silently no-op'd on every opencode `read` call inside this repo.
21
- import { spawnSync } from "node:child_process";
21
+ import { spawn } from "node:child_process";
22
22
  import { resolveDevSpawn } from "../../scripts/dev-hook.mjs";
23
23
 
24
24
  const BUS_EVENT_MAP = {
@@ -70,34 +70,58 @@ function canonicalizeToolInput(canonicalToolName, args) {
70
70
  return out;
71
71
  }
72
72
 
73
- function runFailproofai(eventName, payload, directory) {
74
- // Shared with the JSON configs' launcher: locates bun across PATH, ~/.bun/bin,
75
- // $BUN_INSTALL, Homebrew and every nvm version dir. A bare spawnSync("bun")
76
- // dies with ENOENT the moment bun is off the hook's PATH.
77
- const spawn = resolveDevSpawn();
78
- if (!spawn) {
73
+ /**
74
+ * Run failproofai for one event WITHOUT blocking opencode's event loop.
75
+ *
76
+ * This was `spawnSync`, and opencode loads the plugin in-process in the TUI —
77
+ * so every hook froze rendering and input for the subprocess's full duration,
78
+ * with a 60s ceiling. The verdicts are unchanged: every caller already `await`s
79
+ * `applyDecision`, so the deny still lands before the tool runs. What changes
80
+ * is that the wait is now a promise rather than a blocked thread, and the TUI
81
+ * keeps painting while it happens.
82
+ *
83
+ * Fail-open on spawn error, and on the timeout — a policy that never ran must
84
+ * not read as a deny.
85
+ */
86
+ async function runFailproofai(eventName, payload, directory) {
87
+ const resolved = resolveDevSpawn();
88
+ if (!resolved) {
79
89
  process.stderr.write(
80
90
  "[failproofai-dev] bun not found — opencode policies are NOT enforcing. " +
81
91
  "Install bun: curl -fsSL https://bun.sh/install | bash\n",
82
92
  );
83
93
  return { exitCode: 0, stdout: "", stderr: "" };
84
94
  }
85
- const r = spawnSync(spawn.cmd, [...spawn.args, "--hook", eventName, "--cli", "opencode"], {
86
- input: JSON.stringify(payload),
87
- encoding: "utf8",
88
- timeout: 60_000,
89
- cwd: directory,
95
+ const cmd = resolved.cmd;
96
+ const args = [...resolved.args, "--hook", eventName, "--cli", "opencode"];
97
+ return await new Promise((resolveRun) => {
98
+ let child;
99
+ try {
100
+ child = spawn(cmd, args, { cwd: directory });
101
+ } catch {
102
+ resolveRun({ exitCode: 0, stdout: "", stderr: "" });
103
+ return;
104
+ }
105
+ let stdout = "";
106
+ let stderr = "";
107
+ let settled = false;
108
+ const finish = (exitCode) => {
109
+ if (settled) return;
110
+ settled = true;
111
+ clearTimeout(timer);
112
+ resolveRun({ exitCode, stdout, stderr });
113
+ };
114
+ const timer = setTimeout(() => {
115
+ try { child.kill("SIGKILL"); } catch { /* already gone */ }
116
+ finish(0);
117
+ }, 60_000);
118
+ child.stdout.on("data", (d) => { stdout += d; });
119
+ child.stderr.on("data", (d) => { stderr += d; });
120
+ child.on("error", () => finish(0));
121
+ child.on("close", (code) => finish(code ?? 0));
122
+ child.stdin.on("error", () => {});
123
+ child.stdin.end(JSON.stringify(payload));
90
124
  });
91
- // `r.status` is null when the spawn never ran (ENOENT) or the child was
92
- // killed. Coercing that to 0 reports "allowed" for a policy that never got
93
- // to run, so say so on stderr rather than failing open in silence.
94
- if (r.status === null) {
95
- process.stderr.write(
96
- `[failproofai-dev] ${eventName} hook did not run (${r.error?.code ?? r.signal ?? "unknown"}) — not enforcing\n`,
97
- );
98
- return { exitCode: 0, stdout: "", stderr: "" };
99
- }
100
- return { exitCode: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
101
125
  }
102
126
 
103
127
  async function applyDecision(result, ctx, eventName) {
@@ -150,7 +174,7 @@ export default async function failproofaiPlugin({ client, directory }) {
150
174
  }
151
175
  }
152
176
  if (!prompt) prompt = (info.text || info.content || props.text || "").toString();
153
- const r = runFailproofai("UserPromptSubmit", {
177
+ const r = await runFailproofai("UserPromptSubmit", {
154
178
  session_id: sessionID, cwd: directory, hook_event_name: "UserPromptSubmit", prompt,
155
179
  }, directory);
156
180
  await applyDecision(r, { client, sessionID }, "UserPromptSubmit");
@@ -160,7 +184,7 @@ export default async function failproofaiPlugin({ client, directory }) {
160
184
  if (!claudeEvent) return;
161
185
  const props = event.properties || {};
162
186
  const sessionID = props.sessionID || (props.session && props.session.id) || props.id;
163
- const r = runFailproofai(claudeEvent, {
187
+ const r = await runFailproofai(claudeEvent, {
164
188
  session_id: sessionID, cwd: directory, hook_event_name: claudeEvent,
165
189
  }, directory);
166
190
  await applyDecision(r, { client, sessionID }, claudeEvent);
@@ -168,7 +192,7 @@ export default async function failproofaiPlugin({ client, directory }) {
168
192
 
169
193
  "tool.execute.before": async (input, output) => {
170
194
  const canonicalTool = canonicalizeTool(input.tool);
171
- const r = runFailproofai("PreToolUse", {
195
+ const r = await runFailproofai("PreToolUse", {
172
196
  session_id: input.sessionID,
173
197
  cwd: directory,
174
198
  tool_name: canonicalTool,
@@ -180,7 +204,7 @@ export default async function failproofaiPlugin({ client, directory }) {
180
204
 
181
205
  "tool.execute.after": async (input, output) => {
182
206
  const canonicalTool = canonicalizeTool(input.tool);
183
- const r = runFailproofai("PostToolUse", {
207
+ const r = await runFailproofai("PostToolUse", {
184
208
  session_id: input.sessionID,
185
209
  cwd: directory,
186
210
  tool_name: canonicalTool,
@@ -193,7 +217,7 @@ export default async function failproofaiPlugin({ client, directory }) {
193
217
 
194
218
  "permission.ask": async (input, output) => {
195
219
  const canonicalTool = canonicalizeTool(input.tool);
196
- const r = runFailproofai("PermissionRequest", {
220
+ const r = await runFailproofai("PermissionRequest", {
197
221
  session_id: input.sessionID,
198
222
  cwd: directory,
199
223
  tool_name: canonicalTool || input.command || "permission",
@@ -0,0 +1,109 @@
1
+ "use server";
2
+
3
+ import {
4
+ activeFindings,
5
+ dismissFinding,
6
+ markLeakReportViewed,
7
+ readLeakRecord,
8
+ } from "@/src/audit/leak-store";
9
+ import { readConfig, updateConfig } from "@/src/hooks/fp-config";
10
+
11
+ /**
12
+ * One credential, flattened for the report table.
13
+ *
14
+ * A separate shape from `LeakFinding` on purpose: this crosses into a client
15
+ * component, so it carries only what is drawn. The finding's raw sightings, its
16
+ * rule and its salt stay on the server side of the boundary — not because any
17
+ * of them is a secret (the record holds no secret by construction), but because
18
+ * a display type that mirrors the storage type drifts into rendering whatever
19
+ * gets added to storage next.
20
+ */
21
+ export interface LeakRow {
22
+ id: string;
23
+ /** WHAT — `ghp_••••••••4f2a`. Never the value; there is no value to send. */
24
+ display: string;
25
+ label: string;
26
+ length: number;
27
+ /** False means no console to revoke at — the advice changes completely. */
28
+ attributed: boolean;
29
+ /** The identifier it was assigned to, when there was one. */
30
+ name: string | null;
31
+ /** WHO — the harness whose transcript carried it. */
32
+ cli: string;
33
+ /** WHERE — home-shortened project path. */
34
+ project: string;
35
+ /** WHEN — ISO, formatted client-side so it lands in the reader's timezone. */
36
+ lastSeen: string;
37
+ firstSeen: string;
38
+ /** HOW — "read from ~/…/.env". */
39
+ mechanism: string;
40
+ /** WHY IT MATTERS — an input the agent sent can be denied next time; a
41
+ * result it received cannot be un-received. */
42
+ direction: "input" | "result";
43
+ occurrences: number;
44
+ sessions: number;
45
+ }
46
+
47
+ export interface LeaksPayload {
48
+ rows: LeakRow[];
49
+ /** Whether this machine may raise a desktop notification. Drawn as a toggle
50
+ * beside the table, because the table is where somebody decides the banner
51
+ * was worth it or not. */
52
+ notify: boolean;
53
+ }
54
+
55
+ export async function getLeaksAction(): Promise<LeaksPayload> {
56
+ const findings = activeFindings(readLeakRecord());
57
+ // Opening the report is the one signal worth silencing the in-CLI notice on.
58
+ // Recorded here rather than on a button, because reading it is the action —
59
+ // there is nothing further for the user to do to say "I have seen this".
60
+ markLeakReportViewed();
61
+ const rows: LeakRow[] = findings.map((f) => {
62
+ // The most recent sighting: where the key is now, not where it debuted.
63
+ const seen = f.sightings?.[f.sightings.length - 1];
64
+ return {
65
+ id: f.id,
66
+ display: f.fingerprint?.display ?? "[credential]",
67
+ label: f.fingerprint?.label ?? "secret",
68
+ length: f.fingerprint?.length ?? 0,
69
+ attributed: f.fingerprint?.attributed === true,
70
+ name: f.name,
71
+ cli: seen?.cli ?? "unknown",
72
+ project: seen?.cwd ?? "unknown",
73
+ lastSeen: f.lastSeen,
74
+ firstSeen: f.firstSeen,
75
+ mechanism: seen?.mechanism?.summary ?? "seen in a transcript",
76
+ direction: seen?.mechanism?.direction ?? "result",
77
+ occurrences: f.occurrences,
78
+ sessions: new Set((f.sightings ?? []).map((s) => s.sessionId)).size,
79
+ };
80
+ });
81
+ rows.sort((a, b) => Date.parse(b.lastSeen) - Date.parse(a.lastSeen));
82
+ return { rows, notify: readConfig().audit.notify };
83
+ }
84
+
85
+ /**
86
+ * Mark one finding as not-a-secret.
87
+ *
88
+ * The finding is RETAINED, not deleted — deleting it would let the next scan
89
+ * rediscover the same value and alert again, which is the one outcome that
90
+ * teaches a user the dismiss button does not work.
91
+ */
92
+ export async function dismissLeakAction(id: string): Promise<boolean> {
93
+ return dismissFinding(id);
94
+ }
95
+
96
+ /**
97
+ * Turn the desktop banner on or off.
98
+ *
99
+ * Writes the same `audit.notify` key `failproofai config` writes and the daemon's
100
+ * audit child reads, so the dashboard and the CLI cannot disagree about it.
101
+ */
102
+ export async function setLeakNotifyAction(notify: boolean): Promise<boolean> {
103
+ try {
104
+ updateConfig({ audit: { notify } });
105
+ return true;
106
+ } catch {
107
+ return false;
108
+ }
109
+ }
@@ -3,8 +3,8 @@
3
3
  /**
4
4
  * Top-level client wrapper for /audit.
5
5
  *
6
- * Composes the calm personality report: classify the agent into one of
7
- * 8 archetypes, derive a score, and render the 5-section flow:
6
+ * The old five-section personality report is SWITCHED OFF and being rebuilt
7
+ * bottom-up around leak detection. What it used to compose:
8
8
  *
9
9
  * 01 AuditPoster — single-screen shareable poster
10
10
  * 02 StrengthsSection — what it's great at
@@ -12,23 +12,36 @@
12
12
  * 04 HowToImproveSection — install / configure
13
13
  * 05 ComeBackBetterSection — spread the audit (invite)
14
14
  *
15
+ * Every one of those components, and the score / persona / strengths / findings
16
+ * modules behind them, is intact and still unit-tested — only the renders and
17
+ * the imports here are commented out, so restoring any of it is deleting a pair
18
+ * of comment markers. See the header of `src/audit/scoring.ts` for why.
19
+ *
20
+ * What this page renders now is `AuditReportPlaceholder`: the scan's own
21
+ * numbers. The scan, the 12-CLI transcript reader and the emailed digest all
22
+ * still run.
23
+ *
15
24
  * Empty / running states fall back to EmptyState and RunProgress.
16
25
  */
17
26
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
18
27
  import { getAuditResultAction } from "@/app/actions/get-audit-result";
19
28
  import type { AuditResult, RunAuditOptions } from "@/src/audit/types";
20
- import { classifyAgent } from "@/src/audit/archetypes";
21
- import { deriveScore, gradeFor, projectedScore } from "@/src/audit/scoring";
22
- import { deriveStrengths } from "@/src/audit/strengths";
23
- import { deriveFindings } from "@/src/audit/findings";
29
+ // import { classifyAgent } from "@/src/audit/archetypes";
30
+ // Score switched off — see the header of `src/audit/scoring.ts`.
31
+ // import { deriveScore, gradeFor, projectedScore } from "@/src/audit/scoring";
32
+ // import { deriveStrengths } from "@/src/audit/strengths";
33
+ // import { deriveFindings } from "@/src/audit/findings";
24
34
  import { usePostHog } from "@/contexts/PostHogContext";
25
35
 
26
- import { AuditPoster } from "./audit-poster";
27
- import { StrengthsSection } from "./strengths-section";
28
- import { QuirksSection } from "./quirks-section";
29
- import { HowToImproveSection } from "./how-to-improve-section";
30
- import { ComeBackBetterSection } from "./come-back-better-section";
36
+ // The old report's five sections — switched off, not deleted. Each component
37
+ // file and its tests are untouched; only this render is commented out.
38
+ // import { AuditPoster } from "./audit-poster";
39
+ // import { StrengthsSection } from "./strengths-section";
40
+ // import { QuirksSection } from "./quirks-section";
41
+ // import { HowToImproveSection } from "./how-to-improve-section";
42
+ // import { ComeBackBetterSection } from "./come-back-better-section";
31
43
  import { ReportFooter } from "./report-footer";
44
+ import { LeakSection } from "./leak-section";
32
45
  import { EmptyState } from "./empty-state";
33
46
  import { RunProgress } from "./run-progress";
34
47
  import { AuditProgressStrip, type RerunStatus } from "./audit-progress-strip";
@@ -46,7 +59,9 @@ type Initial =
46
59
  /** Tag passed to the shared `startRerun()` handler so PostHog can tell
47
60
  * whether the click came from the bottom return-section button or the
48
61
  * empty-state CTA. */
49
- export type RerunSource = "return_section" | "empty_state";
62
+ // `scan_header` is the button in section 01 — the only re-scan control the
63
+ // page has now that the old report's sections are switched off.
64
+ export type RerunSource = "return_section" | "empty_state" | "scan_header";
50
65
 
51
66
  interface Props {
52
67
  initial: Initial;
@@ -59,6 +74,9 @@ interface Props {
59
74
  totalCatalogSize: number;
60
75
  }
61
76
 
77
+ /* Switched off with the persona layer — this named the project that seeded the
78
+ archetype classifier and the leaderboard row. Restore alongside
79
+ `classifyAgent` in this file.
62
80
  function inferProjectName(result: AuditResult, override?: string): string {
63
81
  if (override && override.trim()) return override;
64
82
  // Pick the cwd that appears in the most examples — proxy for "your
@@ -81,6 +99,7 @@ function inferProjectName(result: AuditResult, override?: string): string {
81
99
  if (segs.length >= 2) return `${segs[segs.length - 2]} / ${segs[segs.length - 1]}`;
82
100
  return segs[segs.length - 1] ?? "your agent";
83
101
  }
102
+ */
84
103
 
85
104
  export function AuditDashboard({ initial, projectFromUrl, totalCatalogSize }: Props) {
86
105
  const [cache, setCache] = useState<Initial>(initial);
@@ -279,16 +298,22 @@ function MainReport({
279
298
  onDismissRerun,
280
299
  }: MainReportProps) {
281
300
  const { capture } = usePostHog();
282
- const project = useMemo(() => inferProjectName(result, projectFromUrl), [result, projectFromUrl]);
301
+ // Only fed the persona classifier, which is switched off with it.
302
+ // const project = useMemo(() => inferProjectName(result, projectFromUrl), [result, projectFromUrl]);
283
303
  // Seed classification with the project name so the behaviour fingerprint
284
304
  // (used for tie-breaks + copy variants) is stable per project.
285
- const classification = useMemo(() => classifyAgent(result, project), [result, project]);
286
- const score = useMemo(() => deriveScore(result), [result]);
287
- const projected = useMemo(() => projectedScore(result, score), [result, score]);
288
- const grade = gradeFor(score);
289
- const projectedGrade = gradeFor(projected);
290
- const strengths = useMemo(() => deriveStrengths(result), [result]);
291
- const findings = useMemo(() => deriveFindings(result), [result]);
305
+ // Persona classification is switched off with the rest of the old report. Its
306
+ // last live use was two telemetry properties describing a persona the product
307
+ // no longer shows anyone.
308
+ // const classification = useMemo(() => classifyAgent(result, project), [result, project]);
309
+ // Score switched off — see the header of `src/audit/scoring.ts`. The four
310
+ // functions are intact and still unit-tested; nothing calls them.
311
+ // const score = useMemo(() => deriveScore(result), [result]);
312
+ // const projected = useMemo(() => projectedScore(result, score), [result, score]);
313
+ // const grade = gradeFor(score);
314
+ // const projectedGrade = gradeFor(projected);
315
+ // const strengths = useMemo(() => deriveStrengths(result), [result]);
316
+ // const findings = useMemo(() => deriveFindings(result), [result]);
292
317
 
293
318
  // One pass over result.results: detectors triggered + missing prescribed
294
319
  // policies. Both feed PostHog instrumentation; `missing` also feeds the
@@ -310,10 +335,13 @@ function MainReport({
310
335
  if (dashboardViewedRef.current) return;
311
336
  dashboardViewedRef.current = true;
312
337
  capture("audit_dashboard_viewed", {
313
- score,
314
- grade,
315
- archetype: classification.archetype,
316
- secondary: classification.secondary ?? null,
338
+ // Score switched off — see `src/audit/scoring.ts`. `missing` is the
339
+ // prescription's denominator and is unaffected, so the copy→install
340
+ // funnel keeps both of its ends.
341
+ // score,
342
+ // grade,
343
+ // archetype: classification.archetype,
344
+ // secondary: classification.secondary ?? null,
317
345
  missing,
318
346
  transcripts_scanned: result.transcripts.scanned,
319
347
  results_count: result.results.length,
@@ -321,10 +349,6 @@ function MainReport({
321
349
  });
322
350
  }, [
323
351
  capture,
324
- score,
325
- grade,
326
- classification.archetype,
327
- classification.secondary,
328
352
  missing,
329
353
  result.transcripts.scanned,
330
354
  result.results.length,
@@ -332,19 +356,43 @@ function MainReport({
332
356
  ]);
333
357
 
334
358
  /** Poster ref — captured to PNG by the poster's share buttons. */
335
- const posterRef = useRef<HTMLDivElement>(null);
359
+ // const posterRef = useRef<HTMLDivElement>(null);
336
360
 
337
361
  return (
338
362
  <div className="app">
339
363
  <AuditProgressStrip status={rerunStatus} onDismiss={onDismissRerun} />
340
364
  <div className="app-shell">
341
365
  <div className="report">
366
+ <AuditReportPlaceholder
367
+ transcripts={result.transcripts.scanned}
368
+ events={result.eventsScanned ?? 0}
369
+ projects={result.projectsScanned.length}
370
+ isRunning={isRunning || rerunStatus.kind === "running"}
371
+ onRerun={() => onRerun("scan_header")}
372
+ />
373
+ {/* The rebuilt report. It reads the leak record rather than this
374
+ scan's result, deliberately: a credential found last month and
375
+ not touched by today's transcripts is still a credential to
376
+ rotate, and a report that showed only the current scan's findings
377
+ would go quiet on exactly those. */}
378
+ <LeakSection />
379
+ {/* The whole old report is switched off — the persona poster, the
380
+ strengths and quirks sections, the punch-list with its install-all
381
+ funnel, and the invite section. It is being rebuilt bottom-up
382
+ around leak detection; see the header of `src/audit/scoring.ts`
383
+ for the reasoning and `~/Desktop/failproofai-leak-detection-
384
+ verdict-2026-09-07.md` for the measurements behind it.
385
+
386
+ Every component below is intact and still unit-tested — only the
387
+ render is commented — so restoring any one of them is deleting a
388
+ pair of comment markers.
389
+
342
390
  <AuditPoster
343
391
  ref={posterRef}
344
392
  archetypeKey={classification.archetype}
345
393
  seed={classification.variantSeed}
346
- score={score}
347
- grade={grade}
394
+ // score={score}
395
+ // grade={grade}
348
396
  missing={missing}
349
397
  auditedAt={cachedAt ?? new Date().toISOString()}
350
398
  />
@@ -352,10 +400,11 @@ function MainReport({
352
400
  <QuirksSection findings={findings} />
353
401
  <HowToImproveSection
354
402
  result={result}
355
- projected={projected}
356
- projectedGrade={projectedGrade}
403
+ // projected={projected}
404
+ // projectedGrade={projectedGrade}
357
405
  />
358
- <ComeBackBetterSection score={score} />
406
+ <ComeBackBetterSection />
407
+ */}
359
408
  </div>
360
409
  <ReportFooter cachedAt={cachedAt} />
361
410
  </div>
@@ -363,6 +412,63 @@ function MainReport({
363
412
  );
364
413
  }
365
414
 
415
+ /**
416
+ * What `/audit` shows while the report is being rebuilt.
417
+ *
418
+ * Deliberately states the scan's own numbers rather than nothing at all: the
419
+ * scan still runs, still walks every transcript across all 12 CLIs, and still
420
+ * feeds the emailed digest. Saying so is the difference between "this page is
421
+ * under construction" and "this product is broken".
422
+ */
423
+ function AuditReportPlaceholder(
424
+ {
425
+ transcripts,
426
+ events,
427
+ projects,
428
+ isRunning,
429
+ onRerun,
430
+ }: {
431
+ transcripts: number;
432
+ events: number;
433
+ projects: number;
434
+ isRunning: boolean;
435
+ onRerun: () => void;
436
+ },
437
+ ) {
438
+ const n = (x: number) => x.toLocaleString();
439
+ return (
440
+ <section className="audit-sec" data-screen-label="01 Scan">
441
+ <div className="audit-sec-head">
442
+ <span className="audit-sec-eyebrow">
443
+ <span className="ix">01</span>{"// scan"}
444
+ </span>
445
+ {/* The re-scan control. It lived in the old report's sections and went
446
+ dormant when they were commented out, which left the page with no way
447
+ to run an audit again at all — you had to go back to the terminal. */}
448
+ <button
449
+ type="button"
450
+ className="scan-rerun"
451
+ onClick={onRerun}
452
+ disabled={isRunning}
453
+ >
454
+ {isRunning ? "scanning…" : "run again"}
455
+ </button>
456
+ </div>
457
+ <h2 className="audit-sec-title">scan complete</h2>
458
+ <div className="audit-sec-sub">
459
+ {n(events)} tool call{events === 1 ? "" : "s"} across {n(transcripts)}{" "}
460
+ transcript{transcripts === 1 ? "" : "s"}
461
+ {projects > 0 ? ` · ${n(projects)} project${projects === 1 ? "" : "s"}` : ""}
462
+ </div>
463
+ <div className="audit-sec-sub">
464
+ {"// the report is being rebuilt around leak detection. the scan, the"}
465
+ <br />
466
+ {"// 12-CLI transcript reader and the emailed digest are unaffected."}
467
+ </div>
468
+ </section>
469
+ );
470
+ }
471
+
366
472
  interface ShellEmptyProps {
367
473
  running: boolean;
368
474
  mode?: "no-cache" | "zero-sessions";
@@ -24,7 +24,12 @@
24
24
  import React, { forwardRef, useMemo, useState } from "react";
25
25
  import { pickArchetypeVariant, type ArchetypeKey } from "@/src/audit/archetypes";
26
26
  import { type Grade } from "@/src/audit/scoring";
27
- import { getArchetypeRarityPct } from "@/src/audit/social-proof";
27
+ // Archetype rarity switched off — the percentages are eight hardcoded integers
28
+ // ("Seeded with snapshot values; swap for live aggregates once that pipeline
29
+ // lands"), and this is the ONLY surface that rendered them: baked by
30
+ // html-to-image into the PNG people post publicly. A fabricated population
31
+ // statistic on a shared card is a claim we cannot support.
32
+ // import { getArchetypeRarityPct } from "@/src/audit/social-proof";
28
33
  import { copyOrDownloadCard, downloadCard, shareCardNative, shareCardToastMessage } from "@/lib/share-card";
29
34
  import { toast } from "@/app/components/toast";
30
35
  import { usePostHog } from "@/contexts/PostHogContext";
@@ -45,8 +50,11 @@ interface Props {
45
50
  archetypeKey: ArchetypeKey;
46
51
  /** Stable seed for variant selection (project name is the natural fit). */
47
52
  seed: string;
48
- score: number;
49
- grade: Grade;
53
+ /** The score is switched off (see `src/audit/scoring.ts`). Kept as optional
54
+ * props rather than removed, so restoring is un-commenting rather than
55
+ * re-threading: the dashboard simply stops passing them. */
56
+ score?: number;
57
+ grade?: Grade;
50
58
  /** Count of unenabled prescribed policies — passed to the share-text
51
59
  * templates, not rendered on the poster itself. */
52
60
  missing: number;
@@ -63,7 +71,7 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
63
71
  () => pickArchetypeVariant(archetypeKey, seed),
64
72
  [archetypeKey, seed],
65
73
  );
66
- const rarityPct = getArchetypeRarityPct(archetypeKey);
74
+ // const rarityPct = getArchetypeRarityPct(archetypeKey);
67
75
  const indexLabel = String(archetype.index).padStart(2, "0");
68
76
  const auditedDate = useMemo(() => formatAuditedDate(auditedAt), [auditedAt]);
69
77
 
@@ -134,8 +142,11 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
134
142
  }
135
143
  };
136
144
 
145
+ // Score switched off — the filename is keyed on the archetype instead, which
146
+ // is what the card now actually shows. Original:
147
+ // `failproofai-${channel}-${grade.toLowerCase()}-${score}.png`
137
148
  const filenameFor = (channel: "x" | "linkedin" | "download") =>
138
- `failproofai-${channel}-${grade.toLowerCase()}-${score}.png`;
149
+ `failproofai-${channel}-${archetypeKey}.png`;
139
150
 
140
151
  const handleShare = async (channel: "x" | "linkedin" | "download") => {
141
152
  if (busy) return;
@@ -143,8 +154,11 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
143
154
  capture("audit_card_share_clicked", {
144
155
  channel,
145
156
  source: "poster",
146
- score,
147
- grade,
157
+ // Score switched off — see `src/audit/scoring.ts`. Note for whoever reads
158
+ // the funnel: these two properties stop appearing from this release, so a
159
+ // dashboard keyed on them breaks rather than reading zero.
160
+ // score,
161
+ // grade,
148
162
  missing_policies: missing,
149
163
  });
150
164
  try {
@@ -174,10 +188,10 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
174
188
  }
175
189
 
176
190
  const shareCtx: ShareCtx = {
177
- score,
178
191
  arch: archetype.name.toLowerCase(),
179
- grade,
180
192
  missing,
193
+ // score,
194
+ // grade,
181
195
  };
182
196
  const shareText = channel === "x"
183
197
  ? pickTemplate(X_TEMPLATES, seed, shareCtx)
@@ -248,6 +262,7 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
248
262
  </React.Fragment>
249
263
  ))}
250
264
  </div>
265
+ {/* Rarity switched off — see the import above.
251
266
  {typeof rarityPct === "number" && (
252
267
  <div className="persona-rarity">
253
268
  <span className="lbl">{"// only"}</span>{" "}
@@ -255,13 +270,17 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
255
270
  <span className="lbl">of agents are this archetype</span>
256
271
  </div>
257
272
  )}
273
+ */}
258
274
  </div>
259
275
 
260
- {/* Score block — heroic number, centered in the card */}
276
+ {/* Score block — heroic number, centered in the card. Switched off;
277
+ see `src/audit/scoring.ts` for why. The archetype is the card's
278
+ headline now.
261
279
  <div className="poster-score">
262
280
  <span className="score-n">{score}</span>
263
281
  <span className="score-of">/100</span>
264
282
  </div>
283
+ */}
265
284
  </div>
266
285
 
267
286
  <footer className="poster-foot">
@@ -21,7 +21,12 @@ import { AuthDialog, type AuthedUser } from "./auth-dialog";
21
21
  import { InviteDialog } from "./invite-dialog";
22
22
 
23
23
  interface Props {
24
- /** Current audit score (0–100), forwarded into the invite email body. */
24
+ /** Current audit score (0–100), forwarded into the invite email body.
25
+ *
26
+ * Always undefined while the score is switched off (see
27
+ * `src/audit/scoring.ts`) — the dashboard no longer passes it. Nothing
28
+ * breaks: `/api/audit/invite` already treats it as optional and simply
29
+ * leaves it out of the email body when it is not a finite number. */
25
30
  score?: number;
26
31
  }
27
32
 
@@ -74,7 +74,10 @@ export function EmptyState({ mode, running, onStarted, onCompleted }: Props) {
74
74
  <p className="empty-sub">
75
75
  we&apos;ll walk every transcript across your installed CLIs — Claude Code,
76
76
  Codex, Copilot, Cursor, OpenCode, Pi — and count every wasteful
77
- or risky action. you&apos;ll get a tier, a score, and a punch-list.
77
+ {/* Was "a tier, a score, and a punch-list" — the tier was never
78
+ rendered and the score is switched off, so this promised two
79
+ things the page did not show. */}
80
+ or risky action. you&apos;ll get your agent&apos;s archetype and a punch-list.
78
81
  </p>
79
82
 
80
83
  <div className="empty-actions">