@ambitresearch/genie 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +15 -0
  2. package/dist/auth/bearer.d.ts +49 -0
  3. package/dist/auth/bearer.d.ts.map +1 -0
  4. package/dist/auth/bearer.js +159 -0
  5. package/dist/auth/bearer.js.map +1 -0
  6. package/dist/auth/oauth/authorize.d.ts +19 -0
  7. package/dist/auth/oauth/authorize.d.ts.map +1 -0
  8. package/dist/auth/oauth/authorize.js +111 -0
  9. package/dist/auth/oauth/authorize.js.map +1 -0
  10. package/dist/auth/oauth/config.d.ts +10 -0
  11. package/dist/auth/oauth/config.d.ts.map +1 -0
  12. package/dist/auth/oauth/config.js +16 -0
  13. package/dist/auth/oauth/config.js.map +1 -0
  14. package/dist/auth/oauth/dcr.d.ts +15 -0
  15. package/dist/auth/oauth/dcr.d.ts.map +1 -0
  16. package/dist/auth/oauth/dcr.js +38 -0
  17. package/dist/auth/oauth/dcr.js.map +1 -0
  18. package/dist/auth/oauth/jwt.d.ts +13 -0
  19. package/dist/auth/oauth/jwt.d.ts.map +1 -0
  20. package/dist/auth/oauth/jwt.js +46 -0
  21. package/dist/auth/oauth/jwt.js.map +1 -0
  22. package/dist/auth/oauth/metadata.d.ts +9 -0
  23. package/dist/auth/oauth/metadata.d.ts.map +1 -0
  24. package/dist/auth/oauth/metadata.js +19 -0
  25. package/dist/auth/oauth/metadata.js.map +1 -0
  26. package/dist/auth/oauth/router.d.ts +32 -0
  27. package/dist/auth/oauth/router.d.ts.map +1 -0
  28. package/dist/auth/oauth/router.js +159 -0
  29. package/dist/auth/oauth/router.js.map +1 -0
  30. package/dist/auth/oauth/store.d.ts +55 -0
  31. package/dist/auth/oauth/store.d.ts.map +1 -0
  32. package/dist/auth/oauth/store.js +63 -0
  33. package/dist/auth/oauth/store.js.map +1 -0
  34. package/dist/auth/oauth/token.d.ts +21 -0
  35. package/dist/auth/oauth/token.d.ts.map +1 -0
  36. package/dist/auth/oauth/token.js +77 -0
  37. package/dist/auth/oauth/token.js.map +1 -0
  38. package/dist/auth/oidc/group-policy.d.ts +43 -0
  39. package/dist/auth/oidc/group-policy.d.ts.map +1 -0
  40. package/dist/auth/oidc/group-policy.js +54 -0
  41. package/dist/auth/oidc/group-policy.js.map +1 -0
  42. package/dist/auth/oidc/verifier.d.ts +42 -0
  43. package/dist/auth/oidc/verifier.d.ts.map +1 -0
  44. package/dist/auth/oidc/verifier.js +145 -0
  45. package/dist/auth/oidc/verifier.js.map +1 -0
  46. package/dist/auth/token-cli.d.ts +9 -0
  47. package/dist/auth/token-cli.d.ts.map +1 -0
  48. package/dist/auth/token-cli.js +98 -0
  49. package/dist/auth/token-cli.js.map +1 -0
  50. package/dist/cli.d.ts +3 -0
  51. package/dist/cli.d.ts.map +1 -0
  52. package/dist/cli.js +149 -0
  53. package/dist/cli.js.map +1 -0
  54. package/dist/config/redact.d.ts +20 -0
  55. package/dist/config/redact.d.ts.map +1 -0
  56. package/dist/config/redact.js +77 -0
  57. package/dist/config/redact.js.map +1 -0
  58. package/dist/config/secrets.d.ts +100 -0
  59. package/dist/config/secrets.d.ts.map +1 -0
  60. package/dist/config/secrets.js +130 -0
  61. package/dist/config/secrets.js.map +1 -0
  62. package/dist/framework/html.d.ts +103 -0
  63. package/dist/framework/html.d.ts.map +1 -0
  64. package/dist/framework/html.js +271 -0
  65. package/dist/framework/html.js.map +1 -0
  66. package/dist/framework/interface.d.ts +130 -0
  67. package/dist/framework/interface.d.ts.map +1 -0
  68. package/dist/framework/interface.js +112 -0
  69. package/dist/framework/interface.js.map +1 -0
  70. package/dist/framework/react.d.ts +133 -0
  71. package/dist/framework/react.d.ts.map +1 -0
  72. package/dist/framework/react.js +243 -0
  73. package/dist/framework/react.js.map +1 -0
  74. package/dist/framework/vue.d.ts +99 -0
  75. package/dist/framework/vue.d.ts.map +1 -0
  76. package/dist/framework/vue.js +375 -0
  77. package/dist/framework/vue.js.map +1 -0
  78. package/dist/index.d.ts +3 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +3 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/llm/client.d.ts +81 -0
  83. package/dist/llm/client.d.ts.map +1 -0
  84. package/dist/llm/client.js +141 -0
  85. package/dist/llm/client.js.map +1 -0
  86. package/dist/llm/component-response.d.ts +111 -0
  87. package/dist/llm/component-response.d.ts.map +1 -0
  88. package/dist/llm/component-response.js +167 -0
  89. package/dist/llm/component-response.js.map +1 -0
  90. package/dist/llm/generated-files.d.ts +10 -0
  91. package/dist/llm/generated-files.d.ts.map +1 -0
  92. package/dist/llm/generated-files.js +26 -0
  93. package/dist/llm/generated-files.js.map +1 -0
  94. package/dist/llm/prompts/CHANGELOG.md +70 -0
  95. package/dist/llm/prompts/generate-component.system.md +99 -0
  96. package/dist/llm/prompts/refine-component.system.md +112 -0
  97. package/dist/llm/prompts.d.ts +66 -0
  98. package/dist/llm/prompts.d.ts.map +1 -0
  99. package/dist/llm/prompts.js +78 -0
  100. package/dist/llm/prompts.js.map +1 -0
  101. package/dist/llm/retry.d.ts +183 -0
  102. package/dist/llm/retry.d.ts.map +1 -0
  103. package/dist/llm/retry.js +475 -0
  104. package/dist/llm/retry.js.map +1 -0
  105. package/dist/llm/schema.d.ts +129 -0
  106. package/dist/llm/schema.d.ts.map +1 -0
  107. package/dist/llm/schema.js +147 -0
  108. package/dist/llm/schema.js.map +1 -0
  109. package/dist/llm/validate.d.ts +107 -0
  110. package/dist/llm/validate.d.ts.map +1 -0
  111. package/dist/llm/validate.js +174 -0
  112. package/dist/llm/validate.js.map +1 -0
  113. package/dist/manifest/compiler.d.ts +112 -0
  114. package/dist/manifest/compiler.d.ts.map +1 -0
  115. package/dist/manifest/compiler.js +306 -0
  116. package/dist/manifest/compiler.js.map +1 -0
  117. package/dist/manifest/ensure.d.ts +46 -0
  118. package/dist/manifest/ensure.d.ts.map +1 -0
  119. package/dist/manifest/ensure.js +49 -0
  120. package/dist/manifest/ensure.js.map +1 -0
  121. package/dist/manifest/index.d.ts +13 -0
  122. package/dist/manifest/index.d.ts.map +1 -0
  123. package/dist/manifest/index.js +13 -0
  124. package/dist/manifest/index.js.map +1 -0
  125. package/dist/middleware/plan-guard.d.ts +167 -0
  126. package/dist/middleware/plan-guard.d.ts.map +1 -0
  127. package/dist/middleware/plan-guard.js +190 -0
  128. package/dist/middleware/plan-guard.js.map +1 -0
  129. package/dist/plans/index.d.ts +71 -0
  130. package/dist/plans/index.d.ts.map +1 -0
  131. package/dist/plans/index.js +252 -0
  132. package/dist/plans/index.js.map +1 -0
  133. package/dist/schemas/component.schema.json +114 -0
  134. package/dist/server.d.ts +96 -0
  135. package/dist/server.d.ts.map +1 -0
  136. package/dist/server.js +258 -0
  137. package/dist/server.js.map +1 -0
  138. package/dist/store/empty-manifest.d.ts +58 -0
  139. package/dist/store/empty-manifest.d.ts.map +1 -0
  140. package/dist/store/empty-manifest.js +59 -0
  141. package/dist/store/empty-manifest.js.map +1 -0
  142. package/dist/store/git-host.d.ts +120 -0
  143. package/dist/store/git-host.d.ts.map +1 -0
  144. package/dist/store/git-host.js +796 -0
  145. package/dist/store/git-host.js.map +1 -0
  146. package/dist/store/index.d.ts +5 -0
  147. package/dist/store/index.d.ts.map +1 -0
  148. package/dist/store/index.js +4 -0
  149. package/dist/store/index.js.map +1 -0
  150. package/dist/store/interface.d.ts +322 -0
  151. package/dist/store/interface.d.ts.map +1 -0
  152. package/dist/store/interface.js +100 -0
  153. package/dist/store/interface.js.map +1 -0
  154. package/dist/store/kit-files.d.ts +88 -0
  155. package/dist/store/kit-files.d.ts.map +1 -0
  156. package/dist/store/kit-files.js +199 -0
  157. package/dist/store/kit-files.js.map +1 -0
  158. package/dist/store/local.d.ts +70 -0
  159. package/dist/store/local.d.ts.map +1 -0
  160. package/dist/store/local.js +651 -0
  161. package/dist/store/local.js.map +1 -0
  162. package/dist/store/manifest.d.ts +44 -0
  163. package/dist/store/manifest.d.ts.map +1 -0
  164. package/dist/store/manifest.js +116 -0
  165. package/dist/store/manifest.js.map +1 -0
  166. package/dist/store/viewer-assets.d.ts +20 -0
  167. package/dist/store/viewer-assets.d.ts.map +1 -0
  168. package/dist/store/viewer-assets.js +118 -0
  169. package/dist/store/viewer-assets.js.map +1 -0
  170. package/dist/sync/anchor.d.ts +125 -0
  171. package/dist/sync/anchor.d.ts.map +1 -0
  172. package/dist/sync/anchor.js +210 -0
  173. package/dist/sync/anchor.js.map +1 -0
  174. package/dist/sync/index.d.ts +15 -0
  175. package/dist/sync/index.d.ts.map +1 -0
  176. package/dist/sync/index.js +15 -0
  177. package/dist/sync/index.js.map +1 -0
  178. package/dist/sync/orchestrator.d.ts +160 -0
  179. package/dist/sync/orchestrator.d.ts.map +1 -0
  180. package/dist/sync/orchestrator.js +288 -0
  181. package/dist/sync/orchestrator.js.map +1 -0
  182. package/dist/tools/bind_kit.d.ts +29 -0
  183. package/dist/tools/bind_kit.d.ts.map +1 -0
  184. package/dist/tools/bind_kit.js +70 -0
  185. package/dist/tools/bind_kit.js.map +1 -0
  186. package/dist/tools/conjure.d.ts +141 -0
  187. package/dist/tools/conjure.d.ts.map +1 -0
  188. package/dist/tools/conjure.js +655 -0
  189. package/dist/tools/conjure.js.map +1 -0
  190. package/dist/tools/conjure_screen.d.ts +174 -0
  191. package/dist/tools/conjure_screen.d.ts.map +1 -0
  192. package/dist/tools/conjure_screen.js +458 -0
  193. package/dist/tools/conjure_screen.js.map +1 -0
  194. package/dist/tools/create_kit.d.ts +32 -0
  195. package/dist/tools/create_kit.d.ts.map +1 -0
  196. package/dist/tools/create_kit.js +131 -0
  197. package/dist/tools/create_kit.js.map +1 -0
  198. package/dist/tools/create_project.d.ts +267 -0
  199. package/dist/tools/create_project.d.ts.map +1 -0
  200. package/dist/tools/create_project.js +546 -0
  201. package/dist/tools/create_project.js.map +1 -0
  202. package/dist/tools/delete_files.d.ts +93 -0
  203. package/dist/tools/delete_files.d.ts.map +1 -0
  204. package/dist/tools/delete_files.js +276 -0
  205. package/dist/tools/delete_files.js.map +1 -0
  206. package/dist/tools/delete_project.d.ts +43 -0
  207. package/dist/tools/delete_project.d.ts.map +1 -0
  208. package/dist/tools/delete_project.js +118 -0
  209. package/dist/tools/delete_project.js.map +1 -0
  210. package/dist/tools/get_kit.d.ts +28 -0
  211. package/dist/tools/get_kit.d.ts.map +1 -0
  212. package/dist/tools/get_kit.js +95 -0
  213. package/dist/tools/get_kit.js.map +1 -0
  214. package/dist/tools/get_project.d.ts +37 -0
  215. package/dist/tools/get_project.d.ts.map +1 -0
  216. package/dist/tools/get_project.js +71 -0
  217. package/dist/tools/get_project.js.map +1 -0
  218. package/dist/tools/list_components.d.ts +42 -0
  219. package/dist/tools/list_components.d.ts.map +1 -0
  220. package/dist/tools/list_components.js +125 -0
  221. package/dist/tools/list_components.js.map +1 -0
  222. package/dist/tools/list_files.d.ts +32 -0
  223. package/dist/tools/list_files.d.ts.map +1 -0
  224. package/dist/tools/list_files.js +109 -0
  225. package/dist/tools/list_files.js.map +1 -0
  226. package/dist/tools/list_kits.d.ts +14 -0
  227. package/dist/tools/list_kits.d.ts.map +1 -0
  228. package/dist/tools/list_kits.js +41 -0
  229. package/dist/tools/list_kits.js.map +1 -0
  230. package/dist/tools/list_projects.d.ts +23 -0
  231. package/dist/tools/list_projects.d.ts.map +1 -0
  232. package/dist/tools/list_projects.js +66 -0
  233. package/dist/tools/list_projects.js.map +1 -0
  234. package/dist/tools/plan.d.ts +16 -0
  235. package/dist/tools/plan.d.ts.map +1 -0
  236. package/dist/tools/plan.js +146 -0
  237. package/dist/tools/plan.js.map +1 -0
  238. package/dist/tools/preview.d.ts +289 -0
  239. package/dist/tools/preview.d.ts.map +1 -0
  240. package/dist/tools/preview.js +672 -0
  241. package/dist/tools/preview.js.map +1 -0
  242. package/dist/tools/read_file.d.ts +40 -0
  243. package/dist/tools/read_file.d.ts.map +1 -0
  244. package/dist/tools/read_file.js +114 -0
  245. package/dist/tools/read_file.js.map +1 -0
  246. package/dist/tools/refine.d.ts +220 -0
  247. package/dist/tools/refine.d.ts.map +1 -0
  248. package/dist/tools/refine.js +501 -0
  249. package/dist/tools/refine.js.map +1 -0
  250. package/dist/tools/validate.d.ts +74 -0
  251. package/dist/tools/validate.d.ts.map +1 -0
  252. package/dist/tools/validate.js +224 -0
  253. package/dist/tools/validate.js.map +1 -0
  254. package/dist/tools/write_files.d.ts +158 -0
  255. package/dist/tools/write_files.d.ts.map +1 -0
  256. package/dist/tools/write_files.js +541 -0
  257. package/dist/tools/write_files.js.map +1 -0
  258. package/dist/transport.d.ts +77 -0
  259. package/dist/transport.d.ts.map +1 -0
  260. package/dist/transport.js +448 -0
  261. package/dist/transport.js.map +1 -0
  262. package/dist/ui/card-asset-broker.d.ts +42 -0
  263. package/dist/ui/card-asset-broker.d.ts.map +1 -0
  264. package/dist/ui/card-asset-broker.js +479 -0
  265. package/dist/ui/card-asset-broker.js.map +1 -0
  266. package/dist/ui/grid-resource.d.ts +252 -0
  267. package/dist/ui/grid-resource.d.ts.map +1 -0
  268. package/dist/ui/grid-resource.js +680 -0
  269. package/dist/ui/grid-resource.js.map +1 -0
  270. package/dist/ui/viewer-static/index.html +95 -0
  271. package/dist/ui/viewer-static/viewer.css +451 -0
  272. package/dist/ui/viewer-static/viewer.js +1405 -0
  273. package/dist/validate/full-scan.d.ts +87 -0
  274. package/dist/validate/full-scan.d.ts.map +1 -0
  275. package/dist/validate/full-scan.js +299 -0
  276. package/dist/validate/full-scan.js.map +1 -0
  277. package/dist/validate/index.d.ts +15 -0
  278. package/dist/validate/index.d.ts.map +1 -0
  279. package/dist/validate/index.js +18 -0
  280. package/dist/validate/index.js.map +1 -0
  281. package/dist/validate/marker.d.ts +89 -0
  282. package/dist/validate/marker.d.ts.map +1 -0
  283. package/dist/validate/marker.js +87 -0
  284. package/dist/validate/marker.js.map +1 -0
  285. package/dist/validate/phash.d.ts +135 -0
  286. package/dist/validate/phash.d.ts.map +1 -0
  287. package/dist/validate/phash.js +307 -0
  288. package/dist/validate/phash.js.map +1 -0
  289. package/dist/validate/render.d.ts +38 -0
  290. package/dist/validate/render.d.ts.map +1 -0
  291. package/dist/validate/render.js +155 -0
  292. package/dist/validate/render.js.map +1 -0
  293. package/dist/watch/index.d.ts +12 -0
  294. package/dist/watch/index.d.ts.map +1 -0
  295. package/dist/watch/index.js +12 -0
  296. package/dist/watch/index.js.map +1 -0
  297. package/dist/watch/watcher.d.ts +65 -0
  298. package/dist/watch/watcher.d.ts.map +1 -0
  299. package/dist/watch/watcher.js +238 -0
  300. package/dist/watch/watcher.js.map +1 -0
  301. package/package.json +70 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * M3-06 (DRO-262) — genie's own `.genie/sync.json` verification anchor.
3
+ *
4
+ * `.genie/sync.json` is the LAST file the atomic sync sequence writes: it
5
+ * records the source/render hashes and the
6
+ * `@genie`-validated component list for whatever the sync just wrote, so the
7
+ * *next* sync can diff against it and repair a half-completed prior run. A
8
+ * mid-plan failure must leave this file unwritten (or stale) — never
9
+ * partially written — which is why {@link writeAnchor} always does a
10
+ * temp-file + rename commit (AC7), the same durability pattern
11
+ * `store/local.ts`'s `stageAndCommit` and `plans/index.ts`'s plan snapshots
12
+ * use elsewhere in this codebase.
13
+ *
14
+ * This is genie's **native** anchor shape — NOT the Anthropic `_ds_sync.json`
15
+ * interop shape (CLAUDE.md hard rule 1 / AGENTS.md hard rule 1). An interop
16
+ * adapter may one day map this to `_ds_sync.json`; that mapping is out of
17
+ * scope here.
18
+ *
19
+ * ── Coordination with M3-05 (DRO-261) ───────────────────────────────────────
20
+ * The 5-step atomic orchestrator (M3-05) calls {@link writeAnchor} as its
21
+ * Step 5 (last write). That issue was still unbuilt on `main` as of this PR,
22
+ * so {@link PlanResult} is this module's own minimal seam — "the set of
23
+ * writes a sync touched, plus which components passed validation" — rather
24
+ * than importing a type from a module that doesn't exist yet. Whichever of
25
+ * M3-05 / M3-06 lands second adapts to the other's shape; the signature
26
+ * `writeAnchor(projectRoot, planResult)` itself (AC1) is the stable contract
27
+ * both issues were told to converge on.
28
+ */
29
+ import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
30
+ import { join } from "node:path";
31
+ import { z } from "zod";
32
+ import { sriSha256 } from "../store/kit-files.js";
33
+ // ─── Public shape (AC2) ──────────────────────────────────────────────────────
34
+ /** Kit-root-relative path of genie's verification anchor (D-C). */
35
+ export const ANCHOR_PATH = ".genie/sync.json";
36
+ /** Env var overriding {@link Anchor.by} (AC6) — for forks of genie. */
37
+ export const GENIE_BY_ENV = "GENIE_BY";
38
+ /** Default {@link Anchor.by} value when `GENIE_BY` is unset (AC6). */
39
+ export const DEFAULT_BY = "genie";
40
+ /**
41
+ * Source-file extensions that count toward {@link Anchor.sourceHashes} (AC3).
42
+ * The issue's literal AC3 text says ".tsx/.jsx", written when React was the
43
+ * only shipped framework; Vue landed as a first-class framework in M2-08
44
+ * (merged to `main` the day before this PR) with `.vue` as its canonical
45
+ * single-file-component source (`framework/vue.ts`'s `renderSource` →
46
+ * `<Name>.vue`). Omitting `.vue` here would leave every Vue component's
47
+ * source invisible to the anchor's drift/tamper detection (RFC §10 T-05) —
48
+ * the exact failure mode this file exists to prevent — so the extension list
49
+ * covers both shipped component-source frameworks rather than only the one
50
+ * named in the issue's illustrative example.
51
+ *
52
+ * `.html` is deliberately **not** in this list even though vanilla HTML is now a
53
+ * first-class framework (`HtmlAdapter.renderSource` → `<Name>.html`, DRO-617).
54
+ * For a vanilla-HTML component the source file *is* the browser-ready
55
+ * `<Name>.html` preview — so it is already hash-covered by {@link Anchor.renderHashes}
56
+ * (which hashes every `.html` write, AC4), giving the anchor's drift/tamper
57
+ * detection full coverage of that source with no gap. Adding `.html` here instead
58
+ * would double-count it *and* mislabel every React/Vue `<Name>.html` — which is a
59
+ * *rendered* preview, not source — as `sourceHashes`, so the extension split stays
60
+ * "compiled-source suffixes → sourceHashes, `.html` → renderHashes" and HTML's
61
+ * source rides the render side by construction.
62
+ */
63
+ const SOURCE_EXTENSIONS = [".tsx", ".jsx", ".vue"];
64
+ /** Thrown by {@link readAnchor} when `.genie/sync.json` exists but is not valid genie anchor JSON. */
65
+ export class AnchorParseError extends Error {
66
+ projectRoot;
67
+ reason;
68
+ constructor(projectRoot, reason) {
69
+ super(`Anchor at "${projectRoot}/${ANCHOR_PATH}" is malformed: ${reason}`);
70
+ this.projectRoot = projectRoot;
71
+ this.reason = reason;
72
+ this.name = "AnchorParseError";
73
+ }
74
+ }
75
+ // ─── Schema (validated on read; see AnchorParseError above) ─────────────────
76
+ const anchorSchema = z.object({
77
+ version: z.literal(1),
78
+ writtenAt: z.string(),
79
+ by: z.string(),
80
+ sourceHashes: z.record(z.string(), z.string()),
81
+ renderHashes: z.record(z.string(), z.string()),
82
+ verified: z.array(z.string()),
83
+ });
84
+ // ─── writeAnchor (AC1, AC3–AC7) ──────────────────────────────────────────────
85
+ /**
86
+ * Compute and atomically write `${projectRoot}/.genie/sync.json` (AC1).
87
+ *
88
+ * - `sourceHashes` covers every {@link SOURCE_EXTENSIONS} path in
89
+ * `planResult.writes` (AC3 — `.tsx`/`.jsx`, plus `.vue` for the shipped Vue
90
+ * framework); `renderHashes` covers every `.html` path (AC4). A path with
91
+ * none of those extensions (e.g. `meta.json`) appears in neither map — this
92
+ * anchor is scoped to source + render provenance, not a general write log.
93
+ * - `verified` is `planResult.verified` verbatim (AC5) — the caller (the
94
+ * M3-05 orchestrator, or a direct test) is the one that ran the M3-04
95
+ * validator and knows which `<group>/<Name>` ids passed.
96
+ * - `by` is {@link DEFAULT_BY} unless overridden by `env.GENIE_BY` (AC6).
97
+ * - The write is atomic (AC7): content lands in a temp file staged under
98
+ * `<projectRoot>/.genie-tmp/` — a different directory from the `.genie/`
99
+ * destination, but the same filesystem/mount, which is the one guarantee
100
+ * POSIX `rename()` actually needs — then renamed over the destination. A
101
+ * crash between the temp write and the rename leaves the PRIOR anchor (or
102
+ * no anchor) intact — never a half-written one.
103
+ *
104
+ * @param env Injectable environment (defaults to `process.env`), so tests can
105
+ * assert the `GENIE_BY` override without mutating global state — the same
106
+ * pattern `write_files.ts`'s `resolveByteCap(env)` uses for
107
+ * `GENIE_WRITE_BYTE_CAP`.
108
+ */
109
+ export async function writeAnchor(projectRoot, planResult, env = process.env) {
110
+ const anchor = {
111
+ version: 1,
112
+ writtenAt: new Date().toISOString(),
113
+ by: env[GENIE_BY_ENV] ?? DEFAULT_BY,
114
+ sourceHashes: hashByExtension(planResult.writes, SOURCE_EXTENSIONS),
115
+ renderHashes: hashByExtension(planResult.writes, [".html"]),
116
+ verified: [...planResult.verified],
117
+ };
118
+ const destPath = join(projectRoot, ANCHOR_PATH);
119
+ await mkdir(join(projectRoot, ".genie"), { recursive: true });
120
+ // Stage under `<projectRoot>/.genie-tmp/` (NOT os.tmpdir()) so the final
121
+ // rename stays on the same filesystem — mirrors the load-bearing reasoning
122
+ // in store/local.ts's stageAndCommit (a kit dir and /tmp are commonly
123
+ // different mounts, and rename() is only atomic within one filesystem; it
124
+ // does NOT require source and destination to share a directory). Reusing
125
+ // `.genie-tmp` (rather than a new `.genie/`-nested
126
+ // staging dir) matters beyond consistency: `store/kit-files.ts`'s
127
+ // `DEFAULT_IGNORED_SEGMENTS` already excludes `.genie-tmp` from every kit
128
+ // listing, so a `list_files` call landing mid-write never surfaces this
129
+ // staging directory — the same guarantee `write_files`' own commit already
130
+ // gets from that shared exclusion.
131
+ const genieTmpRoot = join(projectRoot, ".genie-tmp");
132
+ await mkdir(genieTmpRoot, { recursive: true });
133
+ const stagingRoot = await mkdtemp(join(genieTmpRoot, "sync-"));
134
+ try {
135
+ const stagedPath = join(stagingRoot, "sync.json");
136
+ await writeFile(stagedPath, JSON.stringify(anchor, null, 2), "utf-8");
137
+ await rename(stagedPath, destPath);
138
+ }
139
+ finally {
140
+ await rm(stagingRoot, { recursive: true, force: true });
141
+ }
142
+ }
143
+ /**
144
+ * Build a `path -> sha256-<base64>` map over every `planResult.writes` entry
145
+ * whose path ends in one of `extensions` (AC3/AC4). Hashes the exact bytes
146
+ * handed in (`sriSha256` accepts either a `Buffer` or a `string`), never a
147
+ * fresh disk read — the anchor vouches for what THIS sync wrote, not
148
+ * whatever happens to be on disk afterward.
149
+ */
150
+ function hashByExtension(writes, extensions) {
151
+ const result = {};
152
+ for (const { path, content } of writes) {
153
+ if (extensions.some((ext) => path.endsWith(ext))) {
154
+ result[path] = sriSha256(content);
155
+ }
156
+ }
157
+ return result;
158
+ }
159
+ // ─── readAnchor (AC1, AC8) ────────────────────────────────────────────────────
160
+ /**
161
+ * Read + parse `${projectRoot}/.genie/sync.json` (AC1).
162
+ *
163
+ * - Returns `null` — never throws — when the file (or the whole `.genie`
164
+ * dir, or `projectRoot` itself) does not exist (AC8): a fresh kit with no
165
+ * prior sync is a normal, expected state, not an error.
166
+ * - Throws {@link AnchorParseError} when the file EXISTS but is not valid
167
+ * JSON, or is valid JSON that doesn't match the {@link Anchor} shape
168
+ * (including a `version` other than the literal `1`). This is
169
+ * deliberately NOT folded into the `null` case: `null` means "no anchor
170
+ * yet, that's fine"; a corrupt-but-present anchor is a real operability
171
+ * problem for a file whose entire purpose is verification (RFC §10 T-05
172
+ * flags anchor tampering) and must surface rather than silently
173
+ * masquerade as "nothing to verify yet" — the same reasoning
174
+ * `store/manifest.ts`'s `ManifestParseError` already applies to
175
+ * `manifest.json`.
176
+ */
177
+ export async function readAnchor(projectRoot) {
178
+ const destPath = join(projectRoot, ANCHOR_PATH);
179
+ let raw;
180
+ try {
181
+ raw = await readFile(destPath, "utf-8");
182
+ }
183
+ catch (error) {
184
+ if (isMissingPathError(error))
185
+ return null;
186
+ throw error;
187
+ }
188
+ let json;
189
+ try {
190
+ json = JSON.parse(raw);
191
+ }
192
+ catch (error) {
193
+ throw new AnchorParseError(projectRoot, describeError(error, "invalid JSON"));
194
+ }
195
+ const parsed = anchorSchema.safeParse(json);
196
+ if (!parsed.success) {
197
+ throw new AnchorParseError(projectRoot, parsed.error.issues[0]?.message ?? "schema mismatch");
198
+ }
199
+ return parsed.data;
200
+ }
201
+ /** ENOENT (missing file) and ENOTDIR (a parent component is a file) both mean "not there". */
202
+ function isMissingPathError(error) {
203
+ return (error instanceof Error &&
204
+ "code" in error &&
205
+ (error.code === "ENOENT" || error.code === "ENOTDIR"));
206
+ }
207
+ function describeError(error, fallback) {
208
+ return error instanceof Error ? error.message : fallback;
209
+ }
210
+ //# sourceMappingURL=anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../src/sync/anchor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Public re-export barrel for genie's sync layer:
3
+ * - M3-06 (`anchor.ts`) — the `.genie/sync.json` verification anchor.
4
+ * - M3-05 (`orchestrator.ts`) — the 5-step atomic write-sequence orchestrator
5
+ * that writes that anchor last.
6
+ *
7
+ * Mirrors the existing `store/index.ts` / `validate/index.ts` barrel pattern:
8
+ * downstream consumers (the sync-flow caller wiring generation → commit, and any
9
+ * future consumer outside this directory) import from `../sync/index.js` rather
10
+ * than reaching into `./anchor.js` / `./orchestrator.js` directly, so the
11
+ * module's internal file layout can change without breaking callers.
12
+ */
13
+ export { ANCHOR_PATH, GENIE_BY_ENV, DEFAULT_BY, writeAnchor, readAnchor, AnchorParseError, type Anchor, type PlanResult, } from "./anchor.js";
14
+ export { RECOMPILE_SENTINEL_PATH, RECOMPILE_SENTINEL_BODY, runAtomicSync, detectResumeStep, type StepNumber, type StepEvent, type SyncResult, type WriteInput, type SyncArgs, type SyncDeps, } from "./orchestrator.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,KAAK,MAAM,EACX,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Public re-export barrel for genie's sync layer:
3
+ * - M3-06 (`anchor.ts`) — the `.genie/sync.json` verification anchor.
4
+ * - M3-05 (`orchestrator.ts`) — the 5-step atomic write-sequence orchestrator
5
+ * that writes that anchor last.
6
+ *
7
+ * Mirrors the existing `store/index.ts` / `validate/index.ts` barrel pattern:
8
+ * downstream consumers (the sync-flow caller wiring generation → commit, and any
9
+ * future consumer outside this directory) import from `../sync/index.js` rather
10
+ * than reaching into `./anchor.js` / `./orchestrator.js` directly, so the
11
+ * module's internal file layout can change without breaking callers.
12
+ */
13
+ export { ANCHOR_PATH, GENIE_BY_ENV, DEFAULT_BY, writeAnchor, readAnchor, AnchorParseError, } from "./anchor.js";
14
+ export { RECOMPILE_SENTINEL_PATH, RECOMPILE_SENTINEL_BODY, runAtomicSync, detectResumeStep, } from "./orchestrator.js";
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,GAGjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,GAOjB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * M3-05 (DRO-261) — genie's 5-step atomic write-sequence orchestrator.
3
+ *
4
+ * `runAtomicSync` sequences one sync commit as five ordered, stop-on-first-
5
+ * failure steps. The atomic write sequence is load-bearing and unchanged in
6
+ * shape:
7
+ *
8
+ * 1. Write the `.genie/recompile` sentinel FIRST, to fence the manifest/copy
9
+ * machinery — a consumer that sees the sentinel knows a sync is mid-flight
10
+ * and the tree may be inconsistent until the anchor lands.
11
+ * 2. Apply every content write, chunked into batches of ≤ 256 per
12
+ * `write_files` call (the M1-08 `MAX_FILES_PER_CALL` ceiling — a 257-file
13
+ * call would be rejected as `TooManyFilesError`).
14
+ * 3. Apply every delete via one `delete_files` call. A path that is
15
+ * authorized-but-absent is NOT a failure (M1-09 AC5 returns it in
16
+ * `notFoundPaths`) — only a real error (out-of-plan path, unlink failure)
17
+ * trips stop-on-first-failure.
18
+ * 4. RE-ARM the sentinel (write it again), so it still fences the tree after
19
+ * the writes/deletes landed and right up until the anchor is committed.
20
+ * 5. Write `.genie/sync.json` LAST, via M3-06's {@link writeAnchor}. It is the
21
+ * completion proof: its presence means every prior step succeeded.
22
+ *
23
+ * ── Why the anchor is last (AC7) ─────────────────────────────────────────────
24
+ * A mid-plan failure (steps 1–4) returns before step 5, so `.genie/sync.json`
25
+ * is never written for an incomplete sync. The NEXT sync sees "sentinel present,
26
+ * anchor absent" ({@link detectResumeStep} → 2) and knows the tree is a
27
+ * half-write to repair from step 2 — rather than trusting a stale anchor that
28
+ * would mask the incomplete state. This is the whole reason the write order is
29
+ * load-bearing.
30
+ *
31
+ * ── Native surface (CLAUDE.md hard rule 1) ───────────────────────────────────
32
+ * `.genie/recompile` and `.genie/sync.json` are genie-NATIVE artifacts. They are
33
+ * written fs-native to `projectRoot` (NOT through the plan-gated `write_files`
34
+ * verb — they would never match a user plan's `writes` globs, and they are
35
+ * genie's own bookkeeping, not user content). They must NOT be renamed to the
36
+ * Anthropic `_ds_recompile` / `_ds_sync.json` interop shapes; an interop bridge
37
+ * that maps them is a separate, opt-in concern.
38
+ *
39
+ * ── Composition (Kiln's plan, DRO-696) ───────────────────────────────────────
40
+ * Steps 2–3 call the in-process `writeFiles` / `deleteFiles` CORE functions
41
+ * directly (not over the MCP transport) — the same way `conjure_screen.ts`
42
+ * composes tool-layer logic — so the orchestrator reuses their full plan-gating,
43
+ * atomicity, and rollback contracts without re-implementing any of it.
44
+ */
45
+ import type { KitStore } from "../store/interface.js";
46
+ /** Kit-root-relative path of genie's recompile fence sentinel (native surface). */
47
+ export declare const RECOMPILE_SENTINEL_PATH = ".genie/recompile";
48
+ /**
49
+ * The exact sentinel body (AC2). genie's native `{"by":"genie"}` marker — NOT
50
+ * the Anthropic `_ds_recompile` interop shape (CLAUDE.md hard rule 1). Kept as
51
+ * a compact single-line literal so a byte-for-byte assertion is trivial and the
52
+ * re-arm in step 4 reproduces it exactly.
53
+ */
54
+ export declare const RECOMPILE_SENTINEL_BODY = "{\"by\":\"genie\"}";
55
+ /** The five ordered steps of one atomic sync. */
56
+ export type StepNumber = 1 | 2 | 3 | 4 | 5;
57
+ /**
58
+ * One observability event per attempted step (AC9): which `step`, whether it
59
+ * succeeded (`ok`), and how long it took (`ms`, wall-clock via
60
+ * `performance.now()`). Emitted for EVERY attempted step — including the one
61
+ * that failed — and never for a step the sequence stopped short of.
62
+ */
63
+ export interface StepEvent {
64
+ step: StepNumber;
65
+ ok: boolean;
66
+ ms: number;
67
+ }
68
+ /**
69
+ * Result of one `runAtomicSync` call (AC1, AC7).
70
+ * - Success — every step landed; `.genie/sync.json` was written last.
71
+ * - Failure — a step BEFORE the anchor failed; `.genie/sync.json` was NOT
72
+ * written. `failedStep` is the 1-based step that threw, `error` is the
73
+ * underlying cause, and `events` records every step attempted up to and
74
+ * including the failure.
75
+ */
76
+ export type SyncResult = {
77
+ ok: true;
78
+ events: StepEvent[];
79
+ } | {
80
+ ok: false;
81
+ failedStep: StepNumber;
82
+ error: Error;
83
+ events: StepEvent[];
84
+ };
85
+ /**
86
+ * One content write for the sync. Structurally the same as `write_files`'
87
+ * per-file input, so the batch passes straight through to the plan-gated
88
+ * `writeFiles` core fn: exactly one of `data` (inline bytes — the common
89
+ * case for genie-generated component source/render) or `localPath` (a source
90
+ * path resolved against the plan's `localDir`) must be set.
91
+ */
92
+ export interface WriteInput {
93
+ path: string;
94
+ data?: string;
95
+ encoding?: "utf-8" | "base64";
96
+ localPath?: string;
97
+ mimeType?: string;
98
+ }
99
+ /**
100
+ * Arguments to {@link runAtomicSync} (AC1). The issue's literal signature is
101
+ * `{ planId, writes, deletes }`; `verified` is threaded through additively so
102
+ * step 5's anchor can carry the `<group>/<Name>` ids that passed the M3-04
103
+ * validator within THIS sync (M3-06's `PlanResult.verified`). It is optional and
104
+ * defaults to `[]`, so the literal AC1 shape still type-checks and runs — the
105
+ * two issues were told to converge on each other's shape (see `anchor.ts`'s
106
+ * module-doc coordination note), and this is that convergence.
107
+ */
108
+ export interface SyncArgs {
109
+ planId: string;
110
+ writes: WriteInput[];
111
+ deletes: string[];
112
+ /** `<group>/<Name>` ids that passed validation this sync (M3-06 anchor). */
113
+ verified?: string[];
114
+ }
115
+ /**
116
+ * Injected collaborators (mirrors `conjure_screen.ts`'s `ConjureScreenDeps`).
117
+ *
118
+ * - `store` — the kit backend steps 2–3 write/delete through (LocalFs or
119
+ * GitHost), behind the same `KitStore` seam every M1 verb uses.
120
+ * - `projectRoot` — the on-disk kit root the native sentinel + anchor are
121
+ * written to. CONTRACT: this MUST be the same directory the injected `store`
122
+ * resolves the plan's `kitId` to (for `LocalFsKitStore`,
123
+ * `<baseDir>/<kitId>`), so the fs-native sentinel/anchor and the
124
+ * store-written content land in ONE tree. The sync-flow caller computes it;
125
+ * the orchestrator takes it explicitly, matching `writeAnchor`'s own
126
+ * `projectRoot`-first signature (M3-06).
127
+ */
128
+ export interface SyncDeps {
129
+ store: KitStore;
130
+ projectRoot: string;
131
+ }
132
+ /**
133
+ * Run the 5-step atomic sync sequence. Stops on the first failing step (AC7),
134
+ * leaving `.genie/sync.json` unwritten so the next sync's diff repairs the
135
+ * half-write; a not-found delete inside step 3 is NOT a failure (AC4). Every
136
+ * attempted step emits a {@link StepEvent} (AC9).
137
+ */
138
+ export declare function runAtomicSync(deps: SyncDeps, args: SyncArgs): Promise<SyncResult>;
139
+ /**
140
+ * Inspect a kit root and report where the NEXT sync should resume from (AC8):
141
+ *
142
+ * - `2` — sentinel present, anchor ABSENT: a prior sync fenced the tree
143
+ * (step 1) then crashed before the anchor landed (steps 2–4). The
144
+ * tree is a half-write; resume from step 2 to re-apply
145
+ * writes/deletes and re-arm before writing the anchor.
146
+ * - `1` — sentinel AND anchor both absent: a fresh, never-synced kit.
147
+ * Start a normal sync from step 1.
148
+ * - `null` — the anchor is present (a prior sync completed cleanly): there is
149
+ * no partial-write to resume; the next sync is a NEW plan from step
150
+ * 1 with fresh inputs, not a resume.
151
+ *
152
+ * Never mutates the tree. Propagates {@link readAnchor}'s `AnchorParseError`
153
+ * for a corrupt-but-present anchor — a corrupt anchor is a real operability
154
+ * problem, not a "nothing to resume" signal (same reasoning `readAnchor` itself
155
+ * applies). Likewise propagates any non-"missing" error from the sentinel
156
+ * `stat` (e.g. `EACCES` on `.genie/`) rather than treating it as "sentinel
157
+ * absent" — see {@link pathExists}.
158
+ */
159
+ export declare function detectResumeStep(projectRoot: string): Promise<StepNumber | null>;
160
+ //# sourceMappingURL=orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/sync/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAOtD,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,qBAAqB,CAAC;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,uBAAmB,CAAC;AAIxD,iDAAiD;AACjD,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,EAAE,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AAE7E;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CA8BvF;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAMtF"}