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,734 @@
1
+ """Provider-independent Local identity resolution.
2
+
3
+ GitHub fields remain compatibility aliases used by today's Git transport. They
4
+ are never used as the domain account or actor identifier.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import os
11
+ import tempfile
12
+ import fcntl
13
+ import hashlib
14
+ import re
15
+ from contextlib import contextmanager
16
+ from collections.abc import Callable, Mapping
17
+ from dataclasses import dataclass
18
+ from pathlib import Path
19
+ from typing import Any
20
+ from uuid import uuid4
21
+
22
+ from .contracts import (
23
+ AccountIdentity,
24
+ ActorContext,
25
+ ActorIdentity,
26
+ ActorKind,
27
+ OrgMembership,
28
+ )
29
+ from .entitlements import resolve_entitlements
30
+ from .profile import initialize_org_config, load_profile, render_identity_document
31
+
32
+
33
+ IDENTITY_SCHEMA = "egregore-identity/v2"
34
+
35
+
36
+ @dataclass(frozen=True, slots=True)
37
+ class OrganizationMemberPresentation:
38
+ """Presentation-only view of one canonical organization identity.
39
+
40
+ Authorization continues to use ActorContext and opaque actor/membership
41
+ identifiers. This local directory exists only so harnesses can present a
42
+ teammate's chosen name when evidence contains a provider handle or legacy
43
+ alias.
44
+ """
45
+
46
+ person_id: str
47
+ display_name: str
48
+ aliases: tuple[str, ...]
49
+
50
+
51
+ def _profile_identity(path: Path) -> dict[str, Any]:
52
+ try:
53
+ text = path.read_text(encoding="utf-8", errors="replace")[:4_000]
54
+ except OSError:
55
+ return {}
56
+ title = re.search(r"(?m)^#\s+(.+?)\s*$", text)
57
+ fields = {
58
+ match.group(1).casefold(): match.group(2).strip()
59
+ for match in re.finditer(
60
+ r"(?m)^([A-Za-z][A-Za-z-]*)[ \t]*:[ \t]*(.*)$",
61
+ text,
62
+ )
63
+ }
64
+ return {
65
+ "path": path,
66
+ "title": title.group(1).strip() if title else "",
67
+ "person_id": fields.get("person-id", ""),
68
+ "github": fields.get("github", ""),
69
+ "github_aliases": tuple(
70
+ item.strip()
71
+ for item in fields.get("github-aliases", "").split(",")
72
+ if item.strip()
73
+ ),
74
+ "previous_names": tuple(
75
+ item.strip()
76
+ for item in fields.get("previous-names", "").split(",")
77
+ if item.strip()
78
+ ),
79
+ "alias_of": fields.get("alias-of", ""),
80
+ }
81
+
82
+
83
+ def organization_member_presentations(
84
+ root: Path,
85
+ actor: ActorContext | None = None,
86
+ ) -> tuple[OrganizationMemberPresentation, ...]:
87
+ """Load stable member names without treating aliases as extra people.
88
+
89
+ ``memory/people`` is the canonical local witness produced by identity
90
+ reconciliation. Profiles with the same Person-ID collapse into one member;
91
+ an explicit Alias-Of title is the preferred presentation witness for
92
+ legacy canonical profiles whose title is still a provider handle.
93
+ """
94
+
95
+ people = root / "memory" / "people"
96
+ if not people.is_dir():
97
+ return ()
98
+ # Durable removals: the committed ledger in egregore.json outranks file
99
+ # presence — a person file resurrected by a session write or sync never
100
+ # resurfaces a removed member in the roster.
101
+ removed: set[str] = set()
102
+ try:
103
+ config = json.loads((root / "egregore.json").read_text(encoding="utf-8"))
104
+ removed = {
105
+ str(handle).strip().casefold()
106
+ for handle in (config.get("people_removed") or [])
107
+ if str(handle).strip()
108
+ }
109
+ except (OSError, ValueError):
110
+ removed = set()
111
+ grouped: dict[str, list[dict[str, Any]]] = {}
112
+ for path in sorted(people.glob("*.md")):
113
+ if path.name == "index.md":
114
+ continue
115
+ if path.stem.casefold() in removed:
116
+ continue
117
+ profile = _profile_identity(path)
118
+ if not profile:
119
+ continue
120
+ if str(profile.get("github") or "").strip().casefold() in removed:
121
+ continue
122
+ identity = str(profile["person_id"] or profile["github"]).strip()
123
+ if not identity:
124
+ continue
125
+ grouped.setdefault(identity.casefold(), []).append(profile)
126
+
127
+ current_references: set[str] = set()
128
+ if actor is not None:
129
+ current_references = {
130
+ actor.actor.display_name.casefold(),
131
+ *(str(value).casefold() for value in actor.actor.aliases.values()),
132
+ }
133
+ account = getattr(actor, "account", None)
134
+ if account is not None:
135
+ current_references.add(account.display_name.casefold())
136
+ current_references.update(
137
+ str(value).casefold()
138
+ for value in getattr(account, "provider_aliases", {}).values()
139
+ )
140
+
141
+ members: list[OrganizationMemberPresentation] = []
142
+ for profiles in grouped.values():
143
+ aliases: dict[str, str] = {}
144
+ explicit_titles: list[str] = []
145
+ canonical_titles: list[str] = []
146
+ previous_names: list[str] = []
147
+ for profile in profiles:
148
+ values = (
149
+ profile["path"].stem,
150
+ profile["github"],
151
+ *profile["github_aliases"],
152
+ profile["title"],
153
+ *profile["previous_names"],
154
+ )
155
+ for value in values:
156
+ clean = str(value).strip()
157
+ if clean:
158
+ aliases.setdefault(clean.casefold(), clean)
159
+ title = str(profile["title"]).strip()
160
+ if title:
161
+ if profile["alias_of"]:
162
+ explicit_titles.append(title)
163
+ else:
164
+ canonical_titles.append(title)
165
+ previous_names.extend(profile["previous_names"])
166
+
167
+ display_name = ""
168
+ if actor is not None and current_references.intersection(aliases):
169
+ display_name = actor.actor.display_name
170
+ elif explicit_titles:
171
+ display_name = explicit_titles[0]
172
+ else:
173
+ machine_names = {
174
+ str(profile["path"].stem).casefold() for profile in profiles
175
+ } | {
176
+ str(profile["github"]).casefold()
177
+ for profile in profiles
178
+ if profile["github"]
179
+ }
180
+ display_name = next(
181
+ (
182
+ title
183
+ for title in canonical_titles
184
+ if title.casefold() not in machine_names
185
+ ),
186
+ "",
187
+ )
188
+ if not display_name:
189
+ display_name = next(iter(previous_names), "")
190
+ if not display_name:
191
+ display_name = next(iter(canonical_titles), "Egregore member")
192
+
193
+ compact_aliases = tuple(
194
+ value
195
+ for key, value in sorted(aliases.items())
196
+ if key != display_name.casefold()
197
+ and (
198
+ key in {str(profile["path"].stem).casefold() for profile in profiles}
199
+ or key
200
+ in {
201
+ str(profile["github"]).casefold()
202
+ for profile in profiles
203
+ if profile["github"]
204
+ }
205
+ )
206
+ )
207
+ members.append(
208
+ OrganizationMemberPresentation(
209
+ person_id=str(profiles[0]["person_id"] or profiles[0]["github"]),
210
+ display_name=display_name,
211
+ aliases=compact_aliases,
212
+ )
213
+ )
214
+ return tuple(sorted(members, key=lambda item: item.display_name.casefold()))
215
+
216
+
217
+ def new_identity_id(prefix: str) -> str:
218
+ return f"{prefix}_{uuid4().hex}"
219
+
220
+
221
+ def actor_identity(
222
+ *,
223
+ kind: ActorKind,
224
+ display_name: str,
225
+ actor_id: str | None = None,
226
+ account_id: str | None = None,
227
+ parent_actor_id: str | None = None,
228
+ aliases: Mapping[str, str] | None = None,
229
+ ) -> ActorIdentity:
230
+ """Construct a person, agent, or service actor without provider coupling."""
231
+
232
+ if kind is ActorKind.PERSON and not account_id:
233
+ raise ValueError("person actors require an account_id")
234
+ return ActorIdentity(
235
+ actor_id=actor_id or new_identity_id("actor"),
236
+ kind=kind,
237
+ display_name=display_name,
238
+ account_id=account_id,
239
+ parent_actor_id=parent_actor_id,
240
+ aliases=dict(aliases or {}),
241
+ )
242
+
243
+
244
+ def actor_presentation_name(
245
+ actor: ActorContext,
246
+ actor_id: str | None,
247
+ declared_alias: str | None = None,
248
+ ) -> str:
249
+ """Render an identity reference without weakening stable-ID authority.
250
+
251
+ Canonical artifacts use stable actor IDs for ownership and may carry a
252
+ display-only alias captured at write time. The active ActorContext is the
253
+ authority for the current actor's presentation; otherwise the declared
254
+ alias is preferred. Stable references remain structured identity fields
255
+ and are never used as presentation labels.
256
+ """
257
+
258
+ stable_reference = str(actor_id or "").strip()
259
+ alias = str(declared_alias or "").strip()
260
+ current_references = {
261
+ actor.actor.actor_id.casefold(),
262
+ actor.actor.display_name.casefold(),
263
+ *(
264
+ str(value).casefold()
265
+ for value in actor.actor.aliases.values()
266
+ if str(value).strip()
267
+ ),
268
+ }
269
+ if actor.account is not None:
270
+ current_references.add(actor.account.account_id.casefold())
271
+ current_references.add(actor.account.display_name.casefold())
272
+ current_references.update(
273
+ str(value).casefold()
274
+ for value in actor.account.provider_aliases.values()
275
+ if str(value).strip()
276
+ )
277
+ if (
278
+ stable_reference.casefold() in current_references
279
+ or alias.casefold() in current_references
280
+ ):
281
+ return actor.actor.display_name
282
+ return alias or "Egregore member"
283
+
284
+
285
+ def migrate_local_identity_state(
286
+ state: Mapping[str, Any],
287
+ *,
288
+ org_id: str,
289
+ id_factory: Callable[[str], str] = new_identity_id,
290
+ ) -> tuple[dict[str, Any], bool]:
291
+ """Upgrade legacy GitHub-shaped state while preserving its old readers."""
292
+
293
+ result = dict(state)
294
+ changed = False
295
+
296
+ identity = dict(result.get("identity") or {})
297
+ account_id = str(result.get("account_id") or identity.get("account_id") or "").strip()
298
+ actor_id = str(result.get("actor_id") or identity.get("actor_id") or "").strip()
299
+ membership_id = str(
300
+ result.get("membership_id") or identity.get("membership_id") or ""
301
+ ).strip()
302
+ if not account_id:
303
+ account_id = id_factory("acct")
304
+ if result.get("account_id") != account_id:
305
+ result["account_id"] = account_id
306
+ changed = True
307
+ if not actor_id:
308
+ actor_id = id_factory("actor")
309
+ if result.get("actor_id") != actor_id:
310
+ result["actor_id"] = actor_id
311
+ changed = True
312
+ if not membership_id:
313
+ membership_id = id_factory("membership")
314
+ if result.get("membership_id") != membership_id:
315
+ result["membership_id"] = membership_id
316
+ changed = True
317
+
318
+ providers = dict(
319
+ result.get("provider_identities")
320
+ or identity.get("provider_identities")
321
+ or {}
322
+ )
323
+ github: dict[str, str] = dict(providers.get("github") or {})
324
+ if result.get("github_id") is not None:
325
+ github["subject"] = str(result["github_id"])
326
+ if result.get("github_username"):
327
+ github["username"] = str(result["github_username"])
328
+ if github:
329
+ providers["github"] = github
330
+ if result.get("email"):
331
+ providers.setdefault("email", {"subject": str(result["email"]).lower()})
332
+
333
+ next_identity = {
334
+ **identity,
335
+ "schema_version": IDENTITY_SCHEMA,
336
+ "account_id": account_id,
337
+ "actor_id": actor_id,
338
+ "actor_kind": "person",
339
+ "membership_id": membership_id,
340
+ "org_id": org_id,
341
+ "provider_identities": providers,
342
+ }
343
+ if next_identity != identity:
344
+ result["identity"] = next_identity
345
+ result["provider_identities"] = providers
346
+ result["identity_version"] = 2
347
+ changed = True
348
+ return result, changed
349
+
350
+
351
+ def _atomic_json(path: Path, payload: Mapping[str, Any]) -> None:
352
+ if path.is_symlink():
353
+ path = path.resolve()
354
+ path.parent.mkdir(parents=True, exist_ok=True)
355
+ descriptor, temporary = tempfile.mkstemp(prefix=f"{path.name}.", dir=path.parent)
356
+ try:
357
+ with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
358
+ json.dump(payload, handle, indent=2, ensure_ascii=False)
359
+ handle.write("\n")
360
+ os.chmod(temporary, 0o600)
361
+ os.replace(temporary, path)
362
+ finally:
363
+ if os.path.exists(temporary):
364
+ os.unlink(temporary)
365
+
366
+
367
+ def _atomic_committed_json(path: Path, payload: Mapping[str, Any]) -> None:
368
+ """Atomically update non-secret committed config while retaining its mode."""
369
+
370
+ path.parent.mkdir(parents=True, exist_ok=True)
371
+ mode = path.stat().st_mode & 0o777 if path.exists() else 0o644
372
+ descriptor, temporary = tempfile.mkstemp(prefix=f"{path.name}.", dir=path.parent)
373
+ try:
374
+ with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
375
+ json.dump(payload, handle, indent=2, ensure_ascii=False)
376
+ handle.write("\n")
377
+ os.chmod(temporary, mode)
378
+ os.replace(temporary, path)
379
+ finally:
380
+ if os.path.exists(temporary):
381
+ os.unlink(temporary)
382
+
383
+
384
+ def control_plane_identity_from_records(
385
+ state: Mapping[str, Any],
386
+ organization: Mapping[str, Any],
387
+ roster: Mapping[str, Any],
388
+ *,
389
+ expected_slug: str,
390
+ ) -> dict[str, str]:
391
+ """Select one existing membership for safe, read-only reconciliation.
392
+
393
+ Numeric provider subjects are preferred and never downgraded to mutable
394
+ usernames. The caller may persist the returned IDs only after both records
395
+ came from the authenticated control plane for the configured organization.
396
+ This function never creates an organization, account, actor, or membership.
397
+ """
398
+
399
+ org_id = str(organization.get("org_id") or "").strip()
400
+ org_slug = str(organization.get("slug") or "").strip()
401
+ roster_slug = str(roster.get("org_slug") or "").strip()
402
+ roster_org_id = str(roster.get("org_id") or "").strip()
403
+ if not org_id or org_id.startswith("legacy-org:"):
404
+ raise ValueError("control-plane organization identity is unavailable")
405
+ if not expected_slug or org_slug != expected_slug or roster_slug != expected_slug:
406
+ raise ValueError("control-plane identity records belong to another organization")
407
+ if roster_org_id and roster_org_id != org_id:
408
+ raise ValueError("control-plane organization records disagree")
409
+
410
+ nested_identity = state.get("identity") or {}
411
+ providers = state.get("provider_identities") or (
412
+ nested_identity.get("provider_identities")
413
+ if isinstance(nested_identity, Mapping)
414
+ else {}
415
+ ) or {}
416
+ github = providers.get("github") if isinstance(providers, Mapping) else None
417
+ provider_subject = ""
418
+ provider_username = ""
419
+ if isinstance(github, Mapping):
420
+ provider_subject = str(github.get("subject") or "").strip()
421
+ provider_username = str(github.get("username") or "").strip().casefold()
422
+ provider_subject = provider_subject or str(state.get("github_id") or "").strip()
423
+ provider_username = provider_username or str(
424
+ state.get("github_username") or ""
425
+ ).strip().casefold()
426
+ if not provider_subject and not provider_username:
427
+ raise ValueError("local identity has no verified provider reference")
428
+
429
+ members = roster.get("members")
430
+ if not isinstance(members, list):
431
+ raise ValueError("control-plane membership roster is unavailable")
432
+ matches: list[Mapping[str, Any]] = []
433
+ for member in members:
434
+ if not isinstance(member, Mapping) or member.get("status") != "active":
435
+ continue
436
+ if provider_subject:
437
+ if str(member.get("github_id") or "").strip() == provider_subject:
438
+ matches.append(member)
439
+ continue
440
+ aliases = {
441
+ str(member.get("github_username") or "").strip().casefold(),
442
+ *(
443
+ str(alias).strip().casefold()
444
+ for alias in (member.get("github_aliases") or [])
445
+ ),
446
+ }
447
+ if provider_username in aliases:
448
+ matches.append(member)
449
+ if len(matches) != 1:
450
+ raise ValueError("control-plane membership identity is missing or ambiguous")
451
+
452
+ member = matches[0]
453
+ required = {
454
+ "org_id": org_id,
455
+ "account_id": member.get("account_id"),
456
+ "actor_id": member.get("actor_id"),
457
+ "runtime_membership_id": member.get("runtime_membership_id"),
458
+ }
459
+ missing = [key for key, value in required.items() if not str(value or "").strip()]
460
+ if missing:
461
+ raise ValueError(
462
+ "control-plane identity migration is incomplete: " + ", ".join(missing)
463
+ )
464
+ return {
465
+ "status": "ok",
466
+ **{key: str(value).strip() for key, value in required.items()},
467
+ }
468
+
469
+
470
+ def reconcile_control_plane_identity(
471
+ root: Path,
472
+ response: Mapping[str, Any],
473
+ ) -> dict[str, str]:
474
+ """Adopt authoritative Connected IDs without inventing or relinking them.
475
+
476
+ The response must come from the authenticated Egregore control plane. A
477
+ legacy or absent local organization id may advance to its returned opaque
478
+ id; a different established opaque id is never silently replaced.
479
+ """
480
+
481
+ required = {
482
+ "org_id": response.get("org_id"),
483
+ "account_id": response.get("account_id"),
484
+ "actor_id": response.get("actor_id"),
485
+ "membership_id": response.get("runtime_membership_id")
486
+ or response.get("membership_id"),
487
+ }
488
+ missing = [key for key, value in required.items() if not str(value or "").strip()]
489
+ if response.get("status") != "ok" or missing:
490
+ raise ValueError(
491
+ "control-plane identity response is incomplete: " + ", ".join(missing)
492
+ )
493
+ resolved = {key: str(value).strip() for key, value in required.items()}
494
+ root = root.resolve()
495
+ config_path = root / "egregore.json"
496
+ state_path = root / ".egregore-state.json"
497
+
498
+ with _identity_lock(state_path):
499
+ config = json.loads(config_path.read_text(encoding="utf-8"))
500
+ current_org = str(config.get("org_id") or "")
501
+ if current_org and not current_org.startswith("legacy-org:") and current_org != resolved["org_id"]:
502
+ raise ValueError("control-plane org_id conflicts with committed organization identity")
503
+ config = initialize_org_config(config, org_id=resolved["org_id"])
504
+ config["org_id"] = resolved["org_id"]
505
+ _atomic_committed_json(config_path, config)
506
+
507
+ try:
508
+ state = json.loads(state_path.read_text(encoding="utf-8"))
509
+ except (OSError, json.JSONDecodeError):
510
+ state = {}
511
+ state.update(resolved)
512
+ identity = dict(state.get("identity") or {})
513
+ providers = dict(
514
+ state.get("provider_identities")
515
+ or identity.get("provider_identities")
516
+ or {}
517
+ )
518
+ state["provider_identities"] = providers
519
+ identity.update(
520
+ {
521
+ "schema_version": IDENTITY_SCHEMA,
522
+ **resolved,
523
+ "actor_kind": identity.get("actor_kind") or "person",
524
+ "provider_identities": providers,
525
+ }
526
+ )
527
+ state["identity"] = identity
528
+ state["identity_version"] = 2
529
+ _atomic_json(state_path, state)
530
+
531
+ _ensure_identity_declaration(root, config)
532
+ return resolved
533
+
534
+
535
+ def initialize_local_instance_identity(root: Path) -> dict[str, str]:
536
+ """Create opaque Local identities once, under the same instance lock.
537
+
538
+ Connected instances must reconcile authoritative control-plane ids instead.
539
+ This path exists for legacy Local installs that predate the identity model.
540
+ """
541
+
542
+ root = root.resolve()
543
+ config_path = root / "egregore.json"
544
+ state_path = root / ".egregore-state.json"
545
+ with _identity_lock(state_path):
546
+ config = json.loads(config_path.read_text(encoding="utf-8"))
547
+ if str(config.get("mode") or "local") == "connected" or config.get("api_url"):
548
+ raise ValueError(
549
+ "connected identity must be reconciled from the control plane"
550
+ )
551
+ current_org = str(config.get("org_id") or "").strip()
552
+ org_id = (
553
+ current_org
554
+ if current_org and not current_org.startswith("legacy-org:")
555
+ else new_identity_id("org")
556
+ )
557
+ config = initialize_org_config(config, org_id=org_id)
558
+ config["org_id"] = org_id
559
+ _atomic_committed_json(config_path, config)
560
+ try:
561
+ state = json.loads(state_path.read_text(encoding="utf-8"))
562
+ except (OSError, json.JSONDecodeError):
563
+ state = {}
564
+ migrated, _ = migrate_local_identity_state(state, org_id=org_id)
565
+ _atomic_json(state_path, migrated)
566
+
567
+ _ensure_identity_declaration(root, config)
568
+ return {
569
+ "org_id": org_id,
570
+ "account_id": str(migrated["account_id"]),
571
+ "actor_id": str(migrated["actor_id"]),
572
+ "membership_id": str(migrated["membership_id"]),
573
+ }
574
+
575
+
576
+ def _ensure_identity_declaration(root: Path, config: Mapping[str, Any]) -> None:
577
+ declaration = root / str(config["profile"]["identity_path"])
578
+ if declaration.exists():
579
+ return
580
+ declaration.parent.mkdir(parents=True, exist_ok=True)
581
+ declaration.write_text(
582
+ render_identity_document(
583
+ str(config.get("org_name") or config.get("name") or config.get("slug"))
584
+ ),
585
+ encoding="utf-8",
586
+ )
587
+
588
+
589
+ @contextmanager
590
+ def _identity_lock(state_path: Path):
591
+ if state_path.is_symlink():
592
+ state_path = state_path.resolve()
593
+ lock_path = state_path.with_name(f"{state_path.name}.identity.lock")
594
+ lock_path.parent.mkdir(parents=True, exist_ok=True)
595
+ with lock_path.open("a+", encoding="utf-8") as handle:
596
+ os.chmod(lock_path, 0o600)
597
+ fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
598
+ try:
599
+ yield
600
+ finally:
601
+ fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
602
+
603
+
604
+ class LocalIdentityResolver:
605
+ """Resolve actor context from committed org config and local user state."""
606
+
607
+ def __init__(
608
+ self,
609
+ root: Path | str,
610
+ *,
611
+ persist_migrations: bool = True,
612
+ entitlement_row: Mapping[str, Any] | None = None,
613
+ ) -> None:
614
+ self.root = Path(root)
615
+ self.persist_migrations = persist_migrations
616
+ self.entitlement_row = entitlement_row
617
+
618
+ def resolve(self, *, session_id: str, harness: str) -> ActorContext:
619
+ profile = load_profile(self.root / "egregore.json")
620
+ state_path = self.root / ".egregore-state.json"
621
+ try:
622
+ state = json.loads(state_path.read_text(encoding="utf-8"))
623
+ except FileNotFoundError as exc:
624
+ raise ValueError(f"local identity state is missing: {state_path}") from exc
625
+ if not isinstance(state, dict):
626
+ raise ValueError(f"local identity state must be an object: {state_path}")
627
+
628
+ config = json.loads((self.root / "egregore.json").read_text(encoding="utf-8"))
629
+ id_factory = new_identity_id
630
+ if (
631
+ profile.org_id.startswith("legacy-org:")
632
+ and (str(config.get("mode") or "local") == "connected" or config.get("api_url"))
633
+ ):
634
+ # A pre-migration Connected install must remain usable offline, but
635
+ # it must not mint a new random actor in each detached checkout.
636
+ # These explicitly legacy ids are deterministic compatibility
637
+ # placeholders and are replaced only by control-plane IDs.
638
+ legacy_seed = json.dumps(
639
+ {
640
+ "org": profile.org_id,
641
+ "person_id": state.get("person_id"),
642
+ "github_id": state.get("github_id"),
643
+ "github_username": state.get("github_username"),
644
+ "email": state.get("email"),
645
+ },
646
+ sort_keys=True,
647
+ separators=(",", ":"),
648
+ )
649
+
650
+ def id_factory(prefix: str) -> str:
651
+ digest = hashlib.sha256(f"{prefix}:{legacy_seed}".encode()).hexdigest()[:24]
652
+ return f"legacy-{prefix}:{digest}"
653
+
654
+ migrated, changed = migrate_local_identity_state(
655
+ state, org_id=profile.org_id, id_factory=id_factory
656
+ )
657
+ if changed and self.persist_migrations:
658
+ # First-session races must not mint competing account/actor ids.
659
+ # Re-read after taking the instance lock, then persist once.
660
+ with _identity_lock(state_path):
661
+ current = json.loads(state_path.read_text(encoding="utf-8"))
662
+ if not isinstance(current, dict):
663
+ raise ValueError(f"local identity state must be an object: {state_path}")
664
+ migrated, changed = migrate_local_identity_state(
665
+ current, org_id=profile.org_id, id_factory=id_factory
666
+ )
667
+ if changed:
668
+ _atomic_json(state_path, migrated)
669
+
670
+ provider_aliases: dict[str, str] = {}
671
+ for provider, record in (migrated.get("provider_identities") or {}).items():
672
+ if isinstance(record, Mapping) and record.get("subject"):
673
+ provider_aliases[str(provider)] = str(record["subject"])
674
+
675
+ display_name = str(
676
+ migrated.get("display_name")
677
+ or migrated.get("github_name")
678
+ or migrated.get("name")
679
+ or "Egregore member"
680
+ )
681
+ account = AccountIdentity(
682
+ account_id=str(migrated["account_id"]),
683
+ display_name=display_name,
684
+ primary_email=migrated.get("email"),
685
+ provider_aliases=provider_aliases,
686
+ )
687
+ aliases = {}
688
+ if migrated.get("person_id"):
689
+ aliases["legacy.person"] = str(migrated["person_id"])
690
+ if migrated.get("github_username"):
691
+ aliases["github.username"] = str(migrated["github_username"])
692
+ actor = actor_identity(
693
+ kind=ActorKind.PERSON,
694
+ actor_id=str(migrated["actor_id"]),
695
+ display_name=display_name,
696
+ account_id=account.account_id,
697
+ aliases=aliases,
698
+ )
699
+
700
+ roles = tuple(
701
+ dict.fromkeys(
702
+ str(value).lower()
703
+ for value in (
704
+ migrated.get("role") or "member",
705
+ migrated.get("onboarding", {}).get("role")
706
+ if isinstance(migrated.get("onboarding"), Mapping)
707
+ else None,
708
+ )
709
+ if value
710
+ )
711
+ )
712
+ teams_value = migrated.get("teams") or ()
713
+ teams = tuple(str(item) for item in teams_value) if isinstance(teams_value, list) else ()
714
+ membership = OrgMembership(
715
+ membership_id=str(migrated["membership_id"]),
716
+ org_id=profile.org_id,
717
+ actor_id=actor.actor_id,
718
+ roles=roles,
719
+ teams=teams,
720
+ status=str(migrated.get("membership_status") or "active"),
721
+ )
722
+ entitlements = resolve_entitlements(
723
+ mode=str(config.get("mode") or "local"),
724
+ row=self.entitlement_row,
725
+ )
726
+ return ActorContext(
727
+ account=account,
728
+ actor=actor,
729
+ profile=profile,
730
+ membership=membership,
731
+ entitlements=entitlements,
732
+ session_id=session_id,
733
+ harness=harness,
734
+ )