@stravica-ai/digital-operator-pa 0.1.0

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 (372) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/bin/desk +9 -0
  4. package/bin/distill +2 -0
  5. package/bin/estate-sync +2 -0
  6. package/bin/launch +2 -0
  7. package/bin/memory-sync +2 -0
  8. package/bin/migrate-inspect +2 -0
  9. package/bin/pa +2 -0
  10. package/bin/pa-coordinator-hook +2 -0
  11. package/bin/pa-init +2 -0
  12. package/bin/research +2 -0
  13. package/bin/scheduler +2 -0
  14. package/bin/secrets +2 -0
  15. package/bin/slice +2 -0
  16. package/bin/smoke +9 -0
  17. package/bin/sync +2 -0
  18. package/dist/engineering-pa-cli/README.md +205 -0
  19. package/dist/engineering-pa-cli/bin/estate-sync +2 -0
  20. package/dist/engineering-pa-cli/bin/launch +2 -0
  21. package/dist/engineering-pa-cli/bin/pa +2 -0
  22. package/dist/engineering-pa-cli/bin/pa-coordinator-hook +12 -0
  23. package/dist/engineering-pa-cli/bin/research +2 -0
  24. package/dist/engineering-pa-cli/bin/scheduler +2 -0
  25. package/dist/engineering-pa-cli/bin/secrets +2 -0
  26. package/dist/engineering-pa-cli/bin/smoke +9 -0
  27. package/dist/engineering-pa-cli/bin/sync +2 -0
  28. package/dist/engineering-pa-cli/config/coordinator-allowlist.json +118 -0
  29. package/dist/engineering-pa-cli/docs/coordinator-hook.md +211 -0
  30. package/dist/engineering-pa-cli/docs/estate-sync.md +412 -0
  31. package/dist/engineering-pa-cli/docs/gsc.md +175 -0
  32. package/dist/engineering-pa-cli/docs/lint-linkedin.md +139 -0
  33. package/dist/engineering-pa-cli/docs/secrets.md +221 -0
  34. package/dist/engineering-pa-cli/docs/smoke.md +113 -0
  35. package/dist/engineering-pa-cli/docs/staging-alias.md +114 -0
  36. package/dist/engineering-pa-cli/index.mjs +17 -0
  37. package/dist/engineering-pa-cli/package.json +37 -0
  38. package/dist/engineering-pa-cli/src/autonomy/cli.mjs +329 -0
  39. package/dist/engineering-pa-cli/src/autonomy/envelope.mjs +301 -0
  40. package/dist/engineering-pa-cli/src/coordinator-hook/cli.mjs +140 -0
  41. package/dist/engineering-pa-cli/src/coordinator-hook/config.mjs +134 -0
  42. package/dist/engineering-pa-cli/src/coordinator-hook/message.mjs +47 -0
  43. package/dist/engineering-pa-cli/src/coordinator-hook/policy.mjs +211 -0
  44. package/dist/engineering-pa-cli/src/coordinator-hook/repo-root.mjs +44 -0
  45. package/dist/engineering-pa-cli/src/coordinator-hook/self.mjs +30 -0
  46. package/dist/engineering-pa-cli/src/environment/cli.mjs +287 -0
  47. package/dist/engineering-pa-cli/src/environment/probes.mjs +302 -0
  48. package/dist/engineering-pa-cli/src/gsc/auth.mjs +148 -0
  49. package/dist/engineering-pa-cli/src/gsc/cli.mjs +93 -0
  50. package/dist/engineering-pa-cli/src/gsc/cloudflare.mjs +318 -0
  51. package/dist/engineering-pa-cli/src/gsc/commands/add-property.mjs +70 -0
  52. package/dist/engineering-pa-cli/src/gsc/commands/list-properties.mjs +33 -0
  53. package/dist/engineering-pa-cli/src/gsc/commands/pull-analytics.mjs +130 -0
  54. package/dist/engineering-pa-cli/src/gsc/commands/submit-sitemap.mjs +41 -0
  55. package/dist/engineering-pa-cli/src/gsc/commands/verify-via-cf-dns.mjs +189 -0
  56. package/dist/engineering-pa-cli/src/knowledge/call-log.mjs +59 -0
  57. package/dist/engineering-pa-cli/src/knowledge/cli.mjs +242 -0
  58. package/dist/engineering-pa-cli/src/knowledge/eval.mjs +446 -0
  59. package/dist/engineering-pa-cli/src/knowledge/formatters.mjs +331 -0
  60. package/dist/engineering-pa-cli/src/knowledge/graph.mjs +710 -0
  61. package/dist/engineering-pa-cli/src/launch/README.md +214 -0
  62. package/dist/engineering-pa-cli/src/launch/cli.mjs +51 -0
  63. package/dist/engineering-pa-cli/src/launch/commands/launch.mjs +106 -0
  64. package/dist/engineering-pa-cli/src/launch/identity.mjs +43 -0
  65. package/dist/engineering-pa-cli/src/launch/planner.mjs +44 -0
  66. package/dist/engineering-pa-cli/src/launch/repo-root.mjs +53 -0
  67. package/dist/engineering-pa-cli/src/launch/runner.mjs +147 -0
  68. package/dist/engineering-pa-cli/src/launch/task-loader.mjs +85 -0
  69. package/dist/engineering-pa-cli/src/lint/cli.mjs +61 -0
  70. package/dist/engineering-pa-cli/src/lint/commands/linkedin.mjs +133 -0
  71. package/dist/engineering-pa-cli/src/lint/detectors.mjs +90 -0
  72. package/dist/engineering-pa-cli/src/lint/linter.mjs +69 -0
  73. package/dist/engineering-pa-cli/src/lint/reporter.mjs +32 -0
  74. package/dist/engineering-pa-cli/src/lint/rules.mjs +132 -0
  75. package/dist/engineering-pa-cli/src/pa/cli.mjs +369 -0
  76. package/dist/engineering-pa-cli/src/pa/doc-groups.mjs +164 -0
  77. package/dist/engineering-pa-cli/src/pa/io.mjs +80 -0
  78. package/dist/engineering-pa-cli/src/pa/project-cli.mjs +385 -0
  79. package/dist/engineering-pa-cli/src/pa/projects.mjs +352 -0
  80. package/dist/engineering-pa-cli/src/pa/repo-root.mjs +64 -0
  81. package/dist/engineering-pa-cli/src/pa/session.mjs +254 -0
  82. package/dist/engineering-pa-cli/src/pa/work-cli.mjs +164 -0
  83. package/dist/engineering-pa-cli/src/pa/work.mjs +196 -0
  84. package/dist/engineering-pa-cli/src/pa/workstreams.mjs +201 -0
  85. package/dist/engineering-pa-cli/src/research/README.md +360 -0
  86. package/dist/engineering-pa-cli/src/research/briefs/extractor.md +103 -0
  87. package/dist/engineering-pa-cli/src/research/briefs/searcher.md +113 -0
  88. package/dist/engineering-pa-cli/src/research/briefs/synthesizer.md +124 -0
  89. package/dist/engineering-pa-cli/src/research/briefs/verifier.md +86 -0
  90. package/dist/engineering-pa-cli/src/research/cli.mjs +141 -0
  91. package/dist/engineering-pa-cli/src/research/commands/gates.mjs +176 -0
  92. package/dist/engineering-pa-cli/src/research/commands/list.mjs +56 -0
  93. package/dist/engineering-pa-cli/src/research/commands/mark.mjs +97 -0
  94. package/dist/engineering-pa-cli/src/research/commands/paths.mjs +69 -0
  95. package/dist/engineering-pa-cli/src/research/commands/prune.mjs +86 -0
  96. package/dist/engineering-pa-cli/src/research/commands/run.mjs +94 -0
  97. package/dist/engineering-pa-cli/src/research/commands/show.mjs +43 -0
  98. package/dist/engineering-pa-cli/src/research/docs/operator-present-flow.md +120 -0
  99. package/dist/engineering-pa-cli/src/research/format.mjs +59 -0
  100. package/dist/engineering-pa-cli/src/research/promote.mjs +260 -0
  101. package/dist/engineering-pa-cli/src/research/schema/finding.schema.json +55 -0
  102. package/dist/engineering-pa-cli/src/research/schema/gates.schema.json +34 -0
  103. package/dist/engineering-pa-cli/src/research/schema/manifest.schema.json +59 -0
  104. package/dist/engineering-pa-cli/src/research/schema/plan.schema.json +41 -0
  105. package/dist/engineering-pa-cli/src/research/schema/sources.schema.json +31 -0
  106. package/dist/engineering-pa-cli/src/research/store/findings.mjs +61 -0
  107. package/dist/engineering-pa-cli/src/research/store/gates.mjs +73 -0
  108. package/dist/engineering-pa-cli/src/research/store/index.mjs +312 -0
  109. package/dist/engineering-pa-cli/src/research/store/manifest.mjs +56 -0
  110. package/dist/engineering-pa-cli/src/research/store/plan.mjs +59 -0
  111. package/dist/engineering-pa-cli/src/research/store/sources.mjs +44 -0
  112. package/dist/engineering-pa-cli/src/research/store/validate.mjs +92 -0
  113. package/dist/engineering-pa-cli/src/research/workflows/research-normal.js +573 -0
  114. package/dist/engineering-pa-cli/src/scheduler/cli.mjs +197 -0
  115. package/dist/engineering-pa-cli/src/secrets/cli.mjs +254 -0
  116. package/dist/engineering-pa-cli/src/secrets/commands/backup-template.mjs +67 -0
  117. package/dist/engineering-pa-cli/src/secrets/commands/backup.mjs +85 -0
  118. package/dist/engineering-pa-cli/src/secrets/commands/delete.mjs +58 -0
  119. package/dist/engineering-pa-cli/src/secrets/commands/find.mjs +36 -0
  120. package/dist/engineering-pa-cli/src/secrets/commands/get.mjs +49 -0
  121. package/dist/engineering-pa-cli/src/secrets/commands/init.mjs +69 -0
  122. package/dist/engineering-pa-cli/src/secrets/commands/list.mjs +37 -0
  123. package/dist/engineering-pa-cli/src/secrets/commands/rotate.mjs +46 -0
  124. package/dist/engineering-pa-cli/src/secrets/commands/set.mjs +123 -0
  125. package/dist/engineering-pa-cli/src/secrets/commands/status.mjs +146 -0
  126. package/dist/engineering-pa-cli/src/secrets/commands/vaults.mjs +57 -0
  127. package/dist/engineering-pa-cli/src/secrets/commands/verify.mjs +143 -0
  128. package/dist/engineering-pa-cli/src/secrets/vault.mjs +766 -0
  129. package/dist/engineering-pa-cli/src/smoke/checks.mjs +199 -0
  130. package/dist/engineering-pa-cli/src/smoke/cli.mjs +108 -0
  131. package/dist/engineering-pa-cli/src/smoke/commands/run.mjs +237 -0
  132. package/dist/engineering-pa-cli/src/smoke/runner.mjs +191 -0
  133. package/dist/engineering-pa-cli/src/staging-alias/cli.mjs +91 -0
  134. package/dist/engineering-pa-cli/src/staging-alias/cloudflare.mjs +380 -0
  135. package/dist/engineering-pa-cli/src/staging-alias/commands/list.mjs +36 -0
  136. package/dist/engineering-pa-cli/src/staging-alias/commands/set.mjs +188 -0
  137. package/dist/engineering-pa-cli/src/staging-alias/commands/status.mjs +113 -0
  138. package/dist/engineering-pa-cli/src/staging-alias/commands/unset.mjs +95 -0
  139. package/dist/engineering-pa-cli/src/staging-alias/sites.mjs +105 -0
  140. package/dist/engineering-pa-cli/src/sync/cli.mjs +151 -0
  141. package/dist/engineering-pa-cli/src/sync/git.mjs +149 -0
  142. package/dist/engineering-pa-cli/src/sync/lock.mjs +30 -0
  143. package/dist/engineering-pa-cli/src/sync/regenerated-artifacts.mjs +114 -0
  144. package/dist/engineering-pa-cli/src/sync/registry-scope.mjs +260 -0
  145. package/dist/engineering-pa-cli/src/sync/run.mjs +531 -0
  146. package/dist/engineering-pa-cli/src/sync/runner.mjs +62 -0
  147. package/dist/engineering-pa-cli/src/sync/runs-digest.mjs +138 -0
  148. package/dist/engineering-pa-core/README.md +60 -0
  149. package/dist/engineering-pa-core/bin/desk +9 -0
  150. package/dist/engineering-pa-core/bin/distill +2 -0
  151. package/dist/engineering-pa-core/bin/memory-sync +2 -0
  152. package/dist/engineering-pa-core/bin/slice +2 -0
  153. package/dist/engineering-pa-core/docs/autonomy-envelope-tracker.md +111 -0
  154. package/dist/engineering-pa-core/docs/blueprints/README.md +45 -0
  155. package/dist/engineering-pa-core/docs/blueprints/docker-services/blueprint.md +168 -0
  156. package/dist/engineering-pa-core/docs/blueprints/git-identity/blueprint.md +116 -0
  157. package/dist/engineering-pa-core/docs/blueprints/ideas/custom-workspace-views.md +9 -0
  158. package/dist/engineering-pa-core/docs/blueprints/ideas/knowledge-domain-patterns.md +15 -0
  159. package/dist/engineering-pa-core/docs/blueprints/ideas/office-integration.md +11 -0
  160. package/dist/engineering-pa-core/docs/blueprints/ideas/team-scaling.md +9 -0
  161. package/dist/engineering-pa-core/docs/blueprints/ideas/work-personal-separation.md +9 -0
  162. package/dist/engineering-pa-core/docs/blueprints/scheduled-distillation/blueprint.md +129 -0
  163. package/dist/engineering-pa-core/docs/blueprints/tool-integration/blueprint.md +110 -0
  164. package/dist/engineering-pa-core/docs/commands/init.md +159 -0
  165. package/dist/engineering-pa-core/docs/commands/schedule.md +98 -0
  166. package/dist/engineering-pa-core/docs/commands/upgrade.md +67 -0
  167. package/dist/engineering-pa-core/docs/entity-location-map.md +93 -0
  168. package/dist/engineering-pa-core/docs/knowledge-scaffold.md +121 -0
  169. package/dist/engineering-pa-core/docs/lock.md +155 -0
  170. package/dist/engineering-pa-core/docs/memory-sync.md +28 -0
  171. package/dist/engineering-pa-core/docs/pa-operating-model.md +457 -0
  172. package/dist/engineering-pa-core/docs/scheduling.md +35 -0
  173. package/dist/engineering-pa-core/docs/standards/README.md +24 -0
  174. package/dist/engineering-pa-core/docs/standards/briefing-contract.md +161 -0
  175. package/dist/engineering-pa-core/docs/standards/tooling.md +240 -0
  176. package/dist/engineering-pa-core/docs/tools/distill.md +230 -0
  177. package/dist/engineering-pa-core/docs/tools/memory-sync.md +98 -0
  178. package/dist/engineering-pa-core/docs/tools/scheduler.md +243 -0
  179. package/dist/engineering-pa-core/docs/tools/slice.md +150 -0
  180. package/dist/engineering-pa-core/docs/upstream-issue-filing.md +88 -0
  181. package/dist/engineering-pa-core/docs/working-directory.md +29 -0
  182. package/dist/engineering-pa-core/docs/workspace-operations.md +46 -0
  183. package/dist/engineering-pa-core/index.mjs +21 -0
  184. package/dist/engineering-pa-core/knowledge/README.md +53 -0
  185. package/dist/engineering-pa-core/knowledge/canonical/.gitkeep +0 -0
  186. package/dist/engineering-pa-core/knowledge/content-voice/README.md +64 -0
  187. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-baseline.md +133 -0
  188. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-shape.md +58 -0
  189. package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-taxonomy.md +33 -0
  190. package/dist/engineering-pa-core/knowledge/content-voice/content-pipeline-routing.md +58 -0
  191. package/dist/engineering-pa-core/knowledge/content-voice/new-tell-capture-procedure.md +40 -0
  192. package/dist/engineering-pa-core/knowledge/content-voice/voice-guide-shape.md +43 -0
  193. package/dist/engineering-pa-core/knowledge/delegation-dispatch/README.md +34 -0
  194. package/dist/engineering-pa-core/knowledge/delegation-dispatch/briefing-contract.md +77 -0
  195. package/dist/engineering-pa-core/knowledge/delegation-dispatch/delegation-modes.md +84 -0
  196. package/dist/engineering-pa-core/knowledge/delegation-dispatch/dispatch-envelope.md +238 -0
  197. package/dist/engineering-pa-core/knowledge/delegation-dispatch/task-groups-and-orchestrators.md +62 -0
  198. package/dist/engineering-pa-core/knowledge/delegation-dispatch/team-improvement-loop.md +48 -0
  199. package/dist/engineering-pa-core/knowledge/delegation-dispatch/tool-development-policy.md +48 -0
  200. package/dist/engineering-pa-core/knowledge/engineering-discipline/README.md +33 -0
  201. package/dist/engineering-pa-core/knowledge/engineering-discipline/blast-radius-and-safety.md +50 -0
  202. package/dist/engineering-pa-core/knowledge/engineering-discipline/investigate-root-causes.md +48 -0
  203. package/dist/engineering-pa-core/knowledge/engineering-discipline/role-ladder.md +57 -0
  204. package/dist/engineering-pa-core/knowledge/engineering-discipline/verify-runtime-behaviour.md +53 -0
  205. package/dist/engineering-pa-core/knowledge/engineering-discipline/worktree-isolation.md +50 -0
  206. package/dist/engineering-pa-core/knowledge/knowledge-memory/README.md +33 -0
  207. package/dist/engineering-pa-core/knowledge/knowledge-memory/claude-md-hygiene.md +47 -0
  208. package/dist/engineering-pa-core/knowledge/knowledge-memory/journal-and-self-improvement.md +53 -0
  209. package/dist/engineering-pa-core/knowledge/knowledge-memory/knowledge-retrieval-discipline.md +62 -0
  210. package/dist/engineering-pa-core/knowledge/knowledge-memory/librarian-synthesis-mandate.md +56 -0
  211. package/dist/engineering-pa-core/knowledge/knowledge-memory/staging-and-observations.md +58 -0
  212. package/dist/engineering-pa-core/knowledge/operator-interface/README.md +32 -0
  213. package/dist/engineering-pa-core/knowledge/operator-interface/communication-style.md +33 -0
  214. package/dist/engineering-pa-core/knowledge/operator-interface/escalation-and-correction.md +45 -0
  215. package/dist/engineering-pa-core/knowledge/operator-interface/push-back-discipline.md +42 -0
  216. package/dist/engineering-pa-core/knowledge/operator-interface/session-boot-and-end.md +56 -0
  217. package/dist/engineering-pa-core/knowledge/platform-tooling/README.md +37 -0
  218. package/dist/engineering-pa-core/knowledge/platform-tooling/desk-cli-reference.md +52 -0
  219. package/dist/engineering-pa-core/knowledge/platform-tooling/environment-awareness.md +91 -0
  220. package/dist/engineering-pa-core/knowledge/platform-tooling/mcp-servers-catalogue.md +52 -0
  221. package/dist/engineering-pa-core/knowledge/platform-tooling/pa-cli-reference.md +55 -0
  222. package/dist/engineering-pa-core/knowledge/platform-tooling/scheduling-and-launchd.md +56 -0
  223. package/dist/engineering-pa-core/knowledge/platform-tooling/tool-discovery-discipline.md +49 -0
  224. package/dist/engineering-pa-core/knowledge/platform-tooling/working-directory-vs-project-root.md +43 -0
  225. package/dist/engineering-pa-core/knowledge/platform-tooling/workspace-server.md +51 -0
  226. package/dist/engineering-pa-core/knowledge/raw/.gitkeep +0 -0
  227. package/dist/engineering-pa-core/knowledge/scaffold/domains.json +5 -0
  228. package/dist/engineering-pa-core/knowledge/schema/.gitkeep +0 -0
  229. package/dist/engineering-pa-core/knowledge/work-management/README.md +33 -0
  230. package/dist/engineering-pa-core/knowledge/work-management/desk-cli.md +84 -0
  231. package/dist/engineering-pa-core/knowledge/work-management/progress-md.md +48 -0
  232. package/dist/engineering-pa-core/knowledge/work-management/projects.md +64 -0
  233. package/dist/engineering-pa-core/knowledge/work-management/spawn-log.md +48 -0
  234. package/dist/engineering-pa-core/knowledge/work-management/work-items.md +67 -0
  235. package/dist/engineering-pa-core/package.json +26 -0
  236. package/dist/engineering-pa-core/roles/agent-ops/definition.md +79 -0
  237. package/dist/engineering-pa-core/roles/dispatch-briefer/definition.md +195 -0
  238. package/dist/engineering-pa-core/roles/librarian/definition.md +176 -0
  239. package/dist/engineering-pa-core/roles/pa/definition.md +93 -0
  240. package/dist/engineering-pa-core/roles/system-admin/definition.md +106 -0
  241. package/dist/engineering-pa-core/roles/tool-developer/definition.md +116 -0
  242. package/dist/engineering-pa-core/roles/writer/definition.md +130 -0
  243. package/dist/engineering-pa-core/schema/autonomy-envelope.schema.json +61 -0
  244. package/dist/engineering-pa-core/schema/project.json +114 -0
  245. package/dist/engineering-pa-core/schema/schedule.json +63 -0
  246. package/dist/engineering-pa-core/schema/work-item.json +92 -0
  247. package/dist/engineering-pa-core/src/config/pa-package-name.mjs +30 -0
  248. package/dist/engineering-pa-core/src/desk/cli.mjs +95 -0
  249. package/dist/engineering-pa-core/src/desk/commands.mjs +398 -0
  250. package/dist/engineering-pa-core/src/desk/format.mjs +84 -0
  251. package/dist/engineering-pa-core/src/desk/repo.mjs +172 -0
  252. package/dist/engineering-pa-core/src/desk/team-scope.mjs +142 -0
  253. package/dist/engineering-pa-core/src/distill/cli.mjs +112 -0
  254. package/dist/engineering-pa-core/src/distill/prereqs.mjs +171 -0
  255. package/dist/engineering-pa-core/src/distill/prompt.mjs +74 -0
  256. package/dist/engineering-pa-core/src/distill/run.mjs +230 -0
  257. package/dist/engineering-pa-core/src/distill/runner.mjs +86 -0
  258. package/dist/engineering-pa-core/src/distill/staging.mjs +59 -0
  259. package/dist/engineering-pa-core/src/knowledge/retrieval/backlink-indexer.mjs +154 -0
  260. package/dist/engineering-pa-core/src/knowledge/retrieval/graph-builder.mjs +186 -0
  261. package/dist/engineering-pa-core/src/knowledge/retrieval/index-builder.mjs +225 -0
  262. package/dist/engineering-pa-core/src/knowledge/retrieval/query.mjs +263 -0
  263. package/dist/engineering-pa-core/src/lock/lock.mjs +202 -0
  264. package/dist/engineering-pa-core/src/memory-sync/cli-support.mjs +12 -0
  265. package/dist/engineering-pa-core/src/memory-sync/cli.mjs +56 -0
  266. package/dist/engineering-pa-core/src/memory-sync/commands/pull.mjs +44 -0
  267. package/dist/engineering-pa-core/src/memory-sync/commands/push.mjs +46 -0
  268. package/dist/engineering-pa-core/src/memory-sync/mirror.mjs +114 -0
  269. package/dist/engineering-pa-core/src/memory-sync/repo.mjs +65 -0
  270. package/dist/engineering-pa-core/src/scheduler/detect.mjs +111 -0
  271. package/dist/engineering-pa-core/src/scheduler/host.mjs +38 -0
  272. package/dist/engineering-pa-core/src/scheduler/index.mjs +13 -0
  273. package/dist/engineering-pa-core/src/scheduler/install.mjs +854 -0
  274. package/dist/engineering-pa-core/src/scheduler/list.mjs +168 -0
  275. package/dist/engineering-pa-core/src/scheduler/remove.mjs +176 -0
  276. package/dist/engineering-pa-core/src/scheduler/verify.mjs +435 -0
  277. package/dist/engineering-pa-core/src/session-boot/autonomy-injection.mjs +84 -0
  278. package/dist/engineering-pa-core/src/slice/cli.mjs +139 -0
  279. package/dist/engineering-pa-core/src/slice/inputs.mjs +174 -0
  280. package/dist/engineering-pa-core/src/slice/prompt.mjs +94 -0
  281. package/dist/engineering-pa-core/src/slice/prompts/dave-boot.md +91 -0
  282. package/dist/engineering-pa-core/src/slice/run.mjs +334 -0
  283. package/dist/engineering-pa-core-extensions-atlassian/README.md +31 -0
  284. package/dist/engineering-pa-core-extensions-atlassian/index.mjs +9 -0
  285. package/dist/engineering-pa-core-extensions-atlassian/package.json +19 -0
  286. package/dist/engineering-pa-core-extensions-n8n/README.md +29 -0
  287. package/dist/engineering-pa-core-extensions-n8n/index.mjs +8 -0
  288. package/dist/engineering-pa-core-extensions-n8n/package.json +19 -0
  289. package/dist/engineering-pa-librarian/README.md +38 -0
  290. package/dist/engineering-pa-librarian/index.mjs +12 -0
  291. package/dist/engineering-pa-librarian/package.json +19 -0
  292. package/dist/engineering-pa-migrate-inspect/README.md +131 -0
  293. package/dist/engineering-pa-migrate-inspect/bin/migrate-inspect +2 -0
  294. package/dist/engineering-pa-migrate-inspect/package.json +21 -0
  295. package/dist/engineering-pa-migrate-inspect/src/age-key-migrate.mjs +83 -0
  296. package/dist/engineering-pa-migrate-inspect/src/cli.mjs +97 -0
  297. package/dist/engineering-pa-migrate-inspect/src/inspector.mjs +237 -0
  298. package/dist/engineering-pa-migrate-inspect/src/platforms/linux.mjs +87 -0
  299. package/dist/engineering-pa-migrate-inspect/src/platforms/macos.mjs +105 -0
  300. package/dist/engineering-pa-migrate-inspect/src/runner.mjs +87 -0
  301. package/dist/engineering-pa-runbook/README.md +103 -0
  302. package/dist/engineering-pa-runbook/package.json +18 -0
  303. package/dist/engineering-pa-runbook/reference/dex-template.runbook.json +66 -0
  304. package/dist/engineering-pa-runbook/reference/hq-dave.runbook.json +69 -0
  305. package/dist/engineering-pa-runbook/reference/smarthome01.runbook.json +92 -0
  306. package/dist/engineering-pa-runbook/schema/inspection.schema.json +113 -0
  307. package/dist/engineering-pa-runbook/schema/runbook.schema.json +67 -0
  308. package/dist/engineering-pa-runbook/src/validator.mjs +167 -0
  309. package/dist/engineering-pa-scaffold/README.md +164 -0
  310. package/dist/engineering-pa-scaffold/bin/pa-init +2 -0
  311. package/dist/engineering-pa-scaffold/index.mjs +42 -0
  312. package/dist/engineering-pa-scaffold/package.json +22 -0
  313. package/dist/engineering-pa-scaffold/src/cli.mjs +115 -0
  314. package/dist/engineering-pa-scaffold/src/commands/init.mjs +169 -0
  315. package/dist/engineering-pa-scaffold/src/generator.mjs +354 -0
  316. package/dist/engineering-pa-scaffold/src/prompts.mjs +125 -0
  317. package/dist/engineering-pa-scaffold/src/runner.mjs +142 -0
  318. package/dist/engineering-pa-scaffold/templates/CLAUDE.md.template +196 -0
  319. package/dist/engineering-pa-scaffold/templates/operator-README.md.template +44 -0
  320. package/dist/engineering-pa-types-engineering/README.md +35 -0
  321. package/dist/engineering-pa-types-engineering/docs/commands/product-build.md +120 -0
  322. package/dist/engineering-pa-types-engineering/index.mjs +9 -0
  323. package/dist/engineering-pa-types-engineering/package.json +19 -0
  324. package/dist/engineering-pa-types-engineering/roles/product-engineer/definition.md +650 -0
  325. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/build-spec.md +82 -0
  326. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/preflight-checklist.md +158 -0
  327. package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/worker-brief.md +130 -0
  328. package/dist/engineering-pa-workspace/README.md +197 -0
  329. package/dist/engineering-pa-workspace/app/assets/md.js +187 -0
  330. package/dist/engineering-pa-workspace/app/assets/modal.js +120 -0
  331. package/dist/engineering-pa-workspace/app/assets/theme.css +132 -0
  332. package/dist/engineering-pa-workspace/app/assets/theme.js +159 -0
  333. package/dist/engineering-pa-workspace/app/banner.js +320 -0
  334. package/dist/engineering-pa-workspace/app/delegations.html +345 -0
  335. package/dist/engineering-pa-workspace/app/home.html +1060 -0
  336. package/dist/engineering-pa-workspace/app/index.html +361 -0
  337. package/dist/engineering-pa-workspace/app/knowledge-map.html +163 -0
  338. package/dist/engineering-pa-workspace/app/knowledge.html +304 -0
  339. package/dist/engineering-pa-workspace/app/nav.json +75 -0
  340. package/dist/engineering-pa-workspace/app/notes.html +199 -0
  341. package/dist/engineering-pa-workspace/app/onboarding.html +592 -0
  342. package/dist/engineering-pa-workspace/app/profile.html +243 -0
  343. package/dist/engineering-pa-workspace/app/project-detail.html +432 -0
  344. package/dist/engineering-pa-workspace/app/projects.html +297 -0
  345. package/dist/engineering-pa-workspace/app/repos.html +598 -0
  346. package/dist/engineering-pa-workspace/app/schedules.html +486 -0
  347. package/dist/engineering-pa-workspace/app/secrets.html +164 -0
  348. package/dist/engineering-pa-workspace/app/staging.html +319 -0
  349. package/dist/engineering-pa-workspace/app/tasks.html +465 -0
  350. package/dist/engineering-pa-workspace/app/vendor/chart.umd.min.js +21 -0
  351. package/dist/engineering-pa-workspace/app/viewer.html +116 -0
  352. package/dist/engineering-pa-workspace/docs/DAEMON.md +104 -0
  353. package/dist/engineering-pa-workspace/docs/page-blueprint.md +286 -0
  354. package/dist/engineering-pa-workspace/docs/status-mon.md +168 -0
  355. package/dist/engineering-pa-workspace/index.mjs +12 -0
  356. package/dist/engineering-pa-workspace/nav.json.example +8 -0
  357. package/dist/engineering-pa-workspace/package.json +27 -0
  358. package/dist/engineering-pa-workspace/src/api.mjs +2086 -0
  359. package/dist/engineering-pa-workspace/src/install-daemon.mjs +573 -0
  360. package/dist/engineering-pa-workspace/src/nav.mjs +190 -0
  361. package/dist/engineering-pa-workspace/src/secrets-api.mjs +175 -0
  362. package/dist/engineering-pa-workspace/src/server.mjs +516 -0
  363. package/dist/engineering-pa-workspace/src/status-mon/poll.mjs +220 -0
  364. package/dist/engineering-pa-workspace/src/status-mon/poller.mjs +113 -0
  365. package/dist/engineering-pa-workspace/src/status-mon/run.mjs +133 -0
  366. package/dist/engineering-pa-workspace/src/supervisor.mjs +264 -0
  367. package/dist/engineering-pa-workspace/src/ws.mjs +633 -0
  368. package/index.mjs +60 -0
  369. package/package.json +58 -0
  370. package/templates/banned-ai-tells.md +102 -0
  371. package/templates/purpose.md +105 -0
  372. package/templates/voice-guide.md +98 -0
@@ -0,0 +1,531 @@
1
+ // estate-sync — orchestrator.
2
+ //
3
+ // The single git writer per host for ESTATE-NATIVE content only. Designed
4
+ // for a 30-minute cron: quiet on success, loud on failure, exit-code
5
+ // accurate for monitoring.
6
+ //
7
+ // Flow (commit-first; see note below):
8
+ // 1. acquire the process lock (PID file; stale after 10m)
9
+ // 2. git fetch origin — refresh remote refs FIRST so ahead/behind is
10
+ // accurate (without this, a clean tree short-circuits to noop using
11
+ // a stale origin/main pointer and never discovers remote commits)
12
+ // 3. discover working-tree state — clean? ahead/behind upstream?
13
+ // 4. if nothing to do (clean tree + ahead == 0 + behind == 0) -> noop
14
+ // audit + exit 0
15
+ // 5. compose a commit body from the cron tools' runs.jsonl files, plus
16
+ // the registry-scoped commit-scope decision (see registry-scope.mjs)
17
+ // 6. if dirty: compute the commit scope — when
18
+ // <repoRoot>/operator/registry/{devices.json,repos.json} exist,
19
+ // resolve this device and exclude every entitled mount's `to`
20
+ // subtree (+ .vault/** unconditionally), then stage ONLY the
21
+ // in-scope dirty paths via explicit pathspecs (`git add -- <paths>`)
22
+ // — NEVER a bare `git add -A`. No registry present -> previous
23
+ // (pre-rewrite) behaviour: unscoped `git add -A`, but loud about it
24
+ // (stderr + commit body). Then
25
+ // `git commit -m "estate-sync <ISO ts>" -m "<body>"`.
26
+ // (this happens BEFORE the pull — git pull --rebase refuses to run
27
+ // with uncommitted changes in the index or working tree)
28
+ // 7. git pull --rebase origin <branch> (fetch already done in step 2)
29
+ // on rebase conflict: git rebase --abort + fail-rebase audit + exit 2
30
+ // 8. git push. on rejection: fetch + pull --rebase + push retry. one
31
+ // retry only.
32
+ // 9. append success audit to logs/runs.jsonl
33
+ //
34
+ // Why commit-first (Option A): `git pull --rebase` refuses to run when
35
+ // the index or working tree contains uncommitted changes. The original
36
+ // "add -> pull --rebase -> commit -> push" ordering is therefore
37
+ // impossible — sync would abort with "cannot pull with rebase: Your
38
+ // index contains uncommitted changes". Committing first means the
39
+ // rebase replays one clean local commit on top of origin's history;
40
+ // this is the same shape a human follows when integrating with origin.
41
+ // Failure modes are also cleaner: a conflicting rebase can be safely
42
+ // aborted (the commit stays local, next tick reconciles), whereas a
43
+ // stash-pop-conflict would leave the tree in a half-merged state with
44
+ // no good automated recovery.
45
+ //
46
+ // Audit-record contract: every non-dry-run exit path writes exactly one
47
+ // record. Status vocabulary (§15 of the tooling standard):
48
+ // ok commit + push (or pull-only) succeeded
49
+ // noop-nothing-to-do working tree clean and no commits ahead of origin
50
+ // noop-locked another sync process is running — try again
51
+ // fail-rebase pull --rebase conflicted; rebase aborted
52
+ // fail-push push rejected even after rebase retry
53
+ // fail-git any other git command failed
54
+ //
55
+ // Exit codes (§6 of the tooling standard):
56
+ // 0 ok (including noop)
57
+ // 1 usage (handled in cli.mjs)
58
+ // 2 logical / operational failure (rebase, push reject, dirty rebase)
59
+ // 3 transport / IO failure (git command itself errored, not the result)
60
+
61
+ import { appendFile } from 'node:fs/promises';
62
+ import { join } from 'node:path';
63
+
64
+ import {
65
+ addAll,
66
+ addPaths,
67
+ aheadBehind,
68
+ checkoutOursDuringRebase,
69
+ commit,
70
+ currentBranch,
71
+ fetchOrigin,
72
+ hasStagedChanges,
73
+ listConflictedPaths,
74
+ pullRebase,
75
+ push,
76
+ rebaseAbort,
77
+ rebaseContinue,
78
+ statusPorcelain,
79
+ } from './git.mjs';
80
+ import { acquire } from './lock.mjs';
81
+ import {
82
+ classifyConflicts,
83
+ loadRegeneratedArtifacts,
84
+ } from './regenerated-artifacts.mjs';
85
+ import { computeCommitScope, filterInScopePaths, renderScopeSection } from './registry-scope.mjs';
86
+ import { composeBody } from './runs-digest.mjs';
87
+
88
+ function log(line, { stderr = process.stderr } = {}) {
89
+ stderr.write(`[estate-sync] ${line}\n`);
90
+ }
91
+
92
+ async function writeAudit(runsLog, record, stderr) {
93
+ try {
94
+ await appendFile(runsLog, JSON.stringify(record) + '\n');
95
+ } catch (err) {
96
+ log(`warning: failed to append audit record to ${runsLog}: ${err.message}`, { stderr });
97
+ }
98
+ }
99
+
100
+ export async function runSync({
101
+ repoRoot,
102
+ logDir,
103
+ runsLog,
104
+ lockPath,
105
+ dryRun = false,
106
+ runner,
107
+ stderr = process.stderr,
108
+ lockOptions = {},
109
+ // Test-only injection seam: overrides os.hostname() inside
110
+ // computeCommitScope's device resolution. Production callers never pass
111
+ // this — computeCommitScope defaults to the real os.hostname().
112
+ hostnameFn,
113
+ } = {}) {
114
+ if (!runner) throw new Error('runSync: runner is required');
115
+ if (!repoRoot) throw new Error('runSync: repoRoot is required');
116
+ if (!runsLog) throw new Error('runSync: runsLog is required');
117
+ if (!lockPath) throw new Error('runSync: lockPath is required');
118
+
119
+ const tsIso = runner.tsIso();
120
+ const startedAtMs = Date.now();
121
+
122
+ if (dryRun) {
123
+ log('DRY RUN — would:', { stderr });
124
+ }
125
+
126
+ // Step 1 — lock.
127
+ const lock = await acquire(lockPath, lockOptions);
128
+ if (!lock.acquired) {
129
+ log(`another sync is running: ${lock.reason} — exiting noop`, { stderr });
130
+ if (!dryRun) {
131
+ await writeAudit(runsLog, {
132
+ ts: tsIso,
133
+ status: 'noop-locked',
134
+ exitCode: 0,
135
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
136
+ reason: `lock contention: ${lock.reason}`,
137
+ }, stderr);
138
+ }
139
+ return 0;
140
+ }
141
+
142
+ try {
143
+ // Step 2 — fetch upstream FIRST so ahead/behind reflects what origin
144
+ // actually has. Without this, a clean tree with no local commits
145
+ // short-circuits to the step-4 noop using whatever origin/<branch>
146
+ // pointed at on the last fetch — which on a long-idle clone is
147
+ // arbitrarily stale. Fetch failures (network, auth) are logged but
148
+ // not blocking: we'd rather proceed on stale refs for one cycle than
149
+ // miss a chance to commit local work. The next tick will retry.
150
+ //
151
+ // Skip in dry-run mode — even though fetch only updates remote-
152
+ // tracking refs (no local branch / working tree mutation), the
153
+ // dry-run contract is "no git commands spawned." Dry-run's
154
+ // ahead/behind reporting may therefore be stale by up to one cron
155
+ // tick; that's an acceptable cost for the cleaner contract.
156
+ let upfrontFetch = { code: 0 };
157
+ if (!dryRun) {
158
+ upfrontFetch = await fetchOrigin(runner, { cwd: repoRoot });
159
+ if (upfrontFetch.code !== 0) {
160
+ log(`git fetch (up-front) failed — proceeding with stale upstream refs: ${upfrontFetch.stderr.trim()}`, { stderr });
161
+ }
162
+ }
163
+
164
+ // Step 3 — examine working tree and upstream relation.
165
+ const status = await statusPorcelain(runner, { cwd: repoRoot });
166
+ if (status.code !== 0) {
167
+ log(`git status failed: ${status.stderr.trim()}`, { stderr });
168
+ if (!dryRun) {
169
+ await writeAudit(runsLog, {
170
+ ts: tsIso,
171
+ status: 'fail-git',
172
+ exitCode: 3,
173
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
174
+ reason: `git status failed: ${status.stderr.trim()}`,
175
+ }, stderr);
176
+ }
177
+ return 3;
178
+ }
179
+ const dirty = status.stdout.trim().length > 0;
180
+ const { ahead, behind } = await aheadBehind(runner, { cwd: repoRoot });
181
+ const aheadCount = Number.isFinite(ahead) ? ahead : 0;
182
+ const behindCount = Number.isFinite(behind) ? behind : 0;
183
+ log(`tree dirty=${dirty}, ahead=${aheadCount}, behind=${behindCount}`, { stderr });
184
+
185
+ const branch = await currentBranch(runner, { cwd: repoRoot });
186
+
187
+ // Step 4 — noop short-circuit.
188
+ if (!dirty && aheadCount === 0 && behindCount === 0) {
189
+ log('nothing to do — tree clean, no local commits ahead, up-to-date', { stderr });
190
+ if (!dryRun) {
191
+ await writeAudit(runsLog, {
192
+ ts: tsIso,
193
+ status: 'noop-nothing-to-do',
194
+ exitCode: 0,
195
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
196
+ dirty,
197
+ ahead: aheadCount,
198
+ behind: behindCount,
199
+ branch,
200
+ }, stderr);
201
+ }
202
+ return 0;
203
+ }
204
+
205
+ // Step 5 — compose body (cheap; pure file read) + registry-scoped
206
+ // commit-scope decision (also cheap; two small JSON reads at most).
207
+ const digestBody = await composeBody({ repoRoot });
208
+ const scope = computeCommitScope(hostnameFn ? { repoRoot, hostnameFn } : { repoRoot });
209
+
210
+ if (dryRun) {
211
+ log(` branch: ${branch}`, { stderr });
212
+ log(` dirty: ${dirty}`, { stderr });
213
+ log(` ahead/behind: ${aheadCount}/${behindCount}`, { stderr });
214
+ log(` commit title: estate-sync ${tsIso}`, { stderr });
215
+ log(' commit body (preview):', { stderr });
216
+ for (const line of digestBody.split('\n')) {
217
+ log(` ${line}`, { stderr });
218
+ }
219
+ log(' would: git fetch origin (real run does this BEFORE the noop check; dry-run skipped to keep this side-effect-free)', { stderr });
220
+ if (dirty) {
221
+ if (scope.registryPresent) {
222
+ const { inScope, excludedCount } = filterInScopePaths(status.stdout, scope.excludeDirs);
223
+ log(` would: git add -- <${inScope.length} in-scope path(s), device=${scope.deviceId}, ${excludedCount} excluded by registry scope>`, { stderr });
224
+ } else {
225
+ log(' would: git add -A (no registry found — unscoped commit)', { stderr });
226
+ }
227
+ log(` would: git commit -m "estate-sync ${tsIso}" -m "<body above>"`, { stderr });
228
+ }
229
+ log(` would: git pull --rebase origin ${branch}`, { stderr });
230
+ log(' would: git push (with one rebase-and-retry on rejection)', { stderr });
231
+ return 0;
232
+ }
233
+
234
+ // Step 6 — stage + commit (commit-first ordering).
235
+ //
236
+ // We commit BEFORE pulling because `git pull --rebase` refuses to
237
+ // run when the index or working tree has uncommitted changes — it
238
+ // aborts with "cannot pull with rebase: Your index contains
239
+ // uncommitted changes". By committing first, the subsequent rebase
240
+ // replays a clean local commit on top of origin's history. This is
241
+ // the same shape a human follows when integrating: stage, commit,
242
+ // then rebase onto upstream.
243
+ let didCommit = false;
244
+ if (dirty) {
245
+ let addRes;
246
+ let stagedCount = 0;
247
+ let excludedPathCount = 0;
248
+ if (scope.registryPresent) {
249
+ // Registry-scoped staging: explicit pathspecs of in-scope dirty
250
+ // paths only — NEVER a bare `git add -A`. Untracked files inside
251
+ // an excluded subtree are simply never passed to `git add`, so
252
+ // they can never be staged (pathspec approach handles this by
253
+ // construction, not by a follow-up filter).
254
+ const { inScope, excludedCount } = filterInScopePaths(status.stdout, scope.excludeDirs);
255
+ stagedCount = inScope.length;
256
+ excludedPathCount = excludedCount;
257
+ if (inScope.length > 0) {
258
+ addRes = await addPaths(runner, { cwd: repoRoot, paths: inScope });
259
+ } else {
260
+ addRes = { code: 0, stdout: '', stderr: '' };
261
+ log('all dirty paths are excluded by registry scope — nothing to stage this tick', { stderr });
262
+ }
263
+ } else {
264
+ // No registry present at this repo root — previous (pre-rewrite)
265
+ // behaviour: unscoped `git add -A`, but LOUD about it, both in
266
+ // the run's own log output and in the commit body (below).
267
+ const reason = scope.warnings.length > 0 ? ` (${scope.warnings.join('; ')})` : '';
268
+ log(`estate-sync: no registry found, unscoped commit${reason}`, { stderr });
269
+ addRes = await addAll(runner, { cwd: repoRoot });
270
+ }
271
+ const scopeSection = renderScopeSection(scope, { staged: stagedCount, excludedPaths: excludedPathCount });
272
+ const body = `${scopeSection}\n\n${digestBody}`;
273
+
274
+ if (addRes.code !== 0) {
275
+ log(`git add failed: ${addRes.stderr.trim()}`, { stderr });
276
+ await writeAudit(runsLog, {
277
+ ts: tsIso,
278
+ status: 'fail-git',
279
+ exitCode: 3,
280
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
281
+ reason: `git add failed: ${addRes.stderr.trim()}`,
282
+ }, stderr);
283
+ return 3;
284
+ }
285
+ // Belt-and-braces: if add somehow produced no staged content (e.g.
286
+ // every dirty path was ignored), skip the commit and continue.
287
+ const haveStaged = await hasStagedChanges(runner, { cwd: repoRoot });
288
+ if (haveStaged) {
289
+ const title = `estate-sync ${tsIso}`;
290
+ const c = await commit(runner, { cwd: repoRoot, title, body });
291
+ if (c.code !== 0) {
292
+ log(`git commit failed: ${c.stderr.trim()}`, { stderr });
293
+ await writeAudit(runsLog, {
294
+ ts: tsIso,
295
+ status: 'fail-git',
296
+ exitCode: 3,
297
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
298
+ reason: `git commit failed: ${c.stderr.trim()}`,
299
+ }, stderr);
300
+ return 3;
301
+ }
302
+ didCommit = true;
303
+ log('committed', { stderr });
304
+ } else {
305
+ log('nothing staged after add — skipping commit', { stderr });
306
+ }
307
+ }
308
+
309
+ // Step 7 — rebase. Fetch already done in step 2. Rebase failures
310
+ // are operational, not transport. Now safe to rebase because the
311
+ // tree is clean (either we committed in step 6, or it was clean to
312
+ // begin with — behind-only). If rebase conflicts on a path our
313
+ // fresh commit touches, we abort and surface; the commit stays
314
+ // local and the next tick will retry.
315
+ //
316
+ // We only attempt pull --rebase if the up-front fetch succeeded.
317
+ // If fetch failed earlier, we'd be rebasing onto stale refs which
318
+ // either no-ops (if local refs already match) or fails spuriously.
319
+ let autoResolvedPaths = [];
320
+ if (upfrontFetch.code === 0) {
321
+ const prRes = await pullRebase(runner, { cwd: repoRoot, branch });
322
+ if (prRes.code !== 0) {
323
+ log(`git pull --rebase failed: ${prRes.stderr.trim()}`, { stderr });
324
+ // Before aborting, see if the conflict is limited to files that
325
+ // distillation regenerates on every host. Those files are
326
+ // byte-different but semantically equivalent across hosts; a
327
+ // conflict just means two hosts pushed regenerations in the same
328
+ // window. Auto-resolve by preferring origin's version (see the
329
+ // rebase-inversion note on `checkoutOursDuringRebase`) and
330
+ // continue. If ANY conflict is outside the allowlist, we still
331
+ // abort and surface fail-rebase — real conflicts need a human.
332
+ const resolved = await tryAutoResolveRebase({
333
+ runner,
334
+ repoRoot,
335
+ stderr,
336
+ });
337
+ if (resolved.ok) {
338
+ autoResolvedPaths = resolved.paths;
339
+ log(
340
+ `auto-resolved rebase conflict on ${resolved.paths.length} regenerated artifact(s): ${resolved.paths.join(', ')}`,
341
+ { stderr },
342
+ );
343
+ } else {
344
+ log(`aborting rebase: ${resolved.reason}`, { stderr });
345
+ // Defensive abort. If no rebase is in progress (auto-resolve
346
+ // already ended it, or nothing was in progress), abort exits
347
+ // non-zero — we ignore that case.
348
+ await rebaseAbort(runner, { cwd: repoRoot });
349
+ await writeAudit(runsLog, {
350
+ ts: tsIso,
351
+ status: 'fail-rebase',
352
+ exitCode: 2,
353
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
354
+ reason: `git pull --rebase failed — rebase aborted; ${resolved.reason}`,
355
+ committed: didCommit,
356
+ ...(resolved.disallowed ? { conflictedPaths: resolved.disallowed } : {}),
357
+ }, stderr);
358
+ return 2;
359
+ }
360
+ }
361
+ }
362
+
363
+ // Step 7 — push (only if we have something to push). Note: after a
364
+ // pull --rebase that fast-forwarded, we may have new local refs
365
+ // pointing at remote refs (nothing to push). If we committed, we have
366
+ // at least one local-only commit. If we were just behind, pull
367
+ // brought us up to date — nothing to push.
368
+ const autoResolveFields = autoResolvedPaths.length > 0
369
+ ? { autoResolved: autoResolvedPaths.length, autoResolvedPaths }
370
+ : {};
371
+
372
+ const { ahead: aheadAfter } = await aheadBehind(runner, { cwd: repoRoot });
373
+ const needsPush = Number.isFinite(aheadAfter) && aheadAfter > 0;
374
+ if (!needsPush) {
375
+ log('nothing to push (up-to-date with origin)', { stderr });
376
+ await writeAudit(runsLog, {
377
+ ts: tsIso,
378
+ status: 'ok',
379
+ exitCode: 0,
380
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
381
+ committed: didCommit,
382
+ pushed: false,
383
+ branch,
384
+ ...autoResolveFields,
385
+ }, stderr);
386
+ return 0;
387
+ }
388
+
389
+ const p1 = await push(runner, { cwd: repoRoot });
390
+ if (p1.code === 0) {
391
+ log('pushed', { stderr });
392
+ await writeAudit(runsLog, {
393
+ ts: tsIso,
394
+ status: 'ok',
395
+ exitCode: 0,
396
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
397
+ committed: didCommit,
398
+ pushed: true,
399
+ branch,
400
+ ...autoResolveFields,
401
+ }, stderr);
402
+ return 0;
403
+ }
404
+
405
+ log(`git push rejected — fetching and rebasing for one retry: ${p1.stderr.trim()}`, { stderr });
406
+ const f2 = await fetchOrigin(runner, { cwd: repoRoot });
407
+ if (f2.code !== 0) {
408
+ log(`git fetch (for push-retry) failed: ${f2.stderr.trim()}`, { stderr });
409
+ await writeAudit(runsLog, {
410
+ ts: tsIso,
411
+ status: 'fail-push',
412
+ exitCode: 2,
413
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
414
+ reason: 'push rejected and follow-up fetch failed; commit is local-only',
415
+ }, stderr);
416
+ return 2;
417
+ }
418
+ const pr2 = await pullRebase(runner, { cwd: repoRoot, branch });
419
+ if (pr2.code !== 0) {
420
+ log(`git pull --rebase (for push-retry) failed: ${pr2.stderr.trim()}`, { stderr });
421
+ // Symmetric with step 7: if the push-retry rebase conflicts only
422
+ // on regenerated artifacts, auto-resolve and continue. Otherwise
423
+ // abort as before.
424
+ const resolved2 = await tryAutoResolveRebase({
425
+ runner,
426
+ repoRoot,
427
+ stderr,
428
+ });
429
+ if (resolved2.ok) {
430
+ autoResolvedPaths = [...autoResolvedPaths, ...resolved2.paths];
431
+ log(
432
+ `auto-resolved push-retry rebase on ${resolved2.paths.length} regenerated artifact(s): ${resolved2.paths.join(', ')}`,
433
+ { stderr },
434
+ );
435
+ } else {
436
+ log(`aborting push-retry rebase: ${resolved2.reason}`, { stderr });
437
+ await rebaseAbort(runner, { cwd: repoRoot });
438
+ await writeAudit(runsLog, {
439
+ ts: tsIso,
440
+ status: 'fail-rebase',
441
+ exitCode: 2,
442
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
443
+ reason: `push rejected; follow-up rebase aborted; ${resolved2.reason}`,
444
+ ...(resolved2.disallowed ? { conflictedPaths: resolved2.disallowed } : {}),
445
+ }, stderr);
446
+ return 2;
447
+ }
448
+ }
449
+ const p2 = await push(runner, { cwd: repoRoot });
450
+ if (p2.code !== 0) {
451
+ log(`git push (retry) failed: ${p2.stderr.trim()}`, { stderr });
452
+ await writeAudit(runsLog, {
453
+ ts: tsIso,
454
+ status: 'fail-push',
455
+ exitCode: 2,
456
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
457
+ reason: 'push rejected even after rebase-retry; commit is local-only',
458
+ }, stderr);
459
+ return 2;
460
+ }
461
+ log('pushed after rebase', { stderr });
462
+ // Recompute so any auto-resolve during the push-retry is captured.
463
+ const autoResolveFieldsFinal = autoResolvedPaths.length > 0
464
+ ? { autoResolved: autoResolvedPaths.length, autoResolvedPaths }
465
+ : {};
466
+ await writeAudit(runsLog, {
467
+ ts: tsIso,
468
+ status: 'ok',
469
+ exitCode: 0,
470
+ durationSec: Math.round((Date.now() - startedAtMs) / 1000),
471
+ committed: didCommit,
472
+ pushed: true,
473
+ retriedAfterRebase: true,
474
+ branch,
475
+ ...autoResolveFieldsFinal,
476
+ }, stderr);
477
+ return 0;
478
+ } finally {
479
+ await lock.release();
480
+ }
481
+ }
482
+
483
+ // Attempt to auto-resolve an in-progress rebase whose conflicts are all
484
+ // on regenerated knowledge artifacts (see `regenerated-artifacts.mjs`).
485
+ // Preferring origin's version is safe here because the next distillation
486
+ // cycle will regenerate again with combined inputs — nothing is lost.
487
+ //
488
+ // Returns:
489
+ // { ok: true, paths: [...] } — resolved, rebase continued
490
+ // { ok: false, reason: '<short>', disallowed?: [...] } — do NOT continue
491
+ async function tryAutoResolveRebase({ runner, repoRoot, stderr }) {
492
+ const conflicts = await listConflictedPaths(runner, { cwd: repoRoot });
493
+ if (conflicts.code !== 0) {
494
+ return { ok: false, reason: `git status (post-rebase) failed: ${conflicts.stderr.trim()}` };
495
+ }
496
+ if (conflicts.paths.length === 0) {
497
+ // pull --rebase failed but no unmerged entries — not a content
498
+ // conflict. Could be untracked-would-be-overwritten, empty commit,
499
+ // etc. Nothing safe to auto-resolve.
500
+ return { ok: false, reason: 'no unmerged paths detected — not a content conflict' };
501
+ }
502
+
503
+ const cfg = await loadRegeneratedArtifacts({ repoRoot });
504
+ if (cfg.warning) {
505
+ log(`.sync-config.json warning: ${cfg.warning} — falling back to defaults`, { stderr });
506
+ }
507
+ const { allowed, disallowed } = classifyConflicts(conflicts.paths, cfg.patterns);
508
+ if (disallowed.length > 0) {
509
+ return {
510
+ ok: false,
511
+ reason: `conflict on ${disallowed.length} path(s) outside the regenerated-artifacts allowlist (${disallowed.join(', ')}); manual reconciliation required`,
512
+ disallowed,
513
+ };
514
+ }
515
+
516
+ // All conflicts are allowlisted. Prefer origin's version, stage, and
517
+ // continue the rebase.
518
+ const co = await checkoutOursDuringRebase(runner, { cwd: repoRoot, paths: allowed });
519
+ if (co.code !== 0) {
520
+ return { ok: false, reason: `git checkout --ours failed: ${co.stderr.trim()}` };
521
+ }
522
+ const add = await addPaths(runner, { cwd: repoRoot, paths: allowed });
523
+ if (add.code !== 0) {
524
+ return { ok: false, reason: `git add (post-resolve) failed: ${add.stderr.trim()}` };
525
+ }
526
+ const cont = await rebaseContinue(runner, { cwd: repoRoot });
527
+ if (cont.code !== 0) {
528
+ return { ok: false, reason: `git rebase --continue failed: ${cont.stderr.trim()}` };
529
+ }
530
+ return { ok: true, paths: allowed };
531
+ }
@@ -0,0 +1,62 @@
1
+ // sync — chokepoint for external effects.
2
+ //
3
+ // Every process spawn and clock read funnels through here. This module is
4
+ // the ONLY place that touches the outside world at runtime. Tests inject a
5
+ // fake `spawner` to assert on argv without spawning real processes.
6
+ //
7
+ // Contract (mirrors distill/src/runner.mjs):
8
+ // - spawn(cmd, args, { cwd?, env?, capture? }) -> { code, stdout, stderr }
9
+ // * No shell interpolation. Arguments are passed as an array with
10
+ // shell:false. User-supplied data (paths) flows through untouched.
11
+ // * capture:true (default) collects stdout/stderr into strings.
12
+ // - now() -> Date — single source of wall-clock time, for testing.
13
+ // - tsIso() -> strict RFC-3339 / ISO-8601 UTC timestamp `YYYY-MM-DDTHH:MM:SSZ`
14
+ // (second precision). Safe to round-trip via `new Date()`.
15
+
16
+ import { spawn as realSpawn } from 'node:child_process';
17
+
18
+ export function createRunner({ spawner = realSpawn, clock = () => new Date() } = {}) {
19
+ return {
20
+ async spawn(cmd, args, { cwd, env, capture = true } = {}) {
21
+ return new Promise((resolve, reject) => {
22
+ const child = spawner(cmd, args, {
23
+ shell: false,
24
+ cwd,
25
+ env: env ?? process.env,
26
+ stdio: capture ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'inherit', 'inherit'],
27
+ });
28
+
29
+ let stdout = '';
30
+ let stderr = '';
31
+
32
+ if (capture) {
33
+ child.stdout.on('data', (d) => {
34
+ stdout += d.toString('utf8');
35
+ });
36
+ child.stderr.on('data', (d) => {
37
+ stderr += d.toString('utf8');
38
+ });
39
+ }
40
+
41
+ child.on('error', (err) => reject(err));
42
+ child.on('close', (code) => resolve({ code: code ?? 0, stdout, stderr }));
43
+ child.stdin.end();
44
+ });
45
+ },
46
+
47
+ now() {
48
+ return clock();
49
+ },
50
+
51
+ tsIso() {
52
+ const d = clock();
53
+ const pad = (n) => String(n).padStart(2, '0');
54
+ return (
55
+ `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` +
56
+ `T${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}Z`
57
+ );
58
+ },
59
+ };
60
+ }
61
+
62
+ export const defaultRunner = createRunner();