failproofai 0.0.11-beta.7 → 0.0.11-beta.9

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 (233) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/standalone/.next/build-manifest.json +6 -6
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +6 -0
  7. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
  8. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  19. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  20. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  21. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  27. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  30. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  31. package/.next/standalone/.next/server/app/api/audit/invite/route/app-paths-manifest.json +3 -0
  32. package/.next/standalone/.next/server/app/api/audit/invite/route/build-manifest.json +9 -0
  33. package/.next/standalone/.next/server/app/api/audit/invite/route/server-reference-manifest.json +4 -0
  34. package/.next/standalone/.next/server/app/api/audit/invite/route.js +7 -0
  35. package/.next/standalone/.next/server/app/api/audit/invite/route.js.map +5 -0
  36. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -0
  37. package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +3 -0
  38. package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +1 -1
  41. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +2 -2
  43. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/logout/route.js +2 -2
  45. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +2 -2
  47. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/auth/status/route.js +2 -2
  49. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  52. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  53. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  54. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/app/index.html +1 -1
  57. package/.next/standalone/.next/server/app/index.rsc +15 -15
  58. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  59. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  60. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  61. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  62. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  63. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  64. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  65. package/.next/standalone/.next/server/app/page.js +2 -2
  66. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  69. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  70. package/.next/standalone/.next/server/app/policies/page.js +2 -2
  71. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  74. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  75. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
  76. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  78. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  79. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  80. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  81. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +3 -3
  82. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  85. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  86. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  87. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  89. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_mqemn._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1r1h8v9._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1uatkiv._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1y6gxxb._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_audit_invite_route_actions_0-2n5sy.js +3 -0
  96. package/.next/standalone/.next/server/chunks/node_modules_0-tu4ot._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/node_modules_0ttxbz7._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/node_modules_1bnh1y0._.js +3 -0
  99. package/.next/standalone/.next/server/chunks/node_modules_1epycqa._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/node_modules_1wpdcgo._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +8 -8
  102. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0808sha._.js +4 -0
  106. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1cd25c7._.js → [root-of-the-server]__0e4-6d8._.js} +3 -3
  107. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__08gyb62._.js → [root-of-the-server]__0e446gb._.js} +2 -2
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ehe24g._.js +4 -0
  109. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1-scthx._.js → [root-of-the-server]__0f62vu9._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g253ve._.js +4 -0
  111. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l13qf2._.js → [root-of-the-server]__0k65l27._.js} +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vxf0_g._.js +4 -0
  113. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__08v7sn2._.js → [root-of-the-server]__0wprfyc._.js} +2 -2
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12mcauo._.js +4 -0
  115. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0989_dx._.js → [root-of-the-server]__1e-x7j4._.js} +2 -2
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mt35_w._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pcxxwg._.js +2 -2
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1uvfwgr._.js +4 -0
  119. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/_0il3fl1._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  122. package/.next/standalone/.next/server/chunks/ssr/_1kje4fm._.js +1 -1
  123. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +21 -21
  124. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  126. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0y4a-0q.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  129. package/.next/standalone/.next/server/functions-config-manifest.json +0 -3
  130. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  131. package/.next/standalone/.next/server/pages/404.html +1 -1
  132. package/.next/standalone/.next/server/pages/500.html +1 -1
  133. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  134. package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
  135. package/.next/standalone/.next/static/chunks/{0hmxjseydp6as.js → 07_d165p5h5ys.js} +1 -1
  136. package/.next/standalone/.next/static/chunks/0f7d7hnbh4djs.js +1 -0
  137. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +1 -0
  138. package/.next/standalone/.next/static/chunks/168k-8z6k7e8z.css +1 -0
  139. package/.next/standalone/.next/static/chunks/{3n28elq6kwpy3.js → 1kvadxkgnapyj.js} +1 -1
  140. package/.next/standalone/.next/static/chunks/{0fpw1jzeg66_x.js → 277oc363p56n6.js} +2 -2
  141. package/.next/standalone/.next/static/chunks/28mkxkl_d91-l.js +1 -0
  142. package/.next/standalone/.next/static/chunks/28x7jvo3kxd3u.js +41 -0
  143. package/.next/standalone/.next/static/chunks/29nrs5xs9c4hx.css +2 -0
  144. package/.next/standalone/.next/static/chunks/{41vwoxpkauj1w.js → 29tg7deqmq32l.js} +1 -1
  145. package/.next/standalone/.next/static/chunks/{3w8d8k_dca5rp.js → 2h0dkzyy0vocp.js} +1 -1
  146. package/.next/standalone/.next/static/chunks/{1eq7f419t6ln3.js → 2z42u62k-8-_q.js} +1 -1
  147. package/.next/standalone/.next/static/chunks/{0avgb78edu0i-.js → 3nj6g3xu9uy78.js} +1 -1
  148. package/.next/standalone/.next/static/chunks/{0xkxbaa-oo180.js → 3ty6dhcuogout.js} +1 -1
  149. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
  150. package/.next/standalone/.next/static/chunks/{turbopack-00qy7zfa7m--m.js → turbopack-3lrm4f20fz89b.js} +1 -1
  151. package/.next/standalone/app/api/audit/_state.ts +40 -33
  152. package/.next/standalone/app/api/audit/invite/route.ts +183 -0
  153. package/.next/standalone/app/api/audit/run/route.ts +30 -16
  154. package/.next/standalone/app/api/audit/status/route.ts +1 -0
  155. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +33 -65
  156. package/.next/standalone/app/audit/_components/audit-poster.tsx +322 -0
  157. package/.next/standalone/app/audit/_components/audit-progress-strip.tsx +1 -1
  158. package/.next/standalone/app/audit/_components/auth-dialog.tsx +21 -49
  159. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +316 -0
  160. package/.next/standalone/app/audit/_components/empty-state.tsx +8 -2
  161. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +187 -0
  162. package/.next/standalone/app/audit/_components/invite-dialog.tsx +227 -0
  163. package/.next/standalone/app/audit/_components/quirks-section.tsx +75 -0
  164. package/.next/standalone/app/audit/_components/rerun-button.tsx +43 -14
  165. package/.next/standalone/app/audit/_components/run-progress.tsx +2 -2
  166. package/.next/standalone/app/audit/_components/share-templates.ts +23 -22
  167. package/.next/standalone/app/audit/_components/sigil.tsx +9 -66
  168. package/.next/standalone/app/audit/_components/strengths-section.tsx +20 -32
  169. package/.next/standalone/app/audit/audit-styles.css +781 -1784
  170. package/.next/standalone/app/components/sessions-list.tsx +77 -80
  171. package/.next/standalone/app/globals.css +214 -32
  172. package/.next/standalone/app/layout.tsx +1 -10
  173. package/.next/standalone/app/policies/hooks-client.tsx +12 -4
  174. package/.next/standalone/app/project/[name]/page.tsx +23 -79
  175. package/.next/standalone/app/projects/page.tsx +14 -23
  176. package/.next/standalone/assets/audit/poster-styles.css +1 -1
  177. package/.next/standalone/assets/audit/styles.css +11 -11
  178. package/.next/standalone/components/navbar.tsx +2 -37
  179. package/.next/standalone/lib/auth/api-server-client.ts +25 -0
  180. package/.next/standalone/node_modules/@next/env/package.json +2 -2
  181. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  182. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
  183. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  184. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  185. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  186. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  187. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  188. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  189. package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
  190. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  191. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  192. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  193. package/.next/standalone/node_modules/next/package.json +15 -15
  194. package/.next/standalone/package.json +18 -15
  195. package/.next/standalone/server.js +1 -1
  196. package/dist/cli.mjs +2 -2
  197. package/lib/auth/api-server-client.ts +25 -0
  198. package/package.json +18 -15
  199. package/src/audit/dashboard-cache.ts +13 -6
  200. package/src/audit/social-proof.ts +34 -0
  201. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07tgnzi._.js +0 -3
  202. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oeun7z._.js +0 -3
  203. package/.next/standalone/.next/server/chunks/[root-of-the-server]__12pit4m._.js +0 -3
  204. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13ra2jq._.js +0 -3
  205. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1b9z5-i._.js +0 -3
  206. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ixjiy8._.js +0 -3
  207. package/.next/standalone/.next/server/chunks/_1-1804f._.js +0 -3
  208. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00jkjmt._.js +0 -4
  209. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013du6r._.js +0 -4
  210. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e85wxv._.js +0 -4
  211. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0gfxvb1._.js +0 -3
  212. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1hlrq6y._.js +0 -4
  213. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ihxdo5._.js +0 -4
  214. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1vvfde2._.js +0 -4
  215. package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_0q4m0_h.js +0 -3
  216. package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_1n-0xws.js +0 -3
  217. package/.next/standalone/.next/static/chunks/09ueq8s1as8xs.css +0 -2
  218. package/.next/standalone/.next/static/chunks/0qassxjx1ef04.js +0 -1
  219. package/.next/standalone/.next/static/chunks/2452blhu4bcdr.js +0 -41
  220. package/.next/standalone/.next/static/chunks/2so39wg7mjbi7.js +0 -1
  221. package/.next/standalone/.next/static/chunks/3gti1qdk5epqn.js +0 -1
  222. package/.next/standalone/.next/static/chunks/3osmt-t2hsrng.js +0 -1
  223. package/.next/standalone/.next/static/chunks/3wycox197ouus.css +0 -1
  224. package/.next/standalone/app/audit/_components/findings-section.tsx +0 -135
  225. package/.next/standalone/app/audit/_components/identity-section.tsx +0 -126
  226. package/.next/standalone/app/audit/_components/policies-section.tsx +0 -194
  227. package/.next/standalone/app/audit/_components/return-section.tsx +0 -416
  228. package/.next/standalone/app/audit/_components/score-section.tsx +0 -179
  229. package/.next/standalone/app/audit/_components/share-dock.tsx +0 -265
  230. package/.next/standalone/app/audit/_components/show-off-cta.tsx +0 -135
  231. /package/.next/standalone/.next/static/{90NFQus9VOnEzD6sP0EyP → NYPiJP6Rv_exQdSFVS8HP}/_buildManifest.js +0 -0
  232. /package/.next/standalone/.next/static/{90NFQus9VOnEzD6sP0EyP → NYPiJP6Rv_exQdSFVS8HP}/_clientMiddlewareManifest.js +0 -0
  233. /package/.next/standalone/.next/static/{90NFQus9VOnEzD6sP0EyP → NYPiJP6Rv_exQdSFVS8HP}/_ssgManifest.js +0 -0
@@ -1,194 +0,0 @@
1
- "use client";
2
-
3
- /**
4
- * Section 05 — PRESCRIBED POLICIES. "enable these. close the gap."
5
- *
6
- * Grid of unenabled-builtin cards with install commands + projected
7
- * score uplift callout.
8
- *
9
- * Sources two layers of "hits":
10
- * 1. Unenabled builtin policies that fired on their own
11
- * 2. Audit detectors → mapped via DETECTOR_TO_POLICY in findings.ts.
12
- * The detector's hits get attributed to its primary policy so the
13
- * report frames everything as failproofai-coverable.
14
- *
15
- * Same policy can collect hits from multiple sources; we sum them and
16
- * render one card per policy.
17
- */
18
- import React, { useMemo, useState } from "react";
19
- import type { AuditResult } from "@/src/audit/types";
20
- import { type Grade, tierName } from "@/src/audit/scoring";
21
- import { usePostHog } from "@/contexts/PostHogContext";
22
-
23
- interface Props {
24
- result: AuditResult;
25
- projected: number;
26
- projectedGrade: Grade;
27
- }
28
-
29
- // Mirror of DETECTOR_TO_POLICY in findings.ts. Could re-export but keep
30
- // the dependency tree shallow — both modules are stable.
31
- const DETECTOR_TO_PRIMARY_POLICY: Record<string, string> = {
32
- "redundant-cd-cwd": "warn-repeated-tool-calls",
33
- "prefer-edit-over-read-cat": "block-read-outside-cwd",
34
- "prefer-edit-over-sed-awk": "warn-repeated-tool-calls",
35
- "prefer-write-over-heredoc": "block-env-files",
36
- "sleep-polling-loop": "warn-background-process",
37
- "find-from-root": "block-read-outside-cwd",
38
- "git-commit-no-verify": "warn-git-amend",
39
- "reread-after-edit": "warn-repeated-tool-calls",
40
- };
41
-
42
- const POLICY_DESC: Record<string, string> = {
43
- "warn-repeated-tool-calls": "warns when the same tool is called 3+ times with identical parameters — catches the loops before they spiral.",
44
- "block-read-outside-cwd": "denies any file read whose absolute path falls outside the project root, including symlinks.",
45
- "block-env-files": "blocks reads and writes of `.env` files at the tool layer.",
46
- "block-secrets-write": "blocks writes to .pem, id_rsa, credentials.json, and other secret-key files.",
47
- "warn-background-process": "warns before starting nohup / & / screen / tmux / disown processes that get forgotten about.",
48
- "warn-git-amend": "warns before amending git commits — dangerous-commit-flag class.",
49
- "require-ci-green-before-stop": "requires CI checks to pass on HEAD before the agent declares the task done.",
50
- };
51
-
52
- function shortName(name: string): string {
53
- const slash = name.indexOf("/");
54
- return slash >= 0 ? name.slice(slash + 1) : name;
55
- }
56
-
57
- interface PolicyCard {
58
- name: string; // short slug
59
- desc: string; // displayTitle (low-res) or impact
60
- catches: string; // "would have caught X occurrences..." copy
61
- hits: number;
62
- }
63
-
64
- function buildPolicyCards(result: AuditResult): PolicyCard[] {
65
- const enabledSet = new Set(result.enabledBuiltinNames ?? []);
66
- // policyName → aggregated counts
67
- const buckets = new Map<string, { hits: number; projects: number; sources: Set<string> }>();
68
-
69
- for (const row of result.results) {
70
- if (row.hits === 0) continue;
71
-
72
- let target: string;
73
- let isFromDetector = false;
74
- if (row.source === "audit-detector") {
75
- const mapped = DETECTOR_TO_PRIMARY_POLICY[shortName(row.name)];
76
- if (!mapped) continue;
77
- target = mapped;
78
- isFromDetector = true;
79
- } else if (row.source === "builtin" && !row.enabledInConfig) {
80
- target = shortName(row.name);
81
- } else {
82
- continue; // already-enabled builtins don't need to be prescribed
83
- }
84
-
85
- // Skip if the target policy is already in the user's enabled set
86
- // (detector hits would land there in production already).
87
- if (enabledSet.has(target)) continue;
88
-
89
- const bucket = buckets.get(target) ?? { hits: 0, projects: 0, sources: new Set() };
90
- bucket.hits += row.hits;
91
- bucket.projects = Math.max(bucket.projects, row.projects);
92
- bucket.sources.add(isFromDetector ? shortName(row.name) : "self");
93
- buckets.set(target, bucket);
94
- }
95
-
96
- return [...buckets.entries()]
97
- .sort((a, b) => b[1].hits - a[1].hits)
98
- .map(([name, b]) => {
99
- const viaList = [...b.sources].filter((s) => s !== "self");
100
- const viaCopy = viaList.length > 0
101
- ? ` (via ${viaList.join(", ")})`
102
- : "";
103
- const catches = `would have caught ${b.hits} occurrence${b.hits === 1 ? "" : "s"} across ${b.projects} project${b.projects === 1 ? "" : "s"}${viaCopy}.`;
104
- return {
105
- name,
106
- desc: POLICY_DESC[name] ?? "enable this builtin policy to close the gap.",
107
- catches,
108
- hits: b.hits,
109
- };
110
- });
111
- }
112
-
113
- export function PoliciesSection({ result, projected, projectedGrade }: Props) {
114
- // Builds a Map+Set aggregation + sort over result.results — non-trivial.
115
- // Memoize so unrelated parent re-renders don't recompute every frame.
116
- const policies = useMemo(() => buildPolicyCards(result), [result]);
117
-
118
- if (policies.length === 0) return null;
119
-
120
- return (
121
- <section className="section" data-screen-label="05 Policies">
122
- <div className="section-mast">
123
- <div className="section-label">
124
- <span className="glyph">━━</span> policies{" "}
125
- <span style={{ color: "var(--dim)" }}>·</span> prescribed
126
- </div>
127
- <div className="section-meta">
128
- {policies.length} polic{policies.length === 1 ? "y" : "ies"}{" "}
129
- <span style={{ color: "var(--dim)" }}>·</span>{" "}
130
- <span className="g">covers your slipping-through hits</span>
131
- </div>
132
- </div>
133
- <h2 className="section-h">enable these. close the gap.</h2>
134
-
135
- <div className="policy-callout">
136
- <span>
137
- enable all {policies.length === 1 ? "one" : policies.length}
138
- </span>
139
- <span className="arrow">→</span>
140
- <span>projected score</span>
141
- <span className="new-score">{projected}</span>
142
- <span style={{ color: "var(--dim)" }}>·</span>
143
- <span className="new-tier">{tierName(projectedGrade)}</span>
144
- </div>
145
-
146
- <div className="policies-grid">
147
- {policies.map((p, i) => (
148
- <PolicyTile key={p.name} policy={p} idx={i} />
149
- ))}
150
- </div>
151
- </section>
152
- );
153
- }
154
-
155
- function PolicyTile({ policy, idx }: { policy: PolicyCard; idx: number }) {
156
- const { capture } = usePostHog();
157
- const [copied, setCopied] = useState(false);
158
- const install = `failproof policy add ${policy.name}`;
159
-
160
- const handleCopy = async (e: React.MouseEvent) => {
161
- e.stopPropagation();
162
- try {
163
- await navigator.clipboard.writeText(install);
164
- setCopied(true);
165
- capture("audit_copy_clicked", {
166
- source: "policies_section",
167
- item_type: "single_policy_install_command",
168
- policy_name: policy.name,
169
- policy_rank: idx + 1,
170
- });
171
- setTimeout(() => setCopied(false), 1500);
172
- } catch { /* ignore */ }
173
- };
174
-
175
- return (
176
- <article className="policy-card">
177
- <div className="head">
178
- <div className="policy-name">{policy.name}</div>
179
- <div className="policy-slug">№{String(idx + 1).padStart(2, "0")}</div>
180
- </div>
181
- <div className="policy-desc">{policy.desc}</div>
182
- <div className="policy-impact">
183
- <span className="check">✓</span>{policy.catches}
184
- </div>
185
- <div className="policy-install">
186
- <span className="prompt">$</span>
187
- <span>{install}</span>
188
- <span className="copy" onClick={handleCopy}>
189
- {copied ? "copied" : "copy"}
190
- </span>
191
- </div>
192
- </article>
193
- );
194
- }
@@ -1,416 +0,0 @@
1
- "use client";
2
-
3
- /**
4
- * Section 06 — NEXT AUDIT / "come back better." Re-audit loop CTA.
5
- *
6
- * Behavior matrix:
7
- * - unknown (probe in flight) → buttons disabled
8
- * - anon (no session) → [ set a reminder ] opens AuthDialog,
9
- * on success we flip to the authed panel
10
- * below and persist the 7-day reminder.
11
- * - authed (any) → consolidated status panel: "signed in as
12
- * …" + either the persisted "next audit in
13
- * X days" line OR a "no reminder set yet"
14
- * line with an inline [ set a reminder ]
15
- * button. The reminder persists across
16
- * reloads via ~/.failproofai/next-audit.json
17
- * — same as the CLI's auth.json.
18
- *
19
- * Also exposes [ re-audit now ] next to [ install policies ] so the user
20
- * can trigger a fresh scan inline without leaving the page. The button
21
- * fires POST /api/audit/run (same backend the empty-state CTA uses).
22
- */
23
- import React, { useCallback, useEffect, useRef, useState } from "react";
24
- import type { AuditResult } from "@/src/audit/types";
25
- import { usePostHog } from "@/contexts/PostHogContext";
26
- import { isAbortError } from "@/lib/fetch-with-timeout";
27
- import { AuthDialog, type AuthedUser } from "./auth-dialog";
28
-
29
- interface Props {
30
- result: AuditResult;
31
- /** Lifted by AuditDashboard so the bottom button and the top-of-page
32
- * re-audit bar share state — only one rerun at a time. */
33
- isRunning: boolean;
34
- /** Lifted handler. Drives the sticky progress strip + soft refresh. */
35
- onRerun: () => void;
36
- }
37
-
38
- const BULK_INSTALL_CMD = "failproofai policies --install";
39
- const DEFAULT_REMINDER_DAYS = 7;
40
-
41
- type AuthStatus =
42
- | { kind: "unknown" }
43
- | { kind: "anon" }
44
- | { kind: "authed"; user: { id: string; email: string } };
45
-
46
- interface Reminder {
47
- next_audit_at: number; // unix seconds
48
- user_email: string;
49
- set_at: number;
50
- }
51
-
52
- function daysUntil(unixSecs: number): number {
53
- const nowSecs = Math.floor(Date.now() / 1000);
54
- return Math.max(0, Math.ceil((unixSecs - nowSecs) / 86400));
55
- }
56
-
57
- function formatNextAudit(unixSecs: number): string {
58
- const d = new Date(unixSecs * 1000);
59
- return d.toLocaleDateString(undefined, {
60
- weekday: "short",
61
- month: "short",
62
- day: "numeric",
63
- });
64
- }
65
-
66
- export function ReturnSection({ result, isRunning, onRerun }: Props) {
67
- const { capture } = usePostHog();
68
- const hasUnenabled = result.results.some(
69
- (r) => r.source === "builtin" && !r.enabledInConfig && r.hits > 0,
70
- );
71
-
72
- const [copied, setCopied] = useState(false);
73
- const [authStatus, setAuthStatus] = useState<AuthStatus>({ kind: "unknown" });
74
- const [reminder, setReminder] = useState<Reminder | null>(null);
75
- const [dialogOpen, setDialogOpen] = useState(false);
76
- const [reminderBusy, setReminderBusy] = useState(false);
77
- const ctaShownRef = useRef(false);
78
- /** Throttle gate for focus/visibility-triggered refreshStatus calls —
79
- * rapid alt-tabbing would otherwise hit /api/auth/status (two disk
80
- * reads each) once per event. */
81
- const lastRefreshAtRef = useRef(0);
82
-
83
- // Probe /api/auth/status on mount — also returns the persisted reminder
84
- // when one exists and belongs to the active session.
85
- const refreshStatus = useCallback(async () => {
86
- lastRefreshAtRef.current = Date.now();
87
- try {
88
- const res = await fetch("/api/auth/status", { cache: "no-store" });
89
- const body = (await res.json()) as {
90
- authenticated?: boolean;
91
- user?: { id: string; email: string };
92
- reminder?: Reminder | null;
93
- };
94
- if (body.authenticated && body.user) {
95
- setAuthStatus({ kind: "authed", user: body.user });
96
- setReminder(body.reminder ?? null);
97
- } else {
98
- setAuthStatus({ kind: "anon" });
99
- setReminder(null);
100
- }
101
- } catch {
102
- setAuthStatus({ kind: "anon" });
103
- setReminder(null);
104
- }
105
- }, []);
106
-
107
- useEffect(() => {
108
- void refreshStatus();
109
- // Re-probe whenever the tab regains focus or visibility — picks up
110
- // CLI `failproofai auth login` / `logout` and api-server restarts
111
- // without the user having to hit reload manually. Throttled to 5s so
112
- // rapid alt-tabbing (a focus + a visibility event for one switch)
113
- // doesn't double-fire.
114
- const REFRESH_MIN_INTERVAL_MS = 5_000;
115
- const maybeRefresh = () => {
116
- if (Date.now() - lastRefreshAtRef.current < REFRESH_MIN_INTERVAL_MS) return;
117
- void refreshStatus();
118
- };
119
- const onFocus = () => maybeRefresh();
120
- const onVisibility = () => {
121
- if (document.visibilityState === "visible") maybeRefresh();
122
- };
123
- window.addEventListener("focus", onFocus);
124
- document.addEventListener("visibilitychange", onVisibility);
125
- return () => {
126
- window.removeEventListener("focus", onFocus);
127
- document.removeEventListener("visibilitychange", onVisibility);
128
- };
129
- }, [refreshStatus]);
130
-
131
- // Fire-once "user saw the reminder CTA" event so we can compute the
132
- // funnel shown → clicked → auth → saved. We wait until the auth probe
133
- // finishes — before that the buttons are disabled and the CTA isn't
134
- // really "shown".
135
- useEffect(() => {
136
- if (ctaShownRef.current) return;
137
- if (authStatus.kind === "unknown") return;
138
- ctaShownRef.current = true;
139
- capture("audit_reminder_cta_shown", {
140
- auth_state: authStatus.kind,
141
- has_existing_reminder: reminder !== null,
142
- source: "return_section",
143
- });
144
- }, [authStatus, capture, reminder]);
145
-
146
- const persistReminder = useCallback(async (): Promise<Reminder | null> => {
147
- // 10s ceiling so a hung route can't permanently disable the CTA.
148
- const controller = new AbortController();
149
- const timer = setTimeout(() => controller.abort(), 10_000);
150
- try {
151
- setReminderBusy(true);
152
- const res = await fetch("/api/auth/reminder", {
153
- method: "POST",
154
- headers: { "content-type": "application/json" },
155
- body: JSON.stringify({ in_days: DEFAULT_REMINDER_DAYS }),
156
- signal: controller.signal,
157
- });
158
- if (!res.ok) {
159
- // A 401 here means our local "authed" view of the world is out of
160
- // date — the server-side session was revoked or expired between the
161
- // status probe and this write. Flip back to anon so the user can
162
- // re-authenticate instead of leaving them on a panel whose actions
163
- // silently no-op.
164
- if (res.status === 401) {
165
- setAuthStatus({ kind: "anon" });
166
- setReminder(null);
167
- }
168
- capture("audit_reminder_saved", {
169
- status: `http_${res.status}`,
170
- source: "return_section",
171
- });
172
- return null;
173
- }
174
- const body = (await res.json()) as { reminder?: Reminder };
175
- capture("audit_reminder_saved", {
176
- status: body.reminder ? "success" : "empty",
177
- source: "return_section",
178
- });
179
- return body.reminder ?? null;
180
- } catch (err) {
181
- const kind = isAbortError(err) ? "timeout" : "error";
182
- capture("audit_reminder_saved", {
183
- status: kind,
184
- source: "return_section",
185
- });
186
- return null;
187
- } finally {
188
- clearTimeout(timer);
189
- setReminderBusy(false);
190
- }
191
- }, [capture]);
192
-
193
- const handleInstall = async () => {
194
- capture("audit_install_policies_clicked", {
195
- source: "return_section",
196
- });
197
- try {
198
- await navigator.clipboard.writeText(BULK_INSTALL_CMD);
199
- setCopied(true);
200
- capture("audit_copy_clicked", {
201
- source: "return_section_install_policies",
202
- item_type: "bulk_install_command",
203
- });
204
- setTimeout(() => setCopied(false), 1500);
205
- } catch {
206
- /* ignore */
207
- }
208
- };
209
-
210
- const handleSetReminder = useCallback(async () => {
211
- if (authStatus.kind === "unknown") return;
212
- capture("audit_reminder_cta_clicked", {
213
- auth_state: authStatus.kind,
214
- has_existing_reminder: reminder !== null,
215
- source: "return_section",
216
- });
217
- if (authStatus.kind === "authed") {
218
- const next = await persistReminder();
219
- if (next) setReminder(next);
220
- return;
221
- }
222
- setDialogOpen(true);
223
- }, [authStatus, capture, persistReminder, reminder]);
224
-
225
- const handleAuthed = useCallback(
226
- async (user: AuthedUser) => {
227
- setAuthStatus({ kind: "authed", user });
228
- capture("audit_auth_completed", {
229
- source: "return_section",
230
- });
231
- // The dialog opened because the user wanted a reminder → persist
232
- // immediately, no second click required.
233
- const next = await persistReminder();
234
- if (next) setReminder(next);
235
- },
236
- [capture, persistReminder],
237
- );
238
-
239
- // Re-audit is now lifted into AuditDashboard so the top-of-page bar and
240
- // this bottom button share state — concurrent clicks are impossible and
241
- // success path soft-refreshes the report rather than reloading the page.
242
- // PostHog `source: "return_section"` capture happens inside the lifted
243
- // handler via the `source` argument it receives.
244
- const handleRerun = useCallback(() => {
245
- if (isRunning) return;
246
- onRerun();
247
- }, [isRunning, onRerun]);
248
-
249
- const authed = authStatus.kind === "authed";
250
- const hasReminder = authed && reminder !== null;
251
- const days = reminder ? daysUntil(reminder.next_audit_at) : 0;
252
- const authedEmail =
253
- authStatus.kind === "authed" ? authStatus.user.email : "";
254
-
255
- return (
256
- <section className="section" data-screen-label="06 Next audit">
257
- <div className="section-mast">
258
- <div className="section-label">
259
- <span className="glyph">━━</span> next audit{" "}
260
- <span style={{ color: "var(--dim)" }}>·</span> improvement
261
- </div>
262
- <div className="section-meta">
263
- <span className="g">●</span> recommended in 7d
264
- </div>
265
- </div>
266
- <h2 className="section-h">come back better.</h2>
267
-
268
- <div className="return-hook">
269
- <div className="label">━━ the loop</div>
270
- <h3>re-audit in 7 days.</h3>
271
- <p>
272
- after the prescribed policies have been live for a week, we&apos;ll show
273
- your before/after score and which detectors went quiet.
274
- </p>
275
- <p style={{ marginTop: 16, color: "var(--dim)" }}>
276
- most agents move from C to B in one session. some make it in a day.
277
- </p>
278
-
279
- {/* Once authed, the section stays in the consolidated status panel —
280
- with the reminder line if one is set, or a "no reminder yet" line
281
- + inline [ set a reminder ] button otherwise. The anonymous CTA
282
- layout only shows for genuinely-unauthed sessions. The action
283
- buttons ([ set a reminder ] / [ re-audit now ] / [ install
284
- policies ]) are identical in both branches — extracted into
285
- <ReturnActions> below to keep them in sync. */}
286
- {authed ? (
287
- <div className="return-status">
288
- {hasReminder && reminder ? (
289
- <div className="rs-row rs-row-primary">
290
- <span className="rs-dot rs-dot-pink" aria-hidden="true" />
291
- <span>
292
- next audit set for{" "}
293
- <span className="rs-strong">{formatNextAudit(reminder.next_audit_at)}</span>
294
- {" "}<span style={{ color: "var(--dim)" }}>·</span>{" "}
295
- <span className="rs-strong">in {days} day{days === 1 ? "" : "s"}</span>
296
- </span>
297
- </div>
298
- ) : (
299
- <div className="rs-row rs-row-primary">
300
- <span className="rs-dot rs-dot-pink" aria-hidden="true" />
301
- <span>
302
- <span className="rs-strong">no reminder set yet</span>
303
- {" "}<span style={{ color: "var(--dim)" }}>·</span>{" "}
304
- recommended in {DEFAULT_REMINDER_DAYS} days
305
- </span>
306
- </div>
307
- )}
308
- <div className="rs-row">
309
- <span className="rs-dot rs-dot-green" aria-hidden="true" />
310
- <span>
311
- signed in as <span className="rs-email">{authedEmail}</span>
312
- </span>
313
- </div>
314
- <ReturnActions
315
- style={{ marginTop: 18 }}
316
- showSetReminder={!hasReminder}
317
- setReminderDisabled={reminderBusy}
318
- reminderBusy={reminderBusy}
319
- rerunBusy={isRunning}
320
- hasUnenabled={hasUnenabled}
321
- copied={copied}
322
- onSetReminder={handleSetReminder}
323
- onRerun={handleRerun}
324
- onInstall={handleInstall}
325
- />
326
- </div>
327
- ) : (
328
- <ReturnActions
329
- showSetReminder
330
- setReminderDisabled={authStatus.kind === "unknown" || reminderBusy}
331
- reminderBusy={reminderBusy}
332
- rerunBusy={isRunning}
333
- hasUnenabled={hasUnenabled}
334
- copied={copied}
335
- onSetReminder={handleSetReminder}
336
- onRerun={handleRerun}
337
- onInstall={handleInstall}
338
- />
339
- )}
340
- </div>
341
-
342
- <AuthDialog
343
- open={dialogOpen}
344
- source="return_section"
345
- headline="oops — you are unknown."
346
- reason="verify yourself to get the re-audit reminder."
347
- onClose={() => setDialogOpen(false)}
348
- onAuthed={(u) => {
349
- setDialogOpen(false);
350
- void handleAuthed(u);
351
- }}
352
- />
353
- </section>
354
- );
355
- }
356
-
357
- interface ReturnActionsProps {
358
- /** Whether to render the `[ set a reminder ]` button. False when the
359
- * user already has a reminder set (the authed-with-reminder case
360
- * shows the panel meta instead). */
361
- showSetReminder: boolean;
362
- setReminderDisabled: boolean;
363
- reminderBusy: boolean;
364
- rerunBusy: boolean;
365
- hasUnenabled: boolean;
366
- copied: boolean;
367
- onSetReminder: () => void;
368
- onRerun: () => void;
369
- onInstall: () => void;
370
- style?: React.CSSProperties;
371
- }
372
-
373
- /** Action button strip shared by the authed and anon branches. Extracted
374
- * to keep the three buttons in sync — the prior inline copies had
375
- * already drifted on margin-top styling. */
376
- function ReturnActions(props: ReturnActionsProps): React.ReactElement {
377
- const {
378
- showSetReminder,
379
- setReminderDisabled,
380
- reminderBusy,
381
- rerunBusy,
382
- hasUnenabled,
383
- copied,
384
- onSetReminder,
385
- onRerun,
386
- onInstall,
387
- style,
388
- } = props;
389
- return (
390
- <div className="return-actions" style={style}>
391
- {showSetReminder && (
392
- <button
393
- type="button"
394
- className="share-btn"
395
- onClick={onSetReminder}
396
- disabled={setReminderDisabled}
397
- >
398
- {reminderBusy ? "[ saving… ]" : "[ set a reminder ]"}
399
- </button>
400
- )}
401
- <button
402
- type="button"
403
- className="share-btn alt"
404
- onClick={onRerun}
405
- disabled={rerunBusy}
406
- >
407
- {rerunBusy ? "[ scanning… ]" : "[ re-audit now ]"}
408
- </button>
409
- {hasUnenabled && (
410
- <button type="button" className="share-btn alt" onClick={onInstall}>
411
- {copied ? "[ ✓ copied — paste in your shell ]" : "[ install policies ]"}
412
- </button>
413
- )}
414
- </div>
415
- );
416
- }