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,523 @@
1
+ """Canonical Markdown artifact envelope, compatibility reader, and local store.
2
+
3
+ Markdown remains the canonical organizational state. This module standardizes
4
+ only the small machine-readable envelope around that Markdown; type-specific
5
+ payloads and lifecycles remain owned by their rituals (for example, Scrolls
6
+ remain mutable living documents while handoffs are append-only captures).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hashlib
12
+ import json
13
+ import os
14
+ import re
15
+ import unicodedata
16
+ from dataclasses import dataclass, field, replace
17
+ from datetime import UTC, date, datetime
18
+ from pathlib import Path, PurePosixPath
19
+ from typing import Any, Mapping, Protocol, runtime_checkable
20
+
21
+ from .contracts import ArtifactRelationship, CanonicalArtifact, SourceProvenance
22
+
23
+
24
+ ARTIFACT_SCHEMA_VERSION = "egregore-artifact/v1"
25
+ _FRONTMATTER_KEY = re.compile(r"^([A-Za-z][A-Za-z0-9_-]*):(?:[ \t]*(.*))?$")
26
+ # Existing hosted artifact ids are URL-safe opaque tokens and may begin with
27
+ # ``-`` or ``_``. Keep those stable during explicit v1 migration.
28
+ _OPAQUE_ID = re.compile(r"^[A-Za-z0-9_-][A-Za-z0-9._:/-]{2,255}$")
29
+
30
+
31
+ class ArtifactSchemaError(ValueError):
32
+ """A canonical artifact envelope is invalid or cannot be migrated safely."""
33
+
34
+
35
+ class ArtifactConflictError(FileExistsError):
36
+ """An append-only artifact path already contains a different artifact."""
37
+
38
+
39
+ @dataclass(frozen=True, slots=True)
40
+ class CanonicalDocument:
41
+ """A canonical artifact plus its Markdown body and advisory policy hints."""
42
+
43
+ artifact: CanonicalArtifact
44
+ body: str
45
+ policy_hints: Mapping[str, Any] = field(default_factory=dict)
46
+ legacy_fields: Mapping[str, Any] = field(default_factory=dict)
47
+ migrated_from: str | None = None
48
+ warnings: tuple[str, ...] = ()
49
+
50
+
51
+ @runtime_checkable
52
+ class CanonicalArtifactStore(Protocol):
53
+ """Persistence seam for canonical Markdown, independent of Git transport."""
54
+
55
+ def persist(self, document: CanonicalDocument) -> Path: ...
56
+
57
+ def read(self, canonical_path: str) -> CanonicalDocument: ...
58
+
59
+
60
+ def normalize_markdown_body(content: str) -> str:
61
+ """Return a deterministic UTF-8 Markdown body with LF endings and one EOF LF."""
62
+
63
+ normalized = unicodedata.normalize("NFC", content.replace("\r\n", "\n").replace("\r", "\n"))
64
+ return normalized.rstrip("\n") + "\n"
65
+
66
+
67
+ def content_digest(content: str) -> str:
68
+ return hashlib.sha256(normalize_markdown_body(content).encode("utf-8")).hexdigest()
69
+
70
+
71
+ def canonical_relative_path(raw_path: str) -> str:
72
+ """Normalize a repo-relative path and reject traversal/absolute targets."""
73
+
74
+ value = raw_path.strip().replace("\\", "/")
75
+ while value.startswith("./"):
76
+ value = value[2:]
77
+ value = re.sub(r"/{2,}", "/", value)
78
+ candidate = PurePosixPath(value)
79
+ if not value or candidate.is_absolute() or ".." in candidate.parts:
80
+ raise ArtifactSchemaError("canonical_path must be a non-empty relative path")
81
+ return candidate.as_posix()
82
+
83
+
84
+ def artifact_id_from_path(raw_path: str) -> str:
85
+ """Match ``bin/lib/artifact-id.sh`` and the JavaScript artifact package."""
86
+
87
+ canonical = canonical_relative_path(raw_path)
88
+ extension = PurePosixPath(canonical).suffix.lower()
89
+ prefix = {".md": "m", ".html": "h"}.get(extension)
90
+ if prefix is None:
91
+ raise ArtifactSchemaError(f"unsupported canonical artifact extension: {extension or '(none)'}")
92
+ digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()[:12]
93
+ return f"{prefix}-{digest}"
94
+
95
+
96
+ def canonical_artifact_id(markdown: str, canonical_path: str) -> str:
97
+ """Resolve one stable artifact id for retrieval and direct source opens.
98
+
99
+ Typed/hosted artifacts keep their declared opaque id. Historical Markdown
100
+ without one uses the established deterministic path id. Keeping this in the
101
+ domain layer prevents retrieval adapters and harness opens from inventing
102
+ different identities for the same canonical document.
103
+ """
104
+
105
+ try:
106
+ fields, _ = split_frontmatter(markdown)
107
+ except ArtifactSchemaError:
108
+ fields = {}
109
+ for key in ("artifact_id", "id", "session_id", "slug"):
110
+ value = fields.get(key)
111
+ if value is None:
112
+ continue
113
+ candidate = str(value).strip()
114
+ if _OPAQUE_ID.fullmatch(candidate):
115
+ return candidate
116
+ relative = canonical_relative_path(canonical_path).removeprefix("memory/")
117
+ return artifact_id_from_path(relative)
118
+
119
+
120
+ def _json_value(value: Any) -> str:
121
+ return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
122
+
123
+
124
+ def _parse_scalar(raw: str) -> Any:
125
+ value = raw.strip()
126
+ if not value:
127
+ return None
128
+ try:
129
+ return json.loads(value)
130
+ except json.JSONDecodeError:
131
+ pass
132
+ if value.startswith("[") and value.endswith("]"):
133
+ inner = value[1:-1].strip()
134
+ if not inner:
135
+ return []
136
+ return [part.strip().strip("\"'") for part in inner.split(",")]
137
+ if len(value) >= 2 and value[0] == value[-1] == "'":
138
+ return value[1:-1].replace("''", "'")
139
+ lowered = value.lower()
140
+ if lowered in {"true", "false"}:
141
+ return lowered == "true"
142
+ if lowered in {"null", "~"}:
143
+ return None
144
+ # Deliberately retain plain values verbatim. In particular, a title such
145
+ # as ``Launch: architecture`` is one scalar, not malformed YAML.
146
+ return value
147
+
148
+
149
+ def split_frontmatter(markdown: str) -> tuple[dict[str, Any], str]:
150
+ """Parse the deterministic JSON-in-YAML subset used by v1 and legacy scalars.
151
+
152
+ JSON values are valid YAML and let the runtime round-trip nested provenance
153
+ without a YAML dependency. Unknown indented legacy fields are ignored and
154
+ preserved in the original file until an explicit migration/write occurs.
155
+ """
156
+
157
+ normalized = markdown.replace("\r\n", "\n").replace("\r", "\n")
158
+ if not normalized.startswith("---\n"):
159
+ return {}, normalized
160
+ closing = normalized.find("\n---\n", 4)
161
+ if closing < 0:
162
+ raise ArtifactSchemaError("incomplete frontmatter block")
163
+ raw_header = normalized[4:closing]
164
+ result: dict[str, Any] = {}
165
+ for line_number, line in enumerate(raw_header.splitlines(), 2):
166
+ if not line.strip() or line.lstrip().startswith("#") or line[:1].isspace():
167
+ continue
168
+ match = _FRONTMATTER_KEY.match(line)
169
+ if match is None:
170
+ raise ArtifactSchemaError(f"invalid frontmatter line {line_number}")
171
+ key, raw = match.groups()
172
+ if key in result:
173
+ raise ArtifactSchemaError(f"duplicate frontmatter key: {key}")
174
+ result[key] = _parse_scalar(raw or "")
175
+ return result, normalized[closing + 5 :]
176
+
177
+
178
+ def _as_strings(value: Any) -> tuple[str, ...]:
179
+ if value is None:
180
+ return ()
181
+ if isinstance(value, str):
182
+ return (value,) if value.strip() else ()
183
+ if isinstance(value, (list, tuple, set)):
184
+ return tuple(str(item) for item in value if str(item).strip())
185
+ raise ArtifactSchemaError("expected a string or list of strings")
186
+
187
+
188
+ def _datetime(value: Any, *, field_name: str) -> datetime:
189
+ if isinstance(value, datetime):
190
+ parsed = value
191
+ elif isinstance(value, date):
192
+ parsed = datetime(value.year, value.month, value.day, tzinfo=UTC)
193
+ elif isinstance(value, str) and value.strip():
194
+ raw = value.strip()
195
+ if re.fullmatch(r"\d{4}-\d{2}-\d{2}", raw):
196
+ parsed = datetime.fromisoformat(raw).replace(tzinfo=UTC)
197
+ else:
198
+ try:
199
+ parsed = datetime.fromisoformat(raw.replace("Z", "+00:00"))
200
+ except ValueError as exc:
201
+ raise ArtifactSchemaError(f"{field_name} must be ISO-8601") from exc
202
+ else:
203
+ raise ArtifactSchemaError(f"{field_name} is required")
204
+ if parsed.tzinfo is None:
205
+ parsed = parsed.replace(tzinfo=UTC)
206
+ return parsed.astimezone(UTC)
207
+
208
+
209
+ def _title(fields: Mapping[str, Any], body: str, canonical_path: str) -> str:
210
+ explicit = fields.get("title") or fields.get("topic") or fields.get("name")
211
+ if explicit:
212
+ return str(explicit).strip()
213
+ heading = re.search(r"^#\s+(.+?)\s*$", body, re.MULTILINE)
214
+ if heading:
215
+ return heading.group(1).strip()
216
+ return PurePosixPath(canonical_path).stem.replace("-", " ").strip()
217
+
218
+
219
+ def _infer_type(fields: Mapping[str, Any], canonical_path: str) -> str:
220
+ explicit = fields.get("type") or fields.get("artifact_type")
221
+ if explicit:
222
+ return str(explicit)
223
+ kind = fields.get("kind") or fields.get("capture_mode")
224
+ if kind == "addressed":
225
+ return "handoff"
226
+ parts = set(PurePosixPath(canonical_path).parts)
227
+ for directory, artifact_type in (
228
+ ("handoffs", "handoff"),
229
+ ("decisions", "decision"),
230
+ ("reflections", "reflection"),
231
+ ("wraps", "reflection"),
232
+ ("sessions", "session"),
233
+ ("threads", "thread"),
234
+ ("scrolls", "scroll"),
235
+ ("quests", "quest"),
236
+ ("questions", "question"),
237
+ ("issues", "issue"),
238
+ ("todos", "todo"),
239
+ ("ingest", "ingest-document"),
240
+ ):
241
+ if directory in parts:
242
+ return artifact_type
243
+ return "artifact"
244
+
245
+
246
+ def _relationships(value: Any) -> tuple[ArtifactRelationship, ...]:
247
+ if value is None:
248
+ return ()
249
+ if not isinstance(value, (list, tuple)):
250
+ raise ArtifactSchemaError("relationships must be a list")
251
+ rows: list[ArtifactRelationship] = []
252
+ for item in value:
253
+ if isinstance(item, str):
254
+ rows.append(ArtifactRelationship("related-to", item))
255
+ elif isinstance(item, Mapping):
256
+ relation = item.get("relation") or item.get("type")
257
+ target = item.get("target_id") or item.get("target") or item.get("id")
258
+ if not relation or not target:
259
+ raise ArtifactSchemaError("each relationship needs relation and target_id")
260
+ rows.append(ArtifactRelationship(str(relation), str(target)))
261
+ else:
262
+ raise ArtifactSchemaError("invalid relationship")
263
+ return tuple(rows)
264
+
265
+
266
+ def _provenance(value: Any) -> tuple[SourceProvenance, ...]:
267
+ if value is None:
268
+ return ()
269
+ candidates = value if isinstance(value, (list, tuple)) else [value]
270
+ rows: list[SourceProvenance] = []
271
+ for item in candidates:
272
+ if not isinstance(item, Mapping):
273
+ continue
274
+ required = ("source_type", "source_id", "revision", "content_hash", "observed_at")
275
+ if not all(item.get(key) is not None for key in required):
276
+ # Historical ingest provenance was an open evidence object. It is
277
+ # preserved as a legacy field, but cannot masquerade as typed v1
278
+ # provenance without its source identity and revision.
279
+ continue
280
+ rows.append(
281
+ SourceProvenance(
282
+ source_type=str(item["source_type"]),
283
+ source_id=str(item["source_id"]),
284
+ revision=str(item["revision"]),
285
+ content_hash=str(item["content_hash"]),
286
+ observed_at=_datetime(item["observed_at"], field_name="provenance.observed_at"),
287
+ source_uri=str(item["source_uri"]) if item.get("source_uri") else None,
288
+ imported_by=str(item["imported_by"]) if item.get("imported_by") else None,
289
+ )
290
+ )
291
+ return tuple(rows)
292
+
293
+
294
+ def validate_artifact(artifact: CanonicalArtifact, body: str) -> CanonicalArtifact:
295
+ """Validate the common v1 envelope and return its normalized form."""
296
+
297
+ if artifact.schema_version != ARTIFACT_SCHEMA_VERSION:
298
+ raise ArtifactSchemaError(f"unsupported artifact schema: {artifact.schema_version}")
299
+ canonical_path = canonical_relative_path(artifact.canonical_path)
300
+ if not _OPAQUE_ID.fullmatch(artifact.artifact_id):
301
+ raise ArtifactSchemaError("artifact_id must be an opaque non-empty identifier")
302
+ for name, value in (
303
+ ("org_id", artifact.org_id),
304
+ ("artifact_type", artifact.artifact_type),
305
+ ("title", artifact.title),
306
+ ("created_by", artifact.created_by),
307
+ ("status", artifact.status),
308
+ ("revision", artifact.revision),
309
+ ):
310
+ if not value or not str(value).strip():
311
+ raise ArtifactSchemaError(f"{name} is required")
312
+ if artifact.created_at.tzinfo is None:
313
+ raise ArtifactSchemaError("created_at must include a timezone")
314
+ actual_hash = content_digest(body)
315
+ if artifact.content_hash not in {actual_hash, f"sha256:{actual_hash}"}:
316
+ raise ArtifactSchemaError("content_hash does not match the normalized Markdown body")
317
+ if any(not row.relation or not row.target_id for row in artifact.relationships):
318
+ raise ArtifactSchemaError("relationships require relation and target_id")
319
+ return replace(
320
+ artifact,
321
+ canonical_path=canonical_path,
322
+ created_at=artifact.created_at.astimezone(UTC),
323
+ content_hash=actual_hash,
324
+ )
325
+
326
+
327
+ def parse_canonical_markdown(
328
+ markdown: str,
329
+ *,
330
+ canonical_path: str,
331
+ default_org_id: str | None = None,
332
+ ) -> CanonicalDocument:
333
+ """Read v1 Markdown or migrate a legacy artifact into the common envelope."""
334
+
335
+ path = canonical_relative_path(canonical_path)
336
+ fields, raw_body = split_frontmatter(markdown)
337
+ body = normalize_markdown_body(raw_body)
338
+ schema = fields.get("schema_version") or fields.get("schema")
339
+ is_v1 = schema == ARTIFACT_SCHEMA_VERSION
340
+ if is_v1:
341
+ required = (
342
+ "id", "org", "type", "title", "created_at", "created_by", "status",
343
+ "canonical_path", "revision", "content_hash",
344
+ )
345
+ missing = [key for key in required if fields.get(key) in (None, "")]
346
+ if missing:
347
+ raise ArtifactSchemaError(f"v1 frontmatter is missing: {', '.join(missing)}")
348
+ declared_path = canonical_relative_path(str(fields["canonical_path"]))
349
+ if declared_path != path:
350
+ raise ArtifactSchemaError("canonical_path does not match the opened source path")
351
+ org_id = fields.get("org") or fields.get("org_id") or default_org_id
352
+ if not org_id:
353
+ raise ArtifactSchemaError("org is required (or supply default_org_id for legacy artifacts)")
354
+ artifact_id = fields.get("id") or fields.get("artifact_id") or artifact_id_from_path(path)
355
+ created_raw = (
356
+ fields.get("created_at")
357
+ or fields.get("date")
358
+ or fields.get("ingested_at")
359
+ or fields.get("started")
360
+ )
361
+ if created_raw is None:
362
+ raise ArtifactSchemaError("created_at is required and could not be migrated")
363
+ created_by = (
364
+ fields.get("created_by")
365
+ or fields.get("actor")
366
+ or fields.get("author")
367
+ or fields.get("from")
368
+ or fields.get("started_by")
369
+ )
370
+ if not created_by:
371
+ raise ArtifactSchemaError("created_by is required and could not be migrated")
372
+
373
+ relationships_value = fields.get("relationships")
374
+ if relationships_value is None:
375
+ legacy_related = fields.get("related") or fields.get("references")
376
+ relationships_value = _as_strings(legacy_related) if legacy_related else ()
377
+ provenance = _provenance(fields.get("source_provenance") or fields.get("provenance"))
378
+ if not provenance and fields.get("source_id") and fields.get("content_hash"):
379
+ provenance = (
380
+ SourceProvenance(
381
+ source_type=str(fields.get("source_type") or "ingest"),
382
+ source_id=str(fields["source_id"]),
383
+ revision=str(fields.get("source_revision") or fields.get("revision") or fields["content_hash"]),
384
+ content_hash=str(fields["content_hash"]),
385
+ observed_at=_datetime(created_raw, field_name="created_at"),
386
+ source_uri=str(fields["source_path"]) if fields.get("source_path") else None,
387
+ imported_by=str(created_by),
388
+ ),
389
+ )
390
+
391
+ actual_hash = content_digest(body)
392
+ artifact = CanonicalArtifact(
393
+ schema_version=ARTIFACT_SCHEMA_VERSION,
394
+ artifact_id=str(artifact_id),
395
+ org_id=str(org_id),
396
+ artifact_type=_infer_type(fields, path),
397
+ title=_title(fields, body, path),
398
+ created_at=_datetime(created_raw, field_name="created_at"),
399
+ created_by=str(created_by),
400
+ status=str(fields.get("status") or ("unverified" if _infer_type(fields, path) == "ingest-document" else "active")),
401
+ canonical_path=path,
402
+ revision=str(fields.get("revision") or f"sha256:{actual_hash[:16]}"),
403
+ content_hash=str(fields["content_hash"]) if is_v1 else actual_hash,
404
+ workstream=str(fields.get("workstream") or fields.get("topic") or fields.get("project"))
405
+ if fields.get("workstream") or fields.get("topic") or fields.get("project")
406
+ else None,
407
+ visibility=_as_strings(fields.get("visibility")),
408
+ relationships=_relationships(relationships_value),
409
+ supersedes=_as_strings(fields.get("supersedes")),
410
+ provenance=provenance,
411
+ )
412
+ artifact = validate_artifact(artifact, body)
413
+
414
+ common_keys = {
415
+ "schema_version", "schema", "id", "artifact_id", "org", "org_id", "type",
416
+ "artifact_type", "title", "created_at", "created_by", "status", "revision",
417
+ "content_hash", "workstream", "visibility", "relationships", "supersedes",
418
+ "source_provenance", "policy_hints", "canonical_path",
419
+ }
420
+ policy_hints = fields.get("policy_hints") if isinstance(fields.get("policy_hints"), Mapping) else {}
421
+ policy_hints = dict(policy_hints)
422
+ for key in ("admin", "boundaries"):
423
+ if key in fields:
424
+ policy_hints[key] = fields[key]
425
+ warnings = () if is_v1 else ("legacy frontmatter migrated in memory; source file was not rewritten",)
426
+ return CanonicalDocument(
427
+ artifact=artifact,
428
+ body=body,
429
+ policy_hints=policy_hints,
430
+ legacy_fields={key: value for key, value in fields.items() if key not in common_keys},
431
+ migrated_from=None if is_v1 else str(schema or "unversioned"),
432
+ warnings=warnings,
433
+ )
434
+
435
+
436
+ def render_canonical_markdown(document: CanonicalDocument) -> str:
437
+ """Serialize a canonical document as deterministic, parseable Markdown."""
438
+
439
+ artifact = validate_artifact(document.artifact, document.body)
440
+ fields: list[tuple[str, Any]] = [
441
+ ("schema_version", artifact.schema_version),
442
+ ("id", artifact.artifact_id),
443
+ ("org", artifact.org_id),
444
+ ("type", artifact.artifact_type),
445
+ ("title", artifact.title),
446
+ ("created_at", artifact.created_at.isoformat().replace("+00:00", "Z")),
447
+ ("created_by", artifact.created_by),
448
+ ("workstream", artifact.workstream),
449
+ ("status", artifact.status),
450
+ ("relationships", [row.to_dict() for row in artifact.relationships]),
451
+ ("supersedes", list(artifact.supersedes)),
452
+ ("source_provenance", [row.to_dict() for row in artifact.provenance]),
453
+ ("visibility", list(artifact.visibility)),
454
+ ("policy_hints", dict(document.policy_hints)),
455
+ ("canonical_path", artifact.canonical_path),
456
+ ("revision", artifact.revision),
457
+ ("content_hash", artifact.content_hash),
458
+ ]
459
+ common_names = {key for key, _ in fields}
460
+ fields.extend(
461
+ (key, value)
462
+ for key, value in document.legacy_fields.items()
463
+ if key not in common_names and value is not None
464
+ )
465
+ header = ["---", *(f"{key}: {_json_value(value)}" for key, value in fields if value not in (None, (), [], {})), "---"]
466
+ return "\n".join(header) + "\n" + normalize_markdown_body(document.body)
467
+
468
+
469
+ class LocalMarkdownArtifactStore:
470
+ """Atomic filesystem store with explicit append-only vs living lifecycles."""
471
+
472
+ def __init__(
473
+ self,
474
+ root: Path,
475
+ *,
476
+ mutable_types: frozenset[str] = frozenset(
477
+ {"scroll", "thread", "question", "quest", "todo", "issue"}
478
+ ),
479
+ ):
480
+ self.root = root.resolve()
481
+ self.mutable_types = mutable_types
482
+
483
+ def _target(self, canonical_path: str) -> Path:
484
+ relative = canonical_relative_path(canonical_path)
485
+ target = (self.root / relative).resolve()
486
+ if target != self.root and self.root not in target.parents:
487
+ raise ArtifactSchemaError("canonical path escapes the artifact store")
488
+ return target
489
+
490
+ def persist(self, document: CanonicalDocument) -> Path:
491
+ artifact = validate_artifact(document.artifact, document.body)
492
+ target = self._target(artifact.canonical_path)
493
+ if target.exists():
494
+ # Legacy sources may not yet declare organization identity. The
495
+ # authorized incoming document supplies the active organization
496
+ # for this one-time typed migration; ordinary reads remain strict.
497
+ existing = parse_canonical_markdown(
498
+ target.read_text(encoding="utf-8"),
499
+ canonical_path=artifact.canonical_path,
500
+ default_org_id=artifact.org_id,
501
+ )
502
+ if existing.artifact.artifact_id != artifact.artifact_id:
503
+ raise ArtifactConflictError("artifact path belongs to a different stable id")
504
+ if artifact.artifact_type not in self.mutable_types:
505
+ if existing.artifact.content_hash == artifact.content_hash:
506
+ # An explicit writeback of a legacy document is its safe,
507
+ # typed migration point. Preserve ritual-specific fields.
508
+ if document.migrated_from is None:
509
+ return target
510
+ else:
511
+ raise ArtifactConflictError(f"{artifact.artifact_type} artifacts are append-only")
512
+ target.parent.mkdir(parents=True, exist_ok=True)
513
+ temporary = target.with_name(f".{target.name}.{os.getpid()}.tmp")
514
+ temporary.write_text(render_canonical_markdown(replace(document, artifact=artifact)), encoding="utf-8")
515
+ temporary.replace(target)
516
+ return target
517
+
518
+ def read(self, canonical_path: str) -> CanonicalDocument:
519
+ target = self._target(canonical_path)
520
+ return parse_canonical_markdown(
521
+ target.read_text(encoding="utf-8"),
522
+ canonical_path=canonical_relative_path(canonical_path),
523
+ )