failproofai 1.0.0-beta.11 → 1.0.0-beta.13

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 (177) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js +2 -3
  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 +2 -2
  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/_head.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  15. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page.js +2 -3
  18. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  23. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  24. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  26. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  27. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/api/audit/invite/route.js +2 -2
  29. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/audit/run/route.js +2 -2
  31. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +3 -2
  34. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +2 -2
  36. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/logout/route.js +2 -2
  38. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +2 -2
  40. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/auth/status/route.js +2 -2
  42. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  45. package/.next/standalone/.next/server/app/audit/page.js +2 -3
  46. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/index.html +1 -1
  49. package/.next/standalone/.next/server/app/index.rsc +15 -15
  50. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  52. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  53. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  54. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  55. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  56. package/.next/standalone/.next/server/app/page.js +2 -3
  57. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  59. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
  60. package/.next/standalone/.next/server/app/policies/page.js +2 -3
  61. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -4
  65. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +3 -4
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  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 +3 -4
  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/server-reference-manifest.json +4 -4
  77. package/.next/standalone/.next/server/app/settings/page.js +2 -3
  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/{[root-of-the-server]__1a7i2q4._.js → [root-of-the-server]__00ni9za._.js} +1 -1
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08y94xv._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1_ps0jw._.js → [root-of-the-server]__0effm9m._.js} +1 -1
  86. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0lk_935._.js → [root-of-the-server]__0ix8gm7._.js} +1 -1
  87. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0ky_n0r._.js → [root-of-the-server]__0ji11a-._.js} +1 -1
  88. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0v2fpxe._.js → [root-of-the-server]__0ojwjxl._.js} +1 -1
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1uyc-z4._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20d06rh._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -3
  95. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -3
  96. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1rdruw4._.js → [root-of-the-server]__0_1tiya._.js} +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__20ie7me._.js → [root-of-the-server]__0eeipso._.js} +2 -2
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +3 -3
  100. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1tyf0tc._.js → [root-of-the-server]__17itmx-._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1gwr8l-._.js → [root-of-the-server]__1dx-a-c._.js} +2 -2
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mmefsr._.js +5 -0
  103. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1yw368c._.js → [root-of-the-server]__1n2vcv4._.js} +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1a2hcb_._.js → [root-of-the-server]__1tlvv9a._.js} +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/{_1fahg9e._.js → _0vo2qyt._.js} +2 -2
  108. package/.next/standalone/.next/server/chunks/ssr/{_0uz7o9y._.js → _0xej-0e._.js} +2 -2
  109. package/.next/standalone/.next/server/chunks/ssr/{_04mekdy._.js → _10399ge._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/{_0se484m._.js → _149k9oy._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/ssr/{_0n4bc3i._.js → _166jvu8._.js} +1 -1
  112. package/.next/standalone/.next/server/chunks/ssr/{_0r-dsre._.js → _1_hwp06._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/{_1w8sola._.js → _1mnhi_r._.js} +2 -2
  114. package/.next/standalone/.next/server/chunks/ssr/{_1w_6p9g._.js → _1ror7k9._.js} +2 -2
  115. package/.next/standalone/.next/server/chunks/ssr/{_1gmzkwj._.js → _1sdgprl._.js} +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  119. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  120. package/.next/standalone/.next/server/pages/404.html +1 -1
  121. package/.next/standalone/.next/server/pages/500.html +1 -1
  122. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  123. package/.next/standalone/.next/server/server-reference-manifest.json +15 -15
  124. package/.next/standalone/.next/static/chunks/{0-m1vctj2jny6.js → 01s3vpzky_qew.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
  126. package/.next/standalone/.next/static/chunks/{0on899o88y54u.js → 0zbcd_n8ku2ov.js} +1 -1
  127. package/.next/standalone/.next/static/chunks/{04y6igmx5olt6.js → 1erji0ratofla.js} +1 -1
  128. package/.next/standalone/.next/static/chunks/{06abmgw7cvecc.js → 23hnlzqx44vbe.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/{1a9qlmo5s678i.js → 2cwsiyhc7d2cw.js} +1 -1
  130. package/.next/standalone/.next/static/chunks/{37nu_nos28w1x.js → 2y42ebzynyxdv.js} +1 -1
  131. package/.next/standalone/.next/static/chunks/{0ug1y_6-uwu5t.js → 315lncj7dsp9c.js} +1 -1
  132. package/.next/standalone/.next/static/chunks/31_px0dfgdg9j.js +6 -0
  133. package/.next/standalone/.next/static/chunks/{43ylh_2r15k-w.js → 3l4-fu3xt75j8.js} +1 -1
  134. package/.next/standalone/.next/static/chunks/3poscoxn69x9g.js +1 -0
  135. package/.next/standalone/app/policies/hooks-client.tsx +4 -4
  136. package/.next/standalone/ino.ujh3/.failproofai/VERSION +4 -0
  137. package/.next/standalone/ino.ujh3/.failproofai/state/last-version +1 -0
  138. package/.next/standalone/ino.ujh3/.failproofai/state/telemetry-id +1 -0
  139. package/.next/standalone/package.json +10 -10
  140. package/.next/standalone/server.js +1 -1
  141. package/bin/failproofai-worker.mjs +1 -1
  142. package/bin/failproofai.mjs +186 -5
  143. package/dist/cli.mjs +1130 -1352
  144. package/dist/worker.mjs +171 -1010
  145. package/package.json +10 -10
  146. package/src/hooks/backfill-cli.ts +1 -1
  147. package/src/hooks/cloud-connection.ts +4 -4
  148. package/src/hooks/cloud-enrollment-cli.ts +1 -1
  149. package/src/hooks/cloud-enrollment.ts +3 -3
  150. package/src/hooks/cloud-managed-policies.ts +60 -22
  151. package/src/hooks/configure-wizard.ts +124 -53
  152. package/src/hooks/custom-hooks-loader.ts +57 -8
  153. package/src/hooks/daemon-service.ts +66 -11
  154. package/src/hooks/flush-cli.ts +180 -0
  155. package/src/hooks/fp-config.ts +242 -118
  156. package/src/hooks/fp-home.ts +138 -22
  157. package/src/hooks/fp-reset.ts +357 -43
  158. package/src/hooks/handler.ts +11 -11
  159. package/src/hooks/harness-cli.ts +309 -0
  160. package/src/hooks/hook-activity-store.ts +5 -5
  161. package/src/hooks/manager.ts +35 -0
  162. package/src/hooks/onboarding-attempt.ts +22 -6
  163. package/src/hooks/tui.ts +64 -7
  164. package/src/hooks/uninstall-cli.ts +1 -1
  165. package/src/hooks/worker-server.ts +69 -12
  166. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +0 -3
  167. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +0 -3
  168. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +0 -3
  169. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +0 -5
  170. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +0 -4
  171. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +0 -5
  172. package/.next/standalone/.next/static/chunks/0p_y4ndalc68e.js +0 -6
  173. package/.next/standalone/.next/static/chunks/13vrxdsdihqd_.css +0 -2
  174. package/.next/standalone/.next/static/chunks/19lrnqxn7ni7i.js +0 -1
  175. /package/.next/standalone/.next/static/{BZDo0aAHzDwh5ntxgIgpc → ziAsXfNE0cH_S7LHK-Py3}/_buildManifest.js +0 -0
  176. /package/.next/standalone/.next/static/{BZDo0aAHzDwh5ntxgIgpc → ziAsXfNE0cH_S7LHK-Py3}/_clientMiddlewareManifest.js +0 -0
  177. /package/.next/standalone/.next/static/{BZDo0aAHzDwh5ntxgIgpc → ziAsXfNE0cH_S7LHK-Py3}/_ssgManifest.js +0 -0
@@ -22,18 +22,39 @@
22
22
  * machine denied every tool call), its cloud policy directory still said
23
23
  * `cloud-managed`, and its credential still said `cloud.json`.
24
24
  *
25
- * ## Layout 2
25
+ * ## Layout 3
26
+ *
27
+ * Three things changed from layout 2, all of them about a directory saying what
28
+ * it holds:
29
+ *
30
+ * - **Everything is JSON.** `config.toml` and `credentials.toml` are now
31
+ * `.json`. Two serialisation formats in one home meant two parsers, two
32
+ * escaping rules, and a `toml` dependency in both the CLI and the daemon to
33
+ * read files that were only ever flat key/value.
34
+ * - **`policies/` holds policies, and only policies.** It also held
35
+ * `local-policies/` — our config, not a policy at all, and the one file a
36
+ * user must not hand-edit sitting among the ones they should. That moved to
37
+ * the root. What stays is every policy on the machine: the user's own `*.mjs`
38
+ * directly in it, the fleet's under `cloud-policies/`. Nesting them is safe
39
+ * because the convention loader does not recurse (see `cloudPoliciesDir`),
40
+ * and it means one directory answers "what governs this machine".
41
+ * - **No per-deployment directories.** `cloud-policies/generations/<n>/` held a
42
+ * full copy of every artifact per deployment — a tree to create, prune and
43
+ * keep consistent with the manifest, on top of the `artifacts/` copy. They
44
+ * are content-addressed and therefore immutable, so the copies bought nothing
45
+ * the digests did not already give. `cloud-policies/` is flat now: one
46
+ * `artifacts/` directory, and `active.json` naming what is live — see
47
+ * `cloud_policies.rs` for why activation is still atomic without them.
26
48
  *
27
49
  * ```
28
50
  * ~/.failproofai/
29
51
  * VERSION layout / cli / daemon versions
30
- * config.toml 0644 non-secret: mode, daemon, collector prefs
31
- * credentials.toml 0600 every token
52
+ * config.json 0644 non-secret: mode, daemon, collector prefs
53
+ * credentials.json 0600 every token
54
+ * policies-config.json the builtin enable/disable set + params
32
55
  * bin/ downloaded daemon binaries, one per version
33
- * policies/
34
- * local-policies/ the builtin enable/disable set
35
- * cloud-policies/ content-addressed artifacts + active generation
36
- * custom-policies/ user convention policies (*.mjs)
56
+ * policies/ every policy: the user's *.mjs sit directly here
57
+ * cloud-policies/ the fleet's — flat: active.json, desired-state.json, artifacts/
37
58
  * cursors/<source>/ per-source collector watermarks
38
59
  * audit/ audit report + per-session cache
39
60
  * hook-activity/ decision log the dashboard reads
@@ -69,7 +90,7 @@ import { resolve } from "node:path";
69
90
  * 1 — the original flat/`cache`-based layout, through 1.0.0-beta.5.
70
91
  * 2 — this file.
71
92
  */
72
- export const LAYOUT_VERSION = 2;
93
+ export const LAYOUT_VERSION = 3;
73
94
 
74
95
  /**
75
96
  * `~/.failproofai`, or `FAILPROOFAI_HOME`.
@@ -102,7 +123,7 @@ const atHome = (home: string | undefined, ...parts: string[]): string =>
102
123
  export const versionFile = (home?: string) => atHome(home, "VERSION");
103
124
 
104
125
  /** Non-secret configuration. World-readable by design; never holds a token. */
105
- export const configFile = (home?: string) => atHome(home, "config.toml");
126
+ export const configFile = (home?: string) => atHome(home, "config.json");
106
127
 
107
128
  /**
108
129
  * Every credential, owner-only.
@@ -112,7 +133,7 @@ export const configFile = (home?: string) => atHome(home, "config.toml");
112
133
  * there would be readable by every local user on the box — which is exactly
113
134
  * why `ingest.json` and `cloud.json` were separate files before this.
114
135
  */
115
- export const credentialsFile = (home?: string) => atHome(home, "credentials.toml");
136
+ export const credentialsFile = (home?: string) => atHome(home, "credentials.json");
116
137
 
117
138
  // ── Daemon binaries ──────────────────────────────────────────────────────────
118
139
 
@@ -128,19 +149,54 @@ export const daemonBinary = (version: string, home?: string) =>
128
149
 
129
150
  // ── Policies ─────────────────────────────────────────────────────────────────
130
151
 
152
+ /**
153
+ * Where the user drops convention policies (`*.mjs`), loaded with no flag.
154
+ *
155
+ * Layout 3 made this the WHOLE meaning of the directory. It used to hold two
156
+ * subdirectories the user did not put there — `local-policies/` (our config)
157
+ * and `cloud-policies/` (the fleet's) — so "mine" and "not mine" shared a
158
+ * folder and nothing said which was which.
159
+ */
131
160
  export const policiesDir = (home?: string) => atHome(home, "policies");
132
161
 
133
- /** The builtin enable/disable set and per-policy params, GLOBAL scope only. */
134
- export const localPoliciesDir = (home?: string) => atHome(home, "policies", "local-policies");
162
+ /** Convention policies ARE the policies directory now; kept as an alias. */
163
+ export const customPoliciesDir = policiesDir;
164
+
165
+ /**
166
+ * The builtin enable/disable set and per-policy params, GLOBAL scope only.
167
+ *
168
+ * At the root in layout 3. It is configuration, not a policy, and burying it
169
+ * two levels inside a directory users are told to drop files into put the one
170
+ * file they must not hand-edit among the ones they should.
171
+ */
135
172
  export const globalPolicyConfigFile = (home?: string) =>
136
- resolve(localPoliciesDir(home), "policies-config.json");
173
+ atHome(home, "policies-config.json");
137
174
 
138
- /** Cloud-managed generations: `active.json` plus content-addressed artifacts. */
175
+ /**
176
+ * Cloud-managed deployments: `active.json`, `desired-state.json`, and
177
+ * content-addressed `artifacts/`.
178
+ *
179
+ * A CHILD of `policies/`, so everything that is a policy lives under one
180
+ * directory and the home root stays readable — every policy on the machine is
181
+ * under `policies/`, whoever put it there.
182
+ *
183
+ * Safe to nest because the convention loader does not recurse:
184
+ * `discoverPolicyFiles()` and `findSkippedPolicyFiles()` both filter
185
+ * `isFile()`, so this directory is invisible to it. That is the property that
186
+ * makes the nesting safe rather than a coincidence — if either ever walks
187
+ * subdirectories, every cloud artifact becomes a convention policy loaded
188
+ * WITHOUT its digest being checked, which is the one path this whole module
189
+ * exists to prevent. `fp-home.test.ts` pins it.
190
+ *
191
+ * What is flat is the INSIDE of this directory: `artifacts/` holds every
192
+ * content-addressed policy for every deployment, and `active.json` names which
193
+ * of them is live. There is no `deployments/<n>/` tree — a per-deployment
194
+ * directory is one more moving part that has to be created, pruned and kept
195
+ * consistent with the manifest, and it bought nothing the digests did not
196
+ * already give.
197
+ */
139
198
  export const cloudPoliciesDir = (home?: string) => resolve(policiesDir(home), "cloud-policies");
140
199
 
141
- /** User convention policies (`*.mjs`) that load without any flag. */
142
- export const customPoliciesDir = (home?: string) => resolve(policiesDir(home), "custom-policies");
143
-
144
200
  // ── Collector ────────────────────────────────────────────────────────────────
145
201
 
146
202
  /** Per-source watermarks. One directory per source — never shared: the cursor
@@ -275,7 +331,35 @@ export const logsDir = (home?: string) => atHome(home, "logs");
275
331
  * silently reads "no data" is worse than one that says so.
276
332
  */
277
333
  export const legacy = {
334
+ /**
335
+ * Layout 1's policy config — at the home root.
336
+ *
337
+ * Layout 3 puts the CURRENT config back at this exact path, so this is not a
338
+ * discriminating landmark on its own. `detectLayout()` checks for a layout-2+
339
+ * marker before consulting it; see the comment there.
340
+ */
278
341
  policyConfig: () => at("policies-config.json"),
342
+ /**
343
+ * Layout 2's `config.toml`. A landmark in its own right now: layout 1 had no
344
+ * such file, so its presence proves a home is newer than layout 1 even when
345
+ * `VERSION` is missing.
346
+ */
347
+ configToml: () => at("config.toml"),
348
+ /** Layout 2's credentials, replaced by `credentials.json`. */
349
+ credentialsToml: () => at("credentials.toml"),
350
+ /** Layout 2 nested the policy config two levels down. */
351
+ localPoliciesDir: () => at("policies", "local-policies"),
352
+ /** Layout 2 kept convention policies in a subdirectory. */
353
+ customPoliciesDir: () => at("policies", "custom-policies"),
354
+ // NO `cloudPoliciesDir` HERE. Layout 2 put cloud deployments at
355
+ // `policies/cloud-policies`, which is where layout 3 puts them too — so there
356
+ // is no old location to migrate from or prune, and an entry here would be a
357
+ // "legacy" path identical to the live one. That is not a harmless duplicate:
358
+ // it is exactly the shape that made `legacy.policyConfig()` unusable as a
359
+ // landmark, and anything treating this list as "safe to delete, it's old"
360
+ // would be deleting the current deployment. What DID change inside it is the
361
+ // shape — `deployments/<n>/` trees flattened into one `artifacts/` dir —
362
+ // which `cloud_policies.rs` prunes after the flip, not this list.
279
363
  policyConfigLocal: () => at("policies-config.local.json"),
280
364
  cloudCredentials: () => at("cloud.json"),
281
365
  ingestCredentials: () => at("ingest.json"),
@@ -326,7 +410,23 @@ export function resettablePaths(): string[] {
326
410
  legacy.collectorHealth(),
327
411
  legacy.onboardingLock(),
328
412
  legacy.cloudManagedPolicies(),
329
- // The MACHINE-OWNED children of `policies/`, never the parent.
413
+ // Layout 2's files, by their layout-2 names. Without these a layout-2 home
414
+ // upgrading to 3 keeps `config.toml` and `credentials.toml` sitting next to
415
+ // the new `.json` pair — two configs, one of them stale, and the reset that
416
+ // was supposed to clear the old layout leaving its most important files
417
+ // behind. `credentials.toml` is the worse half: a live token in a file
418
+ // nothing reads any more and nothing will clean up.
419
+ legacy.configToml(),
420
+ legacy.credentialsToml(),
421
+ legacy.localPoliciesDir(),
422
+ // NOT `legacy.customPoliciesDir()`. `migrateConventionPolicies()` empties it
423
+ // and removes it itself, and whatever it could NOT move is the user's own
424
+ // hand-written source — which this list would delete moments after that
425
+ // function deliberately decided to preserve it. That is not hypothetical:
426
+ // a `lib/` of helpers colliding with a stale `policies/lib/` was destroyed
427
+ // outright, and the policy that did move was left importing a file that no
428
+ // longer existed.
429
+ // The MACHINE-OWNED things, never the directory the user owns.
330
430
  //
331
431
  // `at("policies")` stood here, and on layout 1 that directory IS the
332
432
  // documented home for hand-written personal policies — `docs/
@@ -338,10 +438,26 @@ export function resettablePaths(): string[] {
338
438
  // that still sees the agent CLIs' untouched settings files — so the wizard
339
439
  // was skipped and hooks kept firing against an empty policy set.
340
440
  //
341
- // `custom-policies/` is deliberately absent from this list for the same
342
- // reason. Both directories below are re-derived: `local-policies/` by
343
- // setup, `cloud-policies/` by the next daemon poll.
344
- localPoliciesDir(),
441
+ // `policies/` is deliberately absent from this list for the same reason —
442
+ // the user's own convention policies sit directly in it and nothing
443
+ // re-derives them. Its `cloud-policies/` CHILD is listed instead, which is
444
+ // the whole reason this is a per-entry list and not `rm -rf policies/`:
445
+ // one directory now holds both the files a person wrote by hand and the
446
+ // ones the fleet sent, and only the second kind can be thrown away. Both
447
+ // entries below are re-derived — the policy config by setup,
448
+ // `cloud-policies/` by the next daemon poll.
449
+ //
450
+ // The policy config holds ONE thing — the user's enabled-policy selection —
451
+ // and clearing it on a layout migration is deliberate and tested. But it is
452
+ // also the destination `migratePolicyConfig()` carries layout 1's selection
453
+ // into, so the carry is written AFTER this list runs, and `resetHome` skips
454
+ // this entry when the reset is not a layout migration at all. See both.
455
+ //
456
+ // Layout 3 moved it from `policies/local-policies/policies-config.json` to
457
+ // the home root, so this is a FILE now rather than the directory that held
458
+ // it — `policies/` itself must survive, because in layout 3 it is the
459
+ // user's.
460
+ globalPolicyConfigFile(),
345
461
  cloudPoliciesDir(),
346
462
  // `at("cursors")` is deliberately NOT here, and it is load-bearing for the
347
463
  // activity migration above rather than a separate opinion. Cursors are keyed