dsh-compound-engineering 0.0.0-stage → 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 (461) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -2
  3. package/README.zh.md +161 -0
  4. package/cordis.patch.yml +6 -0
  5. package/lib/frontmatter.js +167 -0
  6. package/lib/index.js +46 -0
  7. package/lib/provider.js +201 -0
  8. package/package.json +62 -4
  9. package/skills/CODING_STANDARDS.md +68 -0
  10. package/skills/ce-babysit-pr/SKILL.md +59 -0
  11. package/skills/ce-babysit-pr/references/branch-currency.md +22 -0
  12. package/skills/ce-babysit-pr/references/envelope.md +44 -0
  13. package/skills/ce-babysit-pr/references/pipeline.md +15 -0
  14. package/skills/ce-babysit-pr/references/report.md +20 -0
  15. package/skills/ce-babysit-pr/references/settle.md +37 -0
  16. package/skills/ce-babysit-pr/references/setup.md +33 -0
  17. package/skills/ce-babysit-pr/references/stack-commands.md +39 -0
  18. package/skills/ce-babysit-pr/references/stack.md +43 -0
  19. package/skills/ce-babysit-pr/references/tick.md +102 -0
  20. package/skills/ce-babysit-pr/references/watch-loop.md +222 -0
  21. package/skills/ce-babysit-pr/scripts/pr-snapshot +3656 -0
  22. package/skills/ce-bakeoff/SKILL.md +48 -0
  23. package/skills/ce-bakeoff/references/candidates.md +37 -0
  24. package/skills/ce-bakeoff/references/judging.md +19 -0
  25. package/skills/ce-bakeoff/references/output.md +11 -0
  26. package/skills/ce-bakeoff/references/verification.md +15 -0
  27. package/skills/ce-brainstorm/SKILL.md +69 -0
  28. package/skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
  29. package/skills/ce-brainstorm/references/approaches.md +57 -0
  30. package/skills/ce-brainstorm/references/bakeoff.md +9 -0
  31. package/skills/ce-brainstorm/references/blindspot-pass.md +70 -0
  32. package/skills/ce-brainstorm/references/brainstorm-sections.md +458 -0
  33. package/skills/ce-brainstorm/references/dialogue.md +89 -0
  34. package/skills/ce-brainstorm/references/handoff.md +187 -0
  35. package/skills/ce-brainstorm/references/html-rendering.md +638 -0
  36. package/skills/ce-brainstorm/references/interaction-rules.md +33 -0
  37. package/skills/ce-brainstorm/references/markdown-rendering.md +242 -0
  38. package/skills/ce-brainstorm/references/model-tiers.md +13 -0
  39. package/skills/ce-brainstorm/references/output-mode.md +26 -0
  40. package/skills/ce-brainstorm/references/phase-0.md +104 -0
  41. package/skills/ce-brainstorm/references/plan-write.md +32 -0
  42. package/skills/ce-brainstorm/references/product-pressure-test.md +42 -0
  43. package/skills/ce-brainstorm/references/reasoning-elevation.md +124 -0
  44. package/skills/ce-brainstorm/references/settled-decisions.md +45 -0
  45. package/skills/ce-brainstorm/references/synthesis-summary.md +149 -0
  46. package/skills/ce-brainstorm/references/universal-brainstorming.md +72 -0
  47. package/skills/ce-brainstorm/references/verdict-routing.md +26 -0
  48. package/skills/ce-brainstorm/references/visual-probes.md +164 -0
  49. package/skills/ce-brainstorm/scripts/elevation-dispatch.sh +288 -0
  50. package/skills/ce-brainstorm/scripts/light-webserver.js +1275 -0
  51. package/skills/ce-brainstorm/scripts/packs-resolve.py +737 -0
  52. package/skills/ce-brainstorm/scripts/peer-job-runner.py +2594 -0
  53. package/skills/ce-code-review/SKILL.md +40 -0
  54. package/skills/ce-code-review/references/action-class-rubric.md +67 -0
  55. package/skills/ce-code-review/references/cross-model-recovery.md +38 -0
  56. package/skills/ce-code-review/references/cross-model-review.md +191 -0
  57. package/skills/ce-code-review/references/depth-paths.md +32 -0
  58. package/skills/ce-code-review/references/diff-scope.md +59 -0
  59. package/skills/ce-code-review/references/dispatch-reviewers.md +135 -0
  60. package/skills/ce-code-review/references/findings-schema.json +125 -0
  61. package/skills/ce-code-review/references/finish-input.md +118 -0
  62. package/skills/ce-code-review/references/finish-review.md +202 -0
  63. package/skills/ce-code-review/references/intent-and-plan.md +73 -0
  64. package/skills/ce-code-review/references/modes-and-output.md +156 -0
  65. package/skills/ce-code-review/references/persona-catalog.md +66 -0
  66. package/skills/ce-code-review/references/personas/adversarial-reviewer.md +111 -0
  67. package/skills/ce-code-review/references/personas/agent-native-reviewer.md +173 -0
  68. package/skills/ce-code-review/references/personas/api-contract-reviewer.md +44 -0
  69. package/skills/ce-code-review/references/personas/correctness-reviewer.md +48 -0
  70. package/skills/ce-code-review/references/personas/data-migration-reviewer.md +111 -0
  71. package/skills/ce-code-review/references/personas/deployment-verification-agent.md +157 -0
  72. package/skills/ce-code-review/references/personas/julik-frontend-races-reviewer.md +45 -0
  73. package/skills/ce-code-review/references/personas/learnings-researcher.md +260 -0
  74. package/skills/ce-code-review/references/personas/maintainability-reviewer.md +85 -0
  75. package/skills/ce-code-review/references/personas/performance-reviewer.md +45 -0
  76. package/skills/ce-code-review/references/personas/previous-comments-reviewer.md +59 -0
  77. package/skills/ce-code-review/references/personas/project-standards-reviewer.md +77 -0
  78. package/skills/ce-code-review/references/personas/reliability-reviewer.md +49 -0
  79. package/skills/ce-code-review/references/personas/security-reviewer.md +50 -0
  80. package/skills/ce-code-review/references/personas/swift-ios-reviewer.md +99 -0
  81. package/skills/ce-code-review/references/personas/testing-reviewer.md +51 -0
  82. package/skills/ce-code-review/references/review-output-template.md +172 -0
  83. package/skills/ce-code-review/references/scope.md +176 -0
  84. package/skills/ce-code-review/references/select-and-route.md +93 -0
  85. package/skills/ce-code-review/references/subagent-template.md +206 -0
  86. package/skills/ce-code-review/references/validator-batch-template.md +67 -0
  87. package/skills/ce-code-review/scripts/cross-model-adversarial-review.sh +1275 -0
  88. package/skills/ce-code-review/scripts/findings-mechanics.py +274 -0
  89. package/skills/ce-code-review/scripts/packs-resolve.py +737 -0
  90. package/skills/ce-code-review/scripts/peer-job-runner.py +2594 -0
  91. package/skills/ce-code-review/scripts/review-scope.py +419 -0
  92. package/skills/ce-code-review/scripts/run-log.py +280 -0
  93. package/skills/ce-commit/SKILL.md +60 -0
  94. package/skills/ce-commit-push-pr/SKILL.md +69 -0
  95. package/skills/ce-commit-push-pr/references/apply-and-handoff.md +79 -0
  96. package/skills/ce-commit-push-pr/references/branch-creation.md +47 -0
  97. package/skills/ce-commit-push-pr/references/commit-and-push.md +26 -0
  98. package/skills/ce-commit-push-pr/references/compose.md +29 -0
  99. package/skills/ce-commit-push-pr/references/context.md +42 -0
  100. package/skills/ce-commit-push-pr/references/gh-stack-cli.md +83 -0
  101. package/skills/ce-commit-push-pr/references/pr-description-writing.md +208 -0
  102. package/skills/ce-commit-push-pr/references/stack-submit.md +81 -0
  103. package/skills/ce-compound/SKILL.md +79 -0
  104. package/skills/ce-compound/assets/resolution-template.md +96 -0
  105. package/skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
  106. package/skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
  107. package/skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
  108. package/skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
  109. package/skills/ce-compound/references/agents/performance-oracle.md +108 -0
  110. package/skills/ce-compound/references/agents/security-sentinel.md +91 -0
  111. package/skills/ce-compound/references/agents/session-historian.md +82 -0
  112. package/skills/ce-compound/references/assembly.md +127 -0
  113. package/skills/ce-compound/references/concepts-vocabulary.md +98 -0
  114. package/skills/ce-compound/references/enhancement.md +42 -0
  115. package/skills/ce-compound/references/grounding-validation.md +89 -0
  116. package/skills/ce-compound/references/lightweight.md +56 -0
  117. package/skills/ce-compound/references/modes.md +13 -0
  118. package/skills/ce-compound/references/refresh-and-discoverability.md +96 -0
  119. package/skills/ce-compound/references/report.md +115 -0
  120. package/skills/ce-compound/references/research.md +144 -0
  121. package/skills/ce-compound/references/schema.yaml +236 -0
  122. package/skills/ce-compound/references/session-history.md +73 -0
  123. package/skills/ce-compound/references/yaml-schema.md +130 -0
  124. package/skills/ce-compound/scripts/packs-resolve.py +737 -0
  125. package/skills/ce-compound/scripts/session-history/__pycache__/dsh_session.cpython-314.pyc +0 -0
  126. package/skills/ce-compound/scripts/session-history/discover-sessions.sh +290 -0
  127. package/skills/ce-compound/scripts/session-history/discover_dsh.py +123 -0
  128. package/skills/ce-compound/scripts/session-history/dsh_session.py +225 -0
  129. package/skills/ce-compound/scripts/session-history/extract-errors.py +178 -0
  130. package/skills/ce-compound/scripts/session-history/extract-metadata.py +301 -0
  131. package/skills/ce-compound/scripts/session-history/extract-skeleton.py +346 -0
  132. package/skills/ce-compound/scripts/validate-doc-claims.py +497 -0
  133. package/skills/ce-compound/scripts/validate-frontmatter.py +137 -0
  134. package/skills/ce-compound-refresh/SKILL.md +88 -0
  135. package/skills/ce-compound-refresh/assets/resolution-template.md +96 -0
  136. package/skills/ce-compound-refresh/references/classify.md +42 -0
  137. package/skills/ce-compound-refresh/references/commit.md +7 -0
  138. package/skills/ce-compound-refresh/references/concepts-vocabulary.md +116 -0
  139. package/skills/ce-compound-refresh/references/discoverability.md +15 -0
  140. package/skills/ce-compound-refresh/references/investigate.md +23 -0
  141. package/skills/ce-compound-refresh/references/modes.md +27 -0
  142. package/skills/ce-compound-refresh/references/per-action-flows.md +142 -0
  143. package/skills/ce-compound-refresh/references/report.md +23 -0
  144. package/skills/ce-compound-refresh/references/schema.yaml +236 -0
  145. package/skills/ce-compound-refresh/references/scope.md +18 -0
  146. package/skills/ce-compound-refresh/references/worth-audit.md +35 -0
  147. package/skills/ce-compound-refresh/references/yaml-schema.md +130 -0
  148. package/skills/ce-compound-refresh/scripts/validate-doc-claims.py +497 -0
  149. package/skills/ce-compound-refresh/scripts/validate-frontmatter.py +137 -0
  150. package/skills/ce-debug/SKILL.md +122 -0
  151. package/skills/ce-debug/references/anti-patterns.md +91 -0
  152. package/skills/ce-debug/references/defense-in-depth.md +39 -0
  153. package/skills/ce-debug/references/fix.md +21 -0
  154. package/skills/ce-debug/references/investigate.md +114 -0
  155. package/skills/ce-debug/references/investigation-techniques.md +384 -0
  156. package/skills/ce-debug/references/pipeline-mode.md +78 -0
  157. package/skills/ce-debug/references/post-fix-handoff.md +60 -0
  158. package/skills/ce-debug/references/return-to-caller.md +57 -0
  159. package/skills/ce-doc-review/SKILL.md +75 -0
  160. package/skills/ce-doc-review/references/bulk-preview.md +146 -0
  161. package/skills/ce-doc-review/references/cross-model-review.md +207 -0
  162. package/skills/ce-doc-review/references/decision-primer.md +47 -0
  163. package/skills/ce-doc-review/references/dispatch.md +56 -0
  164. package/skills/ce-doc-review/references/document-intake.md +44 -0
  165. package/skills/ce-doc-review/references/findings-schema.json +85 -0
  166. package/skills/ce-doc-review/references/modes.md +20 -0
  167. package/skills/ce-doc-review/references/open-questions-defer.md +158 -0
  168. package/skills/ce-doc-review/references/persona-selection.md +41 -0
  169. package/skills/ce-doc-review/references/personas/adversarial-document-reviewer.md +109 -0
  170. package/skills/ce-doc-review/references/personas/coherence-reviewer.md +69 -0
  171. package/skills/ce-doc-review/references/personas/design-lens-reviewer.md +48 -0
  172. package/skills/ce-doc-review/references/personas/feasibility-reviewer.md +50 -0
  173. package/skills/ce-doc-review/references/personas/product-lens-reviewer.md +85 -0
  174. package/skills/ce-doc-review/references/personas/scope-guardian-reviewer.md +83 -0
  175. package/skills/ce-doc-review/references/personas/security-lens-reviewer.md +41 -0
  176. package/skills/ce-doc-review/references/personas/whole-doc-reviewer.md +25 -0
  177. package/skills/ce-doc-review/references/rendering-floor.md +175 -0
  178. package/skills/ce-doc-review/references/review-output-template.md +123 -0
  179. package/skills/ce-doc-review/references/subagent-template.md +188 -0
  180. package/skills/ce-doc-review/references/synthesis-and-presentation.md +350 -0
  181. package/skills/ce-doc-review/references/walkthrough.md +347 -0
  182. package/skills/ce-doc-review/scripts/cross-model-doc-review.sh +1270 -0
  183. package/skills/ce-doc-review/scripts/packs-resolve.py +737 -0
  184. package/skills/ce-doc-review/scripts/peer-job-runner.py +2594 -0
  185. package/skills/ce-dogfood/SKILL.md +66 -0
  186. package/skills/ce-dogfood/agents/openai.yaml +2 -0
  187. package/skills/ce-dogfood/references/dogfood-report-template.md +97 -0
  188. package/skills/ce-dogfood/references/phases.md +167 -0
  189. package/skills/ce-dogfood/references/test-matrix-taxonomy.md +62 -0
  190. package/skills/ce-dogfood/scripts/packs-resolve.py +737 -0
  191. package/skills/ce-explain/SKILL.md +81 -0
  192. package/skills/ce-explain/references/agents/behavior-trace-scout.md +19 -0
  193. package/skills/ce-explain/references/agents/work-recap-scout.md +23 -0
  194. package/skills/ce-explain/references/check-in.md +18 -0
  195. package/skills/ce-explain/references/destinations.md +35 -0
  196. package/skills/ce-explain/references/explainer-html.md +29 -0
  197. package/skills/ce-explain/references/explainer-markdown.md +24 -0
  198. package/skills/ce-explain/references/intake.md +50 -0
  199. package/skills/ce-explain/references/orchestration.md +46 -0
  200. package/skills/ce-handoff/SKILL.md +54 -0
  201. package/skills/ce-handoff/references/create.md +88 -0
  202. package/skills/ce-handoff/references/resume.md +46 -0
  203. package/skills/ce-ideate/SKILL.md +75 -0
  204. package/skills/ce-ideate/references/agents/issue-intelligence-analyst.md +166 -0
  205. package/skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
  206. package/skills/ce-ideate/references/agents/slack-researcher.md +127 -0
  207. package/skills/ce-ideate/references/agents/web-researcher.md +121 -0
  208. package/skills/ce-ideate/references/decomposition.md +39 -0
  209. package/skills/ce-ideate/references/divergent-ideation.md +112 -0
  210. package/skills/ce-ideate/references/grounding.md +138 -0
  211. package/skills/ce-ideate/references/html-rendering.md +638 -0
  212. package/skills/ce-ideate/references/ideation-sections.md +191 -0
  213. package/skills/ce-ideate/references/issue-intelligence.md +44 -0
  214. package/skills/ce-ideate/references/markdown-rendering.md +242 -0
  215. package/skills/ce-ideate/references/output-mode.md +34 -0
  216. package/skills/ce-ideate/references/post-ideation-workflow.md +169 -0
  217. package/skills/ce-ideate/references/scope-gates.md +148 -0
  218. package/skills/ce-ideate/references/universal-ideation.md +119 -0
  219. package/skills/ce-ideate/references/user-research-artifacts.md +28 -0
  220. package/skills/ce-ideate/references/web-research-cache.md +56 -0
  221. package/skills/ce-noslop/SKILL.md +45 -0
  222. package/skills/ce-noslop/references/patterns.md +61 -0
  223. package/skills/ce-noslop/references/terminology.md +3 -0
  224. package/skills/ce-optimize/SKILL.md +64 -0
  225. package/skills/ce-optimize/references/agents/learnings-researcher.md +247 -0
  226. package/skills/ce-optimize/references/agents/repo-research-analyst.md +258 -0
  227. package/skills/ce-optimize/references/example-expensive-benchmark-spec.yaml +88 -0
  228. package/skills/ce-optimize/references/example-hard-spec.yaml +64 -0
  229. package/skills/ce-optimize/references/example-judge-spec.yaml +83 -0
  230. package/skills/ce-optimize/references/example-text-target-spec.yaml +80 -0
  231. package/skills/ce-optimize/references/experiment-log-schema.yaml +563 -0
  232. package/skills/ce-optimize/references/experiment-prompt-template.md +143 -0
  233. package/skills/ce-optimize/references/judge-prompt-template.md +117 -0
  234. package/skills/ce-optimize/references/loop.md +286 -0
  235. package/skills/ce-optimize/references/measurement.md +168 -0
  236. package/skills/ce-optimize/references/optimize-spec-schema.yaml +570 -0
  237. package/skills/ce-optimize/references/persistence.md +93 -0
  238. package/skills/ce-optimize/references/spec.md +107 -0
  239. package/skills/ce-optimize/references/text-targets.md +51 -0
  240. package/skills/ce-optimize/references/usage-guide.md +147 -0
  241. package/skills/ce-optimize/references/wrap-up.md +54 -0
  242. package/skills/ce-optimize/scripts/decide.mjs +676 -0
  243. package/skills/ce-optimize/scripts/experiment-worktree.sh +330 -0
  244. package/skills/ce-optimize/scripts/measure.sh +140 -0
  245. package/skills/ce-optimize/scripts/parallel-probe.sh +134 -0
  246. package/skills/ce-plan/SKILL.md +60 -0
  247. package/skills/ce-plan/references/agents/agent-native-planning-strategist.md +66 -0
  248. package/skills/ce-plan/references/agents/architecture-strategist.md +46 -0
  249. package/skills/ce-plan/references/agents/best-practices-researcher.md +118 -0
  250. package/skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
  251. package/skills/ce-plan/references/agents/framework-docs-researcher.md +97 -0
  252. package/skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
  253. package/skills/ce-plan/references/agents/learnings-researcher.md +268 -0
  254. package/skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
  255. package/skills/ce-plan/references/agents/performance-oracle.md +108 -0
  256. package/skills/ce-plan/references/agents/repo-research-analyst.md +262 -0
  257. package/skills/ce-plan/references/agents/security-sentinel.md +91 -0
  258. package/skills/ce-plan/references/agents/slack-researcher.md +131 -0
  259. package/skills/ce-plan/references/agents/spec-flow-analyzer.md +84 -0
  260. package/skills/ce-plan/references/agents/web-researcher.md +125 -0
  261. package/skills/ce-plan/references/approach-altitude.md +55 -0
  262. package/skills/ce-plan/references/bakeoff.md +9 -0
  263. package/skills/ce-plan/references/deepening-workflow.md +263 -0
  264. package/skills/ce-plan/references/final-review.md +134 -0
  265. package/skills/ce-plan/references/html-rendering.md +638 -0
  266. package/skills/ce-plan/references/intake.md +158 -0
  267. package/skills/ce-plan/references/markdown-rendering.md +242 -0
  268. package/skills/ce-plan/references/output-contracts.md +29 -0
  269. package/skills/ce-plan/references/output-mode.md +45 -0
  270. package/skills/ce-plan/references/plan-handoff.md +116 -0
  271. package/skills/ce-plan/references/plan-sections.md +489 -0
  272. package/skills/ce-plan/references/reasoning-elevation.md +124 -0
  273. package/skills/ce-plan/references/research.md +211 -0
  274. package/skills/ce-plan/references/resume.md +61 -0
  275. package/skills/ce-plan/references/settled-decisions.md +45 -0
  276. package/skills/ce-plan/references/structure.md +207 -0
  277. package/skills/ce-plan/references/synthesis-summary.md +260 -0
  278. package/skills/ce-plan/references/universal-planning.md +170 -0
  279. package/skills/ce-plan/scripts/elevation-dispatch.sh +288 -0
  280. package/skills/ce-plan/scripts/packs-resolve.py +737 -0
  281. package/skills/ce-plan/scripts/peer-job-runner.py +2594 -0
  282. package/skills/ce-polish/SKILL.md +22 -0
  283. package/skills/ce-polish/agents/openai.yaml +2 -0
  284. package/skills/ce-polish/references/dev-server-astro.md +58 -0
  285. package/skills/ce-polish/references/dev-server-detection.md +40 -0
  286. package/skills/ce-polish/references/dev-server-next.md +61 -0
  287. package/skills/ce-polish/references/dev-server-nuxt.md +58 -0
  288. package/skills/ce-polish/references/dev-server-procfile.md +59 -0
  289. package/skills/ce-polish/references/dev-server-rails.md +50 -0
  290. package/skills/ce-polish/references/dev-server-remix.md +58 -0
  291. package/skills/ce-polish/references/dev-server-sveltekit.md +58 -0
  292. package/skills/ce-polish/references/dev-server-vite.md +48 -0
  293. package/skills/ce-polish/references/install-riffrec.md +35 -0
  294. package/skills/ce-polish/references/launch-json-schema.md +177 -0
  295. package/skills/ce-polish/references/live-loop.md +98 -0
  296. package/skills/ce-polish/references/live-remote.md +35 -0
  297. package/skills/ce-polish/references/live-start.md +93 -0
  298. package/skills/ce-polish/references/live-stream-contract.md +253 -0
  299. package/skills/ce-polish/references/run.md +67 -0
  300. package/skills/ce-polish/scripts/detect-project-type.sh +277 -0
  301. package/skills/ce-polish/scripts/detect-riffrec.sh +225 -0
  302. package/skills/ce-polish/scripts/live-endpoint.js +2841 -0
  303. package/skills/ce-polish/scripts/read-launch-json.sh +117 -0
  304. package/skills/ce-polish/scripts/resolve-package-manager.sh +95 -0
  305. package/skills/ce-polish/scripts/resolve-port.sh +307 -0
  306. package/skills/ce-pov/SKILL.md +64 -0
  307. package/skills/ce-pov/references/agents/external-evidence-researcher.md +26 -0
  308. package/skills/ce-pov/references/agents/pov-peer.md +43 -0
  309. package/skills/ce-pov/references/agents/precedent-activity-scout.md +21 -0
  310. package/skills/ce-pov/references/agents/project-grounding-scout.md +32 -0
  311. package/skills/ce-pov/references/boundaries.md +36 -0
  312. package/skills/ce-pov/references/cross-model-panel.md +596 -0
  313. package/skills/ce-pov/references/followup.md +9 -0
  314. package/skills/ce-pov/references/grounding.md +47 -0
  315. package/skills/ce-pov/references/intake.md +61 -0
  316. package/skills/ce-pov/references/invocation.md +47 -0
  317. package/skills/ce-pov/references/method.md +68 -0
  318. package/skills/ce-pov/references/pov-schema.json +56 -0
  319. package/skills/ce-pov/references/report.md +7 -0
  320. package/skills/ce-pov/scripts/cross-model-pov.sh +1110 -0
  321. package/skills/ce-pov/scripts/peer-job-runner.py +2594 -0
  322. package/skills/ce-product-pulse/SKILL.md +81 -0
  323. package/skills/ce-product-pulse/agents/openai.yaml +2 -0
  324. package/skills/ce-product-pulse/references/config.md +20 -0
  325. package/skills/ce-product-pulse/references/interview.md +260 -0
  326. package/skills/ce-product-pulse/references/report-template.md +89 -0
  327. package/skills/ce-product-pulse/references/run.md +46 -0
  328. package/skills/ce-product-pulse/references/setup.md +43 -0
  329. package/skills/ce-promote/SKILL.md +90 -0
  330. package/skills/ce-promote/agents/openai.yaml +2 -0
  331. package/skills/ce-promote/references/spiral-cli.md +149 -0
  332. package/skills/ce-proof/SKILL.md +65 -0
  333. package/skills/ce-proof/references/api.md +176 -0
  334. package/skills/ce-proof/references/workflows.md +124 -0
  335. package/skills/ce-prototype/SKILL.md +59 -0
  336. package/skills/ce-prototype/assets/annotate.css +206 -0
  337. package/skills/ce-prototype/assets/annotate.js +837 -0
  338. package/skills/ce-prototype/references/annotation-loop.md +32 -0
  339. package/skills/ce-prototype/references/build.md +39 -0
  340. package/skills/ce-prototype/references/craft-floor.md +43 -0
  341. package/skills/ce-prototype/references/preview.md +118 -0
  342. package/skills/ce-prototype/references/scoping.md +38 -0
  343. package/skills/ce-prototype/references/write-back.md +37 -0
  344. package/skills/ce-prototype/scripts/light-webserver.js +1275 -0
  345. package/skills/ce-resolve-pr-feedback/SKILL.md +60 -0
  346. package/skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md +59 -0
  347. package/skills/ce-resolve-pr-feedback/references/evaluation-rubric.md +141 -0
  348. package/skills/ce-resolve-pr-feedback/references/full-mode.md +336 -0
  349. package/skills/ce-resolve-pr-feedback/references/pipeline-mode.md +23 -0
  350. package/skills/ce-resolve-pr-feedback/references/resume.md +60 -0
  351. package/skills/ce-resolve-pr-feedback/references/return-to-caller.md +111 -0
  352. package/skills/ce-resolve-pr-feedback/references/targeted-mode.md +48 -0
  353. package/skills/ce-resolve-pr-feedback/scripts/get-pr-comments +176 -0
  354. package/skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment +84 -0
  355. package/skills/ce-resolve-pr-feedback/scripts/pending-feedback.py +287 -0
  356. package/skills/ce-resolve-pr-feedback/scripts/reply-to-pr-thread +57 -0
  357. package/skills/ce-resolve-pr-feedback/scripts/resolve-pr-thread +23 -0
  358. package/skills/ce-retune/SKILL.md +42 -0
  359. package/skills/ce-retune/agents/openai.yaml +2 -0
  360. package/skills/ce-retune/references/baseline-mining.md +122 -0
  361. package/skills/ce-retune/references/corpus-audit.md +128 -0
  362. package/skills/ce-retune/references/cut-passes.md +129 -0
  363. package/skills/ce-retune/references/halt-taxonomy.md +123 -0
  364. package/skills/ce-retune/references/noise-floor.md +131 -0
  365. package/skills/ce-retune/references/workflow-shapes.md +73 -0
  366. package/skills/ce-riffrec-feedback-analysis/SKILL.md +33 -0
  367. package/skills/ce-riffrec-feedback-analysis/references/analyzer.md +18 -0
  368. package/skills/ce-riffrec-feedback-analysis/references/compound-engineering-feedback-format.md +116 -0
  369. package/skills/ce-riffrec-feedback-analysis/references/extensive-analysis.md +98 -0
  370. package/skills/ce-riffrec-feedback-analysis/references/install-riffrec.md +27 -0
  371. package/skills/ce-riffrec-feedback-analysis/references/quick-bug-report.md +38 -0
  372. package/skills/ce-riffrec-feedback-analysis/scripts/analyze_riffrec_zip.py +1306 -0
  373. package/skills/ce-setup/SKILL.md +103 -0
  374. package/skills/ce-setup/agents/openai.yaml +2 -0
  375. package/skills/ce-setup/assets/compounding-directive.md +11 -0
  376. package/skills/ce-setup/assets/noslop-directive.md +7 -0
  377. package/skills/ce-setup/assets/pack-rule-template.md +11 -0
  378. package/skills/ce-setup/references/acpx-cache-warm.md +14 -0
  379. package/skills/ce-setup/references/config-template.yaml +210 -0
  380. package/skills/ce-setup/references/legacy-codex-tool-map.md +15 -0
  381. package/skills/ce-setup/references/pack-scaffold.md +45 -0
  382. package/skills/ce-setup/references/repo-fixes.md +121 -0
  383. package/skills/ce-setup/scripts/check-health +929 -0
  384. package/skills/ce-setup/scripts/packs-resolve.py +737 -0
  385. package/skills/ce-simplify-code/SKILL.md +76 -0
  386. package/skills/ce-simplify-code/references/personas/code-quality-reviewer.md +17 -0
  387. package/skills/ce-simplify-code/references/personas/code-reuse-reviewer.md +7 -0
  388. package/skills/ce-simplify-code/references/personas/comment-reviewer.md +24 -0
  389. package/skills/ce-simplify-code/references/personas/efficiency-reviewer.md +11 -0
  390. package/skills/ce-simplify-code/references/personas/test-value-reviewer.md +23 -0
  391. package/skills/ce-simplify-code/references/reviewer-dispatch.md +11 -0
  392. package/skills/ce-simplify-code/references/standalone-report.md +25 -0
  393. package/skills/ce-strategy/SKILL.md +61 -0
  394. package/skills/ce-strategy/references/grounding.md +22 -0
  395. package/skills/ce-strategy/references/interview.md +179 -0
  396. package/skills/ce-strategy/references/strategy-template.md +94 -0
  397. package/skills/ce-strategy/references/update-run.md +33 -0
  398. package/skills/ce-sweep/SKILL.md +93 -0
  399. package/skills/ce-sweep/agents/openai.yaml +2 -0
  400. package/skills/ce-sweep/references/agents/media-analyzer.md +54 -0
  401. package/skills/ce-sweep/references/interview.md +208 -0
  402. package/skills/ce-sweep/references/model-tiers.md +13 -0
  403. package/skills/ce-sweep/references/plan-template.md +62 -0
  404. package/skills/ce-sweep/references/run.md +126 -0
  405. package/skills/ce-sweep/references/sources/email.md +57 -0
  406. package/skills/ce-sweep/references/sources/github-issues.md +59 -0
  407. package/skills/ce-sweep/references/sources/slack.md +61 -0
  408. package/skills/ce-sweep/references/state-schema.md +157 -0
  409. package/skills/ce-sweep/references/subagent-template.md +60 -0
  410. package/skills/ce-sweep/scripts/analyze_riffrec_zip.py +1306 -0
  411. package/skills/ce-sweep/scripts/sweep-state.py +793 -0
  412. package/skills/ce-test-browser/SKILL.md +50 -0
  413. package/skills/ce-test-browser/references/agent-browser-driver.md +47 -0
  414. package/skills/ce-test-browser/references/pipeline-orchestration.md +45 -0
  415. package/skills/ce-test-browser/references/route-and-report.md +129 -0
  416. package/skills/ce-test-browser/scripts/resolve-port.sh +41 -0
  417. package/skills/ce-test-xcode/SKILL.md +26 -0
  418. package/skills/ce-test-xcode/agents/openai.yaml +2 -0
  419. package/skills/ce-test-xcode/references/setup-and-build.md +32 -0
  420. package/skills/ce-test-xcode/references/test-and-report.md +64 -0
  421. package/skills/ce-work/SKILL.md +62 -0
  422. package/skills/ce-work/references/agents/figma-design-sync.md +27 -0
  423. package/skills/ce-work/references/agents/implementation-worker.md +17 -0
  424. package/skills/ce-work/references/cross-model-execution.md +191 -0
  425. package/skills/ce-work/references/execution-engines.md +64 -0
  426. package/skills/ce-work/references/execution-strategy.md +73 -0
  427. package/skills/ce-work/references/implementation-loop.md +107 -0
  428. package/skills/ce-work/references/implementation-result-schema.json +52 -0
  429. package/skills/ce-work/references/input-triage.md +50 -0
  430. package/skills/ce-work/references/non-code-execution.md +23 -0
  431. package/skills/ce-work/references/return-to-caller.md +33 -0
  432. package/skills/ce-work/references/review-findings-followup.md +82 -0
  433. package/skills/ce-work/references/shipping-workflow.md +89 -0
  434. package/skills/ce-work/references/tracker-defer.md +150 -0
  435. package/skills/ce-work/references/work-intake.md +51 -0
  436. package/skills/ce-work/references/workspace-setup.md +27 -0
  437. package/skills/ce-work/scripts/__pycache__/unit_workspace_jobs.cpython-314.pyc +0 -0
  438. package/skills/ce-work/scripts/__pycache__/unit_workspace_state.cpython-314.pyc +0 -0
  439. package/skills/ce-work/scripts/cross-model-work.sh +1385 -0
  440. package/skills/ce-work/scripts/peer-job-runner.py +2594 -0
  441. package/skills/ce-work/scripts/unit-workspace.py +215 -0
  442. package/skills/ce-work/scripts/unit_workspace_ignored.py +93 -0
  443. package/skills/ce-work/scripts/unit_workspace_integration.py +677 -0
  444. package/skills/ce-work/scripts/unit_workspace_jobs.py +1216 -0
  445. package/skills/ce-work/scripts/unit_workspace_lifecycle.py +981 -0
  446. package/skills/ce-work/scripts/unit_workspace_state.py +1379 -0
  447. package/skills/ce-work/scripts/unit_workspace_transaction.py +612 -0
  448. package/skills/ce-worktree/SKILL.md +54 -0
  449. package/skills/lfg/SKILL.md +59 -0
  450. package/skills/lfg/references/debug-return.md +25 -0
  451. package/skills/lfg/references/intake.md +30 -0
  452. package/skills/lfg/references/next-work-handoff.md +96 -0
  453. package/skills/lfg/references/plan-brief.md +36 -0
  454. package/skills/lfg/references/review-followup.md +78 -0
  455. package/skills/lfg/references/shipping.md +65 -0
  456. package/skills/lfg/references/stage-routing.md +49 -0
  457. package/skills/lfg/references/task-visibility.md +11 -0
  458. package/skills/lfg/references/tracker-defer.md +150 -0
  459. package/skills/lfg/references/work-return.md +27 -0
  460. package/skills/wtf/SKILL.md +33 -0
  461. package/skills/wtf/agents/openai.yaml +2 -0
@@ -0,0 +1,1379 @@
1
+ """Private, crash-recoverable workspace controller for ce-work external units.
2
+
3
+ The generic peer-job runner owns process supervision. This controller owns the
4
+ repository-specific transaction: one private run manifest, detached sibling
5
+ worktrees, complete-tree transport commits, canonical integration evidence,
6
+ exact restoration, retention, and explicit cleanup. It never launches a model
7
+ CLI and never commits a worker's output in the canonical checkout.
8
+
9
+ Every successful command prints a status word and one compact JSON document.
10
+ Trust failures print only ``UNREADABLE`` and an error on stderr.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import base64
17
+ import contextlib
18
+ import hashlib
19
+ import json
20
+ import os
21
+ import re
22
+ import secrets
23
+ import shutil
24
+ import stat
25
+ import subprocess
26
+ import sys
27
+ import tempfile
28
+ import time
29
+ from pathlib import Path
30
+
31
+
32
+ IS_WINDOWS = sys.platform == "win32"
33
+
34
+ # --- platform privacy primitives ---------------------------------------------
35
+ # POSIX expresses "this private directory is mine and nobody else can read it"
36
+ # with two things this module relies on throughout: `st_uid == geteuid()` and
37
+ # the 0700/0600 mode bits. Windows has neither. It has no `fcntl` at all, and
38
+ # `os.stat().st_mode` there is a synthesized value that reflects only the
39
+ # read-only attribute -- a directory created with `os.mkdir(p, 0o700)` reports
40
+ # 0o777 and a file `os.chmod(p, 0o600)` reports 0o666, so every `_mode(st) ==
41
+ # 0o700` gate below would fail closed on a perfectly private directory.
42
+ #
43
+ # So on Windows the equivalent identity is the owner SID of the object as read
44
+ # from its security descriptor, and the equivalent of the mode bits is a DACL
45
+ # hardened to the current user plus the root-equivalents (SYSTEM,
46
+ # Administrators) with inheritance broken -- exactly the substitution
47
+ # peer-job-runner.py already makes. Pure stdlib via ctypes; no pywin32.
48
+ if IS_WINDOWS:
49
+ import ctypes
50
+ import msvcrt
51
+ from ctypes import wintypes
52
+
53
+ _advapi32 = ctypes.WinDLL("advapi32", use_last_error=True)
54
+ _kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
55
+
56
+ _SE_FILE_OBJECT = 1
57
+ _OWNER_SECURITY_INFORMATION = 0x00000001
58
+ _TOKEN_QUERY = 0x0008
59
+ _TOKEN_USER_CLASS = 1
60
+ _TOKEN_OWNER_CLASS = 4
61
+
62
+ _advapi32.ConvertSidToStringSidW.argtypes = [
63
+ ctypes.c_void_p, ctypes.POINTER(ctypes.c_wchar_p)]
64
+ _advapi32.ConvertSidToStringSidW.restype = wintypes.BOOL
65
+ _advapi32.OpenProcessToken.argtypes = [
66
+ wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)]
67
+ _advapi32.OpenProcessToken.restype = wintypes.BOOL
68
+ _advapi32.GetTokenInformation.argtypes = [
69
+ wintypes.HANDLE, ctypes.c_int, ctypes.c_void_p, wintypes.DWORD,
70
+ ctypes.POINTER(wintypes.DWORD)]
71
+ _advapi32.GetTokenInformation.restype = wintypes.BOOL
72
+ _advapi32.GetSecurityInfo.argtypes = [
73
+ wintypes.HANDLE, ctypes.c_int, wintypes.DWORD,
74
+ ctypes.POINTER(ctypes.c_void_p), ctypes.c_void_p, ctypes.c_void_p,
75
+ ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)]
76
+ _advapi32.GetSecurityInfo.restype = wintypes.DWORD
77
+ _advapi32.GetNamedSecurityInfoW.argtypes = [
78
+ wintypes.LPCWSTR, ctypes.c_int, wintypes.DWORD,
79
+ ctypes.POINTER(ctypes.c_void_p), ctypes.c_void_p, ctypes.c_void_p,
80
+ ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)]
81
+ _advapi32.GetNamedSecurityInfoW.restype = wintypes.DWORD
82
+ _kernel32.GetCurrentProcess.restype = wintypes.HANDLE
83
+ _kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
84
+ _kernel32.CloseHandle.restype = wintypes.BOOL
85
+ _kernel32.LocalFree.argtypes = [wintypes.HGLOBAL]
86
+ _kernel32.LocalFree.restype = wintypes.HGLOBAL
87
+
88
+ _WIN_IDENTITY_SIDS = None
89
+
90
+ def _win_sid_to_string(psid) -> str:
91
+ strp = ctypes.c_wchar_p()
92
+ if not _advapi32.ConvertSidToStringSidW(psid, ctypes.byref(strp)):
93
+ raise OSError(f"ConvertSidToStringSid failed: {ctypes.get_last_error()}")
94
+ try:
95
+ return strp.value
96
+ finally:
97
+ _kernel32.LocalFree(ctypes.cast(strp, wintypes.HGLOBAL))
98
+
99
+ def _win_token_sid(token, info_class) -> str:
100
+ size = wintypes.DWORD(0)
101
+ _advapi32.GetTokenInformation(token, info_class, None, 0, ctypes.byref(size))
102
+ buf = (ctypes.c_byte * size.value)()
103
+ if not _advapi32.GetTokenInformation(
104
+ token, info_class, buf, size, ctypes.byref(size)
105
+ ):
106
+ raise OSError(f"GetTokenInformation failed: {ctypes.get_last_error()}")
107
+ # TOKEN_USER / TOKEN_OWNER both begin with a PSID at offset 0.
108
+ sid_ptr = ctypes.cast(buf, ctypes.POINTER(ctypes.c_void_p))[0]
109
+ return _win_sid_to_string(ctypes.c_void_p(sid_ptr))
110
+
111
+ def _win_identity_sids() -> frozenset:
112
+ """The SID strings this process's token creates objects as: the user SID
113
+ and the default-owner SID (they differ for an elevated process). Cached;
114
+ an object owned by any of these is treated as ours."""
115
+ global _WIN_IDENTITY_SIDS
116
+ if _WIN_IDENTITY_SIDS is not None:
117
+ return _WIN_IDENTITY_SIDS
118
+ token = wintypes.HANDLE()
119
+ if not _advapi32.OpenProcessToken(
120
+ _kernel32.GetCurrentProcess(), _TOKEN_QUERY, ctypes.byref(token)
121
+ ):
122
+ raise OSError(f"OpenProcessToken failed: {ctypes.get_last_error()}")
123
+ try:
124
+ sids = {
125
+ _win_token_sid(token, _TOKEN_USER_CLASS),
126
+ _win_token_sid(token, _TOKEN_OWNER_CLASS),
127
+ }
128
+ finally:
129
+ _kernel32.CloseHandle(token)
130
+ _WIN_IDENTITY_SIDS = frozenset(s for s in sids if s)
131
+ return _WIN_IDENTITY_SIDS
132
+
133
+ def _win_owner_sid_handle(handle: int) -> str:
134
+ """Owner SID of an OPENED handle. The SID points inside the returned
135
+ security descriptor, so freeing the descriptor is the only (and
136
+ required) cleanup -- never free the SID separately."""
137
+ psid = ctypes.c_void_p()
138
+ psd = ctypes.c_void_p()
139
+ err = _advapi32.GetSecurityInfo(
140
+ wintypes.HANDLE(handle), _SE_FILE_OBJECT, _OWNER_SECURITY_INFORMATION,
141
+ ctypes.byref(psid), None, None, None, ctypes.byref(psd))
142
+ if err != 0:
143
+ raise OSError(f"GetSecurityInfo failed: {err}")
144
+ try:
145
+ return _win_sid_to_string(psid)
146
+ finally:
147
+ _kernel32.LocalFree(ctypes.cast(psd, wintypes.HGLOBAL))
148
+
149
+ def win_owns_handle(handle: int) -> bool:
150
+ try:
151
+ return _win_owner_sid_handle(handle) in _win_identity_sids()
152
+ except OSError:
153
+ return False
154
+
155
+ def win_owns_path(path: str) -> bool:
156
+ """Owner check by PATH. Required for directories: Windows cannot open a
157
+ directory with os.open(), so a handle-based check cannot reach one."""
158
+ psid = ctypes.c_void_p()
159
+ psd = ctypes.c_void_p()
160
+ err = _advapi32.GetNamedSecurityInfoW(
161
+ path, _SE_FILE_OBJECT, _OWNER_SECURITY_INFORMATION,
162
+ ctypes.byref(psid), None, None, None, ctypes.byref(psd))
163
+ if err != 0:
164
+ return False
165
+ try:
166
+ return _win_sid_to_string(psid) in _win_identity_sids()
167
+ except OSError:
168
+ return False
169
+ finally:
170
+ _kernel32.LocalFree(ctypes.cast(psd, wintypes.HGLOBAL))
171
+
172
+ def win_harden_acl(path: str) -> bool:
173
+ """Break inheritance and grant only the current user plus the
174
+ root-equivalents, so no world/Users grant survives -- the 0700 analog.
175
+ Best-effort: the owner check stays the hard gate if icacls is absent."""
176
+ sids = _win_identity_sids()
177
+ if not sids:
178
+ return False
179
+ system_root = os.environ.get("SystemRoot") or r"C:\Windows"
180
+ icacls = os.path.join(system_root, "System32", "icacls.exe")
181
+ if not os.path.isfile(icacls):
182
+ return False
183
+ inherit = "(OI)(CI)" if os.path.isdir(path) else ""
184
+ # Grant EVERY identity SID, not an arbitrary one from the set: an
185
+ # elevated token carries two (user + default owner), and picking one
186
+ # nondeterministically could grant the wrong principal.
187
+ grants = []
188
+ for sid in sorted(sids) + ["S-1-5-18", "S-1-5-32-544"]:
189
+ grants += ["/grant:r", f"*{sid}:{inherit}F"]
190
+ try:
191
+ return subprocess.run(
192
+ [icacls, path, "/inheritance:r"] + grants,
193
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
194
+ check=False,
195
+ creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000),
196
+ ).returncode == 0
197
+ except OSError:
198
+ return False
199
+
200
+ def _lock_fd(fd: int, exclusive: bool) -> None:
201
+ """Advisory whole-file lock with flock's blocking semantics.
202
+
203
+ msvcrt has no blocking shared lock and no shared mode at all, so a
204
+ contended acquire raises EACCES instead of waiting. flock(fd, LOCK_EX)
205
+ on POSIX blocks, and every caller here relies on that -- a status read
206
+ that raced a writer must queue, not crash. So retry on contention until
207
+ the lock lands, with a bounded wait so a wedged holder cannot hang the
208
+ command forever.
209
+ """
210
+ os.lseek(fd, 0, os.SEEK_SET)
211
+ deadline = time.monotonic() + LOCK_WAIT_SECS
212
+ delay = 0.01
213
+ while True:
214
+ try:
215
+ msvcrt.locking(fd, msvcrt.LK_NBLCK, 1)
216
+ return
217
+ except OSError:
218
+ if time.monotonic() >= deadline:
219
+ raise TrustFailure(
220
+ f"timed out after {LOCK_WAIT_SECS}s waiting for the run lock"
221
+ )
222
+ time.sleep(delay)
223
+ delay = min(delay * 2, 0.25)
224
+
225
+ def _unlock_fd(fd: int) -> None:
226
+ with contextlib.suppress(OSError):
227
+ os.lseek(fd, 0, os.SEEK_SET)
228
+ msvcrt.locking(fd, msvcrt.LK_UNLCK, 1)
229
+
230
+ def _modeis_private(st: os.stat_result, want_dir: bool) -> bool:
231
+ """Windows has no mode privacy to verify; the ACL and owner check carry
232
+ it. Always true so the caller's POSIX gate never fails closed here."""
233
+ return True
234
+ else:
235
+ import fcntl
236
+
237
+ def _lock_fd(fd: int, exclusive: bool) -> None:
238
+ fcntl.flock(fd, fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH)
239
+
240
+ def _unlock_fd(fd: int) -> None:
241
+ with contextlib.suppress(OSError):
242
+ fcntl.flock(fd, fcntl.LOCK_UN)
243
+
244
+ def _modeis_private(st: os.stat_result, want_dir: bool) -> bool:
245
+ expected = 0o700 if want_dir else 0o600
246
+ return stat.S_IMODE(st.st_mode) == expected
247
+
248
+
249
+ SCHEMA_VERSION = 1
250
+ PLAN_CHECKPOINT_MESSAGE = "docs(ce-work): checkpoint selected implementation plan"
251
+ _uid_getter = getattr(os, "geteuid", None) or getattr(os, "getuid", None)
252
+ _EFFECTIVE_UID = _uid_getter() if _uid_getter is not None else None
253
+ if IS_WINDOWS:
254
+ # No geteuid on Windows, so the per-user scratch root is derived from the
255
+ # user profile instead of a uid -- the same substitution peer-job-runner
256
+ # makes. LOCALAPPDATA is per-user and never world-readable, which is what
257
+ # the `/tmp/compound-engineering-<uid>` 0700 root provides on POSIX.
258
+ OWNER_SCRATCH_ROOT = os.path.join(
259
+ os.environ.get("LOCALAPPDATA") or tempfile.gettempdir(),
260
+ "compound-engineering",
261
+ )
262
+ elif _EFFECTIVE_UID is not None:
263
+ OWNER_SCRATCH_ROOT = os.path.join("/tmp", f"compound-engineering-{_EFFECTIVE_UID}")
264
+ else:
265
+ OWNER_SCRATCH_ROOT = None
266
+ MAX_JSON_BYTES = 2 * 1024 * 1024
267
+ MAX_PACKET_BYTES = 200_000
268
+ SAFE_ID = re.compile(r"^[A-Za-z0-9._-]{1,128}$")
269
+ O_NOFOLLOW = getattr(os, "O_NOFOLLOW", 0)
270
+ O_DIRECTORY = getattr(os, "O_DIRECTORY", 0)
271
+ # Windows CPython opens os.open() descriptors in CRT *text* mode by default:
272
+ # writes expand \n -> \r\n and reads stop at the first 0x1A (Ctrl-Z EOF), which
273
+ # would silently corrupt the manifest. O_BINARY is 0 on POSIX, so this is a
274
+ # no-op there and every os.open below stays byte-exact on both platforms.
275
+ O_BINARY = getattr(os, "O_BINARY", 0)
276
+ # Upper bound on waiting for a contended run lock. A legitimate holder is a
277
+ # sub-second manifest read/write; anything longer means a wedged process, and
278
+ # hanging forever is worse than a clear failure.
279
+ LOCK_WAIT_SECS = 30.0
280
+ TERMINAL_PROCESS = {"done", "failed", "timeout", "died-without-result"}
281
+ INTEGRATABLE_STATES = {"integration-pending", "integrated", "verified"}
282
+ UNIT_STATES = {
283
+ "queued", "authoring", "authored", "integration-pending", "integrated",
284
+ "restoring", "verified", "committed", "preserved", "cleaned", "native-completed",
285
+ }
286
+ GIT_LOCAL_ENV_VARS = frozenset({
287
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
288
+ "GIT_COMMON_DIR",
289
+ "GIT_CONFIG",
290
+ "GIT_CONFIG_COUNT",
291
+ "GIT_CONFIG_PARAMETERS",
292
+ "GIT_DIR",
293
+ "GIT_GRAFT_FILE",
294
+ "GIT_IMPLICIT_WORK_TREE",
295
+ "GIT_INDEX_FILE",
296
+ # Listed by `git rev-parse --local-env-vars` on git < 2.41 (e.g. Debian
297
+ # bookworm's 2.39); dropped from the list by newer git. Scrub it so an
298
+ # inherited value cannot leak into unit verification on old git.
299
+ "GIT_INTERNAL_SUPER_PREFIX",
300
+ "GIT_NO_REPLACE_OBJECTS",
301
+ "GIT_OBJECT_DIRECTORY",
302
+ "GIT_PREFIX",
303
+ "GIT_REPLACE_REF_BASE",
304
+ "GIT_SHALLOW_FILE",
305
+ "GIT_WORK_TREE",
306
+ })
307
+
308
+
309
+ class Operational(Exception):
310
+ def __init__(self, word: str, message: str, detail: dict | None = None):
311
+ super().__init__(message)
312
+ self.word = word
313
+ self.detail = detail or {}
314
+
315
+
316
+ class TrustFailure(Operational):
317
+ def __init__(self, message: str):
318
+ super().__init__("UNREADABLE", message)
319
+
320
+
321
+ def now_iso() -> str:
322
+ return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
323
+
324
+
325
+ def test_fault(point: str) -> None:
326
+ """Deterministic crash-window injection for the repository test suite."""
327
+ configured = {value.strip() for value in os.environ.get("CE_WORK_TEST_FAULT", "").split(",") if value.strip()}
328
+ if point in configured:
329
+ raise Operational("INTERRUPTED", f"injected test interruption at {point}")
330
+
331
+
332
+ def _private_root_usable(path: str) -> bool:
333
+ """True when `path` is (or can now be) a directory we own and can write into.
334
+
335
+ Creation is the probe: a sandbox that denies writes under /tmp refuses the
336
+ mkdir, and one that lets a pre-existing root stand still fails the access
337
+ check, so both land on the fallback instead of failing at the first run.
338
+ """
339
+ try:
340
+ os.mkdir(path, 0o700)
341
+ except FileExistsError:
342
+ pass
343
+ except OSError:
344
+ return False
345
+ try:
346
+ st = os.lstat(path)
347
+ except OSError:
348
+ return False
349
+ if not stat.S_ISDIR(st.st_mode):
350
+ return False
351
+ if IS_WINDOWS:
352
+ if not win_owns_path(path):
353
+ return False
354
+ elif _EFFECTIVE_UID is not None and st.st_uid != _EFFECTIVE_UID:
355
+ return False
356
+ return os.access(path, os.W_OK)
357
+
358
+
359
+ def _fallback_scratch_root() -> str:
360
+ if IS_WINDOWS:
361
+ return os.path.join(tempfile.gettempdir(), "compound-engineering")
362
+ return os.path.join(os.environ.get("TMPDIR") or "/tmp", f"compound-engineering-{_EFFECTIVE_UID}")
363
+
364
+
365
+ def owner_scratch_root() -> str:
366
+ """The owner-private scratch root, in the same candidate order as the skills' shell preamble."""
367
+ if OWNER_SCRATCH_ROOT is None:
368
+ raise TrustFailure("effective user ID is unavailable; cannot derive the runs root")
369
+ if _private_root_usable(OWNER_SCRATCH_ROOT):
370
+ return OWNER_SCRATCH_ROOT
371
+ return _fallback_scratch_root()
372
+
373
+
374
+ def runs_root() -> str:
375
+ configured = os.environ.get("CE_WORK_RUNS_ROOT")
376
+ if configured:
377
+ return os.path.abspath(configured)
378
+ peer_root = os.environ.get("CE_PEER_JOBS_ROOT")
379
+ if peer_root:
380
+ return os.path.join(os.path.abspath(peer_root), "ce-work")
381
+ return os.path.join(owner_scratch_root(), "ce-work")
382
+
383
+
384
+ def safe_id(value: str, label: str) -> str:
385
+ if not SAFE_ID.fullmatch(value) or not value.strip("."):
386
+ raise Operational("REFUSED", f"unsafe {label}: {value!r}")
387
+ return value
388
+
389
+
390
+ def digest_bytes(data: bytes) -> str:
391
+ return hashlib.sha256(data).hexdigest()
392
+
393
+
394
+ def _valid_git_object_id(value: object) -> bool:
395
+ if not isinstance(value, str):
396
+ return False
397
+ try:
398
+ raw = bytes.fromhex(value)
399
+ except ValueError:
400
+ return False
401
+ return len(raw) in {20, 32} and raw.hex() == value
402
+
403
+
404
+ def _native_completion_commit(unit: dict) -> str | None:
405
+ attempts = unit.get("attempts")
406
+ if not isinstance(attempts, list) or not attempts or not isinstance(attempts[-1], dict):
407
+ return None
408
+ fallback = attempts[-1].get("fallback")
409
+ if not isinstance(fallback, dict):
410
+ return None
411
+ claim = fallback.get("claimed")
412
+ completion = fallback.get("completed")
413
+ if not isinstance(claim, dict) or not isinstance(completion, dict) or completion.get("claim") != claim:
414
+ return None
415
+ claim_mode = claim.get("mode")
416
+ if claim_mode not in {"prefer", "require"}:
417
+ return None
418
+ accepted_head = completion.get("accepted_head")
419
+ base = unit.get("workspace", {}).get("base")
420
+ snapshot = completion.get("snapshot")
421
+ wave = unit.get("wave", {})
422
+ changed_paths = completion.get("changed_paths")
423
+ if not (
424
+ _valid_git_object_id(accepted_head)
425
+ and _valid_git_object_id(base)
426
+ and completion.get("base") == base
427
+ and isinstance(completion.get("at"), str)
428
+ and bool(completion["at"])
429
+ and isinstance(completion.get("summary"), str)
430
+ and bool(completion["summary"])
431
+ and isinstance(completion.get("evidence_digest"), str)
432
+ and len(completion["evidence_digest"]) == 64
433
+ and _valid_git_object_id(completion["evidence_digest"])
434
+ and isinstance(snapshot, dict)
435
+ and snapshot.get("head") == accepted_head
436
+ and snapshot.get("status_empty") is True
437
+ and snapshot.get("worktree_index_empty") is True
438
+ and _valid_git_object_id(snapshot.get("head_tree"))
439
+ and snapshot.get("head_tree") == snapshot.get("index_tree")
440
+ and snapshot.get("status_sha256") == digest_bytes(b"")
441
+ and (
442
+ not wave.get("id")
443
+ or (
444
+ _valid_git_object_id(claim.get("canonical_head"))
445
+ and isinstance(changed_paths, list)
446
+ and all(isinstance(path, str) for path in changed_paths)
447
+ )
448
+ )
449
+ ):
450
+ return None
451
+ return accepted_head
452
+
453
+
454
+ def unit_accepted_commit(unit: dict) -> str | None:
455
+ if unit.get("state") == "native-completed":
456
+ return _native_completion_commit(unit)
457
+ if unit.get("state") != "cleaned":
458
+ return None
459
+ integration = unit.get("integration")
460
+ if not isinstance(integration, dict):
461
+ return None
462
+ canonical = integration.get("canonical_commit")
463
+ if not (
464
+ isinstance(canonical, dict)
465
+ and all(_valid_git_object_id(canonical.get(field)) for field in ("commit", "parent", "tree"))
466
+ and isinstance(canonical.get("at"), str)
467
+ and bool(canonical["at"])
468
+ ):
469
+ return None
470
+ return canonical["commit"]
471
+
472
+
473
+ def unit_ready_for_run_verification(unit: object) -> bool:
474
+ return isinstance(unit, dict) and unit_accepted_commit(unit) is not None
475
+
476
+
477
+ def accepted_unit_commit_snapshot(units: object) -> dict[str, str] | None:
478
+ if not isinstance(units, dict):
479
+ return None
480
+ snapshot: dict[str, str] = {}
481
+ for unit_id in sorted(units):
482
+ if not isinstance(unit_id, str) or not SAFE_ID.fullmatch(unit_id):
483
+ return None
484
+ unit = units[unit_id]
485
+ if not isinstance(unit, dict):
486
+ return None
487
+ commit = unit_accepted_commit(unit)
488
+ if commit is None:
489
+ return None
490
+ snapshot[unit_id] = commit
491
+ return snapshot
492
+
493
+
494
+ def _mode(st: os.stat_result) -> int:
495
+ return stat.S_IMODE(st.st_mode)
496
+
497
+
498
+ def is_private(st: os.stat_result, want_dir: bool) -> bool:
499
+ """True when the object carries the privacy this module requires: mode
500
+ 0700/0600 on POSIX, a verified owner SID on Windows (its DACL is what
501
+ provides privacy there, and `_mode` cannot see it)."""
502
+ return _modeis_private(st, want_dir)
503
+
504
+
505
+ def _owned_by_us(st: os.stat_result) -> bool:
506
+ if IS_WINDOWS:
507
+ return True # the descriptor/handle owner check already ran
508
+ return _euid() is None or st.st_uid == _euid()
509
+
510
+
511
+ def _euid() -> int | None:
512
+ return _EFFECTIVE_UID
513
+
514
+
515
+ def validate_private_dir(path: str) -> None:
516
+ if IS_WINDOWS:
517
+ # Windows cannot open a directory with os.open() (the CRT maps it to
518
+ # CreateFileW without FILE_FLAG_BACKUP_SEMANTICS, which the kernel
519
+ # rejects with ERROR_ACCESS_DENIED for any directory), so there is no
520
+ # descriptor to fstat here. The path-based owner check is what
521
+ # peer-job-runner's `_check_owned_dir` uses on Windows for the same
522
+ # reason, and the DACL it verifies is the privacy boundary.
523
+ try:
524
+ st = os.lstat(path)
525
+ except OSError as exc:
526
+ raise TrustFailure(f"cannot safely stat directory {path}: {exc}") from exc
527
+ if not stat.S_ISDIR(st.st_mode):
528
+ raise TrustFailure(f"not a real directory: {path}")
529
+ if not win_owns_path(path):
530
+ raise TrustFailure(f"directory is not owned by current user: {path}")
531
+ return
532
+ try:
533
+ fd = os.open(path, os.O_RDONLY | O_DIRECTORY | O_NOFOLLOW)
534
+ except OSError as exc:
535
+ raise TrustFailure(f"cannot safely open directory {path}: {exc}") from exc
536
+ try:
537
+ st = os.fstat(fd)
538
+ if not stat.S_ISDIR(st.st_mode):
539
+ raise TrustFailure(f"not a real directory: {path}")
540
+ if _euid() is not None and st.st_uid != _euid():
541
+ raise TrustFailure(f"directory is not owned by current user: {path}")
542
+ if _mode(st) != 0o700:
543
+ raise TrustFailure(f"directory mode is {_mode(st):04o}, expected 0700: {path}")
544
+ finally:
545
+ os.close(fd)
546
+
547
+
548
+ def ensure_private_dir(path: str) -> None:
549
+ created = False
550
+ try:
551
+ os.mkdir(path, 0o700)
552
+ created = True
553
+ except FileExistsError:
554
+ pass
555
+ if IS_WINDOWS and created:
556
+ win_harden_acl(path)
557
+ validate_private_dir(path)
558
+
559
+
560
+ def _owner_root_for_runs(root: str) -> str | None:
561
+ if OWNER_SCRATCH_ROOT is None:
562
+ return None
563
+ for candidate in (OWNER_SCRATCH_ROOT, _fallback_scratch_root()):
564
+ owner_root = os.path.abspath(candidate)
565
+ try:
566
+ if os.path.commonpath([owner_root, os.path.abspath(root)]) == owner_root:
567
+ return owner_root
568
+ except ValueError: # different drives on Windows: not under this candidate
569
+ continue
570
+ return None
571
+
572
+
573
+ def _ensure_owner_scratch_root(path: str) -> None:
574
+ try:
575
+ os.mkdir(path, 0o700)
576
+ except FileExistsError:
577
+ pass
578
+ if IS_WINDOWS:
579
+ # No directory descriptor on Windows (see validate_private_dir); the
580
+ # owner check plus the hardened ACL are the whole guarantee, and the
581
+ # POSIX chmod-repair has no Windows analogue -- icacls is idempotent
582
+ # so the repair is simply re-hardening.
583
+ try:
584
+ st = os.lstat(path)
585
+ except OSError as exc:
586
+ raise TrustFailure(f"cannot safely stat owner scratch root {path}: {exc}") from exc
587
+ if not stat.S_ISDIR(st.st_mode):
588
+ raise TrustFailure(f"owner scratch root is not a real directory: {path}")
589
+ if not win_owns_path(path):
590
+ raise TrustFailure(f"owner scratch root is not owned by current user: {path}")
591
+ win_harden_acl(path)
592
+ return
593
+ try:
594
+ fd = os.open(path, os.O_RDONLY | O_DIRECTORY | O_NOFOLLOW)
595
+ except OSError as exc:
596
+ raise TrustFailure(f"cannot safely open owner scratch root {path}: {exc}") from exc
597
+ try:
598
+ current = os.fstat(fd)
599
+ if not stat.S_ISDIR(current.st_mode):
600
+ raise TrustFailure(f"owner scratch root is not a real directory: {path}")
601
+ if _euid() is not None and current.st_uid != _euid():
602
+ raise TrustFailure(f"owner scratch root is not owned by current user: {path}")
603
+ if _mode(current) != 0o700:
604
+ os.fchmod(fd, 0o700)
605
+ repaired = os.fstat(fd)
606
+ if repaired.st_uid != current.st_uid or _mode(repaired) != 0o700:
607
+ raise TrustFailure(f"could not repair owner scratch root mode to 0700: {path}")
608
+ finally:
609
+ os.close(fd)
610
+
611
+
612
+ def ensure_root() -> str:
613
+ return ensure_runs_root(runs_root())
614
+
615
+
616
+ def ensure_runs_root(root: str) -> str:
617
+ """Create or verify one runs root (the creation root, or the other candidate
618
+ root an existing run was found under) and its private lock directory."""
619
+ owner_root = _owner_root_for_runs(root)
620
+ if owner_root is not None:
621
+ _ensure_owner_scratch_root(owner_root)
622
+ parent = os.path.dirname(root)
623
+ # The configured root's ancestors are caller-controlled; the private root
624
+ # itself and everything below it are the durable confidentiality boundary.
625
+ os.makedirs(parent, mode=0o700, exist_ok=True)
626
+ ensure_private_dir(root)
627
+ ensure_private_dir(os.path.join(root, ".locks"))
628
+ return root
629
+
630
+
631
+ def read_private(path: str, cap: int = MAX_JSON_BYTES) -> bytes:
632
+ try:
633
+ fd = os.open(path, os.O_RDONLY | O_NOFOLLOW)
634
+ except OSError as exc:
635
+ raise TrustFailure(f"cannot safely open state file {path}: {exc}") from exc
636
+ try:
637
+ st = os.fstat(fd)
638
+ if not stat.S_ISREG(st.st_mode):
639
+ raise TrustFailure(f"state is not a regular file: {path}")
640
+ if IS_WINDOWS:
641
+ if not win_owns_handle(msvcrt.get_osfhandle(fd)):
642
+ raise TrustFailure(f"state is not owned by current user: {path}")
643
+ elif _euid() is not None and st.st_uid != _euid():
644
+ raise TrustFailure(f"state is not owned by current user: {path}")
645
+ if not is_private(st, want_dir=False):
646
+ raise TrustFailure(f"state mode is {_mode(st):04o}, expected 0600: {path}")
647
+ if st.st_size > cap:
648
+ raise TrustFailure(f"state exceeds {cap}-byte limit: {path}")
649
+ out = bytearray()
650
+ while len(out) <= cap:
651
+ part = os.read(fd, min(65536, cap + 1 - len(out)))
652
+ if not part:
653
+ break
654
+ out.extend(part)
655
+ if len(out) > cap:
656
+ raise TrustFailure(f"state grew beyond {cap}-byte limit: {path}")
657
+ return bytes(out)
658
+ finally:
659
+ os.close(fd)
660
+
661
+
662
+ def stat_private_file(path: str) -> os.stat_result:
663
+ """Validate a private file by descriptor without consuming its content."""
664
+ try:
665
+ fd = os.open(path, os.O_RDONLY | O_NOFOLLOW)
666
+ except OSError as exc:
667
+ raise TrustFailure(f"cannot safely open state file {path}: {exc}") from exc
668
+ try:
669
+ st = os.fstat(fd)
670
+ if not stat.S_ISREG(st.st_mode):
671
+ raise TrustFailure(f"state is not a regular file: {path}")
672
+ if IS_WINDOWS:
673
+ if not win_owns_handle(msvcrt.get_osfhandle(fd)):
674
+ raise TrustFailure(f"state is not owned by current user: {path}")
675
+ elif _euid() is not None and st.st_uid != _euid():
676
+ raise TrustFailure(f"state is not owned by current user: {path}")
677
+ if not is_private(st, want_dir=False):
678
+ raise TrustFailure(f"state mode is {_mode(st):04o}, expected 0600: {path}")
679
+ return st
680
+ finally:
681
+ os.close(fd)
682
+
683
+
684
+ def read_private_json(path: str) -> dict:
685
+ try:
686
+ value = json.loads(read_private(path))
687
+ except TrustFailure:
688
+ raise
689
+ except (ValueError, UnicodeDecodeError) as exc:
690
+ raise TrustFailure(f"malformed JSON state: {path}") from exc
691
+ if not isinstance(value, dict):
692
+ raise TrustFailure(f"JSON state is not an object: {path}")
693
+ return value
694
+
695
+
696
+ def create_private(path: str, data: bytes) -> None:
697
+ try:
698
+ fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_NOFOLLOW, 0o600)
699
+ except OSError as exc:
700
+ raise Operational("BLOCKED", f"cannot exclusively create {path}: {exc}") from exc
701
+ try:
702
+ os.write(fd, data)
703
+ os.fsync(fd)
704
+ finally:
705
+ os.close(fd)
706
+
707
+
708
+ def atomic_private_json(path: str, doc: dict) -> None:
709
+ data = (json.dumps(doc, sort_keys=True, separators=(",", ":")) + "\n").encode()
710
+ if len(data) > MAX_JSON_BYTES:
711
+ raise Operational("BLOCKED", "manifest exceeds bounded state size")
712
+ parent = os.path.dirname(path)
713
+ fd, tmp = tempfile.mkstemp(prefix=".manifest-", dir=parent)
714
+ try:
715
+ if not IS_WINDOWS:
716
+ os.fchmod(fd, 0o600)
717
+ with os.fdopen(fd, "wb", closefd=True) as stream:
718
+ stream.write(data)
719
+ stream.flush()
720
+ os.fsync(stream.fileno())
721
+ os.replace(tmp, path)
722
+ if not IS_WINDOWS:
723
+ # Directory fsync makes the rename durable. Windows cannot open a
724
+ # directory for fsync and does not need it: NTFS journals the rename.
725
+ dfd = os.open(parent, os.O_RDONLY | O_DIRECTORY | O_NOFOLLOW)
726
+ try:
727
+ os.fsync(dfd)
728
+ finally:
729
+ os.close(dfd)
730
+ except BaseException:
731
+ with contextlib.suppress(OSError):
732
+ os.unlink(tmp)
733
+ raise
734
+
735
+
736
+ def candidate_runs_roots() -> list:
737
+ """Every root an existing run may live under (configured root alone, or the
738
+ /tmp root and the $TMPDIR fallback, primary first). Creation uses runs_root();
739
+ lookup must not depend on which root this invocation would create under."""
740
+ configured = os.environ.get("CE_WORK_RUNS_ROOT")
741
+ if configured:
742
+ return [os.path.abspath(configured)]
743
+ peer_root = os.environ.get("CE_PEER_JOBS_ROOT")
744
+ if peer_root:
745
+ return [os.path.join(os.path.abspath(peer_root), "ce-work")]
746
+ if OWNER_SCRATCH_ROOT is None:
747
+ raise TrustFailure("effective user ID is unavailable; cannot derive the runs root")
748
+ roots = [os.path.join(os.path.abspath(OWNER_SCRATCH_ROOT), "ce-work")]
749
+ fallback = os.path.join(os.path.abspath(_fallback_scratch_root()), "ce-work")
750
+ if fallback not in roots:
751
+ roots.append(fallback)
752
+ return roots
753
+
754
+
755
+ def run_dir(run_id: str) -> str:
756
+ rid = safe_id(run_id, "run id")
757
+ for root in candidate_runs_roots():
758
+ existing = os.path.join(root, rid)
759
+ if os.path.isdir(existing) and not os.path.islink(existing):
760
+ return existing
761
+ return os.path.join(runs_root(), rid)
762
+
763
+
764
+ @contextlib.contextmanager
765
+ def locked_manifest(run_id: str, write: bool = False):
766
+ run_id = safe_id(run_id, "run id")
767
+ rd = run_dir(run_id)
768
+ ensure_runs_root(os.path.dirname(rd))
769
+ validate_private_dir(rd)
770
+ lock_path = os.path.join(rd, "manifest.lock")
771
+ try:
772
+ fd = os.open(lock_path, os.O_RDWR | O_NOFOLLOW)
773
+ except OSError as exc:
774
+ raise TrustFailure(f"cannot safely open manifest lock: {exc}") from exc
775
+ try:
776
+ st = os.fstat(fd)
777
+ if not stat.S_ISREG(st.st_mode):
778
+ raise TrustFailure("manifest lock is not a regular file")
779
+ if IS_WINDOWS:
780
+ if not win_owns_handle(msvcrt.get_osfhandle(fd)):
781
+ raise TrustFailure("manifest lock owner validation failed")
782
+ elif (_euid() is not None and st.st_uid != _euid()) or _mode(st) != 0o600:
783
+ raise TrustFailure("manifest lock owner/type/mode validation failed")
784
+ _lock_fd(fd, exclusive=write)
785
+ doc = read_private_json(os.path.join(rd, "manifest.json"))
786
+ if doc.get("schema_version") != SCHEMA_VERSION or doc.get("run_id") != run_id:
787
+ raise TrustFailure("manifest schema or run identity mismatch")
788
+ before = json.dumps(doc, sort_keys=True, separators=(",", ":"))
789
+ yield doc
790
+ after = json.dumps(doc, sort_keys=True, separators=(",", ":"))
791
+ if write and after != before:
792
+ doc["revision"] = int(doc.get("revision", 0)) + 1
793
+ doc["updated_at"] = now_iso()
794
+ atomic_private_json(os.path.join(rd, "manifest.json"), doc)
795
+ finally:
796
+ _unlock_fd(fd)
797
+ os.close(fd)
798
+
799
+
800
+ def sanitized_git_environment(overrides: dict | None = None) -> dict[str, str]:
801
+ process_env = {key: value for key, value in os.environ.items() if key not in GIT_LOCAL_ENV_VARS}
802
+ process_env.update(overrides or {})
803
+ return process_env
804
+
805
+
806
+ def git(repo: str, *args: str, input_data: bytes | None = None, check: bool = True, env: dict | None = None) -> bytes:
807
+ proc = subprocess.run(
808
+ ["git", "-C", repo, *args], input=input_data, capture_output=True,
809
+ env=sanitized_git_environment(env), check=False,
810
+ )
811
+ if check and proc.returncode != 0:
812
+ message = proc.stderr.decode("utf-8", "replace").strip()
813
+ raise Operational("BLOCKED", f"git {' '.join(args)} failed: {message}")
814
+ return proc.stdout
815
+
816
+
817
+ def git_text(repo: str, *args: str, check: bool = True) -> str:
818
+ return git(repo, *args, check=check).decode("utf-8", "surrogateescape").strip()
819
+
820
+
821
+ def commit_index_tree(repo: str, message: str) -> str:
822
+ """Commit the verified index directly, without invoking repository hooks."""
823
+ if not message.strip() or "\0" in message:
824
+ raise Operational("REFUSED", "commit message must be non-empty and contain no NUL")
825
+ parent = git_text(repo, "rev-parse", "HEAD")
826
+ branch_ref = git_text(repo, "symbolic-ref", "-q", "HEAD")
827
+ tree = git_text(repo, "write-tree")
828
+ commit = git(
829
+ repo,
830
+ "commit-tree", tree, "-p", parent,
831
+ input_data=f"{message.rstrip()}\n".encode("utf-8"),
832
+ ).decode("ascii", "strict").strip()
833
+ git(repo, "update-ref", branch_ref, commit, parent)
834
+ return commit
835
+
836
+
837
+ def repo_info(repo: str) -> dict:
838
+ repo = os.path.realpath(repo)
839
+ top = os.path.realpath(git_text(repo, "rev-parse", "--show-toplevel"))
840
+ if top != repo:
841
+ repo = top
842
+ branch = git_text(repo, "symbolic-ref", "-q", "HEAD", check=False)
843
+ if not branch:
844
+ raise Operational("REFUSED", "canonical checkout must be on a branch")
845
+ git_dir = os.path.realpath(git_text(repo, "rev-parse", "--path-format=absolute", "--absolute-git-dir"))
846
+ common = os.path.realpath(git_text(repo, "rev-parse", "--path-format=absolute", "--git-common-dir"))
847
+ st = os.stat(common)
848
+ roots = sorted(git_text(repo, "rev-list", "--max-parents=0", "HEAD").splitlines())
849
+ identity = digest_bytes((common + f"\0{st.st_dev}\0{st.st_ino}\0" + "\n".join(roots)).encode())
850
+ return {
851
+ "toplevel": repo,
852
+ "git_dir": git_dir,
853
+ "common_dir": common,
854
+ "common_dev": st.st_dev,
855
+ "common_ino": st.st_ino,
856
+ "identity_digest": identity,
857
+ "branch_ref": branch,
858
+ "head": git_text(repo, "rev-parse", "HEAD"),
859
+ "head_tree": git_text(repo, "rev-parse", "HEAD^{tree}"),
860
+ }
861
+
862
+
863
+ def validate_source(doc: dict) -> None:
864
+ source = doc.get("source")
865
+ if source is not None:
866
+ if not isinstance(source, dict):
867
+ raise TrustFailure("manifest source record is malformed")
868
+ kind = source.get("kind")
869
+ if kind == "prompt":
870
+ if source.get("storage") != "run" or source.get("path") != "source/bare-prompt.md":
871
+ raise TrustFailure("prompt source location is malformed")
872
+ if not isinstance(source.get("digest"), str) or not re.fullmatch(r"[0-9a-f]{64}", source["digest"]):
873
+ raise TrustFailure("prompt source digest is malformed")
874
+ data = read_private(os.path.join(run_dir(doc["run_id"]), source["path"]), MAX_PACKET_BYTES)
875
+ if digest_bytes(data) != source.get("digest"):
876
+ raise TrustFailure("prompt source digest does not match private content")
877
+ elif kind == "plan":
878
+ if source.get("storage") != "repository" or not isinstance(source.get("path"), str):
879
+ raise TrustFailure("plan source location is malformed")
880
+ if not isinstance(source.get("digest"), str) or not re.fullmatch(r"[0-9a-f]{64}", source["digest"]):
881
+ raise TrustFailure("plan source digest is malformed")
882
+ else:
883
+ raise TrustFailure("manifest source kind is invalid")
884
+
885
+
886
+ def validate_repo(doc: dict) -> dict:
887
+ validate_source(doc)
888
+ recorded = doc["repository"]
889
+ current = repo_info(recorded["toplevel"])
890
+ for key in ("toplevel", "git_dir", "common_dir", "common_dev", "common_ino", "identity_digest"):
891
+ if current[key] != recorded[key]:
892
+ raise Operational("BLOCKED", f"canonical repository identity changed ({key})")
893
+ if current["branch_ref"] != doc["branch"]["ref"]:
894
+ raise Operational("BLOCKED", "canonical branch changed")
895
+ return current
896
+
897
+
898
+ def resolve_plan(repo: str, plan: str) -> tuple[str, str]:
899
+ supplied = os.path.abspath(plan if os.path.isabs(plan) else os.path.join(repo, plan))
900
+ try:
901
+ st = os.lstat(supplied)
902
+ except OSError as exc:
903
+ raise Operational("REFUSED", f"selected plan is missing: {exc}") from exc
904
+ if stat.S_ISLNK(st.st_mode) or not stat.S_ISREG(st.st_mode):
905
+ raise Operational("REFUSED", "selected plan must be one regular non-symlink file")
906
+ # OS temp roots may themselves be compatibility symlinks (macOS /var ->
907
+ # /private/var). Reject a symlink at the selected file, then compare the
908
+ # resolved file against the already-resolved canonical repository.
909
+ absolute = os.path.realpath(supplied)
910
+ if os.path.commonpath([repo, absolute]) != repo:
911
+ raise Operational("REFUSED", "plan must be inside the canonical repository")
912
+ return absolute, os.path.relpath(absolute, repo)
913
+
914
+
915
+ def parse_json_arg(raw: str, label: str) -> dict:
916
+ try:
917
+ value = json.loads(raw)
918
+ except ValueError as exc:
919
+ raise Operational("REFUSED", f"invalid {label} JSON") from exc
920
+ if not isinstance(value, dict):
921
+ raise Operational("REFUSED", f"{label} must be a JSON object")
922
+ return value
923
+
924
+
925
+ ROUTE_CONTRACTS = {
926
+ "codex": {"target": "codex", "harness": "codex", "intermediaries": [], "default_model": "auto", "restriction_posture": "cooperative"},
927
+ "claude": {"target": "claude", "harness": "claude", "intermediaries": [], "default_model": "auto", "restriction_posture": "cooperative"},
928
+ "grok-cli": {"target": "grok", "harness": "grok", "intermediaries": [], "default_model": "auto", "restriction_posture": "cooperative"},
929
+ "cursor": {"target": "cursor", "harness": "cursor-agent", "intermediaries": [], "default_model": "auto", "restriction_posture": "cooperative"},
930
+ "composer": {"target": "composer", "harness": "cursor-agent", "intermediaries": ["cursor"], "default_model": "composer-2.5[fast=true]", "restriction_posture": "cooperative"},
931
+ "grok-cursor": {"target": "grok", "harness": "cursor-agent", "intermediaries": ["cursor"], "default_model": "grok-4.7[context=256k,reasoning_effort=high,fast=true]", "restriction_posture": "cooperative"},
932
+ "opencode": {"target": "opencode", "harness": "opencode", "intermediaries": [], "default_model": "auto", "restriction_posture": "cooperative"},
933
+ "antigravity": {"target": "antigravity", "harness": "antigravity", "intermediaries": [], "default_model": "gemini-4-argon", "restriction_posture": "cooperative"},
934
+ # DeepSeek Harness. Model ids are `provider/model` (e.g.
935
+ # `workbuddy/deepseek-v4.1-flash`), the same shape opencode uses, because a
936
+ # DSH profile can front several providers at once and the provider is part
937
+ # of the identity -- `deepseek-v4.1-flash` alone is ambiguous across them.
938
+ "dsh": {"target": "dsh", "harness": "dsh", "intermediaries": [], "default_model": "auto", "restriction_posture": "cooperative"},
939
+ }
940
+ # Cursor's ACP model ids may carry a bracketed preset; route families apply to
941
+ # the id before the bracket.
942
+ CURSOR_MODEL = re.compile(r"([A-Za-z0-9][A-Za-z0-9._:/-]*)(?:\[[A-Za-z0-9._=,:-]*\])?")
943
+
944
+
945
+ def route_model_allowed(route: str, model: str) -> bool:
946
+ if route in {"cursor", "composer", "grok-cursor"}:
947
+ match = CURSOR_MODEL.fullmatch(model)
948
+ if not match:
949
+ return False
950
+ base = match.group(1)
951
+ if route == "composer":
952
+ return bool(re.fullmatch(r"composer-[A-Za-z0-9._-]+", base))
953
+ if route == "grok-cursor":
954
+ return bool(re.fullmatch(r"(?:cursor-)?grok-[A-Za-z0-9._-]+", base))
955
+ lowered = base.lower()
956
+ return not (lowered in {"composer", "grok"} or lowered.startswith(("composer-", "grok-", "cursor-grok-")))
957
+ if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:/-]*", model):
958
+ return False
959
+ if route == "codex":
960
+ return model == "auto" or bool(re.fullmatch(r"(?:gpt-[A-Za-z0-9._-]+|o[0-9][A-Za-z0-9._-]*)", model))
961
+ if route == "claude":
962
+ return model in {"auto", "fable", "opus", "sonnet", "haiku"} or bool(re.fullmatch(r"claude-[A-Za-z0-9._-]+", model))
963
+ if route == "grok-cli":
964
+ return model == "auto" or bool(re.fullmatch(r"grok-[A-Za-z0-9._-]+", model))
965
+ if route == "opencode":
966
+ return model == "auto" or bool(re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9._-]+", model))
967
+ if route == "dsh":
968
+ # `auto` lets the profile's configured default model decide;
969
+ # `provider/model` pins one explicitly, provider first.
970
+ return model == "auto" or bool(re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*", model))
971
+ if route == "antigravity":
972
+ # Antigravity also serves Claude and gpt-oss models; this route exists for Gemini.
973
+ return bool(re.fullmatch(r"gemini-[A-Za-z0-9._-]+", model))
974
+ return False
975
+
976
+
977
+ EFFORT_TOKEN = re.compile(r"[A-Za-z0-9][A-Za-z0-9_-]{0,31}")
978
+
979
+
980
+ def fixed_route_contract(binding: dict, egress: dict, word: str = "BLOCKED") -> dict:
981
+ if not isinstance(binding, dict) or not isinstance(egress, dict):
982
+ raise Operational(word, "run binding or egress sanction is malformed")
983
+ expected_binding_fields = {"mode", "target", "model", "source"}
984
+ if set(binding) != expected_binding_fields:
985
+ raise Operational(word, "binding must contain exactly mode, target, model, and source")
986
+ if binding.get("mode") not in {"prefer", "require"}:
987
+ raise Operational(word, "binding mode must be 'prefer' or 'require'")
988
+ source = binding.get("source")
989
+ if not isinstance(source, str) or not source or "\0" in source or len(source.encode()) > 256:
990
+ raise Operational(word, "binding source must be a non-empty string of at most 256 bytes")
991
+ route = egress.get("route")
992
+ contract = ROUTE_CONTRACTS.get(route)
993
+ if not contract:
994
+ allowed = ", ".join(ROUTE_CONTRACTS)
995
+ raise Operational(word, f"unsupported egress route {route!r}; expected one of: {allowed}")
996
+ if binding.get("target") != contract["target"]:
997
+ raise Operational(word, "binding target does not match the sanctioned fixed route")
998
+ intermediaries = egress.get("intermediaries")
999
+ if intermediaries != contract["intermediaries"]:
1000
+ raise Operational(word, "egress intermediaries do not match the fixed route")
1001
+ model = binding.get("model")
1002
+ if model is not None and (not isinstance(model, str) or not model):
1003
+ raise Operational(word, "binding model must be null or a non-empty string")
1004
+ requested_model = model or contract["default_model"]
1005
+ if not route_model_allowed(route, requested_model):
1006
+ raise Operational(word, "binding model is not compatible with the sanctioned fixed route")
1007
+ restrictions = egress.get("restrictions", [])
1008
+ if not isinstance(restrictions, list) or not all(isinstance(item, str) for item in restrictions):
1009
+ raise Operational(word, "egress restrictions must be a string list")
1010
+ effort = egress.get("effort")
1011
+ if effort is not None and (not isinstance(effort, str) or not EFFORT_TOKEN.fullmatch(effort)):
1012
+ raise Operational(word, "egress effort must be a short plain token")
1013
+ return contract
1014
+
1015
+
1016
+ def attempt_authorization(
1017
+ doc: dict,
1018
+ activity_posture: str,
1019
+ unit_id: str,
1020
+ attempt_id: str,
1021
+ packet_digest: str,
1022
+ ) -> dict:
1023
+ binding = doc.get("binding")
1024
+ egress = doc.get("egress")
1025
+ contract = fixed_route_contract(binding, egress)
1026
+ route = egress.get("route")
1027
+ intermediaries = egress.get("intermediaries")
1028
+ model = binding.get("model")
1029
+ restrictions = egress.get("restrictions", [])
1030
+ authorization = {
1031
+ "schema_version": 1,
1032
+ "run_id": doc["run_id"],
1033
+ "unit_id": unit_id,
1034
+ "attempt_id": attempt_id,
1035
+ "route": route,
1036
+ "target": contract["target"],
1037
+ "harness": contract["harness"],
1038
+ "intermediaries": list(contract["intermediaries"]),
1039
+ "model_requested": model or contract["default_model"],
1040
+ "restriction_posture": contract["restriction_posture"],
1041
+ "restrictions": list(restrictions),
1042
+ "activity_posture": activity_posture,
1043
+ "packet_digest": packet_digest,
1044
+ }
1045
+ # Present only when requested, so an unset run keeps the original key set.
1046
+ if egress.get("effort") is not None:
1047
+ authorization["effort_requested"] = egress["effort"]
1048
+ return authorization
1049
+
1050
+
1051
+ def read_external_packet(path: str, label: str = "unit packet") -> bytes:
1052
+ supplied = os.path.abspath(path)
1053
+ try:
1054
+ fd = os.open(supplied, os.O_RDONLY | O_NOFOLLOW)
1055
+ except OSError as exc:
1056
+ raise Operational("REFUSED", f"cannot safely open {label}: {exc}") from exc
1057
+ try:
1058
+ st = os.fstat(fd)
1059
+ if not stat.S_ISREG(st.st_mode):
1060
+ raise Operational("REFUSED", f"{label} must be one regular non-symlink file")
1061
+ if st.st_size > MAX_PACKET_BYTES:
1062
+ raise Operational("REFUSED", f"{label} exceeds {MAX_PACKET_BYTES}-byte limit")
1063
+ data = bytearray()
1064
+ while len(data) <= MAX_PACKET_BYTES:
1065
+ part = os.read(fd, min(65536, MAX_PACKET_BYTES + 1 - len(data)))
1066
+ if not part:
1067
+ break
1068
+ data.extend(part)
1069
+ if len(data) > MAX_PACKET_BYTES:
1070
+ raise Operational("REFUSED", f"{label} exceeds {MAX_PACKET_BYTES}-byte limit")
1071
+ return bytes(data)
1072
+ finally:
1073
+ os.close(fd)
1074
+
1075
+
1076
+ def event(doc: dict, kind: str, unit_id: str | None = None, detail: dict | None = None) -> None:
1077
+ row = {"at": now_iso(), "kind": kind}
1078
+ if unit_id is not None:
1079
+ row["unit_id"] = unit_id
1080
+ if detail:
1081
+ row["detail"] = detail
1082
+ doc.setdefault("events", []).append(row)
1083
+
1084
+
1085
+ def cmd_init(args) -> tuple[str, dict]:
1086
+ ensure_root()
1087
+ rid = safe_id(args.run_id, "run id")
1088
+ info = repo_info(args.repo)
1089
+ if args.plan:
1090
+ if not args.plan_digest or args.prompt_digest:
1091
+ raise Operational("REFUSED", "plan source requires only --plan-digest")
1092
+ plan_abs, plan_rel = resolve_plan(info["toplevel"], args.plan)
1093
+ source_bytes = Path(plan_abs).read_bytes()
1094
+ source_kind = "plan"
1095
+ supplied_digest = args.plan_digest
1096
+ source_record = {
1097
+ "kind": source_kind,
1098
+ "storage": "repository",
1099
+ "path": plan_rel,
1100
+ "digest": digest_bytes(source_bytes),
1101
+ }
1102
+ else:
1103
+ if not args.prompt_digest or args.plan_digest:
1104
+ raise Operational("REFUSED", "prompt source requires only --prompt-digest")
1105
+ prompt_abs = os.path.realpath(os.path.abspath(args.prompt_brief))
1106
+ if os.path.commonpath([info["toplevel"], prompt_abs]) == info["toplevel"]:
1107
+ raise Operational("REFUSED", "prompt brief must be outside the canonical repository")
1108
+ source_bytes = read_external_packet(args.prompt_brief, "prompt brief")
1109
+ source_kind = "prompt"
1110
+ supplied_digest = args.prompt_digest
1111
+ source_record = {
1112
+ "kind": source_kind,
1113
+ "storage": "run",
1114
+ "path": "source/bare-prompt.md",
1115
+ "digest": digest_bytes(source_bytes),
1116
+ }
1117
+ actual_digest = source_record["digest"]
1118
+ if actual_digest != supplied_digest:
1119
+ raise Operational("REFUSED", f"selected {source_kind} digest does not match content")
1120
+ binding = parse_json_arg(args.binding_json, "binding")
1121
+ egress = parse_json_arg(args.egress_json, "egress")
1122
+ fixed_route_contract(binding, egress, "REFUSED")
1123
+ rd = run_dir(rid)
1124
+ try:
1125
+ os.mkdir(rd, 0o700)
1126
+ except FileExistsError:
1127
+ try:
1128
+ existing = os.lstat(rd)
1129
+ except OSError as exc:
1130
+ raise TrustFailure(f"cannot safely inspect run directory {rd}: {exc}") from exc
1131
+ if stat.S_ISDIR(existing.st_mode) and not os.path.lexists(os.path.join(rd, "manifest.json")):
1132
+ raise Operational(
1133
+ "BLOCKED",
1134
+ "run directory exists without a controller manifest; choose a new run id or remove the directory after confirming no initialization is active",
1135
+ )
1136
+ validate_private_dir(rd)
1137
+ with locked_manifest(rid) as existing:
1138
+ validate_repo(existing)
1139
+ existing_source = existing.get("source")
1140
+ if not isinstance(existing_source, dict):
1141
+ plan = existing.get("plan")
1142
+ existing_source = {
1143
+ "kind": "plan",
1144
+ "storage": "repository",
1145
+ "path": plan.get("path") if isinstance(plan, dict) else None,
1146
+ "digest": plan.get("digest") if isinstance(plan, dict) else None,
1147
+ }
1148
+ if (
1149
+ existing["repository"]["identity_digest"] != info["identity_digest"]
1150
+ or existing_source.get("kind") != source_kind
1151
+ or existing_source.get("digest") != actual_digest
1152
+ ):
1153
+ raise Operational("BLOCKED", "run id already belongs to another repository or source")
1154
+ if existing.get("binding") != binding or existing.get("egress") != egress:
1155
+ raise Operational(
1156
+ "BLOCKED",
1157
+ "run id binding or egress sanction differs from the recorded fixed contract; resume with the recorded contract or choose a new run id",
1158
+ )
1159
+ return "READY", {
1160
+ "run_id": rid,
1161
+ "revision": existing["revision"],
1162
+ "resumed": True,
1163
+ "source_kind": source_kind,
1164
+ "source_digest": actual_digest,
1165
+ "recovery_path": rd,
1166
+ }
1167
+ validate_private_dir(rd)
1168
+ for child in ("units", "jobs", "packets", "source"):
1169
+ ensure_private_dir(os.path.join(rd, child))
1170
+ if source_kind == "prompt":
1171
+ create_private(os.path.join(rd, source_record["path"]), source_bytes)
1172
+ create_private(os.path.join(rd, "manifest.lock"), b"")
1173
+ created = now_iso()
1174
+ doc = {
1175
+ "schema_version": SCHEMA_VERSION,
1176
+ "revision": 0,
1177
+ "run_id": rid,
1178
+ "created_at": created,
1179
+ "updated_at": created,
1180
+ "repository": {k: info[k] for k in ("toplevel", "git_dir", "common_dir", "common_dev", "common_ino", "identity_digest")},
1181
+ "branch": {"ref": info["branch_ref"], "initial_head": info["head"]},
1182
+ "source": source_record,
1183
+ "plan": {
1184
+ "kind": source_kind,
1185
+ "path": plan_rel if source_kind == "plan" else None,
1186
+ "digest": actual_digest,
1187
+ "checkpoint": None,
1188
+ },
1189
+ "binding": binding,
1190
+ "egress": egress,
1191
+ "integration_lock": None,
1192
+ "units": {},
1193
+ "verification_attempts": [],
1194
+ "verifications": [],
1195
+ "blockers": [],
1196
+ "events": [{"at": created, "kind": "run-created"}],
1197
+ }
1198
+ create_private(os.path.join(rd, "manifest.json"), (json.dumps(doc, sort_keys=True, separators=(",", ":")) + "\n").encode())
1199
+ return "READY", {
1200
+ "run_id": rid,
1201
+ "revision": 0,
1202
+ "resumed": False,
1203
+ "source_kind": source_kind,
1204
+ "source_digest": actual_digest,
1205
+ "recovery_path": rd,
1206
+ }
1207
+
1208
+
1209
+ def status_paths(repo: str) -> set[str]:
1210
+ raw = git(repo, "status", "--porcelain=v1", "-z", "--untracked-files=all")
1211
+ parts = raw.split(b"\0")
1212
+ paths: set[str] = set()
1213
+ i = 0
1214
+ while i < len(parts):
1215
+ entry = parts[i]
1216
+ i += 1
1217
+ if not entry:
1218
+ continue
1219
+ if len(entry) < 4:
1220
+ raise Operational("BLOCKED", "unexpected porcelain status record")
1221
+ code = entry[:2]
1222
+ paths.add(entry[3:].decode("utf-8", "surrogateescape"))
1223
+ if b"R" in code or b"C" in code:
1224
+ if i >= len(parts) or not parts[i]:
1225
+ raise Operational("BLOCKED", "incomplete rename status record")
1226
+ paths.add(parts[i].decode("utf-8", "surrogateescape"))
1227
+ i += 1
1228
+ return paths
1229
+
1230
+
1231
+ def reconcile_plan_checkpoint(repo: str, doc: dict, info: dict, plan_rel: str) -> dict | None:
1232
+ """Recover the controller's plan commit when its manifest receipt was interrupted."""
1233
+ prior = doc.get("branch", {}).get("initial_head")
1234
+ commit = info["head"]
1235
+ if commit == prior:
1236
+ return None
1237
+ lineage = git_text(repo, "rev-list", "--parents", "-n", "1", commit).split()
1238
+ changed = set(filter(None, git(repo, "diff-tree", "--no-commit-id", "--name-only", "-r", "-z", commit).decode("utf-8", "surrogateescape").split("\0")))
1239
+ message = git(repo, "show", "-s", "--format=%B", commit).decode("utf-8", "surrogateescape").rstrip("\n")
1240
+ plan_bytes = git(repo, "show", f"{commit}:{plan_rel}", check=False)
1241
+ if (
1242
+ not _valid_git_object_id(prior)
1243
+ or lineage != [commit, prior]
1244
+ or changed != {plan_rel}
1245
+ or message != PLAN_CHECKPOINT_MESSAGE
1246
+ or digest_bytes(plan_bytes) != doc["plan"]["digest"]
1247
+ ):
1248
+ raise Operational(
1249
+ "BLOCKED",
1250
+ "canonical HEAD advanced without a recorded matching plan checkpoint",
1251
+ {"expected_prior_head": prior, "head": commit},
1252
+ )
1253
+ committed_at = int(git_text(repo, "show", "-s", "--format=%ct", commit))
1254
+ return {
1255
+ "prior_head": prior,
1256
+ "commit": commit,
1257
+ "tree": info["head_tree"],
1258
+ "path": plan_rel,
1259
+ "digest": doc["plan"]["digest"],
1260
+ "at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(committed_at)),
1261
+ }
1262
+
1263
+
1264
+ def cmd_checkpoint_plan(args) -> tuple[str, dict]:
1265
+ with locked_manifest(args.run_id, write=True) as doc:
1266
+ info = validate_repo(doc)
1267
+ repo = info["toplevel"]
1268
+ plan = doc.get("plan")
1269
+ if not isinstance(plan, dict) or plan.get("kind", "plan") != "plan" or not plan.get("path"):
1270
+ dirty = status_paths(repo)
1271
+ if dirty:
1272
+ raise Operational("BLOCKED", "prompt-backed external execution requires a clean canonical checkout", {"dirty_paths": sorted(dirty)})
1273
+ return "NOOP", {"checkpoint": None, "head": info["head"], "source_kind": "prompt"}
1274
+ plan_rel = plan["path"]
1275
+ plan_abs, _ = resolve_plan(repo, plan_rel)
1276
+ if digest_bytes(Path(plan_abs).read_bytes()) != doc["plan"]["digest"]:
1277
+ raise Operational("BLOCKED", "selected plan content no longer matches recorded digest")
1278
+ dirty = status_paths(repo)
1279
+ if not dirty:
1280
+ checkpoint = doc["plan"].get("checkpoint")
1281
+ if checkpoint is not None:
1282
+ return "NOOP", {"checkpoint": checkpoint, "head": info["head"]}
1283
+ checkpoint = reconcile_plan_checkpoint(repo, doc, info, plan_rel)
1284
+ if checkpoint is None:
1285
+ return "NOOP", {"checkpoint": None, "head": info["head"]}
1286
+ doc["plan"]["checkpoint"] = checkpoint
1287
+ event(doc, "plan-checkpoint", detail={"commit": checkpoint["commit"], "path": plan_rel})
1288
+ return "CHECKPOINTED", {"checkpoint": checkpoint}
1289
+ if dirty != {plan_rel}:
1290
+ raise Operational("BLOCKED", "canonical dirt is not exactly the selected plan", {"dirty_paths": sorted(dirty)})
1291
+ prior = info["head"]
1292
+ git(repo, "add", "--", plan_rel)
1293
+ staged = set(filter(None, git(repo, "diff", "--cached", "--name-only", "-z").decode("utf-8", "surrogateescape").split("\0")))
1294
+ if staged != {plan_rel}:
1295
+ git(repo, "reset", "--mixed", prior)
1296
+ raise Operational("BLOCKED", "staged paths are not exactly the selected plan")
1297
+ try:
1298
+ commit_index_tree(repo, PLAN_CHECKPOINT_MESSAGE)
1299
+ except Operational:
1300
+ git(repo, "reset", "--mixed", prior, check=False)
1301
+ raise
1302
+ commit = git_text(repo, "rev-parse", "HEAD")
1303
+ test_fault("checkpoint-plan-after-commit")
1304
+ if status_paths(repo):
1305
+ raise Operational("BLOCKED", "checkpoint committed but canonical checkout is not clean")
1306
+ cp = {"prior_head": prior, "commit": commit, "tree": git_text(repo, "rev-parse", "HEAD^{tree}"), "path": plan_rel, "digest": doc["plan"]["digest"], "at": now_iso()}
1307
+ with locked_manifest(args.run_id, write=True) as doc:
1308
+ validate_repo(doc)
1309
+ doc["plan"]["checkpoint"] = cp
1310
+ event(doc, "plan-checkpoint", detail={"commit": commit, "path": plan_rel})
1311
+ return "CHECKPOINTED", {"checkpoint": cp}
1312
+
1313
+
1314
+ @contextlib.contextmanager
1315
+ def admin_lock(common_dir: str):
1316
+ root = ensure_root()
1317
+ key = digest_bytes(os.path.realpath(common_dir).encode())
1318
+ path = os.path.join(root, ".locks", f"worktree-{key}.lock")
1319
+ try:
1320
+ create_private(path, b"")
1321
+ except Operational:
1322
+ pass
1323
+ data = read_private(path, 64)
1324
+ del data
1325
+ fd = os.open(path, os.O_RDWR | O_NOFOLLOW)
1326
+ try:
1327
+ _lock_fd(fd, exclusive=True)
1328
+ yield
1329
+ finally:
1330
+ _unlock_fd(fd)
1331
+ os.close(fd)
1332
+
1333
+
1334
+ def worktree_rows(repo: str) -> list[dict]:
1335
+ raw = git_text(repo, "worktree", "list", "--porcelain")
1336
+ rows, row = [], {}
1337
+ for line in raw.splitlines() + [""]:
1338
+ if not line:
1339
+ if row:
1340
+ rows.append(row)
1341
+ row = {}
1342
+ continue
1343
+ key, _, value = line.partition(" ")
1344
+ row[key] = value if value else True
1345
+ return rows
1346
+
1347
+
1348
+ def validate_workspace(doc: dict, unit: dict) -> dict:
1349
+ repo = doc["repository"]["toplevel"]
1350
+ workspace = unit["workspace"]["path"]
1351
+ owned = os.path.join(run_dir(doc["run_id"]), "units", unit["unit_id"])
1352
+ if os.path.commonpath([os.path.realpath(workspace), os.path.realpath(owned)]) != os.path.realpath(owned):
1353
+ raise Operational("BLOCKED", "workspace escaped its owned unit directory")
1354
+ validate_private_dir(workspace)
1355
+ matches = [r for r in worktree_rows(repo) if os.path.realpath(str(r.get("worktree", ""))) == os.path.realpath(workspace)]
1356
+ if len(matches) != 1:
1357
+ raise Operational("BLOCKED", "workspace is not registered exactly once")
1358
+ if "detached" not in matches[0]:
1359
+ raise Operational("BLOCKED", "unit workspace is not detached")
1360
+ common = os.path.realpath(git_text(workspace, "rev-parse", "--path-format=absolute", "--git-common-dir"))
1361
+ if common != doc["repository"]["common_dir"]:
1362
+ raise Operational("BLOCKED", "unit workspace belongs to another repository")
1363
+ return matches[0]
1364
+
1365
+
1366
+ def validate_pristine_unit_base(doc: dict, unit: dict) -> dict:
1367
+ row = validate_workspace(doc, unit)
1368
+ workspace = unit["workspace"]["path"]
1369
+ base = unit["workspace"]["base"]
1370
+ if git_text(workspace, "rev-parse", "HEAD") != base:
1371
+ raise Operational("BLOCKED", "unit workspace HEAD no longer equals the recorded base")
1372
+ dirty = status_paths(workspace)
1373
+ if dirty:
1374
+ raise Operational(
1375
+ "BLOCKED",
1376
+ "unit workspace is dirty before dispatch authorization",
1377
+ {"dirty_paths": sorted(dirty)},
1378
+ )
1379
+ return row