create-egregore 0.20.2 → 0.21.0-runtime-mvp.2

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 (706) hide show
  1. package/assets/egregore-launcher.js +1013 -16
  2. package/bin/cli.js +11 -0
  3. package/lib/claude-runtime.js +192 -0
  4. package/lib/local.js +38 -31
  5. package/lib/pi-runtime.js +1 -0
  6. package/lib/prime-runtime.js +1 -0
  7. package/lib/runtime-identity.js +146 -0
  8. package/lib/setup.js +116 -17
  9. package/package.json +10 -6
  10. package/runtime/claude/.claude/context/commit-format.md +110 -0
  11. package/runtime/claude/.claude/context/git-language.md +46 -0
  12. package/runtime/claude/.claude/context/notification-consent.md +66 -0
  13. package/runtime/claude/.claude/context/pr-format.md +67 -0
  14. package/runtime/claude/.claude/context/telemetry.md +133 -0
  15. package/runtime/claude/.claude/hooks/boundary-check.sh +333 -0
  16. package/runtime/claude/.claude/hooks/branch-guard.sh +231 -0
  17. package/runtime/claude/.claude/hooks/card-context.sh +38 -0
  18. package/runtime/claude/.claude/hooks/emissary-detect.sh +53 -0
  19. package/runtime/claude/.claude/hooks/memory-refresh.sh +67 -0
  20. package/runtime/claude/.claude/hooks/onboarding-guard.sh +86 -0
  21. package/runtime/claude/.claude/hooks/retrieval-context.sh +43 -0
  22. package/runtime/claude/.claude/rules/voice-bedrock.md +73 -0
  23. package/runtime/claude/.claude/settings.json +131 -0
  24. package/runtime/claude/.claude/skills/activity/SKILL.md +68 -0
  25. package/runtime/claude/.claude/skills/announce/SKILL.md +54 -0
  26. package/runtime/claude/.claude/skills/archive/SKILL.md +71 -0
  27. package/runtime/claude/.claude/skills/branch/SKILL.md +147 -0
  28. package/runtime/claude/.claude/skills/checkup/SKILL.md +159 -0
  29. package/runtime/claude/.claude/skills/commit/SKILL.md +103 -0
  30. package/runtime/claude/.claude/skills/contribute/SKILL.md +343 -0
  31. package/runtime/claude/.claude/skills/create-skill/SKILL.md +135 -0
  32. package/runtime/claude/.claude/skills/dark-mode/SKILL.md +410 -0
  33. package/runtime/claude/.claude/skills/dashboard/SKILL.md +43 -0
  34. package/runtime/claude/.claude/skills/deep-reflect/REPORT.md +140 -0
  35. package/runtime/claude/.claude/skills/deep-reflect/RESEARCH.md +132 -0
  36. package/runtime/claude/.claude/skills/deep-reflect/ROLES.md +104 -0
  37. package/runtime/claude/.claude/skills/deep-reflect/SKILL.md +170 -0
  38. package/runtime/claude/.claude/skills/delete-user/SKILL.md +150 -0
  39. package/runtime/claude/.claude/skills/emissary/SKILL.md +220 -0
  40. package/runtime/claude/.claude/skills/handoff/SKILL.md +124 -0
  41. package/runtime/claude/.claude/skills/harvest/AUDIT.md +546 -0
  42. package/runtime/claude/.claude/skills/harvest/FORMAT.md +436 -0
  43. package/runtime/claude/.claude/skills/harvest/PROCESS.md +395 -0
  44. package/runtime/claude/.claude/skills/harvest/QUESTION_PALETTE.md +76 -0
  45. package/runtime/claude/.claude/skills/harvest/SKILL.md +100 -0
  46. package/runtime/claude/.claude/skills/ingest/SKILL.md +112 -0
  47. package/runtime/claude/.claude/skills/ingest-corpus/SKILL.md +187 -0
  48. package/runtime/claude/.claude/skills/ingest-notion/SKILL.md +214 -0
  49. package/runtime/claude/.claude/skills/ingest-user-interview/SKILL.md +65 -0
  50. package/runtime/claude/.claude/skills/ingest-user-interview/references/analysis-contract.md +125 -0
  51. package/runtime/claude/.claude/skills/invite/SKILL.md +111 -0
  52. package/runtime/claude/.claude/skills/issue/SKILL.md +104 -0
  53. package/runtime/claude/.claude/skills/me/SKILL.md +81 -0
  54. package/runtime/claude/.claude/skills/meeting/SKILL.md +76 -0
  55. package/runtime/claude/.claude/skills/meeting/references/analysis-contract.md +102 -0
  56. package/runtime/claude/.claude/skills/note/SKILL.md +71 -0
  57. package/runtime/claude/.claude/skills/notion-connect/SKILL.md +124 -0
  58. package/runtime/claude/.claude/skills/onboarding/SKILL.md +159 -0
  59. package/runtime/claude/.claude/skills/pr/SKILL.md +85 -0
  60. package/runtime/claude/.claude/skills/pull/SKILL.md +79 -0
  61. package/runtime/claude/.claude/skills/push/SKILL.md +47 -0
  62. package/runtime/claude/.claude/skills/quest/SKILL.md +121 -0
  63. package/runtime/claude/.claude/skills/reflect/SKILL.md +69 -0
  64. package/runtime/claude/.claude/skills/review-pr/SKILL.md +276 -0
  65. package/runtime/claude/.claude/skills/save/SKILL.md +92 -0
  66. package/runtime/claude/.claude/skills/slack-connect/SKILL.md +148 -0
  67. package/runtime/claude/.claude/skills/summon/SKILL.md +240 -0
  68. package/runtime/claude/.claude/skills/sync-repos/SKILL.md +82 -0
  69. package/runtime/claude/.claude/skills/teams-connect/SKILL.md +216 -0
  70. package/runtime/claude/.claude/skills/telegram-connect/SKILL.md +110 -0
  71. package/runtime/claude/.claude/skills/telemetry/SKILL.md +117 -0
  72. package/runtime/claude/.claude/skills/test/SKILL.md +87 -0
  73. package/runtime/claude/.claude/skills/the-spiral/SKILL.md +492 -0
  74. package/runtime/claude/.claude/skills/tui-design/SKILL.md +188 -0
  75. package/runtime/claude/.claude/skills/tutorial/SKILL.md +100 -0
  76. package/runtime/claude/.claude/skills/update/SKILL.md +265 -0
  77. package/runtime/claude/.claude/skills/view/SKILL.md +393 -0
  78. package/runtime/claude/.claude/skills/view/compose-scaffold.html +221 -0
  79. package/runtime/claude/.claude/skills/visual-explain/SKILL.md +132 -0
  80. package/runtime/claude/.claude/skills/wrap/SKILL.md +98 -0
  81. package/runtime/claude/CLAUDE.md +447 -0
  82. package/runtime/claude/bin/activity-action.sh +92 -0
  83. package/runtime/claude/bin/activity-data.sh +17 -0
  84. package/runtime/claude/bin/agent.sh +783 -0
  85. package/runtime/claude/bin/artifact-register.sh +164 -0
  86. package/runtime/claude/bin/artifact-writeback.sh +53 -0
  87. package/runtime/claude/bin/artifacts.sh +288 -0
  88. package/runtime/claude/bin/attendant.sh +707 -0
  89. package/runtime/claude/bin/autosave.sh +112 -0
  90. package/runtime/claude/bin/boundary.sh +253 -0
  91. package/runtime/claude/bin/build-sealed-dataroom.mjs +722 -0
  92. package/runtime/claude/bin/capture-reconcile.sh +69 -0
  93. package/runtime/claude/bin/capture-run.sh +357 -0
  94. package/runtime/claude/bin/codex-install-skills.sh +78 -0
  95. package/runtime/claude/bin/codex-render-spec.mjs +462 -0
  96. package/runtime/claude/bin/codex-session-start.sh +275 -0
  97. package/runtime/claude/bin/codex-skill-render.mjs +349 -0
  98. package/runtime/claude/bin/codex-sync-skills.sh +237 -0
  99. package/runtime/claude/bin/connect-refresh.sh +33 -0
  100. package/runtime/claude/bin/contribute-guard.sh +79 -0
  101. package/runtime/claude/bin/corpus_map.py +806 -0
  102. package/runtime/claude/bin/corpus_survey.py +304 -0
  103. package/runtime/claude/bin/dashboard-data.sh +18 -0
  104. package/runtime/claude/bin/github-auth.sh +121 -0
  105. package/runtime/claude/bin/graph-batch.sh +86 -0
  106. package/runtime/claude/bin/graph-op.sh +1106 -0
  107. package/runtime/claude/bin/graph-projection.sh +54 -0
  108. package/runtime/claude/bin/graph-wal.sh +242 -0
  109. package/runtime/claude/bin/graph.sh +267 -0
  110. package/runtime/claude/bin/handoff-lifecycle-job.sh +146 -0
  111. package/runtime/claude/bin/handoff-lifecycle.sh +280 -0
  112. package/runtime/claude/bin/handoff-pr-backfill.sh +86 -0
  113. package/runtime/claude/bin/handoff-preview.sh +169 -0
  114. package/runtime/claude/bin/handoff-run.sh +692 -0
  115. package/runtime/claude/bin/handoff-save-egregore.sh +224 -0
  116. package/runtime/claude/bin/index-handoff.sh +322 -0
  117. package/runtime/claude/bin/ingest-graph.sh +86 -0
  118. package/runtime/claude/bin/ingest-item.sh +10 -0
  119. package/runtime/claude/bin/ingest-surface.html +495 -0
  120. package/runtime/claude/bin/ingest.py +1011 -0
  121. package/runtime/claude/bin/ingest.sh +308 -0
  122. package/runtime/claude/bin/ingest_graph.py +856 -0
  123. package/runtime/claude/bin/ingest_surface.py +544 -0
  124. package/runtime/claude/bin/invite.sh +6 -0
  125. package/runtime/claude/bin/issue.sh +10 -0
  126. package/runtime/claude/bin/knowledge.sh +10 -0
  127. package/runtime/claude/bin/lib/artifact-id.sh +46 -0
  128. package/runtime/claude/bin/lib/config.sh +136 -0
  129. package/runtime/claude/bin/lib/context.sh +642 -0
  130. package/runtime/claude/bin/lib/dashboard-artifact.sh +242 -0
  131. package/runtime/claude/bin/lib/ensure-develop.sh +183 -0
  132. package/runtime/claude/bin/lib/git-message.sh +37 -0
  133. package/runtime/claude/bin/lib/git-sync.sh +494 -0
  134. package/runtime/claude/bin/lib/greeting.sh +548 -0
  135. package/runtime/claude/bin/lib/handoff-meta.sh +97 -0
  136. package/runtime/claude/bin/lib/hash.sh +13 -0
  137. package/runtime/claude/bin/lib/identity.sh +213 -0
  138. package/runtime/claude/bin/lib/metrics.sh +374 -0
  139. package/runtime/claude/bin/lib/noncode.sh +31 -0
  140. package/runtime/claude/bin/lib/permissions.sh +106 -0
  141. package/runtime/claude/bin/lib/time.sh +56 -0
  142. package/runtime/claude/bin/lib/worktree-links.sh +85 -0
  143. package/runtime/claude/bin/node-run.sh +42 -0
  144. package/runtime/claude/bin/notification.sh +6 -0
  145. package/runtime/claude/bin/notify.sh +636 -0
  146. package/runtime/claude/bin/observe-context.sh +14 -0
  147. package/runtime/claude/bin/onboarding.sh +6 -0
  148. package/runtime/claude/bin/person.py +634 -0
  149. package/runtime/claude/bin/person.sh +481 -0
  150. package/runtime/claude/bin/pi-render-spec.mjs +140 -0
  151. package/runtime/claude/bin/post-update.sh +37 -0
  152. package/runtime/claude/bin/prime-render-spec.mjs +147 -0
  153. package/runtime/claude/bin/publish-artifact.sh +324 -0
  154. package/runtime/claude/bin/publish-references.sh +144 -0
  155. package/runtime/claude/bin/quest.sh +9 -0
  156. package/runtime/claude/bin/question.sh +8 -0
  157. package/runtime/claude/bin/repo-state.sh +96 -0
  158. package/runtime/claude/bin/research-ingest.sh +7 -0
  159. package/runtime/claude/bin/restore-owned-skills.sh +67 -0
  160. package/runtime/claude/bin/retrieval_eval.py +385 -0
  161. package/runtime/claude/bin/runtime-identity.sh +70 -0
  162. package/runtime/claude/bin/runtime-upgrade.sh +43 -0
  163. package/runtime/claude/bin/scroll.sh +9 -0
  164. package/runtime/claude/bin/search.sh +318 -0
  165. package/runtime/claude/bin/session-autosave.sh +220 -0
  166. package/runtime/claude/bin/session-log.sh +199 -0
  167. package/runtime/claude/bin/session-start.sh +696 -0
  168. package/runtime/claude/bin/settings.sh +532 -0
  169. package/runtime/claude/bin/startup-check.sh +136 -0
  170. package/runtime/claude/bin/statusline.sh +41 -0
  171. package/runtime/claude/bin/telemetry.sh +666 -0
  172. package/runtime/claude/bin/test-changes.sh +344 -0
  173. package/runtime/claude/bin/thread.sh +9 -0
  174. package/runtime/claude/bin/todo.sh +9 -0
  175. package/runtime/claude/bin/view-render.sh +126 -0
  176. package/runtime/claude/bin/worktree-create.sh +145 -0
  177. package/runtime/claude/bin/worktree-remove.sh +50 -0
  178. package/runtime/claude/bin/worktree.sh +198 -0
  179. package/runtime/claude/egregore_runtime/__init__.py +228 -0
  180. package/runtime/claude/egregore_runtime/adapters/__init__.py +5 -0
  181. package/runtime/claude/egregore_runtime/adapters/invitation.py +237 -0
  182. package/runtime/claude/egregore_runtime/adapters/notification.py +59 -0
  183. package/runtime/claude/egregore_runtime/adapters/qmd.py +1701 -0
  184. package/runtime/claude/egregore_runtime/adapters/qmd_cli.py +210 -0
  185. package/runtime/claude/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  186. package/runtime/claude/egregore_runtime/admin_gate.py +102 -0
  187. package/runtime/claude/egregore_runtime/artifacts.py +523 -0
  188. package/runtime/claude/egregore_runtime/contracts.py +499 -0
  189. package/runtime/claude/egregore_runtime/entitlements.py +85 -0
  190. package/runtime/claude/egregore_runtime/errors.py +10 -0
  191. package/runtime/claude/egregore_runtime/graph.py +356 -0
  192. package/runtime/claude/egregore_runtime/handoff_lookup.py +196 -0
  193. package/runtime/claude/egregore_runtime/harness_cli.py +1221 -0
  194. package/runtime/claude/egregore_runtime/identity.py +734 -0
  195. package/runtime/claude/egregore_runtime/identity_cli.py +91 -0
  196. package/runtime/claude/egregore_runtime/ingest.py +575 -0
  197. package/runtime/claude/egregore_runtime/ingest_cli.py +254 -0
  198. package/runtime/claude/egregore_runtime/ingest_index_cli.py +147 -0
  199. package/runtime/claude/egregore_runtime/invitation_cli.py +51 -0
  200. package/runtime/claude/egregore_runtime/invitations.py +116 -0
  201. package/runtime/claude/egregore_runtime/issue_cli.py +305 -0
  202. package/runtime/claude/egregore_runtime/issues.py +359 -0
  203. package/runtime/claude/egregore_runtime/knowledge.py +376 -0
  204. package/runtime/claude/egregore_runtime/knowledge_cli.py +173 -0
  205. package/runtime/claude/egregore_runtime/lifecycle.py +888 -0
  206. package/runtime/claude/egregore_runtime/lifecycle_cli.py +269 -0
  207. package/runtime/claude/egregore_runtime/notification_cli.py +125 -0
  208. package/runtime/claude/egregore_runtime/notifications.py +230 -0
  209. package/runtime/claude/egregore_runtime/observe.py +342 -0
  210. package/runtime/claude/egregore_runtime/onboarding.py +290 -0
  211. package/runtime/claude/egregore_runtime/onboarding_cli.py +110 -0
  212. package/runtime/claude/egregore_runtime/policy.py +144 -0
  213. package/runtime/claude/egregore_runtime/profile.py +193 -0
  214. package/runtime/claude/egregore_runtime/quest_cli.py +216 -0
  215. package/runtime/claude/egregore_runtime/question_cli.py +180 -0
  216. package/runtime/claude/egregore_runtime/questions.py +304 -0
  217. package/runtime/claude/egregore_runtime/quests.py +419 -0
  218. package/runtime/claude/egregore_runtime/readiness.py +379 -0
  219. package/runtime/claude/egregore_runtime/research_ingest.py +256 -0
  220. package/runtime/claude/egregore_runtime/research_ingest_cli.py +44 -0
  221. package/runtime/claude/egregore_runtime/runtime.py +464 -0
  222. package/runtime/claude/egregore_runtime/scroll_cli.py +185 -0
  223. package/runtime/claude/egregore_runtime/scrolls.py +726 -0
  224. package/runtime/claude/egregore_runtime/status.py +412 -0
  225. package/runtime/claude/egregore_runtime/status_cli.py +174 -0
  226. package/runtime/claude/egregore_runtime/sync.py +218 -0
  227. package/runtime/claude/egregore_runtime/teamsync.py +139 -0
  228. package/runtime/claude/egregore_runtime/telemetry.py +640 -0
  229. package/runtime/claude/egregore_runtime/thread_cli.py +173 -0
  230. package/runtime/claude/egregore_runtime/threads.py +517 -0
  231. package/runtime/claude/egregore_runtime/todo_cli.py +142 -0
  232. package/runtime/claude/egregore_runtime/todos.py +244 -0
  233. package/runtime/claude/egregore_runtime/upgrade.py +770 -0
  234. package/runtime/claude/egregore_runtime/upgrade_worker.py +53 -0
  235. package/runtime/claude/egregore_runtime/writeback.py +391 -0
  236. package/runtime/claude/egregore_runtime/writeback_cli.py +114 -0
  237. package/runtime/claude/manifest.json +284 -0
  238. package/runtime/codex/.codex/hooks/search-hint.js +21 -52
  239. package/runtime/codex/.codex/hooks.json +3 -3
  240. package/runtime/codex/.codex/skills/activity/SKILL.md +23 -66
  241. package/runtime/codex/.codex/skills/announce/SKILL.md +27 -50
  242. package/runtime/codex/.codex/skills/archive/SKILL.md +21 -36
  243. package/runtime/codex/.codex/skills/checkup/SKILL.md +77 -36
  244. package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
  245. package/runtime/codex/.codex/skills/dashboard/SKILL.md +17 -47
  246. package/runtime/codex/.codex/skills/handoff/SKILL.md +84 -133
  247. package/runtime/codex/.codex/skills/harvest/SKILL.md +32 -117
  248. package/runtime/codex/.codex/skills/ingest/SKILL.md +47 -25
  249. package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +18 -36
  250. package/runtime/codex/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
  251. package/runtime/codex/.codex/skills/invite/SKILL.md +21 -35
  252. package/runtime/codex/.codex/skills/issue/SKILL.md +60 -41
  253. package/runtime/codex/.codex/skills/me/SKILL.md +1 -1
  254. package/runtime/codex/.codex/skills/meeting/SKILL.md +16 -36
  255. package/runtime/codex/.codex/skills/meeting/references/analysis-contract.md +48 -0
  256. package/runtime/codex/.codex/skills/note/SKILL.md +30 -25
  257. package/runtime/codex/.codex/skills/onboarding/SKILL.md +36 -41
  258. package/runtime/codex/.codex/skills/pull/SKILL.md +1 -1
  259. package/runtime/codex/.codex/skills/quest/SKILL.md +27 -66
  260. package/runtime/codex/.codex/skills/reflect/SKILL.md +22 -36
  261. package/runtime/codex/.codex/skills/save/SKILL.md +2 -2
  262. package/runtime/codex/.codex/skills/telemetry/SKILL.md +1 -1
  263. package/runtime/codex/.codex/skills/test/SKILL.md +1 -1
  264. package/runtime/codex/.codex/skills/tutorial/SKILL.md +40 -41
  265. package/runtime/codex/.codex/skills/view/SKILL.md +115 -111
  266. package/runtime/codex/.codex/skills/wrap/SKILL.md +45 -41
  267. package/runtime/codex/.codex/spec-manifest.json +9 -9
  268. package/runtime/codex/AGENTS.md +62 -58
  269. package/runtime/codex/bin/activity-action.sh +92 -0
  270. package/runtime/codex/bin/activity-data.sh +12 -233
  271. package/runtime/codex/bin/agent.sh +84 -100
  272. package/runtime/codex/bin/artifact-register.sh +164 -0
  273. package/runtime/codex/bin/artifact-writeback.sh +53 -0
  274. package/runtime/codex/bin/artifacts.sh +288 -0
  275. package/runtime/codex/bin/build-sealed-dataroom.mjs +722 -0
  276. package/runtime/codex/bin/capture-run.sh +18 -2
  277. package/runtime/codex/bin/codex-render-spec.mjs +41 -16
  278. package/runtime/codex/bin/codex-session-start.sh +14 -4
  279. package/runtime/codex/bin/codex-skill-render.mjs +9 -7
  280. package/runtime/codex/bin/corpus_map.py +806 -0
  281. package/runtime/codex/bin/corpus_survey.py +304 -0
  282. package/runtime/codex/bin/dashboard-data.sh +11 -171
  283. package/runtime/codex/bin/github-auth.sh +121 -0
  284. package/runtime/codex/bin/graph-batch.sh +7 -0
  285. package/runtime/codex/bin/graph-op.sh +4 -0
  286. package/runtime/codex/bin/graph-projection.sh +54 -0
  287. package/runtime/codex/bin/graph-wal.sh +13 -0
  288. package/runtime/codex/bin/graph.sh +44 -0
  289. package/runtime/codex/bin/handoff-lifecycle-job.sh +146 -0
  290. package/runtime/codex/bin/handoff-lifecycle.sh +280 -0
  291. package/runtime/codex/bin/handoff-preview.sh +169 -0
  292. package/runtime/codex/bin/handoff-run.sh +42 -26
  293. package/runtime/codex/bin/ingest-graph.sh +6 -0
  294. package/runtime/codex/bin/ingest-item.sh +10 -0
  295. package/runtime/codex/bin/ingest.sh +59 -30
  296. package/runtime/codex/bin/invite.sh +6 -0
  297. package/runtime/codex/bin/issue.sh +10 -0
  298. package/runtime/codex/bin/knowledge.sh +10 -0
  299. package/runtime/codex/bin/lib/config.sh +20 -0
  300. package/runtime/codex/bin/lib/context.sh +46 -25
  301. package/runtime/codex/bin/lib/git-sync.sh +70 -6
  302. package/runtime/codex/bin/lib/greeting.sh +34 -5
  303. package/runtime/codex/bin/lib/handoff-meta.sh +97 -0
  304. package/runtime/codex/bin/lib/worktree-links.sh +6 -0
  305. package/runtime/codex/bin/node-run.sh +42 -0
  306. package/runtime/codex/bin/notification.sh +6 -0
  307. package/runtime/codex/bin/observe-context.sh +14 -0
  308. package/runtime/codex/bin/onboarding.sh +6 -0
  309. package/runtime/codex/bin/person.py +4 -0
  310. package/runtime/codex/bin/person.sh +43 -10
  311. package/runtime/codex/bin/pi-render-spec.mjs +140 -0
  312. package/runtime/codex/bin/post-update.sh +37 -0
  313. package/runtime/codex/bin/prime-render-spec.mjs +147 -0
  314. package/runtime/codex/bin/publish-artifact.sh +55 -2
  315. package/runtime/codex/bin/quest.sh +9 -0
  316. package/runtime/codex/bin/question.sh +8 -0
  317. package/runtime/codex/bin/research-ingest.sh +7 -0
  318. package/runtime/codex/bin/restore-owned-skills.sh +67 -0
  319. package/runtime/codex/bin/retrieval_eval.py +385 -0
  320. package/runtime/codex/bin/runtime-identity.sh +70 -0
  321. package/runtime/codex/bin/runtime-upgrade.sh +43 -0
  322. package/runtime/codex/bin/scroll.sh +9 -0
  323. package/runtime/codex/bin/search.sh +231 -195
  324. package/runtime/codex/bin/settings.sh +174 -5
  325. package/runtime/codex/bin/startup-check.sh +136 -0
  326. package/runtime/codex/bin/telemetry.sh +522 -247
  327. package/runtime/codex/bin/test-changes.sh +344 -0
  328. package/runtime/codex/bin/thread.sh +9 -0
  329. package/runtime/codex/bin/todo.sh +9 -0
  330. package/runtime/codex/bin/view-render.sh +126 -0
  331. package/runtime/codex/bin/worktree-create.sh +19 -5
  332. package/runtime/codex/egregore_runtime/__init__.py +228 -0
  333. package/runtime/codex/egregore_runtime/adapters/__init__.py +5 -0
  334. package/runtime/codex/egregore_runtime/adapters/invitation.py +237 -0
  335. package/runtime/codex/egregore_runtime/adapters/notification.py +59 -0
  336. package/runtime/codex/egregore_runtime/adapters/qmd.py +1701 -0
  337. package/runtime/codex/egregore_runtime/adapters/qmd_cli.py +210 -0
  338. package/runtime/codex/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  339. package/runtime/codex/egregore_runtime/admin_gate.py +102 -0
  340. package/runtime/codex/egregore_runtime/artifacts.py +523 -0
  341. package/runtime/codex/egregore_runtime/contracts.py +499 -0
  342. package/runtime/codex/egregore_runtime/entitlements.py +85 -0
  343. package/runtime/codex/egregore_runtime/errors.py +10 -0
  344. package/runtime/codex/egregore_runtime/graph.py +356 -0
  345. package/runtime/codex/egregore_runtime/handoff_lookup.py +196 -0
  346. package/runtime/codex/egregore_runtime/harness_cli.py +1221 -0
  347. package/runtime/codex/egregore_runtime/identity.py +734 -0
  348. package/runtime/codex/egregore_runtime/identity_cli.py +91 -0
  349. package/runtime/codex/egregore_runtime/ingest.py +575 -0
  350. package/runtime/codex/egregore_runtime/ingest_cli.py +254 -0
  351. package/runtime/codex/egregore_runtime/ingest_index_cli.py +147 -0
  352. package/runtime/codex/egregore_runtime/invitation_cli.py +51 -0
  353. package/runtime/codex/egregore_runtime/invitations.py +116 -0
  354. package/runtime/codex/egregore_runtime/issue_cli.py +305 -0
  355. package/runtime/codex/egregore_runtime/issues.py +359 -0
  356. package/runtime/codex/egregore_runtime/knowledge.py +376 -0
  357. package/runtime/codex/egregore_runtime/knowledge_cli.py +173 -0
  358. package/runtime/codex/egregore_runtime/lifecycle.py +888 -0
  359. package/runtime/codex/egregore_runtime/lifecycle_cli.py +269 -0
  360. package/runtime/codex/egregore_runtime/notification_cli.py +125 -0
  361. package/runtime/codex/egregore_runtime/notifications.py +230 -0
  362. package/runtime/codex/egregore_runtime/observe.py +342 -0
  363. package/runtime/codex/egregore_runtime/onboarding.py +290 -0
  364. package/runtime/codex/egregore_runtime/onboarding_cli.py +110 -0
  365. package/runtime/codex/egregore_runtime/policy.py +144 -0
  366. package/runtime/codex/egregore_runtime/profile.py +193 -0
  367. package/runtime/codex/egregore_runtime/quest_cli.py +216 -0
  368. package/runtime/codex/egregore_runtime/question_cli.py +180 -0
  369. package/runtime/codex/egregore_runtime/questions.py +304 -0
  370. package/runtime/codex/egregore_runtime/quests.py +419 -0
  371. package/runtime/codex/egregore_runtime/readiness.py +379 -0
  372. package/runtime/codex/egregore_runtime/research_ingest.py +256 -0
  373. package/runtime/codex/egregore_runtime/research_ingest_cli.py +44 -0
  374. package/runtime/codex/egregore_runtime/runtime.py +464 -0
  375. package/runtime/codex/egregore_runtime/scroll_cli.py +185 -0
  376. package/runtime/codex/egregore_runtime/scrolls.py +726 -0
  377. package/runtime/codex/egregore_runtime/status.py +412 -0
  378. package/runtime/codex/egregore_runtime/status_cli.py +174 -0
  379. package/runtime/codex/egregore_runtime/sync.py +218 -0
  380. package/runtime/codex/egregore_runtime/teamsync.py +139 -0
  381. package/runtime/codex/egregore_runtime/telemetry.py +640 -0
  382. package/runtime/codex/egregore_runtime/thread_cli.py +173 -0
  383. package/runtime/codex/egregore_runtime/threads.py +517 -0
  384. package/runtime/codex/egregore_runtime/todo_cli.py +142 -0
  385. package/runtime/codex/egregore_runtime/todos.py +244 -0
  386. package/runtime/codex/egregore_runtime/upgrade.py +770 -0
  387. package/runtime/codex/egregore_runtime/upgrade_worker.py +53 -0
  388. package/runtime/codex/egregore_runtime/writeback.py +391 -0
  389. package/runtime/codex/egregore_runtime/writeback_cli.py +114 -0
  390. package/runtime/codex/manifest.json +98 -1
  391. package/runtime/pi/.codex/hooks/search-hint.js +21 -52
  392. package/runtime/pi/.codex/hooks.json +3 -3
  393. package/runtime/pi/.codex/skills/activity/SKILL.md +23 -66
  394. package/runtime/pi/.codex/skills/announce/SKILL.md +27 -50
  395. package/runtime/pi/.codex/skills/archive/SKILL.md +21 -36
  396. package/runtime/pi/.codex/skills/checkup/SKILL.md +77 -36
  397. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
  398. package/runtime/pi/.codex/skills/dashboard/SKILL.md +17 -47
  399. package/runtime/pi/.codex/skills/handoff/SKILL.md +84 -133
  400. package/runtime/pi/.codex/skills/harvest/SKILL.md +32 -117
  401. package/runtime/pi/.codex/skills/ingest/SKILL.md +47 -25
  402. package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +18 -36
  403. package/runtime/pi/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
  404. package/runtime/pi/.codex/skills/invite/SKILL.md +21 -35
  405. package/runtime/pi/.codex/skills/issue/SKILL.md +60 -41
  406. package/runtime/pi/.codex/skills/me/SKILL.md +1 -1
  407. package/runtime/pi/.codex/skills/meeting/SKILL.md +16 -36
  408. package/runtime/pi/.codex/skills/meeting/references/analysis-contract.md +48 -0
  409. package/runtime/pi/.codex/skills/note/SKILL.md +30 -25
  410. package/runtime/pi/.codex/skills/onboarding/SKILL.md +36 -41
  411. package/runtime/pi/.codex/skills/pull/SKILL.md +1 -1
  412. package/runtime/pi/.codex/skills/quest/SKILL.md +27 -66
  413. package/runtime/pi/.codex/skills/reflect/SKILL.md +22 -36
  414. package/runtime/pi/.codex/skills/save/SKILL.md +2 -2
  415. package/runtime/pi/.codex/skills/telemetry/SKILL.md +1 -1
  416. package/runtime/pi/.codex/skills/test/SKILL.md +1 -1
  417. package/runtime/pi/.codex/skills/tutorial/SKILL.md +40 -41
  418. package/runtime/pi/.codex/skills/view/SKILL.md +115 -111
  419. package/runtime/pi/.codex/skills/wrap/SKILL.md +45 -41
  420. package/runtime/pi/.codex/spec-manifest.json +9 -9
  421. package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -48
  422. package/runtime/pi/.pi/extensions/egregore.ts +7 -0
  423. package/runtime/pi/.pi/spec-manifest.json +2 -2
  424. package/runtime/pi/AGENTS.md +62 -58
  425. package/runtime/pi/bin/activity-action.sh +92 -0
  426. package/runtime/pi/bin/activity-data.sh +12 -233
  427. package/runtime/pi/bin/agent.sh +84 -100
  428. package/runtime/pi/bin/artifact-register.sh +164 -0
  429. package/runtime/pi/bin/artifact-writeback.sh +53 -0
  430. package/runtime/pi/bin/artifacts.sh +288 -0
  431. package/runtime/pi/bin/build-sealed-dataroom.mjs +722 -0
  432. package/runtime/pi/bin/capture-run.sh +18 -2
  433. package/runtime/pi/bin/codex-render-spec.mjs +41 -16
  434. package/runtime/pi/bin/codex-session-start.sh +14 -4
  435. package/runtime/pi/bin/codex-skill-render.mjs +9 -7
  436. package/runtime/pi/bin/corpus_map.py +806 -0
  437. package/runtime/pi/bin/corpus_survey.py +304 -0
  438. package/runtime/pi/bin/dashboard-data.sh +11 -171
  439. package/runtime/pi/bin/github-auth.sh +121 -0
  440. package/runtime/pi/bin/graph-batch.sh +7 -0
  441. package/runtime/pi/bin/graph-op.sh +4 -0
  442. package/runtime/pi/bin/graph-projection.sh +54 -0
  443. package/runtime/pi/bin/graph-wal.sh +13 -0
  444. package/runtime/pi/bin/graph.sh +44 -0
  445. package/runtime/pi/bin/handoff-lifecycle-job.sh +146 -0
  446. package/runtime/pi/bin/handoff-lifecycle.sh +280 -0
  447. package/runtime/pi/bin/handoff-preview.sh +169 -0
  448. package/runtime/pi/bin/handoff-run.sh +42 -26
  449. package/runtime/pi/bin/ingest-graph.sh +6 -0
  450. package/runtime/pi/bin/ingest-item.sh +10 -0
  451. package/runtime/pi/bin/ingest.sh +59 -30
  452. package/runtime/pi/bin/invite.sh +6 -0
  453. package/runtime/pi/bin/issue.sh +10 -0
  454. package/runtime/pi/bin/knowledge.sh +10 -0
  455. package/runtime/pi/bin/lib/config.sh +20 -0
  456. package/runtime/pi/bin/lib/context.sh +46 -25
  457. package/runtime/pi/bin/lib/git-sync.sh +70 -6
  458. package/runtime/pi/bin/lib/greeting.sh +34 -5
  459. package/runtime/pi/bin/lib/handoff-meta.sh +97 -0
  460. package/runtime/pi/bin/lib/worktree-links.sh +6 -0
  461. package/runtime/pi/bin/node-run.sh +42 -0
  462. package/runtime/pi/bin/notification.sh +6 -0
  463. package/runtime/pi/bin/observe-context.sh +14 -0
  464. package/runtime/pi/bin/onboarding.sh +6 -0
  465. package/runtime/pi/bin/person.py +4 -0
  466. package/runtime/pi/bin/person.sh +43 -10
  467. package/runtime/pi/bin/pi-observe-context.mjs +93 -0
  468. package/runtime/pi/bin/pi-product-workflows.mjs +1 -1
  469. package/runtime/pi/bin/pi-render-spec.mjs +1 -1
  470. package/runtime/pi/bin/post-update.sh +37 -0
  471. package/runtime/pi/bin/prime-render-spec.mjs +147 -0
  472. package/runtime/pi/bin/publish-artifact.sh +55 -2
  473. package/runtime/pi/bin/quest.sh +9 -0
  474. package/runtime/pi/bin/question.sh +8 -0
  475. package/runtime/pi/bin/research-ingest.sh +7 -0
  476. package/runtime/pi/bin/restore-owned-skills.sh +67 -0
  477. package/runtime/pi/bin/retrieval_eval.py +385 -0
  478. package/runtime/pi/bin/runtime-identity.sh +70 -0
  479. package/runtime/pi/bin/runtime-upgrade.sh +43 -0
  480. package/runtime/pi/bin/scroll.sh +9 -0
  481. package/runtime/pi/bin/search.sh +231 -195
  482. package/runtime/pi/bin/settings.sh +174 -5
  483. package/runtime/pi/bin/startup-check.sh +136 -0
  484. package/runtime/pi/bin/telemetry.sh +522 -247
  485. package/runtime/pi/bin/test-changes.sh +344 -0
  486. package/runtime/pi/bin/thread.sh +9 -0
  487. package/runtime/pi/bin/todo.sh +9 -0
  488. package/runtime/pi/bin/view-render.sh +126 -0
  489. package/runtime/pi/bin/worktree-create.sh +19 -5
  490. package/runtime/pi/egregore_runtime/__init__.py +228 -0
  491. package/runtime/pi/egregore_runtime/adapters/__init__.py +5 -0
  492. package/runtime/pi/egregore_runtime/adapters/invitation.py +237 -0
  493. package/runtime/pi/egregore_runtime/adapters/notification.py +59 -0
  494. package/runtime/pi/egregore_runtime/adapters/qmd.py +1701 -0
  495. package/runtime/pi/egregore_runtime/adapters/qmd_cli.py +210 -0
  496. package/runtime/pi/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  497. package/runtime/pi/egregore_runtime/admin_gate.py +102 -0
  498. package/runtime/pi/egregore_runtime/artifacts.py +523 -0
  499. package/runtime/pi/egregore_runtime/contracts.py +499 -0
  500. package/runtime/pi/egregore_runtime/entitlements.py +85 -0
  501. package/runtime/pi/egregore_runtime/errors.py +10 -0
  502. package/runtime/pi/egregore_runtime/graph.py +356 -0
  503. package/runtime/pi/egregore_runtime/handoff_lookup.py +196 -0
  504. package/runtime/pi/egregore_runtime/harness_cli.py +1221 -0
  505. package/runtime/pi/egregore_runtime/identity.py +734 -0
  506. package/runtime/pi/egregore_runtime/identity_cli.py +91 -0
  507. package/runtime/pi/egregore_runtime/ingest.py +575 -0
  508. package/runtime/pi/egregore_runtime/ingest_cli.py +254 -0
  509. package/runtime/pi/egregore_runtime/ingest_index_cli.py +147 -0
  510. package/runtime/pi/egregore_runtime/invitation_cli.py +51 -0
  511. package/runtime/pi/egregore_runtime/invitations.py +116 -0
  512. package/runtime/pi/egregore_runtime/issue_cli.py +305 -0
  513. package/runtime/pi/egregore_runtime/issues.py +359 -0
  514. package/runtime/pi/egregore_runtime/knowledge.py +376 -0
  515. package/runtime/pi/egregore_runtime/knowledge_cli.py +173 -0
  516. package/runtime/pi/egregore_runtime/lifecycle.py +888 -0
  517. package/runtime/pi/egregore_runtime/lifecycle_cli.py +269 -0
  518. package/runtime/pi/egregore_runtime/notification_cli.py +125 -0
  519. package/runtime/pi/egregore_runtime/notifications.py +230 -0
  520. package/runtime/pi/egregore_runtime/observe.py +342 -0
  521. package/runtime/pi/egregore_runtime/onboarding.py +290 -0
  522. package/runtime/pi/egregore_runtime/onboarding_cli.py +110 -0
  523. package/runtime/pi/egregore_runtime/policy.py +144 -0
  524. package/runtime/pi/egregore_runtime/profile.py +193 -0
  525. package/runtime/pi/egregore_runtime/quest_cli.py +216 -0
  526. package/runtime/pi/egregore_runtime/question_cli.py +180 -0
  527. package/runtime/pi/egregore_runtime/questions.py +304 -0
  528. package/runtime/pi/egregore_runtime/quests.py +419 -0
  529. package/runtime/pi/egregore_runtime/readiness.py +379 -0
  530. package/runtime/pi/egregore_runtime/research_ingest.py +256 -0
  531. package/runtime/pi/egregore_runtime/research_ingest_cli.py +44 -0
  532. package/runtime/pi/egregore_runtime/runtime.py +464 -0
  533. package/runtime/pi/egregore_runtime/scroll_cli.py +185 -0
  534. package/runtime/pi/egregore_runtime/scrolls.py +726 -0
  535. package/runtime/pi/egregore_runtime/status.py +412 -0
  536. package/runtime/pi/egregore_runtime/status_cli.py +174 -0
  537. package/runtime/pi/egregore_runtime/sync.py +218 -0
  538. package/runtime/pi/egregore_runtime/teamsync.py +139 -0
  539. package/runtime/pi/egregore_runtime/telemetry.py +640 -0
  540. package/runtime/pi/egregore_runtime/thread_cli.py +173 -0
  541. package/runtime/pi/egregore_runtime/threads.py +517 -0
  542. package/runtime/pi/egregore_runtime/todo_cli.py +142 -0
  543. package/runtime/pi/egregore_runtime/todos.py +244 -0
  544. package/runtime/pi/egregore_runtime/upgrade.py +770 -0
  545. package/runtime/pi/egregore_runtime/upgrade_worker.py +53 -0
  546. package/runtime/pi/egregore_runtime/writeback.py +391 -0
  547. package/runtime/pi/egregore_runtime/writeback_cli.py +114 -0
  548. package/runtime/pi/manifest.json +98 -1
  549. package/runtime/prime/.codex/hooks/search-hint.js +21 -52
  550. package/runtime/prime/.codex/hooks.json +3 -3
  551. package/runtime/prime/.codex/skills/activity/SKILL.md +23 -66
  552. package/runtime/prime/.codex/skills/announce/SKILL.md +27 -50
  553. package/runtime/prime/.codex/skills/archive/SKILL.md +21 -36
  554. package/runtime/prime/.codex/skills/checkup/SKILL.md +77 -36
  555. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
  556. package/runtime/prime/.codex/skills/dashboard/SKILL.md +17 -47
  557. package/runtime/prime/.codex/skills/handoff/SKILL.md +84 -133
  558. package/runtime/prime/.codex/skills/harvest/SKILL.md +32 -117
  559. package/runtime/prime/.codex/skills/ingest/SKILL.md +47 -25
  560. package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +18 -36
  561. package/runtime/prime/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
  562. package/runtime/prime/.codex/skills/invite/SKILL.md +21 -35
  563. package/runtime/prime/.codex/skills/issue/SKILL.md +60 -41
  564. package/runtime/prime/.codex/skills/me/SKILL.md +1 -1
  565. package/runtime/prime/.codex/skills/meeting/SKILL.md +16 -36
  566. package/runtime/prime/.codex/skills/meeting/references/analysis-contract.md +48 -0
  567. package/runtime/prime/.codex/skills/note/SKILL.md +30 -25
  568. package/runtime/prime/.codex/skills/onboarding/SKILL.md +36 -41
  569. package/runtime/prime/.codex/skills/pull/SKILL.md +1 -1
  570. package/runtime/prime/.codex/skills/quest/SKILL.md +27 -66
  571. package/runtime/prime/.codex/skills/reflect/SKILL.md +22 -36
  572. package/runtime/prime/.codex/skills/save/SKILL.md +2 -2
  573. package/runtime/prime/.codex/skills/telemetry/SKILL.md +1 -1
  574. package/runtime/prime/.codex/skills/test/SKILL.md +1 -1
  575. package/runtime/prime/.codex/skills/tutorial/SKILL.md +40 -41
  576. package/runtime/prime/.codex/skills/view/SKILL.md +115 -111
  577. package/runtime/prime/.codex/skills/wrap/SKILL.md +45 -41
  578. package/runtime/prime/.codex/spec-manifest.json +9 -9
  579. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +57 -48
  580. package/runtime/prime/.prime/agent/extensions/egregore.ts +7 -0
  581. package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
  582. package/runtime/prime/AGENTS.md +62 -58
  583. package/runtime/prime/bin/activity-action.sh +92 -0
  584. package/runtime/prime/bin/activity-data.sh +12 -233
  585. package/runtime/prime/bin/agent.sh +84 -100
  586. package/runtime/prime/bin/artifact-register.sh +164 -0
  587. package/runtime/prime/bin/artifact-writeback.sh +53 -0
  588. package/runtime/prime/bin/artifacts.sh +288 -0
  589. package/runtime/prime/bin/build-sealed-dataroom.mjs +722 -0
  590. package/runtime/prime/bin/capture-run.sh +18 -2
  591. package/runtime/prime/bin/codex-render-spec.mjs +41 -16
  592. package/runtime/prime/bin/codex-session-start.sh +14 -4
  593. package/runtime/prime/bin/codex-skill-render.mjs +9 -7
  594. package/runtime/prime/bin/corpus_map.py +806 -0
  595. package/runtime/prime/bin/corpus_survey.py +304 -0
  596. package/runtime/prime/bin/dashboard-data.sh +11 -171
  597. package/runtime/prime/bin/github-auth.sh +121 -0
  598. package/runtime/prime/bin/graph-batch.sh +7 -0
  599. package/runtime/prime/bin/graph-op.sh +4 -0
  600. package/runtime/prime/bin/graph-projection.sh +54 -0
  601. package/runtime/prime/bin/graph-wal.sh +13 -0
  602. package/runtime/prime/bin/graph.sh +44 -0
  603. package/runtime/prime/bin/handoff-lifecycle-job.sh +146 -0
  604. package/runtime/prime/bin/handoff-lifecycle.sh +280 -0
  605. package/runtime/prime/bin/handoff-preview.sh +169 -0
  606. package/runtime/prime/bin/handoff-run.sh +42 -26
  607. package/runtime/prime/bin/ingest-graph.sh +6 -0
  608. package/runtime/prime/bin/ingest-item.sh +10 -0
  609. package/runtime/prime/bin/ingest.sh +59 -30
  610. package/runtime/prime/bin/invite.sh +6 -0
  611. package/runtime/prime/bin/issue.sh +10 -0
  612. package/runtime/prime/bin/knowledge.sh +10 -0
  613. package/runtime/prime/bin/lib/config.sh +20 -0
  614. package/runtime/prime/bin/lib/context.sh +46 -25
  615. package/runtime/prime/bin/lib/git-sync.sh +70 -6
  616. package/runtime/prime/bin/lib/greeting.sh +34 -5
  617. package/runtime/prime/bin/lib/handoff-meta.sh +97 -0
  618. package/runtime/prime/bin/lib/worktree-links.sh +6 -0
  619. package/runtime/prime/bin/node-run.sh +42 -0
  620. package/runtime/prime/bin/notification.sh +6 -0
  621. package/runtime/prime/bin/observe-context.sh +14 -0
  622. package/runtime/prime/bin/onboarding.sh +6 -0
  623. package/runtime/prime/bin/person.py +4 -0
  624. package/runtime/prime/bin/person.sh +43 -10
  625. package/runtime/prime/bin/pi-observe-context.mjs +93 -0
  626. package/runtime/prime/bin/pi-product-workflows.mjs +1 -1
  627. package/runtime/prime/bin/pi-render-spec.mjs +140 -0
  628. package/runtime/prime/bin/post-update.sh +37 -0
  629. package/runtime/prime/bin/prime-render-spec.mjs +1 -1
  630. package/runtime/prime/bin/publish-artifact.sh +55 -2
  631. package/runtime/prime/bin/quest.sh +9 -0
  632. package/runtime/prime/bin/question.sh +8 -0
  633. package/runtime/prime/bin/research-ingest.sh +7 -0
  634. package/runtime/prime/bin/restore-owned-skills.sh +67 -0
  635. package/runtime/prime/bin/retrieval_eval.py +385 -0
  636. package/runtime/prime/bin/runtime-identity.sh +70 -0
  637. package/runtime/prime/bin/runtime-upgrade.sh +43 -0
  638. package/runtime/prime/bin/scroll.sh +9 -0
  639. package/runtime/prime/bin/search.sh +231 -195
  640. package/runtime/prime/bin/settings.sh +174 -5
  641. package/runtime/prime/bin/startup-check.sh +136 -0
  642. package/runtime/prime/bin/telemetry.sh +522 -247
  643. package/runtime/prime/bin/test-changes.sh +344 -0
  644. package/runtime/prime/bin/thread.sh +9 -0
  645. package/runtime/prime/bin/todo.sh +9 -0
  646. package/runtime/prime/bin/view-render.sh +126 -0
  647. package/runtime/prime/bin/worktree-create.sh +19 -5
  648. package/runtime/prime/egregore_runtime/__init__.py +228 -0
  649. package/runtime/prime/egregore_runtime/adapters/__init__.py +5 -0
  650. package/runtime/prime/egregore_runtime/adapters/invitation.py +237 -0
  651. package/runtime/prime/egregore_runtime/adapters/notification.py +59 -0
  652. package/runtime/prime/egregore_runtime/adapters/qmd.py +1701 -0
  653. package/runtime/prime/egregore_runtime/adapters/qmd_cli.py +210 -0
  654. package/runtime/prime/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  655. package/runtime/prime/egregore_runtime/admin_gate.py +102 -0
  656. package/runtime/prime/egregore_runtime/artifacts.py +523 -0
  657. package/runtime/prime/egregore_runtime/contracts.py +499 -0
  658. package/runtime/prime/egregore_runtime/entitlements.py +85 -0
  659. package/runtime/prime/egregore_runtime/errors.py +10 -0
  660. package/runtime/prime/egregore_runtime/graph.py +356 -0
  661. package/runtime/prime/egregore_runtime/handoff_lookup.py +196 -0
  662. package/runtime/prime/egregore_runtime/harness_cli.py +1221 -0
  663. package/runtime/prime/egregore_runtime/identity.py +734 -0
  664. package/runtime/prime/egregore_runtime/identity_cli.py +91 -0
  665. package/runtime/prime/egregore_runtime/ingest.py +575 -0
  666. package/runtime/prime/egregore_runtime/ingest_cli.py +254 -0
  667. package/runtime/prime/egregore_runtime/ingest_index_cli.py +147 -0
  668. package/runtime/prime/egregore_runtime/invitation_cli.py +51 -0
  669. package/runtime/prime/egregore_runtime/invitations.py +116 -0
  670. package/runtime/prime/egregore_runtime/issue_cli.py +305 -0
  671. package/runtime/prime/egregore_runtime/issues.py +359 -0
  672. package/runtime/prime/egregore_runtime/knowledge.py +376 -0
  673. package/runtime/prime/egregore_runtime/knowledge_cli.py +173 -0
  674. package/runtime/prime/egregore_runtime/lifecycle.py +888 -0
  675. package/runtime/prime/egregore_runtime/lifecycle_cli.py +269 -0
  676. package/runtime/prime/egregore_runtime/notification_cli.py +125 -0
  677. package/runtime/prime/egregore_runtime/notifications.py +230 -0
  678. package/runtime/prime/egregore_runtime/observe.py +342 -0
  679. package/runtime/prime/egregore_runtime/onboarding.py +290 -0
  680. package/runtime/prime/egregore_runtime/onboarding_cli.py +110 -0
  681. package/runtime/prime/egregore_runtime/policy.py +144 -0
  682. package/runtime/prime/egregore_runtime/profile.py +193 -0
  683. package/runtime/prime/egregore_runtime/quest_cli.py +216 -0
  684. package/runtime/prime/egregore_runtime/question_cli.py +180 -0
  685. package/runtime/prime/egregore_runtime/questions.py +304 -0
  686. package/runtime/prime/egregore_runtime/quests.py +419 -0
  687. package/runtime/prime/egregore_runtime/readiness.py +379 -0
  688. package/runtime/prime/egregore_runtime/research_ingest.py +256 -0
  689. package/runtime/prime/egregore_runtime/research_ingest_cli.py +44 -0
  690. package/runtime/prime/egregore_runtime/runtime.py +464 -0
  691. package/runtime/prime/egregore_runtime/scroll_cli.py +185 -0
  692. package/runtime/prime/egregore_runtime/scrolls.py +726 -0
  693. package/runtime/prime/egregore_runtime/status.py +412 -0
  694. package/runtime/prime/egregore_runtime/status_cli.py +174 -0
  695. package/runtime/prime/egregore_runtime/sync.py +218 -0
  696. package/runtime/prime/egregore_runtime/teamsync.py +139 -0
  697. package/runtime/prime/egregore_runtime/telemetry.py +640 -0
  698. package/runtime/prime/egregore_runtime/thread_cli.py +173 -0
  699. package/runtime/prime/egregore_runtime/threads.py +517 -0
  700. package/runtime/prime/egregore_runtime/todo_cli.py +142 -0
  701. package/runtime/prime/egregore_runtime/todos.py +244 -0
  702. package/runtime/prime/egregore_runtime/upgrade.py +770 -0
  703. package/runtime/prime/egregore_runtime/upgrade_worker.py +53 -0
  704. package/runtime/prime/egregore_runtime/writeback.py +391 -0
  705. package/runtime/prime/egregore_runtime/writeback_cli.py +114 -0
  706. package/runtime/prime/manifest.json +98 -1
@@ -0,0 +1,726 @@
1
+ """Runtime-owned lifecycle for living Scroll documents.
2
+
3
+ Scrolls are mutable papers backed by append-only version/turn/review events.
4
+ Their ledger is a disposable fold, and publication is a separately authorized,
5
+ actor-bound SHARE effect against one exact HTML revision.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, replace
11
+ from datetime import UTC, datetime, timedelta
12
+ import hashlib
13
+ import json
14
+ import os
15
+ from pathlib import Path
16
+ import re
17
+ import subprocess
18
+ import tempfile
19
+ from typing import Any, Callable, Mapping, Sequence
20
+ import unicodedata
21
+
22
+ from .artifacts import (
23
+ ARTIFACT_SCHEMA_VERSION,
24
+ CanonicalDocument,
25
+ content_digest,
26
+ parse_canonical_markdown,
27
+ )
28
+ from .contracts import ActorContext, ArtifactRelationship, CanonicalArtifact, Permission, WritebackEvent
29
+ from .errors import AuthorizationDenied
30
+ from .policy import scope_allows_path
31
+ from .runtime import EgregoreRuntime
32
+
33
+
34
+ SCROLL_SCHEMA_VERSION = "egregore-scroll/v1"
35
+ SCROLL_EVENT_SCHEMA_VERSION = "egregore-scroll-event/v1"
36
+ SCROLL_SHARE_SCHEMA_VERSION = "egregore-scroll-share-plan/v1"
37
+ EXACT_SHARE_CONFIRMATION = "APPROVE_EXACT_SHARE"
38
+ SCROLL_EVENT_TYPES = frozenset(
39
+ {"version-published", "turn-received", "turn-reviewed", "share-published"}
40
+ )
41
+
42
+
43
+ class ScrollError(ValueError):
44
+ """A Scroll event, review, version, or share request is invalid."""
45
+
46
+
47
+ def _slug(value: str) -> str:
48
+ normalized = re.sub(r"[^a-z0-9]+", "-", value.casefold()).strip("-")[:50]
49
+ if not normalized:
50
+ raise ScrollError("scroll slug must contain a letter or number")
51
+ return normalized
52
+
53
+
54
+ def _iso(value: datetime) -> str:
55
+ return value.astimezone(UTC).isoformat().replace("+00:00", "Z")
56
+
57
+
58
+ def _normalize(value: Any) -> Any:
59
+ if isinstance(value, str):
60
+ return unicodedata.normalize("NFC", value.replace("\r\n", "\n").replace("\r", "\n"))
61
+ if isinstance(value, list):
62
+ return [_normalize(item) for item in value]
63
+ if isinstance(value, tuple):
64
+ return [_normalize(item) for item in value]
65
+ if isinstance(value, Mapping):
66
+ return {_normalize(str(key)): _normalize(item) for key, item in value.items()}
67
+ return value
68
+
69
+
70
+ def _fnv1a32(text: str) -> str:
71
+ value = 0x811C9DC5
72
+ for byte in text.encode("utf-8"):
73
+ value ^= byte
74
+ value = (value * 0x01000193) & 0xFFFFFFFF
75
+ return f"{value:08x}"
76
+
77
+
78
+ def scroll_turn_id(turn: Mapping[str, Any]) -> str:
79
+ """Return the cross-runtime Scroll turn id defined by the v1 grammar."""
80
+
81
+ normalized = _normalize(dict(turn))
82
+ respondent = str(normalized.get("respondent") or "")
83
+ base_version = str(normalized.get("base_version") or "")
84
+ date = str(normalized.get("date") or "")
85
+ if not respondent or not base_version or not date:
86
+ raise ScrollError("turn requires respondent, base_version, and date")
87
+ answers = []
88
+ for answer in sorted(normalized.get("answers") or (), key=lambda row: str(row.get("fork") or "")):
89
+ fork = str(answer.get("fork") or "")
90
+ mode = str(answer.get("mode") or "single")
91
+ note = str(answer.get("note") or "")
92
+ if not fork:
93
+ raise ScrollError("every turn answer requires a fork id")
94
+ if mode == "multi":
95
+ picks = sorted(str(item) for item in answer.get("picks") or answer.get("pick") or ())
96
+ answers.append([fork, picks, note])
97
+ elif mode == "rank":
98
+ tiers = [sorted(str(item) for item in tier) for tier in answer.get("ranks") or ()]
99
+ answers.append([fork, tiers, note])
100
+ elif mode == "spectrum":
101
+ answers.append([fork, answer.get("position", "UNDECIDED"), note])
102
+ else:
103
+ answers.append([fork, answer.get("pick", "UNDECIDED"), note])
104
+ opens = [
105
+ [str(row.get("prompt") or ""), row.get("text", "UNDECIDED")]
106
+ for row in sorted(normalized.get("opens") or (), key=lambda row: str(row.get("prompt") or ""))
107
+ ]
108
+ digest_input = [respondent, base_version, date, answers, opens]
109
+ encoded = json.dumps(digest_input, ensure_ascii=False, separators=(",", ":"))
110
+ return f"{respondent}|{base_version}|{date}|{_fnv1a32(encoded)}"
111
+
112
+
113
+ @dataclass(frozen=True, slots=True)
114
+ class ScrollSnapshot:
115
+ slug: str
116
+ title: str
117
+ creator_id: str
118
+ trusted_actor_ids: tuple[str, ...]
119
+ respondents: tuple[str, ...]
120
+ version: int
121
+ storage_key: str
122
+ source_html: str
123
+ published_url: str | None
124
+ document: CanonicalDocument
125
+
126
+ @property
127
+ def revision(self) -> str:
128
+ return self.document.artifact.revision
129
+
130
+
131
+ @dataclass(frozen=True, slots=True)
132
+ class ScrollEventReceipt:
133
+ scroll: ScrollSnapshot
134
+ event_id: str
135
+ disposition: str
136
+ receipts: tuple[WritebackEvent, ...]
137
+
138
+
139
+ @dataclass(frozen=True, slots=True)
140
+ class ScrollSharePlan:
141
+ schema_version: str
142
+ plan_id: str
143
+ digest: str
144
+ actor_id: str
145
+ session_id: str
146
+ slug: str
147
+ expected_revision: str
148
+ html_path: str
149
+ html_hash: str
150
+ title: str
151
+ description: str
152
+ created_at: str
153
+ available: bool = True
154
+ reason: str | None = None
155
+
156
+ def to_dict(self) -> dict[str, Any]:
157
+ return {
158
+ "schema_version": self.schema_version,
159
+ "plan_id": self.plan_id,
160
+ "digest": self.digest,
161
+ "actor_id": self.actor_id,
162
+ "session_id": self.session_id,
163
+ "slug": self.slug,
164
+ "expected_revision": self.expected_revision,
165
+ "html_path": self.html_path,
166
+ "html_hash": self.html_hash,
167
+ "title": self.title,
168
+ "description": self.description,
169
+ "created_at": self.created_at,
170
+ "available": self.available,
171
+ "reason": self.reason,
172
+ }
173
+
174
+
175
+ @dataclass(frozen=True, slots=True)
176
+ class ScrollShareReceipt:
177
+ plan_id: str
178
+ slug: str
179
+ url: str
180
+ writeback: tuple[WritebackEvent, ...]
181
+
182
+
183
+ def parse_scroll_markdown(markdown: str, *, slug: str, org_id: str) -> ScrollSnapshot:
184
+ normalized = _slug(slug)
185
+ document = parse_canonical_markdown(
186
+ markdown,
187
+ canonical_path=f"scrolls/{normalized}.md",
188
+ default_org_id=org_id,
189
+ )
190
+ if document.artifact.artifact_type != "scroll":
191
+ raise ScrollError("canonical Scroll record has the wrong artifact type")
192
+ fields = document.legacy_fields
193
+ creator = str(fields.get("creator_id") or fields.get("creator") or "").strip()
194
+ if not creator:
195
+ raise ScrollError("scroll creator is unresolved; backfill one stable actor id before tending")
196
+ trusted = fields.get("trusted_actor_ids") or fields.get("trusted") or ()
197
+ respondents = fields.get("respondents") or ()
198
+ if isinstance(trusted, str):
199
+ trusted = [trusted]
200
+ if isinstance(respondents, str):
201
+ respondents = [respondents]
202
+ version = int(fields.get("version") or 0)
203
+ if version < 1:
204
+ raise ScrollError("scroll version must be positive")
205
+ storage_key = str(fields.get("storage_key") or "").strip()
206
+ source_html = str(fields.get("source_html") or "").strip()
207
+ if not storage_key or not source_html:
208
+ raise ScrollError("scroll requires a storage key and durable HTML source path")
209
+ return ScrollSnapshot(
210
+ slug=normalized,
211
+ title=document.artifact.title,
212
+ creator_id=creator,
213
+ trusted_actor_ids=tuple(dict.fromkeys(str(item) for item in trusted if str(item).strip())),
214
+ respondents=tuple(dict.fromkeys(str(item) for item in respondents if str(item).strip())),
215
+ version=version,
216
+ storage_key=storage_key,
217
+ source_html=source_html,
218
+ published_url=str(fields["published_url"]) if fields.get("published_url") else None,
219
+ document=document,
220
+ )
221
+
222
+
223
+ class CanonicalScrollService:
224
+ """Own the Scroll write-ahead log, creator gate, fold, and SHARE seam."""
225
+
226
+ def __init__(
227
+ self,
228
+ runtime: EgregoreRuntime,
229
+ root: Path,
230
+ *,
231
+ clock: Callable[[], datetime] | None = None,
232
+ publisher: Callable[[ScrollSnapshot, Path, str], str] | None = None,
233
+ state_root: Path | None = None,
234
+ ) -> None:
235
+ self.runtime = runtime
236
+ self.root = root.resolve()
237
+ self.memory_root = (self.root / "memory").resolve()
238
+ self.clock = clock or (lambda: datetime.now(UTC))
239
+ self.publisher = publisher or self._publish
240
+ instance = hashlib.sha256(str(self.memory_root).encode("utf-8")).hexdigest()[:16]
241
+ private_root = state_root or (Path.home() / ".egregore" / "runtime")
242
+ self.plan_root = private_root.resolve() / "share-plans" / instance
243
+
244
+ @staticmethod
245
+ def canonical_path(slug: str) -> str:
246
+ return f"memory/scrolls/{_slug(slug)}.md"
247
+
248
+ def _path(self, slug: str) -> Path:
249
+ return self.memory_root / "scrolls" / f"{_slug(slug)}.md"
250
+
251
+ def _authorize(self, actor: ActorContext, permission: Permission, resource: str, path: str) -> None:
252
+ decision = self.runtime.authorize(actor, permission, (resource,))
253
+ if not decision.allowed:
254
+ raise AuthorizationDenied("; ".join(decision.reasons) or f"scroll {permission.value} denied")
255
+ if permission in {Permission.READ, Permission.WRITE} and not scope_allows_path(path, decision.scopes):
256
+ raise AuthorizationDenied("scroll path is outside the actor's authorized scope")
257
+
258
+ def _source_path(self, value: str | Path) -> Path:
259
+ path = Path(value)
260
+ if not path.is_absolute():
261
+ path = self.root / path
262
+ resolved = path.resolve()
263
+ if resolved != self.root and self.root not in resolved.parents:
264
+ raise ScrollError("scroll source must remain inside the active Egregore workspace")
265
+ return resolved
266
+
267
+ def read(self, actor: ActorContext, slug: str) -> ScrollSnapshot:
268
+ normalized = _slug(slug)
269
+ canonical = self.canonical_path(normalized)
270
+ self._authorize(actor, Permission.READ, f"scroll:{normalized}", canonical)
271
+ path = self._path(normalized)
272
+ if not path.is_file():
273
+ raise ScrollError(f"scroll does not exist: {normalized}")
274
+ return parse_scroll_markdown(
275
+ path.read_text(encoding="utf-8"), slug=normalized, org_id=actor.profile.org_id
276
+ )
277
+
278
+ def _event_document(
279
+ self,
280
+ actor: ActorContext,
281
+ snapshot: ScrollSnapshot | None,
282
+ slug: str,
283
+ event: Mapping[str, Any],
284
+ now: datetime,
285
+ ) -> CanonicalDocument:
286
+ event_id = str(event.get("id") or "").strip()
287
+ if not event_id:
288
+ raise ScrollError("scroll event requires a deterministic id")
289
+ event_key = hashlib.sha256(event_id.encode("utf-8")).hexdigest()[:24]
290
+ normalized = _normalize(dict(event))
291
+ encoded = json.dumps(normalized, sort_keys=True, ensure_ascii=False, separators=(",", ":"))
292
+ body = f"# Scroll event: {event['type']}\n\n```json\n{encoded}\n```\n"
293
+ target_id = snapshot.document.artifact.artifact_id if snapshot else f"scroll:{slug}"
294
+ return CanonicalDocument(
295
+ artifact=CanonicalArtifact(
296
+ schema_version=ARTIFACT_SCHEMA_VERSION,
297
+ artifact_id=f"scroll-event:{slug}:{event_key}",
298
+ org_id=actor.profile.org_id,
299
+ artifact_type="lifecycle-event",
300
+ title=f"scroll {event['type']}: {slug}",
301
+ created_at=now,
302
+ created_by=actor.actor.actor_id,
303
+ status="accepted",
304
+ canonical_path=f"scrolls/.events/{slug}/{event_key}.md",
305
+ revision=f"sha256:{content_digest(body)[:16]}",
306
+ content_hash=content_digest(body),
307
+ relationships=(ArtifactRelationship("transitions", target_id),),
308
+ ),
309
+ body=body,
310
+ legacy_fields={
311
+ "lifecycle_action": f"scroll-{event['type']}",
312
+ "target_artifact_id": target_id,
313
+ "target_path": f"memory/scrolls/{slug}.md",
314
+ "domain_event_schema": SCROLL_EVENT_SCHEMA_VERSION,
315
+ "domain_event_id": event_id,
316
+ "domain_event": normalized,
317
+ },
318
+ )
319
+
320
+ def create(
321
+ self,
322
+ actor: ActorContext,
323
+ *,
324
+ title: str,
325
+ face_markdown: str,
326
+ source_html: str,
327
+ version_event: Mapping[str, Any],
328
+ slug: str | None = None,
329
+ storage_key: str,
330
+ respondents: Sequence[str] = (),
331
+ trusted_actor_ids: Sequence[str] = (),
332
+ review_after_days: int = 7,
333
+ attention_ttl_days: int = 30,
334
+ supersedes: Sequence[str] = (),
335
+ ) -> tuple[ScrollSnapshot, tuple[WritebackEvent, ...]]:
336
+ clean_title = " ".join(title.split()).strip()
337
+ if not clean_title or not face_markdown.strip():
338
+ raise ScrollError("scroll title and explanatory face are required")
339
+ normalized = _slug(slug or clean_title)
340
+ canonical = self.canonical_path(normalized)
341
+ self._authorize(actor, Permission.WRITE, f"scroll:{normalized}", canonical)
342
+ if self._path(normalized).exists():
343
+ raise ScrollError(f"scroll already exists: {normalized}")
344
+ html = self._source_path(source_html)
345
+ if not html.is_file():
346
+ raise ScrollError("scroll HTML source does not exist")
347
+ if min(review_after_days, attention_ttl_days) < 1:
348
+ raise ScrollError("scroll lifecycle windows must be positive days")
349
+ event = _normalize(dict(version_event))
350
+ event["type"] = "version-published"
351
+ event["id"] = "v1"
352
+ event["slug"] = normalized
353
+ event["storage_key"] = storage_key.strip()
354
+ event["respondents"] = list(dict.fromkeys(str(item) for item in respondents if str(item).strip()))
355
+ version = event.get("version") if isinstance(event.get("version"), Mapping) else event
356
+ if int(version.get("v") or 0) != 1:
357
+ raise ScrollError("initial Scroll event must publish v1")
358
+ now = self.clock().astimezone(UTC)
359
+ body = face_markdown
360
+ digest = content_digest(body)
361
+ artifact = CanonicalArtifact(
362
+ schema_version=ARTIFACT_SCHEMA_VERSION,
363
+ artifact_id=f"scroll:{normalized}",
364
+ org_id=actor.profile.org_id,
365
+ artifact_type="scroll",
366
+ title=clean_title,
367
+ created_at=now,
368
+ created_by=actor.actor.actor_id,
369
+ status="active",
370
+ canonical_path=f"scrolls/{normalized}.md",
371
+ revision=f"sha256:{digest[:16]}",
372
+ content_hash=digest,
373
+ supersedes=tuple(dict.fromkeys(item for item in supersedes if item)),
374
+ )
375
+ document = CanonicalDocument(
376
+ artifact=artifact,
377
+ body=body,
378
+ policy_hints={"living_document": True, "publication_requires_share": True},
379
+ legacy_fields={
380
+ "scroll_schema": SCROLL_SCHEMA_VERSION,
381
+ "slug": normalized,
382
+ "creator_id": actor.actor.actor_id,
383
+ "creator_alias": actor.actor.display_name,
384
+ "trusted_actor_ids": list(dict.fromkeys(item for item in trusted_actor_ids if item)),
385
+ "respondents": event["respondents"],
386
+ "version": 1,
387
+ "storage_key": storage_key.strip(),
388
+ "source_html": html.relative_to(self.root).as_posix(),
389
+ "review_at": _iso(now + timedelta(days=review_after_days)),
390
+ "attention_expires_at": _iso(now + timedelta(days=attention_ttl_days)),
391
+ "last_event_id": "v1",
392
+ },
393
+ )
394
+ snapshot = ScrollSnapshot(
395
+ normalized,
396
+ clean_title,
397
+ actor.actor.actor_id,
398
+ tuple(dict.fromkeys(item for item in trusted_actor_ids if item)),
399
+ tuple(event["respondents"]),
400
+ 1,
401
+ storage_key.strip(),
402
+ html.relative_to(self.root).as_posix(),
403
+ None,
404
+ document,
405
+ )
406
+ event_document = self._event_document(actor, snapshot, normalized, event, now)
407
+ receipts = self.runtime.write_documents(actor, (event_document, document))
408
+ return replace(snapshot, document=document), receipts
409
+
410
+ def _events(self, actor: ActorContext, slug: str) -> tuple[dict[str, Any], ...]:
411
+ snapshot = self.read(actor, slug)
412
+ directory = self.memory_root / "scrolls" / ".events" / snapshot.slug
413
+ decision = self.runtime.authorize(
414
+ actor, Permission.READ, (f"scroll-events:{snapshot.slug}",)
415
+ )
416
+ if not decision.allowed:
417
+ raise AuthorizationDenied(
418
+ "; ".join(decision.reasons) or "Scroll event collection read denied"
419
+ )
420
+ rows = []
421
+ if directory.is_dir():
422
+ for path in sorted(directory.glob("*.md")):
423
+ canonical = path.relative_to(self.memory_root).as_posix()
424
+ if not scope_allows_path(f"memory/{canonical}", decision.scopes):
425
+ raise AuthorizationDenied(
426
+ "Scroll event collection is outside the actor's authorized scope"
427
+ )
428
+ document = parse_canonical_markdown(
429
+ path.read_text(encoding="utf-8"),
430
+ canonical_path=canonical,
431
+ default_org_id=actor.profile.org_id,
432
+ )
433
+ event = document.legacy_fields.get("domain_event")
434
+ if isinstance(event, Mapping):
435
+ rows.append(dict(event))
436
+ return tuple(rows)
437
+
438
+ def append_event(
439
+ self,
440
+ actor: ActorContext,
441
+ *,
442
+ slug: str,
443
+ event: Mapping[str, Any],
444
+ expected_revision: str,
445
+ face_markdown: str | None = None,
446
+ source_html: str | None = None,
447
+ ) -> ScrollEventReceipt:
448
+ current = self.read(actor, slug)
449
+ self._authorize(actor, Permission.WRITE, f"scroll:{current.slug}", self.canonical_path(current.slug))
450
+ if current.revision != expected_revision:
451
+ raise ScrollError("scroll revision changed; rebuild from the event ledger before tending")
452
+ row = _normalize(dict(event))
453
+ kind = str(row.get("type") or "")
454
+ if kind not in SCROLL_EVENT_TYPES - {"share-published"}:
455
+ raise ScrollError("unsupported Scroll lifecycle event")
456
+ now = self.clock().astimezone(UTC)
457
+ disposition = "recorded"
458
+ if kind == "turn-received":
459
+ turn = dict(row.get("turn")) if isinstance(row.get("turn"), Mapping) else dict(row)
460
+ turn["id"] = scroll_turn_id(turn)
461
+ turn["merged_into"] = "pending"
462
+ row = {"type": kind, "id": turn["id"], "turn": turn, "submitted_by": actor.actor.actor_id}
463
+ disposition = (
464
+ "trusted"
465
+ if actor.actor.actor_id in {current.creator_id, *current.trusted_actor_ids}
466
+ else "creator-review-required"
467
+ )
468
+ elif kind == "turn-reviewed":
469
+ if actor.actor.actor_id != current.creator_id:
470
+ raise AuthorizationDenied("only the stable creator actor id may review Scroll turns")
471
+ turn_id = str(row.get("turn") or "").strip()
472
+ if not turn_id:
473
+ raise ScrollError("turn review requires a turn id")
474
+ review = dict(row.get("review")) if isinstance(row.get("review"), Mapping) else dict(row)
475
+ review["by"] = actor.actor.actor_id
476
+ review.setdefault("date", now.date().isoformat())
477
+ review.pop("type", None)
478
+ review.pop("id", None)
479
+ review.pop("turn", None)
480
+ if review.get("disposition") not in {"accepted", "declined"}:
481
+ raise ScrollError("turn review disposition must be accepted or declined")
482
+ overrides = review.get("answers") if isinstance(review.get("answers"), Mapping) else {}
483
+ has_decline = review["disposition"] == "declined" or "declined" in overrides.values()
484
+ if has_decline and not str(review.get("note") or "").strip():
485
+ raise ScrollError("a declined Scroll turn or answer requires a nonblank reason")
486
+ row = {"type": kind, "id": f"review|{turn_id}", "turn": turn_id, "review": review}
487
+ else:
488
+ if actor.actor.actor_id not in {current.creator_id, *current.trusted_actor_ids}:
489
+ raise AuthorizationDenied("Scroll version publication requires creator or trusted actor authority")
490
+ version = row.get("version") if isinstance(row.get("version"), Mapping) else row
491
+ next_version = int(version.get("v") or 0)
492
+ if next_version != current.version + 1:
493
+ raise ScrollError("Scroll versions must advance exactly once from the current ledger")
494
+ row["id"] = f"v{next_version}"
495
+ row["type"] = "version-published"
496
+ row["slug"] = current.slug
497
+ row["storage_key"] = current.storage_key
498
+ row["respondents"] = list(current.respondents)
499
+ if face_markdown is None or not face_markdown.strip():
500
+ raise ScrollError("a new Scroll version requires its complete rewritten face")
501
+ if source_html is None:
502
+ raise ScrollError("a new Scroll version requires its updated HTML source")
503
+ html = self._source_path(source_html)
504
+ if not html.is_file():
505
+ raise ScrollError("updated Scroll HTML source does not exist")
506
+ event_id = str(row.get("id") or "")
507
+ existing = {str(item.get("id") or ""): item for item in self._events(actor, current.slug)}
508
+ if event_id in existing:
509
+ if _normalize(existing[event_id]) != row:
510
+ raise ScrollError("Scroll event id already exists with different content")
511
+ return ScrollEventReceipt(current, event_id, disposition, ())
512
+ event_document = self._event_document(actor, current, current.slug, row, now)
513
+ fields = dict(current.document.legacy_fields)
514
+ fields["last_event_id"] = event_id
515
+ body = current.document.body
516
+ source_path = current.source_html
517
+ version_number = current.version
518
+ if kind == "version-published":
519
+ body = face_markdown or body
520
+ source_path = self._source_path(source_html or current.source_html).relative_to(self.root).as_posix()
521
+ version_number = current.version + 1
522
+ fields["version"] = version_number
523
+ fields["source_html"] = source_path
524
+ fields["reviewed_at"] = _iso(now)
525
+ digest = content_digest(body)
526
+ event_hash = hashlib.sha256(event_id.encode("utf-8")).hexdigest()[:8]
527
+ document = CanonicalDocument(
528
+ artifact=replace(
529
+ current.document.artifact,
530
+ revision=f"sha256:{digest[:16]}-{event_hash}",
531
+ content_hash=digest,
532
+ ),
533
+ body=body,
534
+ policy_hints=current.document.policy_hints,
535
+ legacy_fields=fields,
536
+ migrated_from=current.document.migrated_from,
537
+ warnings=current.document.warnings,
538
+ )
539
+ receipts = self.runtime.write_documents(actor, (event_document, document))
540
+ updated = replace(
541
+ current,
542
+ version=version_number,
543
+ source_html=source_path,
544
+ document=document,
545
+ )
546
+ return ScrollEventReceipt(updated, event_id, disposition, receipts)
547
+
548
+ def project_ledger(self, actor: ActorContext, slug: str) -> Mapping[str, Any]:
549
+ """Fold authorized canonical events through the existing deterministic engine."""
550
+
551
+ current = self.read(actor, slug)
552
+ events = self._events(actor, current.slug)
553
+ fold = self.root / ".claude" / "skills" / "scroll" / "assets" / "fold.mjs"
554
+ if not fold.is_file():
555
+ raise ScrollError("Scroll fold engine is unavailable")
556
+ node_runner = self.root / "bin" / "node-run.sh"
557
+ command = (
558
+ ["bash", str(node_runner), str(fold)]
559
+ if node_runner.is_file()
560
+ else ["node", str(fold)]
561
+ )
562
+ with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".jsonl", delete=False) as handle:
563
+ event_file = Path(handle.name)
564
+ os.chmod(event_file, 0o600)
565
+ for event in events:
566
+ handle.write(json.dumps(event, ensure_ascii=False, separators=(",", ":")) + "\n")
567
+ try:
568
+ result = subprocess.run(
569
+ [*command, str(event_file)],
570
+ cwd=self.root,
571
+ text=True,
572
+ capture_output=True,
573
+ check=False,
574
+ )
575
+ finally:
576
+ event_file.unlink(missing_ok=True)
577
+ if result.returncode != 0:
578
+ raise ScrollError(result.stderr.strip() or "Scroll ledger fold failed")
579
+ ledger = json.loads(result.stdout)
580
+ state_dir = self.root / ".egregore" / "runtime" / "scrolls" / current.slug
581
+ state_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
582
+ projection = state_dir / "ledger.json"
583
+ projection.write_text(json.dumps(ledger, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
584
+ os.chmod(projection, 0o600)
585
+ return ledger
586
+
587
+ def _connected(self) -> bool:
588
+ try:
589
+ config = json.loads((self.root / "egregore.json").read_text(encoding="utf-8"))
590
+ except (OSError, json.JSONDecodeError):
591
+ return False
592
+ return config.get("mode") == "connected" and bool(config.get("api_url"))
593
+
594
+ def plan_share(
595
+ self,
596
+ actor: ActorContext,
597
+ *,
598
+ slug: str,
599
+ html_path: str | None = None,
600
+ description: str = "",
601
+ ) -> ScrollSharePlan:
602
+ current = self.read(actor, slug)
603
+ self._authorize(actor, Permission.SHARE, f"scroll:{current.slug}", self.canonical_path(current.slug))
604
+ html = self._source_path(html_path or current.source_html)
605
+ if not html.is_file():
606
+ raise ScrollError("Scroll HTML source does not exist")
607
+ now = self.clock().astimezone(UTC)
608
+ html_hash = hashlib.sha256(html.read_bytes()).hexdigest()
609
+ material = {
610
+ "actor_id": actor.actor.actor_id,
611
+ "session_id": actor.session_id,
612
+ "slug": current.slug,
613
+ "expected_revision": current.revision,
614
+ "html_path": str(html),
615
+ "html_hash": html_hash,
616
+ "title": current.title,
617
+ "description": description,
618
+ "created_at": _iso(now),
619
+ }
620
+ digest = hashlib.sha256(
621
+ json.dumps(material, sort_keys=True, separators=(",", ":")).encode("utf-8")
622
+ ).hexdigest()
623
+ available = self._connected()
624
+ plan = ScrollSharePlan(
625
+ SCROLL_SHARE_SCHEMA_VERSION,
626
+ f"share-{digest[:20]}",
627
+ digest,
628
+ actor.actor.actor_id,
629
+ actor.session_id,
630
+ current.slug,
631
+ current.revision,
632
+ str(html),
633
+ html_hash,
634
+ current.title,
635
+ description,
636
+ _iso(now),
637
+ available,
638
+ None if available else "Local mode keeps the Scroll at its workspace source path.",
639
+ )
640
+ if available:
641
+ self.plan_root.mkdir(parents=True, exist_ok=True, mode=0o700)
642
+ plan_path = self.plan_root / f"{plan.plan_id}.json"
643
+ plan_path.write_text(json.dumps(plan.to_dict(), sort_keys=True) + "\n", encoding="utf-8")
644
+ os.chmod(plan_path, 0o600)
645
+ return plan
646
+
647
+ def dispatch_share(
648
+ self,
649
+ actor: ActorContext,
650
+ *,
651
+ plan_id: str,
652
+ digest: str,
653
+ confirmation: str,
654
+ ) -> ScrollShareReceipt:
655
+ if confirmation != EXACT_SHARE_CONFIRMATION:
656
+ raise ScrollError("Scroll publication requires exact SHARE confirmation")
657
+ plan_path = self.plan_root / f"{plan_id}.json"
658
+ try:
659
+ payload = json.loads(plan_path.read_text(encoding="utf-8"))
660
+ except (OSError, json.JSONDecodeError) as exc:
661
+ raise ScrollError("Scroll SHARE plan is missing or invalid; create a fresh plan") from exc
662
+ plan = ScrollSharePlan(**payload)
663
+ if plan.digest != digest or plan.plan_id != plan_id:
664
+ raise ScrollError("Scroll SHARE approval does not match the exact plan")
665
+ if plan.actor_id != actor.actor.actor_id or plan.session_id != actor.session_id:
666
+ raise AuthorizationDenied("Scroll SHARE plan belongs to another actor or session")
667
+ current = self.read(actor, plan.slug)
668
+ self._authorize(actor, Permission.SHARE, f"scroll:{current.slug}", self.canonical_path(current.slug))
669
+ if current.revision != plan.expected_revision:
670
+ raise ScrollError("Scroll changed after preview; create and approve a fresh SHARE plan")
671
+ html = self._source_path(plan.html_path)
672
+ if hashlib.sha256(html.read_bytes()).hexdigest() != plan.html_hash:
673
+ raise ScrollError("Scroll HTML changed after preview; create and approve a fresh SHARE plan")
674
+ url = self.publisher(current, html, plan.description).strip()
675
+ if not url:
676
+ raise ScrollError("Scroll publisher returned no stable URL")
677
+ now = self.clock().astimezone(UTC)
678
+ event = {
679
+ "type": "share-published",
680
+ "id": f"share|{plan.digest}",
681
+ "url": url,
682
+ "scroll_revision": current.revision,
683
+ "by": actor.actor.actor_id,
684
+ "date": _iso(now),
685
+ }
686
+ event_document = self._event_document(actor, current, current.slug, event, now)
687
+ fields = dict(current.document.legacy_fields)
688
+ fields.update({"published_url": url, "last_shared_revision": current.revision})
689
+ document = CanonicalDocument(
690
+ artifact=replace(
691
+ current.document.artifact,
692
+ revision=f"{current.revision}-shared-{plan.digest[:8]}",
693
+ ),
694
+ body=current.document.body,
695
+ policy_hints=current.document.policy_hints,
696
+ legacy_fields=fields,
697
+ )
698
+ receipts = self.runtime.write_documents(actor, (event_document, document))
699
+ plan_path.unlink(missing_ok=True)
700
+ return ScrollShareReceipt(plan.plan_id, current.slug, url, receipts)
701
+
702
+ def _publish(self, snapshot: ScrollSnapshot, html: Path, description: str) -> str:
703
+ result = subprocess.run(
704
+ [
705
+ "bash",
706
+ str(self.root / "bin" / "publish-artifact.sh"),
707
+ "document",
708
+ str(html),
709
+ "--raw-html",
710
+ "--id",
711
+ snapshot.slug,
712
+ "--title",
713
+ snapshot.title,
714
+ "--author",
715
+ snapshot.creator_id,
716
+ "--description",
717
+ description,
718
+ ],
719
+ cwd=self.root,
720
+ text=True,
721
+ capture_output=True,
722
+ check=False,
723
+ )
724
+ if result.returncode != 0:
725
+ raise ScrollError(result.stderr.strip() or "Scroll publication failed")
726
+ return result.stdout.strip().splitlines()[-1] if result.stdout.strip() else ""