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,888 @@
1
+ """Canonical temporal-authority and artifact-lifecycle projection.
2
+
3
+ Markdown and Git remain durable truth. Lifecycle state is derived from artifact
4
+ frontmatter plus append-only lifecycle-event artifacts; this module never
5
+ rewrites or deletes the artifact whose state it projects.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import asdict, dataclass
11
+ from datetime import UTC, datetime, timedelta
12
+ from enum import StrEnum
13
+ import hashlib
14
+ import json
15
+ import os
16
+ from pathlib import Path
17
+ import re
18
+ import subprocess
19
+ from typing import Any, Callable, Iterable, Mapping, Sequence
20
+ from uuid import uuid4
21
+
22
+ from .artifacts import (
23
+ ARTIFACT_SCHEMA_VERSION,
24
+ CanonicalDocument,
25
+ canonical_artifact_id,
26
+ content_digest,
27
+ split_frontmatter,
28
+ )
29
+ from .contracts import (
30
+ ActorContext,
31
+ ArtifactRelationship,
32
+ CanonicalArtifact,
33
+ Serializable,
34
+ )
35
+ from .policy import scope_allows_path
36
+
37
+
38
+ LIFECYCLE_PLAN_SCHEMA = "egregore-lifecycle-plan/v1"
39
+ LIFECYCLE_EVENT_SCHEMA = "egregore-lifecycle-event/v1"
40
+ LIFECYCLE_INDEX_SPEC = "egregore-lifecycle-projection/v1"
41
+
42
+
43
+ class AuthorityState(StrEnum):
44
+ CURRENT = "current"
45
+ SUPERSEDED = "superseded"
46
+ HISTORICAL = "historical"
47
+ AMBIGUOUS = "ambiguous"
48
+
49
+
50
+ class AttentionState(StrEnum):
51
+ ACTIVE = "active"
52
+ REVIEW_DUE = "review_due"
53
+ EXPIRED = "expired"
54
+
55
+
56
+ class ObligationState(StrEnum):
57
+ NONE = "none"
58
+ PENDING = "pending"
59
+ READ = "read"
60
+ CLAIMED = "claimed"
61
+ COMPLETED = "completed"
62
+ REVIEW_REQUIRED = "review_required"
63
+
64
+
65
+ class LifecycleAction(StrEnum):
66
+ ACKNOWLEDGE = "acknowledge"
67
+ CLAIM = "claim"
68
+ COMPLETE = "complete"
69
+ EXPIRE_ATTENTION = "expire_attention"
70
+ REOPEN = "reopen"
71
+ REQUEST_REVIEW = "request_review"
72
+
73
+
74
+ class LifecycleError(ValueError):
75
+ """A requested lifecycle transition is invalid or lacks evidence."""
76
+
77
+
78
+ @dataclass(frozen=True, slots=True)
79
+ class LifecyclePolicy(Serializable):
80
+ schema_version: str = "egregore-lifecycle-policy/v1"
81
+ review_after_days: int = 7
82
+ handoff_ttl_days: int = 14
83
+ fyi_expiry_days: int = 14
84
+ retention_days: int | None = None
85
+
86
+
87
+ @dataclass(frozen=True, slots=True)
88
+ class LifecycleRecord(Serializable):
89
+ artifact_id: str
90
+ canonical_path: str
91
+ artifact_type: str
92
+ title: str
93
+ created_at: datetime
94
+ created_by: str
95
+ declared_status: str
96
+ authority_state: AuthorityState
97
+ attention_state: AttentionState
98
+ obligation_state: ObligationState
99
+ current_authority: bool
100
+ lifecycle_revision: str
101
+ age_days: int
102
+ created_by_alias: str | None = None
103
+ intent: str | None = None
104
+ recipients: tuple[str, ...] = ()
105
+ superseded_by: tuple[str, ...] = ()
106
+ review_at: datetime | None = None
107
+ attention_expires_at: datetime | None = None
108
+ retention_until: datetime | None = None
109
+ terminal_reason: str | None = None
110
+ recommendation: str = "keep"
111
+ automatic_transition_safe: bool = False
112
+ evidence_ids: tuple[str, ...] = ()
113
+ warnings: tuple[str, ...] = ()
114
+
115
+
116
+ @dataclass(frozen=True, slots=True)
117
+ class LifecyclePlan(Serializable):
118
+ schema_version: str
119
+ index_spec_version: str
120
+ snapshot_id: str
121
+ generated_at: datetime
122
+ source_revision: str
123
+ records: tuple[LifecycleRecord, ...]
124
+ warnings: tuple[str, ...] = ()
125
+
126
+ @property
127
+ def by_id(self) -> dict[str, LifecycleRecord]:
128
+ return {record.artifact_id: record for record in self.records}
129
+
130
+
131
+ @dataclass(frozen=True, slots=True)
132
+ class LifecycleTransition(Serializable):
133
+ action: LifecycleAction
134
+ target_artifact_id: str
135
+ target_path: str
136
+ previous_obligation: ObligationState
137
+ new_obligation: ObligationState
138
+ previous_attention: AttentionState
139
+ new_attention: AttentionState
140
+ expected_lifecycle_revision: str
141
+ reason: str
142
+ evidence_ids: tuple[str, ...]
143
+ automatic: bool
144
+ document: CanonicalDocument
145
+
146
+
147
+ @dataclass(frozen=True, slots=True)
148
+ class _Snapshot:
149
+ artifact_id: str
150
+ canonical_path: str
151
+ artifact_type: str
152
+ title: str
153
+ created_at: datetime
154
+ created_by: str
155
+ created_by_alias: str | None
156
+ status: str
157
+ intent: str | None
158
+ recipients: tuple[str, ...]
159
+ supersedes: tuple[str, ...]
160
+ review_at: datetime | None
161
+ attention_expires_at: datetime | None
162
+ retention_until: datetime | None
163
+ terminal_reason: str | None
164
+ lifecycle_action: str | None = None
165
+ target_artifact_id: str | None = None
166
+ target_path: str | None = None
167
+ transition_state: str | None = None
168
+ transition_attention: str | None = None
169
+ transition_recipient: str | None = None
170
+ expected_lifecycle_revision: str | None = None
171
+ transition_reason: str | None = None
172
+ transition_evidence: tuple[str, ...] = ()
173
+ transition_automatic: bool = False
174
+ digest: str = ""
175
+
176
+
177
+ def _strings(value: Any) -> tuple[str, ...]:
178
+ if value is None:
179
+ return ()
180
+ if isinstance(value, str):
181
+ stripped = value.strip()
182
+ if not stripped:
183
+ return ()
184
+ if "," in stripped:
185
+ return tuple(part.strip() for part in stripped.split(",") if part.strip())
186
+ return (stripped,)
187
+ if isinstance(value, Mapping):
188
+ return tuple(str(item) for item in value.values() if str(item).strip())
189
+ if isinstance(value, Iterable):
190
+ return tuple(str(item) for item in value if str(item).strip())
191
+ return (str(value),)
192
+
193
+
194
+ def _datetime(value: Any) -> datetime | None:
195
+ if isinstance(value, datetime):
196
+ parsed = value
197
+ elif not isinstance(value, str) or not value.strip():
198
+ return None
199
+ else:
200
+ raw = value.strip()
201
+ try:
202
+ parsed = datetime.fromisoformat(raw.replace("Z", "+00:00"))
203
+ except ValueError:
204
+ return None
205
+ if parsed.tzinfo is None:
206
+ parsed = parsed.replace(tzinfo=UTC)
207
+ return parsed.astimezone(UTC)
208
+
209
+
210
+ def _field_datetime(fields: Mapping[str, Any], *names: str) -> datetime | None:
211
+ for name in names:
212
+ parsed = _datetime(fields.get(name))
213
+ if parsed is not None:
214
+ return parsed
215
+ return None
216
+
217
+
218
+ def _artifact_type(fields: Mapping[str, Any], path: str) -> str:
219
+ explicit = fields.get("type") or fields.get("artifact_type")
220
+ if explicit:
221
+ return str(explicit)
222
+ parts = set(Path(path).parts)
223
+ if "handoffs" in parts:
224
+ return "handoff"
225
+ if "decisions" in parts:
226
+ return "decision"
227
+ if "threads" in parts:
228
+ return "thread"
229
+ if "scrolls" in parts:
230
+ return "scroll"
231
+ return "artifact"
232
+
233
+
234
+ def _title(fields: Mapping[str, Any], path: str) -> str:
235
+ return str(
236
+ fields.get("title")
237
+ or fields.get("topic")
238
+ or fields.get("name")
239
+ or Path(path).stem.replace("-", " ")
240
+ ).strip()
241
+
242
+
243
+ def _created_at(fields: Mapping[str, Any], path: Path) -> datetime:
244
+ parsed = _field_datetime(
245
+ fields, "created_at", "created", "date", "started", "ingested_at"
246
+ )
247
+ if parsed is not None:
248
+ return parsed
249
+ path_date = re.search(r"(?:^|/)(\d{4})-(\d{2})/(\d{2})-", path.as_posix())
250
+ if path_date:
251
+ candidate = _datetime("-".join(path_date.groups()))
252
+ if candidate is not None:
253
+ return candidate
254
+ for part in reversed(path.parts):
255
+ match = re.match(r"(\d{4}-\d{2}-\d{2})", part)
256
+ if match:
257
+ candidate = _datetime(match.group(1))
258
+ if candidate is not None:
259
+ return candidate
260
+ raise LifecycleError("canonical artifact has no deterministic creation date")
261
+
262
+
263
+ def _normal_status(value: Any) -> str:
264
+ normalized = str(value or "active").strip().lower().replace("-", "_")
265
+ aliases = {"completed": "done", "closed": "done", "unread": "pending"}
266
+ return aliases.get(normalized, normalized)
267
+
268
+
269
+ def _obligation(value: str, artifact_type: str) -> ObligationState:
270
+ if artifact_type != "handoff":
271
+ return ObligationState.NONE
272
+ normalized = _normal_status(value)
273
+ return {
274
+ "pending": ObligationState.PENDING,
275
+ "read": ObligationState.READ,
276
+ "claimed": ObligationState.CLAIMED,
277
+ "done": ObligationState.COMPLETED,
278
+ "review_required": ObligationState.REVIEW_REQUIRED,
279
+ }.get(normalized, ObligationState.PENDING)
280
+
281
+
282
+ def _transition_states(
283
+ record: LifecycleRecord, action: LifecycleAction
284
+ ) -> tuple[ObligationState, AttentionState]:
285
+ if action is LifecycleAction.ACKNOWLEDGE:
286
+ return ObligationState.READ, AttentionState.ACTIVE
287
+ if action is LifecycleAction.CLAIM:
288
+ return ObligationState.CLAIMED, AttentionState.ACTIVE
289
+ if action is LifecycleAction.COMPLETE:
290
+ return ObligationState.COMPLETED, AttentionState.ACTIVE
291
+ if action is LifecycleAction.EXPIRE_ATTENTION:
292
+ return record.obligation_state, AttentionState.EXPIRED
293
+ if action is LifecycleAction.REOPEN:
294
+ return ObligationState.PENDING, AttentionState.ACTIVE
295
+ return ObligationState.REVIEW_REQUIRED, AttentionState.REVIEW_DUE
296
+
297
+
298
+ class CanonicalLifecycleService:
299
+ """Build a disposable lifecycle projection from authorized canonical files."""
300
+
301
+ def __init__(
302
+ self,
303
+ memory_root: Path,
304
+ *,
305
+ policy: LifecyclePolicy | None = None,
306
+ clock: Callable[[], datetime] | None = None,
307
+ state_dir: Path | None = None,
308
+ default_org_id: str = "local",
309
+ ) -> None:
310
+ self.memory_root = memory_root.resolve()
311
+ self.policy = policy or LifecyclePolicy()
312
+ self.clock = clock or (lambda: datetime.now(UTC))
313
+ self.state_dir = state_dir.resolve() if state_dir else None
314
+ self.default_org_id = default_org_id
315
+
316
+ def scan(
317
+ self,
318
+ *,
319
+ source_revision: str | None = None,
320
+ authorized_scopes: Sequence[str] = ("memory",),
321
+ allowed_artifact_ids: Sequence[str] = (),
322
+ artifact_types: Sequence[str] = (),
323
+ user: str | Sequence[str] | None = None,
324
+ ) -> LifecyclePlan:
325
+ now = self.clock().astimezone(UTC)
326
+ canonical_revision = source_revision or self._canonical_revision()
327
+ snapshots, warnings = self._snapshots(canonical_revision)
328
+ allowed = frozenset(allowed_artifact_ids)
329
+ kinds = frozenset(artifact_types)
330
+ visible_primary = [
331
+ row
332
+ for row in snapshots
333
+ if row.artifact_type != "lifecycle-event"
334
+ and scope_allows_path(row.canonical_path, authorized_scopes)
335
+ and (
336
+ not allowed
337
+ or row.artifact_id in allowed
338
+ )
339
+ ]
340
+ visible_ids = {row.artifact_id for row in visible_primary}
341
+ # Derived lifecycle events inherit the target artifact's scope. Their
342
+ # storage namespace must not require a broader read grant.
343
+ visible_events = [
344
+ row
345
+ for row in snapshots
346
+ if row.artifact_type == "lifecycle-event"
347
+ and row.target_artifact_id in visible_ids
348
+ ]
349
+ visible = [*visible_primary, *visible_events]
350
+ superseders: dict[str, list[str]] = {}
351
+ tentative_supersession_targets: set[str] = set()
352
+ events: dict[str, list[_Snapshot]] = {}
353
+ for row in visible:
354
+ for target in row.supersedes:
355
+ if target in visible_ids:
356
+ if row.status in {"draft", "proposed", "review"}:
357
+ tentative_supersession_targets.add(target)
358
+ else:
359
+ superseders.setdefault(target, []).append(row.artifact_id)
360
+ if row.artifact_type == "lifecycle-event" and row.target_artifact_id:
361
+ if row.target_artifact_id in visible_ids:
362
+ events.setdefault(row.target_artifact_id, []).append(row)
363
+
364
+ # Supersession outside the authorized result set must not leak a path
365
+ # or id, but it also must not let Observe falsely assert that the old
366
+ # artifact is current. Mark that target ambiguous without naming the
367
+ # hidden successor.
368
+ visible_paths = {row.canonical_path for row in visible}
369
+ hidden_supersession_targets = {
370
+ target
371
+ for row in snapshots
372
+ if row.canonical_path not in visible_paths
373
+ for target in row.supersedes
374
+ if target in visible_ids
375
+ }
376
+ cycle_ids = self._supersession_cycles(visible)
377
+
378
+ requested_users = (user,) if isinstance(user, str) else tuple(user or ())
379
+ user_keys = {str(item).casefold().strip() for item in requested_users if str(item).strip()}
380
+ records: list[LifecycleRecord] = []
381
+ for row in visible:
382
+ if row.artifact_type == "lifecycle-event":
383
+ continue
384
+ if kinds and row.artifact_type not in kinds:
385
+ continue
386
+ if user_keys and row.artifact_type == "handoff" and not any(
387
+ recipient.casefold() in user_keys for recipient in row.recipients
388
+ ):
389
+ continue
390
+ matching_events = events.get(row.artifact_id, ())
391
+ if user_keys:
392
+ matching_events = tuple(
393
+ event
394
+ for event in matching_events
395
+ if not event.transition_recipient
396
+ or event.transition_recipient.casefold() in user_keys
397
+ )
398
+ latest = max(matching_events, key=lambda item: (item.created_at, item.artifact_id), default=None)
399
+ conflicting_events = any(
400
+ len(
401
+ {
402
+ (event.transition_state, event.transition_attention)
403
+ for event in matching_events
404
+ if event.expected_lifecycle_revision == expected
405
+ }
406
+ )
407
+ > 1
408
+ for expected in {
409
+ event.expected_lifecycle_revision
410
+ for event in matching_events
411
+ if event.expected_lifecycle_revision
412
+ }
413
+ )
414
+ successors = tuple(sorted(superseders.get(row.artifact_id, ())))
415
+ ambiguous = (
416
+ row.artifact_id in hidden_supersession_targets
417
+ or row.artifact_id in tentative_supersession_targets
418
+ or row.artifact_id in cycle_ids
419
+ or len(successors) > 1
420
+ or any(target not in visible_ids for target in row.supersedes)
421
+ or conflicting_events
422
+ )
423
+ records.append(self._record(row, latest, successors, ambiguous, now))
424
+
425
+ records.sort(key=lambda item: (item.created_at, item.artifact_id), reverse=True)
426
+ snapshot_payload = "\n".join(
427
+ f"{row.artifact_id}|{row.digest}" for row in sorted(visible, key=lambda item: item.artifact_id)
428
+ )
429
+ snapshot_id = hashlib.sha256(snapshot_payload.encode("utf-8")).hexdigest()
430
+ return LifecyclePlan(
431
+ schema_version=LIFECYCLE_PLAN_SCHEMA,
432
+ index_spec_version=LIFECYCLE_INDEX_SPEC,
433
+ snapshot_id=snapshot_id,
434
+ generated_at=now,
435
+ source_revision=canonical_revision,
436
+ records=tuple(records),
437
+ warnings=warnings,
438
+ )
439
+
440
+ def resolve_paths(
441
+ self,
442
+ paths: Sequence[str],
443
+ *,
444
+ source_revision: str | None,
445
+ authorized_scopes: Sequence[str],
446
+ allowed_artifact_ids: Sequence[str] = (),
447
+ ) -> tuple[Mapping[str, LifecycleRecord], str]:
448
+ requested = {path.replace("\\", "/").strip().lstrip("./") for path in paths}
449
+ plan = self.scan(
450
+ source_revision=source_revision,
451
+ authorized_scopes=authorized_scopes,
452
+ allowed_artifact_ids=allowed_artifact_ids,
453
+ )
454
+ return (
455
+ {record.canonical_path: record for record in plan.records if record.canonical_path in requested},
456
+ plan.snapshot_id,
457
+ )
458
+
459
+ def prepare_transition(
460
+ self,
461
+ actor: ActorContext,
462
+ record: LifecycleRecord,
463
+ action: LifecycleAction,
464
+ *,
465
+ reason: str,
466
+ evidence_ids: Sequence[str] = (),
467
+ automatic: bool = False,
468
+ allow_admin: bool = False,
469
+ expected_lifecycle_revision: str | None = None,
470
+ ) -> LifecycleTransition:
471
+ if (
472
+ expected_lifecycle_revision is not None
473
+ and expected_lifecycle_revision != record.lifecycle_revision
474
+ ):
475
+ raise LifecycleError("lifecycle revision changed; rebuild the plan before applying")
476
+ aliases = {
477
+ actor.actor.actor_id.casefold(),
478
+ actor.actor.display_name.casefold(),
479
+ *(str(value).casefold() for value in actor.actor.aliases.values()),
480
+ }
481
+ if actor.account is not None:
482
+ aliases.add(actor.account.display_name.casefold())
483
+ aliases.update(str(value).casefold() for value in actor.account.provider_aliases.values())
484
+ if record.artifact_type == "handoff" and record.recipients and not allow_admin:
485
+ if aliases.isdisjoint(recipient.casefold() for recipient in record.recipients):
486
+ raise LifecycleError("handoff transition actor is not an addressed recipient")
487
+ if automatic and action is LifecycleAction.COMPLETE:
488
+ # V1 can record explicit completion evidence, but cannot yet prove
489
+ # that an arbitrary id is a recipient-authored terminal IMPLEMENTS
490
+ # artifact. Fail closed until canonical verification exists.
491
+ raise LifecycleError(
492
+ "automatic completion requires verified canonical implementation evidence"
493
+ )
494
+ if automatic and action is LifecycleAction.EXPIRE_ATTENTION and not record.automatic_transition_safe:
495
+ raise LifecycleError("automatic expiry is not safe for this lifecycle state")
496
+ if action is LifecycleAction.REOPEN and (
497
+ record.obligation_state is not ObligationState.COMPLETED
498
+ and record.attention_state not in {AttentionState.EXPIRED, AttentionState.REVIEW_DUE}
499
+ ):
500
+ raise LifecycleError("only terminal or review-due work can be reopened")
501
+ if action is LifecycleAction.COMPLETE and record.authority_state in {
502
+ AuthorityState.SUPERSEDED,
503
+ AuthorityState.HISTORICAL,
504
+ AuthorityState.AMBIGUOUS,
505
+ }:
506
+ raise LifecycleError("non-current artifacts cannot be completed")
507
+ if automatic and record.intent not in {"action", "feedback", "fyi"}:
508
+ raise LifecycleError("legacy or unclassified artifacts cannot transition unattended")
509
+
510
+ occurred_at = self.clock().astimezone(UTC)
511
+ new_obligation, new_attention = _transition_states(record, action)
512
+ event_id = f"lifecycle-{uuid4().hex}"
513
+ canonical_path = f"lifecycle/events/{occurred_at:%Y-%m}/{occurred_at:%d}-{event_id}.md"
514
+ evidence = tuple(dict.fromkeys(str(item) for item in evidence_ids if str(item).strip()))
515
+ body = (
516
+ f"# Lifecycle event: {action.value} {record.title}\n\n"
517
+ f"Target: `{record.artifact_id}`\n\n"
518
+ f"Reason: {reason.strip()}\n"
519
+ )
520
+ artifact = CanonicalArtifact(
521
+ schema_version=ARTIFACT_SCHEMA_VERSION,
522
+ artifact_id=event_id,
523
+ org_id=actor.profile.org_id,
524
+ artifact_type="lifecycle-event",
525
+ title=f"{action.value}: {record.title}",
526
+ created_at=occurred_at,
527
+ created_by=actor.actor.actor_id,
528
+ status="accepted",
529
+ canonical_path=canonical_path,
530
+ revision="",
531
+ content_hash=content_digest(body),
532
+ relationships=(
533
+ ArtifactRelationship("transitions", record.artifact_id),
534
+ *(ArtifactRelationship("evidence", item) for item in evidence),
535
+ ),
536
+ )
537
+ document = CanonicalDocument(
538
+ artifact=artifact,
539
+ body=body,
540
+ legacy_fields={
541
+ "lifecycle_schema": LIFECYCLE_EVENT_SCHEMA,
542
+ "lifecycle_action": action.value,
543
+ "target_artifact_id": record.artifact_id,
544
+ "target_path": record.canonical_path,
545
+ "actor_alias": actor.actor.display_name,
546
+ "recipient": next(
547
+ (
548
+ recipient
549
+ for recipient in record.recipients
550
+ if recipient.casefold() in aliases
551
+ ),
552
+ None,
553
+ ),
554
+ "previous_obligation": record.obligation_state.value,
555
+ "new_state": new_obligation.value,
556
+ "previous_attention": record.attention_state.value,
557
+ "new_attention": new_attention.value,
558
+ "expected_lifecycle_revision": record.lifecycle_revision,
559
+ "reason": reason.strip(),
560
+ "evidence_ids": list(evidence),
561
+ "automatic": automatic,
562
+ "occurred_at": occurred_at.isoformat().replace("+00:00", "Z"),
563
+ },
564
+ )
565
+ return LifecycleTransition(
566
+ action=action,
567
+ target_artifact_id=record.artifact_id,
568
+ target_path=record.canonical_path,
569
+ previous_obligation=record.obligation_state,
570
+ new_obligation=new_obligation,
571
+ previous_attention=record.attention_state,
572
+ new_attention=new_attention,
573
+ expected_lifecycle_revision=record.lifecycle_revision,
574
+ reason=reason.strip(),
575
+ evidence_ids=evidence,
576
+ automatic=automatic,
577
+ document=document,
578
+ )
579
+
580
+ def _record(
581
+ self,
582
+ row: _Snapshot,
583
+ latest: _Snapshot | None,
584
+ superseded_by: tuple[str, ...],
585
+ ambiguous: bool,
586
+ now: datetime,
587
+ ) -> LifecycleRecord:
588
+ status = latest.transition_state if latest and latest.transition_state else row.status
589
+ obligation = _obligation(status, row.artifact_type)
590
+ attention = (
591
+ AttentionState(latest.transition_attention)
592
+ if latest
593
+ and latest.transition_attention
594
+ in {state.value for state in AttentionState}
595
+ else AttentionState.ACTIVE
596
+ )
597
+ authority = AuthorityState.CURRENT
598
+ reason = latest.transition_reason if latest else row.terminal_reason
599
+ evidence = latest.transition_evidence if latest else ()
600
+ review_at = row.review_at
601
+ expires_at = row.attention_expires_at
602
+ if row.artifact_type == "handoff" and expires_at is None:
603
+ days = self.policy.fyi_expiry_days if row.intent == "fyi" else self.policy.handoff_ttl_days
604
+ expires_at = row.created_at + timedelta(days=days)
605
+ retention_until = row.retention_until
606
+ if retention_until is None and self.policy.retention_days is not None:
607
+ retention_until = row.created_at + timedelta(days=self.policy.retention_days)
608
+ age_days = max(0, (now.date() - row.created_at.date()).days)
609
+ recommendation = "keep"
610
+ automatic_safe = False
611
+
612
+ if ambiguous:
613
+ authority = AuthorityState.AMBIGUOUS
614
+ recommendation = "review"
615
+ elif superseded_by:
616
+ authority = AuthorityState.SUPERSEDED
617
+ recommendation = "keep_historical"
618
+ elif row.status in {"archived", "historical"}:
619
+ authority = AuthorityState.HISTORICAL
620
+ recommendation = "keep_historical"
621
+ elif obligation is ObligationState.COMPLETED:
622
+ recommendation = "keep_historical"
623
+ elif attention is AttentionState.EXPIRED:
624
+ recommendation = "review_or_reopen"
625
+ elif review_at is not None and review_at <= now:
626
+ attention = AttentionState.REVIEW_DUE
627
+ if obligation not in {ObligationState.NONE, ObligationState.COMPLETED}:
628
+ obligation = ObligationState.REVIEW_REQUIRED
629
+ recommendation = "review"
630
+ elif expires_at is not None and expires_at <= now:
631
+ if row.artifact_type == "handoff" and row.intent == "fyi":
632
+ attention = AttentionState.EXPIRED
633
+ recommendation = "expire_attention"
634
+ automatic_safe = True
635
+ elif row.artifact_type == "handoff" and row.intent in {"action", "feedback"}:
636
+ attention = AttentionState.EXPIRED
637
+ recommendation = "expire_attention"
638
+ automatic_safe = obligation is ObligationState.PENDING
639
+ else:
640
+ attention = AttentionState.REVIEW_DUE
641
+ if obligation not in {ObligationState.NONE, ObligationState.COMPLETED}:
642
+ obligation = ObligationState.REVIEW_REQUIRED
643
+ recommendation = "review"
644
+ elif row.artifact_type == "handoff" and age_days >= self.policy.review_after_days:
645
+ attention = AttentionState.REVIEW_DUE
646
+ if obligation not in {ObligationState.NONE, ObligationState.COMPLETED}:
647
+ obligation = ObligationState.REVIEW_REQUIRED
648
+ recommendation = "review"
649
+
650
+ lifecycle_revision = hashlib.sha256(
651
+ "|".join(
652
+ (
653
+ row.digest,
654
+ latest.digest if latest else "",
655
+ authority.value,
656
+ attention.value,
657
+ obligation.value,
658
+ )
659
+ ).encode("utf-8")
660
+ ).hexdigest()
661
+ return LifecycleRecord(
662
+ artifact_id=row.artifact_id,
663
+ canonical_path=row.canonical_path,
664
+ artifact_type=row.artifact_type,
665
+ title=row.title,
666
+ created_at=row.created_at,
667
+ created_by=row.created_by,
668
+ declared_status=row.status,
669
+ authority_state=authority,
670
+ attention_state=attention,
671
+ obligation_state=obligation,
672
+ current_authority=authority is AuthorityState.CURRENT,
673
+ lifecycle_revision=f"sha256:{lifecycle_revision}",
674
+ age_days=age_days,
675
+ created_by_alias=row.created_by_alias,
676
+ intent=row.intent,
677
+ recipients=row.recipients,
678
+ superseded_by=superseded_by,
679
+ review_at=review_at,
680
+ attention_expires_at=expires_at,
681
+ retention_until=retention_until,
682
+ terminal_reason=reason,
683
+ recommendation=recommendation,
684
+ automatic_transition_safe=automatic_safe,
685
+ evidence_ids=evidence,
686
+ )
687
+
688
+ @staticmethod
689
+ def _supersession_cycles(rows: Sequence[_Snapshot]) -> frozenset[str]:
690
+ edges = {row.artifact_id: set(row.supersedes) for row in rows if row.supersedes}
691
+ cycles: set[str] = set()
692
+
693
+ def visit(origin: str, current: str, trail: tuple[str, ...]) -> None:
694
+ if current in trail:
695
+ start = trail.index(current)
696
+ cycles.update(trail[start:])
697
+ return
698
+ for target in edges.get(current, ()):
699
+ visit(origin, target, (*trail, current))
700
+
701
+ for artifact_id in edges:
702
+ visit(artifact_id, artifact_id, ())
703
+ return frozenset(cycles)
704
+
705
+ def _snapshots(self, source_revision: str | None) -> tuple[tuple[_Snapshot, ...], tuple[str, ...]]:
706
+ cached = self._read_cache(source_revision) if source_revision else None
707
+ if cached is not None:
708
+ return cached, ()
709
+ rows: list[_Snapshot] = []
710
+ warnings: list[str] = []
711
+ if not self.memory_root.is_dir():
712
+ return (), ("canonical memory root is unavailable",)
713
+ for path in sorted(self.memory_root.rglob("*.md")):
714
+ if ".git" in path.parts or path.name == "index.md":
715
+ continue
716
+ relative = path.relative_to(self.memory_root).as_posix()
717
+ canonical_path = f"memory/{relative}"
718
+ try:
719
+ markdown = path.read_text(encoding="utf-8")
720
+ fields, _ = split_frontmatter(markdown)
721
+ created = _created_at(fields, path)
722
+ except (OSError, ValueError) as exc:
723
+ warnings.append(f"skipped:{canonical_path}:{type(exc).__name__}")
724
+ continue
725
+ artifact_id = canonical_artifact_id(markdown, canonical_path)
726
+ artifact_type = _artifact_type(fields, relative)
727
+ recipients = _strings(
728
+ fields.get("addressed_to")
729
+ or fields.get("addressed_to_user")
730
+ or fields.get("to")
731
+ or fields.get("recipient")
732
+ )
733
+ supersedes = _strings(fields.get("supersedes"))
734
+ rows.append(
735
+ _Snapshot(
736
+ artifact_id=artifact_id,
737
+ canonical_path=canonical_path,
738
+ artifact_type=artifact_type,
739
+ title=_title(fields, relative),
740
+ created_at=created,
741
+ created_by=str(fields.get("created_by") or fields.get("author") or fields.get("from") or "unknown"),
742
+ created_by_alias=(
743
+ str(
744
+ fields.get("creator_alias")
745
+ or fields.get("actor_alias")
746
+ or fields.get("from")
747
+ or fields.get("author")
748
+ or ""
749
+ ).strip()
750
+ or None
751
+ ),
752
+ status=_normal_status(
753
+ fields.get("status")
754
+ or fields.get("handoff_status")
755
+ or ("pending" if artifact_type == "handoff" else "active")
756
+ ),
757
+ intent=str(fields.get("intent")).lower() if fields.get("intent") else None,
758
+ recipients=recipients,
759
+ supersedes=supersedes,
760
+ review_at=_field_datetime(fields, "review_at", "review_on", "review_date"),
761
+ attention_expires_at=_field_datetime(fields, "attention_expires_at", "expires_at"),
762
+ retention_until=_field_datetime(fields, "retention_until"),
763
+ terminal_reason=str(fields.get("terminal_reason") or fields.get("lifecycle_reason"))
764
+ if fields.get("terminal_reason") or fields.get("lifecycle_reason")
765
+ else None,
766
+ lifecycle_action=str(fields.get("lifecycle_action")) if fields.get("lifecycle_action") else None,
767
+ target_artifact_id=str(fields.get("target_artifact_id")) if fields.get("target_artifact_id") else None,
768
+ target_path=str(fields.get("target_path")) if fields.get("target_path") else None,
769
+ transition_state=str(fields.get("new_state")) if fields.get("new_state") else None,
770
+ transition_attention=str(fields.get("new_attention")) if fields.get("new_attention") else None,
771
+ transition_recipient=str(fields.get("recipient")) if fields.get("recipient") else None,
772
+ expected_lifecycle_revision=str(fields.get("expected_lifecycle_revision"))
773
+ if fields.get("expected_lifecycle_revision")
774
+ else None,
775
+ transition_reason=str(fields.get("reason")) if fields.get("reason") else None,
776
+ transition_evidence=_strings(fields.get("evidence_ids")),
777
+ transition_automatic=bool(fields.get("automatic", False)),
778
+ digest=hashlib.sha256(markdown.encode("utf-8")).hexdigest(),
779
+ )
780
+ )
781
+ result = tuple(rows)
782
+ if source_revision:
783
+ self._write_cache(source_revision, result)
784
+ return result, tuple(warnings[:20])
785
+
786
+ def _cache_path(self) -> Path | None:
787
+ return self.state_dir / "projection.json" if self.state_dir else None
788
+
789
+ def _canonical_revision(self) -> str:
790
+ try:
791
+ head = subprocess.run(
792
+ ("git", "-C", str(self.memory_root), "rev-parse", "HEAD"),
793
+ capture_output=True,
794
+ text=True,
795
+ timeout=10,
796
+ check=False,
797
+ )
798
+ status = subprocess.run(
799
+ (
800
+ "git", "-C", str(self.memory_root), "status",
801
+ "--porcelain=v1", "--untracked-files=all",
802
+ ),
803
+ capture_output=True,
804
+ text=True,
805
+ timeout=30,
806
+ check=False,
807
+ )
808
+ if head.returncode == 0 and head.stdout.strip() and status.returncode == 0:
809
+ revision = f"git:{head.stdout.strip()}"
810
+ if not status.stdout.strip():
811
+ return revision
812
+ return f"{revision}+dirty:{self._markdown_manifest_hash()}"
813
+ except (OSError, subprocess.SubprocessError):
814
+ pass
815
+ return f"filesystem:{self._markdown_manifest_hash()}"
816
+
817
+ def _markdown_manifest_hash(self) -> str:
818
+ digest = hashlib.sha256()
819
+ for source in sorted(self.memory_root.rglob("*.md")):
820
+ if not source.is_file():
821
+ continue
822
+ digest.update(source.relative_to(self.memory_root).as_posix().encode("utf-8"))
823
+ digest.update(b"\0")
824
+ try:
825
+ digest.update(source.read_bytes())
826
+ except OSError:
827
+ digest.update(b"<unreadable>")
828
+ digest.update(b"\0")
829
+ return digest.hexdigest()[:20]
830
+
831
+ def _read_cache(self, source_revision: str) -> tuple[_Snapshot, ...] | None:
832
+ path = self._cache_path()
833
+ if path is None or not path.is_file():
834
+ return None
835
+ try:
836
+ payload = json.loads(path.read_text(encoding="utf-8"))
837
+ if (
838
+ payload.get("schema") != LIFECYCLE_PLAN_SCHEMA
839
+ or payload.get("index_spec") != LIFECYCLE_INDEX_SPEC
840
+ or payload.get("source_revision") != source_revision
841
+ ):
842
+ return None
843
+ return tuple(
844
+ _Snapshot(
845
+ **{
846
+ **item,
847
+ "created_at": _datetime(item["created_at"]),
848
+ "review_at": _datetime(item.get("review_at")),
849
+ "attention_expires_at": _datetime(item.get("attention_expires_at")),
850
+ "retention_until": _datetime(item.get("retention_until")),
851
+ "recipients": tuple(item.get("recipients", ())),
852
+ "supersedes": tuple(item.get("supersedes", ())),
853
+ "transition_evidence": tuple(item.get("transition_evidence", ())),
854
+ }
855
+ )
856
+ for item in payload.get("snapshots", ())
857
+ )
858
+ except (OSError, TypeError, ValueError, json.JSONDecodeError):
859
+ return None
860
+
861
+ def _write_cache(self, source_revision: str, snapshots: Sequence[_Snapshot]) -> None:
862
+ path = self._cache_path()
863
+ if path is None:
864
+ return
865
+ path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
866
+ os.chmod(path.parent, 0o700)
867
+ payload = {
868
+ "schema": LIFECYCLE_PLAN_SCHEMA,
869
+ "index_spec": LIFECYCLE_INDEX_SPEC,
870
+ "source_revision": source_revision,
871
+ "snapshots": [
872
+ {
873
+ key: (
874
+ value.isoformat()
875
+ if isinstance(value, datetime)
876
+ else list(value)
877
+ if isinstance(value, tuple)
878
+ else value
879
+ )
880
+ for key, value in asdict(row).items()
881
+ }
882
+ for row in snapshots
883
+ ],
884
+ }
885
+ temporary = path.with_name(f".{path.name}.{os.getpid()}.tmp")
886
+ temporary.write_text(json.dumps(payload, sort_keys=True, separators=(",", ":")), encoding="utf-8")
887
+ os.chmod(temporary, 0o600)
888
+ temporary.replace(path)