create-egregore 0.21.6 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (719) hide show
  1. package/assets/egregore-launcher.js +58 -6
  2. package/assets/egregore-launcher.sh +15 -2
  3. package/lib/claude-runtime.js +2 -0
  4. package/lib/codex-runtime.js +2 -0
  5. package/lib/setup.js +20 -3
  6. package/package.json +2 -2
  7. package/runtime/claude/.claude/context/commit-format.md +7 -4
  8. package/runtime/claude/.claude/context/handoff-claiming.md +32 -0
  9. package/runtime/claude/.claude/context/notification-consent.md +12 -9
  10. package/runtime/claude/.claude/hooks/boundary-check.sh +14 -331
  11. package/runtime/claude/.claude/hooks/memory-read-guard.sh +9 -1
  12. package/runtime/claude/.claude/rules/voice-bedrock.md +6 -2
  13. package/runtime/claude/.claude/settings.json +1 -1
  14. package/runtime/claude/.claude/skills/commit/SKILL.md +5 -3
  15. package/runtime/claude/.claude/skills/dark-mode/SKILL.md +48 -369
  16. package/runtime/claude/.claude/skills/records/SKILL.md +158 -0
  17. package/runtime/claude/.claude/skills/reflect/SKILL.md +50 -21
  18. package/runtime/claude/.claude/skills/review-pr/SKILL.md +4 -2
  19. package/runtime/claude/.claude/skills/save/SKILL.md +9 -3
  20. package/runtime/claude/.claude/skills/summon/SKILL.md +138 -207
  21. package/runtime/claude/.claude/skills/tui-design/SKILL.md +1 -1
  22. package/runtime/claude/.claude/skills/update/SKILL.md +1 -1
  23. package/runtime/claude/.claude/skills/view/SKILL.md +52 -16
  24. package/runtime/claude/.claude/skills/view/compose-scaffold.html +19 -13
  25. package/runtime/claude/CLAUDE.md +59 -95
  26. package/runtime/claude/bin/agent.sh +231 -7
  27. package/runtime/claude/bin/api-call.sh +44 -23
  28. package/runtime/claude/bin/artifact-register.sh +7 -1
  29. package/runtime/claude/bin/artifact-writeback.sh +1 -1
  30. package/runtime/claude/bin/artifacts.sh +66 -265
  31. package/runtime/claude/bin/attendant.sh +4 -1
  32. package/runtime/claude/bin/boundary-guard.cjs +1848 -0
  33. package/runtime/claude/bin/boundary-hook.sh +41 -0
  34. package/runtime/claude/bin/boundary.sh +15 -155
  35. package/runtime/claude/bin/capture-run.sh +32 -2
  36. package/runtime/claude/bin/codex-render-spec.mjs +24 -46
  37. package/runtime/claude/bin/codex-session-start.sh +19 -0
  38. package/runtime/claude/bin/codex-skill-render.mjs +18 -0
  39. package/runtime/claude/bin/config-get.sh +10 -1
  40. package/runtime/claude/bin/connect-refresh.sh +16 -0
  41. package/runtime/claude/bin/corpus_map.py +5 -0
  42. package/runtime/claude/bin/data/capabilities/claude.json +1480 -0
  43. package/runtime/claude/bin/genui.sh +19 -0
  44. package/runtime/claude/bin/graph-batch.sh +1 -1
  45. package/runtime/claude/bin/graph-op.sh +14 -0
  46. package/runtime/claude/bin/graph.sh +1 -1
  47. package/runtime/claude/bin/handoff-pr-backfill.sh +1 -1
  48. package/runtime/claude/bin/handoff-run.sh +11 -6
  49. package/runtime/claude/bin/ingest-item.sh +1 -1
  50. package/runtime/claude/bin/ingest-surface.html +22 -4
  51. package/runtime/claude/bin/issue.sh +1 -1
  52. package/runtime/claude/bin/knowledge.sh +1 -1
  53. package/runtime/claude/bin/lib/card-delivery.sh +329 -0
  54. package/runtime/claude/bin/lib/config.sh +148 -3
  55. package/runtime/claude/bin/lib/context.sh +38 -67
  56. package/runtime/claude/bin/lib/genui-entry.py +36 -0
  57. package/runtime/claude/bin/lib/git-message.sh +25 -6
  58. package/runtime/claude/bin/lib/git-sync.sh +12 -4
  59. package/runtime/claude/bin/lib/greeting.sh +255 -28
  60. package/runtime/claude/bin/lib/handoff_meta.py +294 -0
  61. package/runtime/claude/bin/lib/health-footer.sh +83 -2
  62. package/runtime/claude/bin/lib/identity.sh +31 -10
  63. package/runtime/claude/bin/lib/instance-registry.sh +71 -0
  64. package/runtime/claude/bin/lib/local_ci_receipt.py +249 -0
  65. package/runtime/claude/bin/lib/metrics.sh +121 -8
  66. package/runtime/claude/bin/lib/notices.sh +36 -0
  67. package/runtime/claude/bin/lib/record_outcomes.py +729 -0
  68. package/runtime/claude/bin/lib/reflexes.py +267 -0
  69. package/runtime/claude/bin/lib/retrieval_scorecard.py +624 -0
  70. package/runtime/claude/bin/lib/session-record-prompt.txt +18 -0
  71. package/runtime/claude/bin/lib/session_record.py +3747 -0
  72. package/runtime/claude/bin/lib/session_trail.py +1983 -0
  73. package/runtime/claude/bin/lib/settings-drift.sh +8 -5
  74. package/runtime/claude/bin/lib/startup-restart.sh +41 -0
  75. package/runtime/claude/bin/lib/worktree-links.sh +92 -0
  76. package/runtime/claude/bin/memory-push.sh +6 -0
  77. package/runtime/claude/bin/merge-gate.sh +498 -0
  78. package/runtime/claude/bin/notify.sh +5 -2
  79. package/runtime/claude/bin/page-access.sh +103 -0
  80. package/runtime/claude/bin/person.sh +13 -2
  81. package/runtime/claude/bin/pi-render-spec.mjs +0 -1
  82. package/runtime/claude/bin/pi-spirit-discovery.mjs +34 -0
  83. package/runtime/claude/bin/post-update.sh +181 -0
  84. package/runtime/claude/bin/prime-render-spec.mjs +0 -1
  85. package/runtime/claude/bin/publish-artifact.sh +89 -42
  86. package/runtime/claude/bin/qa-inventory.sh +2 -2
  87. package/runtime/claude/bin/quest.sh +1 -1
  88. package/runtime/claude/bin/records.sh +11 -0
  89. package/runtime/claude/bin/reflexes/05-vitals.sh +9 -0
  90. package/runtime/claude/bin/reflexes/10-open-items.sh +12 -0
  91. package/runtime/claude/bin/reflexes/15-retrieval.sh +8 -0
  92. package/runtime/claude/bin/reflexes/20-nightly.sh +100 -0
  93. package/runtime/claude/bin/reflexes/20-sandbox.sh +85 -0
  94. package/runtime/claude/bin/reflexes/30-cost.py +116 -0
  95. package/runtime/claude/bin/reflexes/40-shells.py +51 -0
  96. package/runtime/claude/bin/reflexes/README.md +61 -0
  97. package/runtime/claude/bin/reflexes.sh +7 -0
  98. package/runtime/claude/bin/runtime-identity.sh +140 -42
  99. package/runtime/claude/bin/screenshot.sh +77 -0
  100. package/runtime/claude/bin/scroll.sh +1 -1
  101. package/runtime/claude/bin/search.sh +31 -5
  102. package/runtime/claude/bin/session-autosave.sh +6 -3
  103. package/runtime/claude/bin/session-id.sh +147 -0
  104. package/runtime/claude/bin/session-log.sh +19 -0
  105. package/runtime/claude/bin/session-record.sh +26 -0
  106. package/runtime/claude/bin/session-start.sh +121 -151
  107. package/runtime/claude/bin/settings.sh +13 -8
  108. package/runtime/claude/bin/spirit-discovery-claude.sh +4 -0
  109. package/runtime/claude/bin/spirit-discovery-codex.sh +4 -0
  110. package/runtime/claude/bin/spirit-discovery-pi.sh +4 -0
  111. package/runtime/claude/bin/spirit-discovery-prime.sh +4 -0
  112. package/runtime/claude/bin/spirit-discovery.sh +4 -0
  113. package/runtime/claude/bin/startup-check.sh +1 -1
  114. package/runtime/claude/bin/telemetry.sh +11 -4
  115. package/runtime/claude/bin/test-baseline.sh +33 -5
  116. package/runtime/claude/bin/todo.sh +1 -1
  117. package/runtime/claude/bin/worktree-create.sh +15 -45
  118. package/runtime/claude/bin/worktree.sh +6 -0
  119. package/runtime/claude/docs/spirit-discovery.md +96 -0
  120. package/runtime/claude/egregore_runtime/__init__.py +18 -12
  121. package/runtime/claude/egregore_runtime/access.py +608 -0
  122. package/runtime/claude/egregore_runtime/access_cli.py +190 -0
  123. package/runtime/claude/egregore_runtime/adapters/invitation.py +5 -3
  124. package/runtime/claude/egregore_runtime/adapters/qmd.py +521 -47
  125. package/runtime/claude/egregore_runtime/admin_entry.py +56 -0
  126. package/runtime/claude/egregore_runtime/artifacts.py +222 -16
  127. package/runtime/claude/egregore_runtime/contracts.py +27 -0
  128. package/runtime/claude/egregore_runtime/eligibility.py +5 -12
  129. package/runtime/claude/egregore_runtime/episode_binding.py +16 -5
  130. package/runtime/claude/egregore_runtime/github_issues.py +2 -2
  131. package/runtime/claude/egregore_runtime/handoff_lookup.py +9 -20
  132. package/runtime/claude/egregore_runtime/harness_cli.py +674 -149
  133. package/runtime/claude/egregore_runtime/held.py +2436 -0
  134. package/runtime/claude/egregore_runtime/hosted_api.py +137 -0
  135. package/runtime/claude/egregore_runtime/identity.py +196 -65
  136. package/runtime/claude/egregore_runtime/identity_transfer.py +6 -2
  137. package/runtime/claude/egregore_runtime/ingest_cli.py +2 -2
  138. package/runtime/claude/egregore_runtime/ingest_index_cli.py +4 -16
  139. package/runtime/claude/egregore_runtime/installation.py +119 -0
  140. package/runtime/claude/egregore_runtime/investigation.py +64 -41
  141. package/runtime/claude/egregore_runtime/investigation_guidance.py +16 -11
  142. package/runtime/claude/egregore_runtime/issue_cli.py +51 -32
  143. package/runtime/claude/egregore_runtime/issue_reporting.py +4 -3
  144. package/runtime/claude/egregore_runtime/issue_triage_service.py +2 -2
  145. package/runtime/claude/egregore_runtime/issues.py +12 -6
  146. package/runtime/claude/egregore_runtime/knowledge.py +331 -5
  147. package/runtime/claude/egregore_runtime/knowledge_cli.py +57 -17
  148. package/runtime/claude/egregore_runtime/lifecycle.py +88 -22
  149. package/runtime/claude/egregore_runtime/lifecycle_cli.py +21 -13
  150. package/runtime/claude/egregore_runtime/line_ids.py +10 -0
  151. package/runtime/claude/egregore_runtime/memory_push.py +349 -0
  152. package/runtime/claude/egregore_runtime/observe.py +64 -25
  153. package/runtime/claude/egregore_runtime/observers.py +19 -0
  154. package/runtime/claude/egregore_runtime/optional_modules.py +13 -0
  155. package/runtime/claude/egregore_runtime/outputs_cli.py +321 -0
  156. package/runtime/claude/egregore_runtime/policy.py +651 -24
  157. package/runtime/claude/egregore_runtime/private_result.py +2 -1
  158. package/runtime/claude/egregore_runtime/projection.py +946 -0
  159. package/runtime/claude/egregore_runtime/quest_cli.py +20 -23
  160. package/runtime/claude/egregore_runtime/question_cli.py +15 -11
  161. package/runtime/claude/egregore_runtime/questions.py +10 -10
  162. package/runtime/claude/egregore_runtime/quests.py +10 -10
  163. package/runtime/claude/egregore_runtime/record_publish.py +548 -0
  164. package/runtime/claude/egregore_runtime/record_subject.py +564 -0
  165. package/runtime/claude/egregore_runtime/research_ingest.py +2 -4
  166. package/runtime/claude/egregore_runtime/runtime.py +169 -60
  167. package/runtime/claude/egregore_runtime/sandbox.py +194 -0
  168. package/runtime/claude/egregore_runtime/scroll_cli.py +22 -32
  169. package/runtime/claude/egregore_runtime/scrolls.py +23 -15
  170. package/runtime/claude/egregore_runtime/shadow.py +262 -0
  171. package/runtime/claude/egregore_runtime/spirit_discovery.py +487 -0
  172. package/runtime/claude/egregore_runtime/spirit_discovery_cli.py +25 -0
  173. package/runtime/claude/egregore_runtime/spirit_discovery_transport.py +18 -0
  174. package/runtime/claude/egregore_runtime/status.py +101 -44
  175. package/runtime/claude/egregore_runtime/status_cli.py +13 -5
  176. package/runtime/claude/egregore_runtime/sync.py +39 -21
  177. package/runtime/claude/egregore_runtime/taste.py +934 -0
  178. package/runtime/claude/egregore_runtime/taste_cli.py +242 -0
  179. package/runtime/claude/egregore_runtime/taste_rules.py +91 -0
  180. package/runtime/claude/egregore_runtime/telemetry.py +26 -3
  181. package/runtime/claude/egregore_runtime/todo_cli.py +15 -12
  182. package/runtime/claude/egregore_runtime/todos.py +3 -2
  183. package/runtime/claude/egregore_runtime/upgrade.py +85 -58
  184. package/runtime/claude/egregore_runtime/writeback.py +330 -24
  185. package/runtime/claude/manifest.json +65 -5
  186. package/runtime/codex/.claude/context/handoff-claiming.md +32 -0
  187. package/runtime/codex/.claude/context/notification-consent.md +12 -9
  188. package/runtime/codex/.claude/skills/view/SKILL.md +438 -0
  189. package/runtime/codex/.codex/hooks.json +11 -0
  190. package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
  191. package/runtime/codex/.codex/skills/records/SKILL.md +29 -0
  192. package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
  193. package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +19 -13
  194. package/runtime/codex/.codex/spec-manifest.json +13 -19
  195. package/runtime/codex/AGENTS.md +54 -105
  196. package/runtime/codex/bin/agent.sh +231 -7
  197. package/runtime/codex/bin/api-call.sh +44 -23
  198. package/runtime/codex/bin/artifact-register.sh +7 -1
  199. package/runtime/codex/bin/artifact-writeback.sh +1 -1
  200. package/runtime/codex/bin/artifacts.sh +66 -265
  201. package/runtime/codex/bin/attendant.sh +4 -1
  202. package/runtime/codex/bin/boundary-guard.cjs +1848 -0
  203. package/runtime/codex/bin/boundary-hook.sh +41 -0
  204. package/runtime/codex/bin/capture-run.sh +32 -2
  205. package/runtime/codex/bin/codex-render-spec.mjs +24 -46
  206. package/runtime/codex/bin/codex-session-start.sh +19 -0
  207. package/runtime/codex/bin/codex-skill-render.mjs +18 -0
  208. package/runtime/codex/bin/config-get.sh +10 -1
  209. package/runtime/codex/bin/connect-refresh.sh +16 -0
  210. package/runtime/codex/bin/corpus_map.py +5 -0
  211. package/runtime/codex/bin/data/capabilities/codex.json +1458 -0
  212. package/runtime/codex/bin/genui.sh +19 -0
  213. package/runtime/codex/bin/graph-batch.sh +1 -1
  214. package/runtime/codex/bin/graph-op.sh +14 -0
  215. package/runtime/codex/bin/graph.sh +1 -1
  216. package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
  217. package/runtime/codex/bin/handoff-run.sh +11 -6
  218. package/runtime/codex/bin/ingest-item.sh +1 -1
  219. package/runtime/codex/bin/ingest-surface.html +22 -4
  220. package/runtime/codex/bin/issue.sh +1 -1
  221. package/runtime/codex/bin/knowledge.sh +1 -1
  222. package/runtime/codex/bin/lib/card-delivery.sh +329 -0
  223. package/runtime/codex/bin/lib/config.sh +148 -3
  224. package/runtime/codex/bin/lib/context.sh +38 -67
  225. package/runtime/codex/bin/lib/genui-entry.py +36 -0
  226. package/runtime/codex/bin/lib/git-message.sh +25 -6
  227. package/runtime/codex/bin/lib/git-sync.sh +12 -4
  228. package/runtime/codex/bin/lib/greeting.sh +255 -28
  229. package/runtime/codex/bin/lib/handoff_meta.py +294 -0
  230. package/runtime/codex/bin/lib/health-footer.sh +83 -2
  231. package/runtime/codex/bin/lib/identity.sh +31 -10
  232. package/runtime/codex/bin/lib/instance-registry.sh +71 -0
  233. package/runtime/codex/bin/lib/local_ci_receipt.py +249 -0
  234. package/runtime/codex/bin/lib/metrics.sh +121 -8
  235. package/runtime/codex/bin/lib/notices.sh +36 -0
  236. package/runtime/codex/bin/lib/record_outcomes.py +729 -0
  237. package/runtime/codex/bin/lib/reflexes.py +267 -0
  238. package/runtime/codex/bin/lib/retrieval_scorecard.py +624 -0
  239. package/runtime/codex/bin/lib/session-record-prompt.txt +18 -0
  240. package/runtime/codex/bin/lib/session_record.py +3747 -0
  241. package/runtime/codex/bin/lib/session_trail.py +1983 -0
  242. package/runtime/codex/bin/lib/settings-drift.sh +8 -5
  243. package/runtime/codex/bin/lib/startup-restart.sh +41 -0
  244. package/runtime/codex/bin/lib/worktree-links.sh +92 -0
  245. package/runtime/codex/bin/memory-push.sh +6 -0
  246. package/runtime/codex/bin/merge-gate.sh +498 -0
  247. package/runtime/codex/bin/notify.sh +5 -2
  248. package/runtime/codex/bin/page-access.sh +103 -0
  249. package/runtime/codex/bin/person.sh +13 -2
  250. package/runtime/codex/bin/pi-render-spec.mjs +0 -1
  251. package/runtime/codex/bin/pi-spirit-discovery.mjs +34 -0
  252. package/runtime/codex/bin/post-update.sh +181 -0
  253. package/runtime/codex/bin/prime-render-spec.mjs +0 -1
  254. package/runtime/codex/bin/publish-artifact.sh +89 -42
  255. package/runtime/codex/bin/qa-inventory.sh +2 -2
  256. package/runtime/codex/bin/quest.sh +1 -1
  257. package/runtime/codex/bin/records.sh +11 -0
  258. package/runtime/codex/bin/reflexes/05-vitals.sh +9 -0
  259. package/runtime/codex/bin/reflexes/10-open-items.sh +12 -0
  260. package/runtime/codex/bin/reflexes/15-retrieval.sh +8 -0
  261. package/runtime/codex/bin/reflexes/20-nightly.sh +100 -0
  262. package/runtime/codex/bin/reflexes/20-sandbox.sh +85 -0
  263. package/runtime/codex/bin/reflexes/30-cost.py +116 -0
  264. package/runtime/codex/bin/reflexes/40-shells.py +51 -0
  265. package/runtime/codex/bin/reflexes/README.md +61 -0
  266. package/runtime/codex/bin/reflexes.sh +7 -0
  267. package/runtime/codex/bin/runtime-identity.sh +140 -42
  268. package/runtime/codex/bin/screenshot.sh +77 -0
  269. package/runtime/codex/bin/scroll.sh +1 -1
  270. package/runtime/codex/bin/search.sh +31 -5
  271. package/runtime/codex/bin/session-autosave.sh +6 -3
  272. package/runtime/codex/bin/session-id.sh +147 -0
  273. package/runtime/codex/bin/session-log.sh +19 -0
  274. package/runtime/codex/bin/session-record.sh +26 -0
  275. package/runtime/codex/bin/settings.sh +13 -8
  276. package/runtime/codex/bin/spirit-discovery-claude.sh +4 -0
  277. package/runtime/codex/bin/spirit-discovery-codex.sh +4 -0
  278. package/runtime/codex/bin/spirit-discovery-pi.sh +4 -0
  279. package/runtime/codex/bin/spirit-discovery-prime.sh +4 -0
  280. package/runtime/codex/bin/spirit-discovery.sh +4 -0
  281. package/runtime/codex/bin/startup-check.sh +1 -1
  282. package/runtime/codex/bin/telemetry.sh +11 -4
  283. package/runtime/codex/bin/test-baseline.sh +33 -5
  284. package/runtime/codex/bin/todo.sh +1 -1
  285. package/runtime/codex/bin/worktree-create.sh +15 -45
  286. package/runtime/codex/bin/worktree.sh +6 -0
  287. package/runtime/codex/docs/spirit-discovery.md +96 -0
  288. package/runtime/codex/egregore_runtime/__init__.py +18 -12
  289. package/runtime/codex/egregore_runtime/access.py +608 -0
  290. package/runtime/codex/egregore_runtime/access_cli.py +190 -0
  291. package/runtime/codex/egregore_runtime/adapters/invitation.py +5 -3
  292. package/runtime/codex/egregore_runtime/adapters/qmd.py +521 -47
  293. package/runtime/codex/egregore_runtime/admin_entry.py +56 -0
  294. package/runtime/codex/egregore_runtime/artifacts.py +222 -16
  295. package/runtime/codex/egregore_runtime/contracts.py +27 -0
  296. package/runtime/codex/egregore_runtime/eligibility.py +5 -12
  297. package/runtime/codex/egregore_runtime/episode_binding.py +16 -5
  298. package/runtime/codex/egregore_runtime/github_issues.py +2 -2
  299. package/runtime/codex/egregore_runtime/handoff_lookup.py +9 -20
  300. package/runtime/codex/egregore_runtime/harness_cli.py +674 -149
  301. package/runtime/codex/egregore_runtime/held.py +2436 -0
  302. package/runtime/codex/egregore_runtime/hosted_api.py +137 -0
  303. package/runtime/codex/egregore_runtime/identity.py +196 -65
  304. package/runtime/codex/egregore_runtime/identity_transfer.py +6 -2
  305. package/runtime/codex/egregore_runtime/ingest_cli.py +2 -2
  306. package/runtime/codex/egregore_runtime/ingest_index_cli.py +4 -16
  307. package/runtime/codex/egregore_runtime/installation.py +119 -0
  308. package/runtime/codex/egregore_runtime/investigation.py +64 -41
  309. package/runtime/codex/egregore_runtime/investigation_guidance.py +16 -11
  310. package/runtime/codex/egregore_runtime/issue_cli.py +51 -32
  311. package/runtime/codex/egregore_runtime/issue_reporting.py +4 -3
  312. package/runtime/codex/egregore_runtime/issue_triage_service.py +2 -2
  313. package/runtime/codex/egregore_runtime/issues.py +12 -6
  314. package/runtime/codex/egregore_runtime/knowledge.py +331 -5
  315. package/runtime/codex/egregore_runtime/knowledge_cli.py +57 -17
  316. package/runtime/codex/egregore_runtime/lifecycle.py +88 -22
  317. package/runtime/codex/egregore_runtime/lifecycle_cli.py +21 -13
  318. package/runtime/codex/egregore_runtime/line_ids.py +10 -0
  319. package/runtime/codex/egregore_runtime/memory_push.py +349 -0
  320. package/runtime/codex/egregore_runtime/observe.py +64 -25
  321. package/runtime/codex/egregore_runtime/observers.py +19 -0
  322. package/runtime/codex/egregore_runtime/optional_modules.py +13 -0
  323. package/runtime/codex/egregore_runtime/outputs_cli.py +321 -0
  324. package/runtime/codex/egregore_runtime/policy.py +651 -24
  325. package/runtime/codex/egregore_runtime/private_result.py +2 -1
  326. package/runtime/codex/egregore_runtime/projection.py +946 -0
  327. package/runtime/codex/egregore_runtime/quest_cli.py +20 -23
  328. package/runtime/codex/egregore_runtime/question_cli.py +15 -11
  329. package/runtime/codex/egregore_runtime/questions.py +10 -10
  330. package/runtime/codex/egregore_runtime/quests.py +10 -10
  331. package/runtime/codex/egregore_runtime/record_publish.py +548 -0
  332. package/runtime/codex/egregore_runtime/record_subject.py +564 -0
  333. package/runtime/codex/egregore_runtime/research_ingest.py +2 -4
  334. package/runtime/codex/egregore_runtime/runtime.py +169 -60
  335. package/runtime/codex/egregore_runtime/sandbox.py +194 -0
  336. package/runtime/codex/egregore_runtime/scroll_cli.py +22 -32
  337. package/runtime/codex/egregore_runtime/scrolls.py +23 -15
  338. package/runtime/codex/egregore_runtime/shadow.py +262 -0
  339. package/runtime/codex/egregore_runtime/spirit_discovery.py +487 -0
  340. package/runtime/codex/egregore_runtime/spirit_discovery_cli.py +25 -0
  341. package/runtime/codex/egregore_runtime/spirit_discovery_transport.py +18 -0
  342. package/runtime/codex/egregore_runtime/status.py +101 -44
  343. package/runtime/codex/egregore_runtime/status_cli.py +13 -5
  344. package/runtime/codex/egregore_runtime/sync.py +39 -21
  345. package/runtime/codex/egregore_runtime/taste.py +934 -0
  346. package/runtime/codex/egregore_runtime/taste_cli.py +242 -0
  347. package/runtime/codex/egregore_runtime/taste_rules.py +91 -0
  348. package/runtime/codex/egregore_runtime/telemetry.py +26 -3
  349. package/runtime/codex/egregore_runtime/todo_cli.py +15 -12
  350. package/runtime/codex/egregore_runtime/todos.py +3 -2
  351. package/runtime/codex/egregore_runtime/upgrade.py +85 -58
  352. package/runtime/codex/egregore_runtime/writeback.py +330 -24
  353. package/runtime/codex/manifest.json +66 -5
  354. package/runtime/pi/.claude/context/handoff-claiming.md +32 -0
  355. package/runtime/pi/.claude/context/notification-consent.md +12 -9
  356. package/runtime/pi/.claude/skills/view/SKILL.md +438 -0
  357. package/runtime/pi/.codex/hooks.json +11 -0
  358. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
  359. package/runtime/pi/.codex/skills/records/SKILL.md +29 -0
  360. package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
  361. package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +19 -13
  362. package/runtime/pi/.codex/spec-manifest.json +13 -19
  363. package/runtime/pi/.pi/APPEND_SYSTEM.md +53 -99
  364. package/runtime/pi/.pi/extensions/egregore.ts +13 -1
  365. package/runtime/pi/.pi/spec-manifest.json +2 -2
  366. package/runtime/pi/AGENTS.md +54 -105
  367. package/runtime/pi/bin/agent.sh +231 -7
  368. package/runtime/pi/bin/api-call.sh +44 -23
  369. package/runtime/pi/bin/artifact-register.sh +7 -1
  370. package/runtime/pi/bin/artifact-writeback.sh +1 -1
  371. package/runtime/pi/bin/artifacts.sh +66 -265
  372. package/runtime/pi/bin/attendant.sh +4 -1
  373. package/runtime/pi/bin/boundary-guard.cjs +1848 -0
  374. package/runtime/pi/bin/boundary-hook.sh +41 -0
  375. package/runtime/pi/bin/capture-run.sh +32 -2
  376. package/runtime/pi/bin/codex-render-spec.mjs +24 -46
  377. package/runtime/pi/bin/codex-session-start.sh +19 -0
  378. package/runtime/pi/bin/codex-skill-render.mjs +18 -0
  379. package/runtime/pi/bin/config-get.sh +10 -1
  380. package/runtime/pi/bin/connect-refresh.sh +16 -0
  381. package/runtime/pi/bin/corpus_map.py +5 -0
  382. package/runtime/pi/bin/data/capabilities/pi.json +1458 -0
  383. package/runtime/pi/bin/genui.sh +19 -0
  384. package/runtime/pi/bin/graph-batch.sh +1 -1
  385. package/runtime/pi/bin/graph-op.sh +14 -0
  386. package/runtime/pi/bin/graph.sh +1 -1
  387. package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
  388. package/runtime/pi/bin/handoff-run.sh +11 -6
  389. package/runtime/pi/bin/ingest-item.sh +1 -1
  390. package/runtime/pi/bin/ingest-surface.html +22 -4
  391. package/runtime/pi/bin/issue.sh +1 -1
  392. package/runtime/pi/bin/knowledge.sh +1 -1
  393. package/runtime/pi/bin/lib/card-delivery.sh +329 -0
  394. package/runtime/pi/bin/lib/config.sh +148 -3
  395. package/runtime/pi/bin/lib/context.sh +38 -67
  396. package/runtime/pi/bin/lib/genui-entry.py +36 -0
  397. package/runtime/pi/bin/lib/git-message.sh +25 -6
  398. package/runtime/pi/bin/lib/git-sync.sh +12 -4
  399. package/runtime/pi/bin/lib/greeting.sh +255 -28
  400. package/runtime/pi/bin/lib/handoff_meta.py +294 -0
  401. package/runtime/pi/bin/lib/health-footer.sh +83 -2
  402. package/runtime/pi/bin/lib/identity.sh +31 -10
  403. package/runtime/pi/bin/lib/instance-registry.sh +71 -0
  404. package/runtime/pi/bin/lib/local_ci_receipt.py +249 -0
  405. package/runtime/pi/bin/lib/metrics.sh +121 -8
  406. package/runtime/pi/bin/lib/notices.sh +36 -0
  407. package/runtime/pi/bin/lib/record_outcomes.py +729 -0
  408. package/runtime/pi/bin/lib/reflexes.py +267 -0
  409. package/runtime/pi/bin/lib/retrieval_scorecard.py +624 -0
  410. package/runtime/pi/bin/lib/session-record-prompt.txt +18 -0
  411. package/runtime/pi/bin/lib/session_record.py +3747 -0
  412. package/runtime/pi/bin/lib/session_trail.py +1983 -0
  413. package/runtime/pi/bin/lib/settings-drift.sh +8 -5
  414. package/runtime/pi/bin/lib/startup-restart.sh +41 -0
  415. package/runtime/pi/bin/lib/worktree-links.sh +92 -0
  416. package/runtime/pi/bin/memory-push.sh +6 -0
  417. package/runtime/pi/bin/merge-gate.sh +498 -0
  418. package/runtime/pi/bin/notify.sh +5 -2
  419. package/runtime/pi/bin/page-access.sh +103 -0
  420. package/runtime/pi/bin/person.sh +13 -2
  421. package/runtime/pi/bin/pi-branch-policy.mjs +37 -0
  422. package/runtime/pi/bin/pi-observe-context.mjs +97 -4
  423. package/runtime/pi/bin/pi-render-spec.mjs +0 -1
  424. package/runtime/pi/bin/pi-session-start.sh +4 -1
  425. package/runtime/pi/bin/pi-spirit-discovery.mjs +34 -0
  426. package/runtime/pi/bin/post-update.sh +181 -0
  427. package/runtime/pi/bin/prime-render-spec.mjs +0 -1
  428. package/runtime/pi/bin/publish-artifact.sh +89 -42
  429. package/runtime/pi/bin/qa-inventory.sh +2 -2
  430. package/runtime/pi/bin/quest.sh +1 -1
  431. package/runtime/pi/bin/records.sh +11 -0
  432. package/runtime/pi/bin/reflexes/05-vitals.sh +9 -0
  433. package/runtime/pi/bin/reflexes/10-open-items.sh +12 -0
  434. package/runtime/pi/bin/reflexes/15-retrieval.sh +8 -0
  435. package/runtime/pi/bin/reflexes/20-nightly.sh +100 -0
  436. package/runtime/pi/bin/reflexes/20-sandbox.sh +85 -0
  437. package/runtime/pi/bin/reflexes/30-cost.py +116 -0
  438. package/runtime/pi/bin/reflexes/40-shells.py +51 -0
  439. package/runtime/pi/bin/reflexes/README.md +61 -0
  440. package/runtime/pi/bin/reflexes.sh +7 -0
  441. package/runtime/pi/bin/runtime-identity.sh +140 -42
  442. package/runtime/pi/bin/screenshot.sh +77 -0
  443. package/runtime/pi/bin/scroll.sh +1 -1
  444. package/runtime/pi/bin/search.sh +31 -5
  445. package/runtime/pi/bin/session-autosave.sh +6 -3
  446. package/runtime/pi/bin/session-id.sh +147 -0
  447. package/runtime/pi/bin/session-log.sh +19 -0
  448. package/runtime/pi/bin/session-record.sh +26 -0
  449. package/runtime/pi/bin/settings.sh +13 -8
  450. package/runtime/pi/bin/spirit-discovery-claude.sh +4 -0
  451. package/runtime/pi/bin/spirit-discovery-codex.sh +4 -0
  452. package/runtime/pi/bin/spirit-discovery-pi.sh +4 -0
  453. package/runtime/pi/bin/spirit-discovery-prime.sh +4 -0
  454. package/runtime/pi/bin/spirit-discovery.sh +4 -0
  455. package/runtime/pi/bin/startup-check.sh +1 -1
  456. package/runtime/pi/bin/telemetry.sh +11 -4
  457. package/runtime/pi/bin/test-baseline.sh +33 -5
  458. package/runtime/pi/bin/todo.sh +1 -1
  459. package/runtime/pi/bin/worktree-create.sh +15 -45
  460. package/runtime/pi/bin/worktree.sh +6 -0
  461. package/runtime/pi/docs/spirit-discovery.md +96 -0
  462. package/runtime/pi/egregore_runtime/__init__.py +18 -12
  463. package/runtime/pi/egregore_runtime/access.py +608 -0
  464. package/runtime/pi/egregore_runtime/access_cli.py +190 -0
  465. package/runtime/pi/egregore_runtime/adapters/invitation.py +5 -3
  466. package/runtime/pi/egregore_runtime/adapters/qmd.py +521 -47
  467. package/runtime/pi/egregore_runtime/admin_entry.py +56 -0
  468. package/runtime/pi/egregore_runtime/artifacts.py +222 -16
  469. package/runtime/pi/egregore_runtime/contracts.py +27 -0
  470. package/runtime/pi/egregore_runtime/eligibility.py +5 -12
  471. package/runtime/pi/egregore_runtime/episode_binding.py +16 -5
  472. package/runtime/pi/egregore_runtime/github_issues.py +2 -2
  473. package/runtime/pi/egregore_runtime/handoff_lookup.py +9 -20
  474. package/runtime/pi/egregore_runtime/harness_cli.py +674 -149
  475. package/runtime/pi/egregore_runtime/held.py +2436 -0
  476. package/runtime/pi/egregore_runtime/hosted_api.py +137 -0
  477. package/runtime/pi/egregore_runtime/identity.py +196 -65
  478. package/runtime/pi/egregore_runtime/identity_transfer.py +6 -2
  479. package/runtime/pi/egregore_runtime/ingest_cli.py +2 -2
  480. package/runtime/pi/egregore_runtime/ingest_index_cli.py +4 -16
  481. package/runtime/pi/egregore_runtime/installation.py +119 -0
  482. package/runtime/pi/egregore_runtime/investigation.py +64 -41
  483. package/runtime/pi/egregore_runtime/investigation_guidance.py +16 -11
  484. package/runtime/pi/egregore_runtime/issue_cli.py +51 -32
  485. package/runtime/pi/egregore_runtime/issue_reporting.py +4 -3
  486. package/runtime/pi/egregore_runtime/issue_triage_service.py +2 -2
  487. package/runtime/pi/egregore_runtime/issues.py +12 -6
  488. package/runtime/pi/egregore_runtime/knowledge.py +331 -5
  489. package/runtime/pi/egregore_runtime/knowledge_cli.py +57 -17
  490. package/runtime/pi/egregore_runtime/lifecycle.py +88 -22
  491. package/runtime/pi/egregore_runtime/lifecycle_cli.py +21 -13
  492. package/runtime/pi/egregore_runtime/line_ids.py +10 -0
  493. package/runtime/pi/egregore_runtime/memory_push.py +349 -0
  494. package/runtime/pi/egregore_runtime/observe.py +64 -25
  495. package/runtime/pi/egregore_runtime/observers.py +19 -0
  496. package/runtime/pi/egregore_runtime/optional_modules.py +13 -0
  497. package/runtime/pi/egregore_runtime/outputs_cli.py +321 -0
  498. package/runtime/pi/egregore_runtime/policy.py +651 -24
  499. package/runtime/pi/egregore_runtime/private_result.py +2 -1
  500. package/runtime/pi/egregore_runtime/projection.py +946 -0
  501. package/runtime/pi/egregore_runtime/quest_cli.py +20 -23
  502. package/runtime/pi/egregore_runtime/question_cli.py +15 -11
  503. package/runtime/pi/egregore_runtime/questions.py +10 -10
  504. package/runtime/pi/egregore_runtime/quests.py +10 -10
  505. package/runtime/pi/egregore_runtime/record_publish.py +548 -0
  506. package/runtime/pi/egregore_runtime/record_subject.py +564 -0
  507. package/runtime/pi/egregore_runtime/research_ingest.py +2 -4
  508. package/runtime/pi/egregore_runtime/runtime.py +169 -60
  509. package/runtime/pi/egregore_runtime/sandbox.py +194 -0
  510. package/runtime/pi/egregore_runtime/scroll_cli.py +22 -32
  511. package/runtime/pi/egregore_runtime/scrolls.py +23 -15
  512. package/runtime/pi/egregore_runtime/shadow.py +262 -0
  513. package/runtime/pi/egregore_runtime/spirit_discovery.py +487 -0
  514. package/runtime/pi/egregore_runtime/spirit_discovery_cli.py +25 -0
  515. package/runtime/pi/egregore_runtime/spirit_discovery_transport.py +18 -0
  516. package/runtime/pi/egregore_runtime/status.py +101 -44
  517. package/runtime/pi/egregore_runtime/status_cli.py +13 -5
  518. package/runtime/pi/egregore_runtime/sync.py +39 -21
  519. package/runtime/pi/egregore_runtime/taste.py +934 -0
  520. package/runtime/pi/egregore_runtime/taste_cli.py +242 -0
  521. package/runtime/pi/egregore_runtime/taste_rules.py +91 -0
  522. package/runtime/pi/egregore_runtime/telemetry.py +26 -3
  523. package/runtime/pi/egregore_runtime/todo_cli.py +15 -12
  524. package/runtime/pi/egregore_runtime/todos.py +3 -2
  525. package/runtime/pi/egregore_runtime/upgrade.py +85 -58
  526. package/runtime/pi/egregore_runtime/writeback.py +330 -24
  527. package/runtime/pi/manifest.json +66 -5
  528. package/runtime/prime/.claude/context/handoff-claiming.md +32 -0
  529. package/runtime/prime/.claude/context/notification-consent.md +12 -9
  530. package/runtime/prime/.claude/skills/view/SKILL.md +438 -0
  531. package/runtime/prime/.codex/hooks.json +11 -0
  532. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
  533. package/runtime/prime/.codex/skills/records/SKILL.md +29 -0
  534. package/runtime/prime/.codex/skills/summon/SKILL.md +1 -1
  535. package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +19 -13
  536. package/runtime/prime/.codex/spec-manifest.json +13 -19
  537. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +53 -99
  538. package/runtime/prime/.prime/agent/extensions/egregore.ts +14 -1
  539. package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +14 -6
  540. package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +66 -1
  541. package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
  542. package/runtime/prime/AGENTS.md +54 -105
  543. package/runtime/prime/bin/agent.sh +231 -7
  544. package/runtime/prime/bin/api-call.sh +44 -23
  545. package/runtime/prime/bin/artifact-register.sh +7 -1
  546. package/runtime/prime/bin/artifact-writeback.sh +1 -1
  547. package/runtime/prime/bin/artifacts.sh +66 -265
  548. package/runtime/prime/bin/attendant.sh +4 -1
  549. package/runtime/prime/bin/boundary-guard.cjs +1848 -0
  550. package/runtime/prime/bin/boundary-hook.sh +41 -0
  551. package/runtime/prime/bin/capture-run.sh +32 -2
  552. package/runtime/prime/bin/codex-render-spec.mjs +24 -46
  553. package/runtime/prime/bin/codex-session-start.sh +19 -0
  554. package/runtime/prime/bin/codex-skill-render.mjs +18 -0
  555. package/runtime/prime/bin/config-get.sh +10 -1
  556. package/runtime/prime/bin/connect-refresh.sh +16 -0
  557. package/runtime/prime/bin/corpus_map.py +5 -0
  558. package/runtime/prime/bin/data/capabilities/prime.json +1458 -0
  559. package/runtime/prime/bin/genui.sh +19 -0
  560. package/runtime/prime/bin/graph-batch.sh +1 -1
  561. package/runtime/prime/bin/graph-op.sh +14 -0
  562. package/runtime/prime/bin/graph.sh +1 -1
  563. package/runtime/prime/bin/handoff-pr-backfill.sh +1 -1
  564. package/runtime/prime/bin/handoff-run.sh +11 -6
  565. package/runtime/prime/bin/ingest-item.sh +1 -1
  566. package/runtime/prime/bin/ingest-surface.html +22 -4
  567. package/runtime/prime/bin/issue.sh +1 -1
  568. package/runtime/prime/bin/knowledge.sh +1 -1
  569. package/runtime/prime/bin/lib/card-delivery.sh +329 -0
  570. package/runtime/prime/bin/lib/config.sh +148 -3
  571. package/runtime/prime/bin/lib/context.sh +38 -67
  572. package/runtime/prime/bin/lib/genui-entry.py +36 -0
  573. package/runtime/prime/bin/lib/git-message.sh +25 -6
  574. package/runtime/prime/bin/lib/git-sync.sh +12 -4
  575. package/runtime/prime/bin/lib/greeting.sh +255 -28
  576. package/runtime/prime/bin/lib/handoff_meta.py +294 -0
  577. package/runtime/prime/bin/lib/health-footer.sh +83 -2
  578. package/runtime/prime/bin/lib/identity.sh +31 -10
  579. package/runtime/prime/bin/lib/instance-registry.sh +71 -0
  580. package/runtime/prime/bin/lib/local_ci_receipt.py +249 -0
  581. package/runtime/prime/bin/lib/metrics.sh +121 -8
  582. package/runtime/prime/bin/lib/notices.sh +36 -0
  583. package/runtime/prime/bin/lib/record_outcomes.py +729 -0
  584. package/runtime/prime/bin/lib/reflexes.py +267 -0
  585. package/runtime/prime/bin/lib/retrieval_scorecard.py +624 -0
  586. package/runtime/prime/bin/lib/session-record-prompt.txt +18 -0
  587. package/runtime/prime/bin/lib/session_record.py +3747 -0
  588. package/runtime/prime/bin/lib/session_trail.py +1983 -0
  589. package/runtime/prime/bin/lib/settings-drift.sh +8 -5
  590. package/runtime/prime/bin/lib/startup-restart.sh +41 -0
  591. package/runtime/prime/bin/lib/worktree-links.sh +92 -0
  592. package/runtime/prime/bin/memory-push.sh +6 -0
  593. package/runtime/prime/bin/merge-gate.sh +498 -0
  594. package/runtime/prime/bin/notify.sh +5 -2
  595. package/runtime/prime/bin/page-access.sh +103 -0
  596. package/runtime/prime/bin/person.sh +13 -2
  597. package/runtime/prime/bin/pi-branch-policy.mjs +37 -0
  598. package/runtime/prime/bin/pi-observe-context.mjs +97 -4
  599. package/runtime/prime/bin/pi-render-spec.mjs +0 -1
  600. package/runtime/prime/bin/pi-spirit-discovery.mjs +34 -0
  601. package/runtime/prime/bin/post-update.sh +181 -0
  602. package/runtime/prime/bin/prime-render-spec.mjs +0 -1
  603. package/runtime/prime/bin/prime-session-start.sh +3 -1
  604. package/runtime/prime/bin/publish-artifact.sh +89 -42
  605. package/runtime/prime/bin/qa-inventory.sh +2 -2
  606. package/runtime/prime/bin/quest.sh +1 -1
  607. package/runtime/prime/bin/records.sh +11 -0
  608. package/runtime/prime/bin/reflexes/05-vitals.sh +9 -0
  609. package/runtime/prime/bin/reflexes/10-open-items.sh +12 -0
  610. package/runtime/prime/bin/reflexes/15-retrieval.sh +8 -0
  611. package/runtime/prime/bin/reflexes/20-nightly.sh +100 -0
  612. package/runtime/prime/bin/reflexes/20-sandbox.sh +85 -0
  613. package/runtime/prime/bin/reflexes/30-cost.py +116 -0
  614. package/runtime/prime/bin/reflexes/40-shells.py +51 -0
  615. package/runtime/prime/bin/reflexes/README.md +61 -0
  616. package/runtime/prime/bin/reflexes.sh +7 -0
  617. package/runtime/prime/bin/runtime-identity.sh +140 -42
  618. package/runtime/prime/bin/screenshot.sh +77 -0
  619. package/runtime/prime/bin/scroll.sh +1 -1
  620. package/runtime/prime/bin/search.sh +31 -5
  621. package/runtime/prime/bin/session-autosave.sh +6 -3
  622. package/runtime/prime/bin/session-id.sh +147 -0
  623. package/runtime/prime/bin/session-log.sh +19 -0
  624. package/runtime/prime/bin/session-record.sh +26 -0
  625. package/runtime/prime/bin/settings.sh +13 -8
  626. package/runtime/prime/bin/spirit-discovery-claude.sh +4 -0
  627. package/runtime/prime/bin/spirit-discovery-codex.sh +4 -0
  628. package/runtime/prime/bin/spirit-discovery-pi.sh +4 -0
  629. package/runtime/prime/bin/spirit-discovery-prime.sh +4 -0
  630. package/runtime/prime/bin/spirit-discovery.sh +4 -0
  631. package/runtime/prime/bin/startup-check.sh +1 -1
  632. package/runtime/prime/bin/telemetry.sh +11 -4
  633. package/runtime/prime/bin/test-baseline.sh +33 -5
  634. package/runtime/prime/bin/todo.sh +1 -1
  635. package/runtime/prime/bin/worktree-create.sh +15 -45
  636. package/runtime/prime/bin/worktree.sh +6 -0
  637. package/runtime/prime/docs/spirit-discovery.md +96 -0
  638. package/runtime/prime/egregore_runtime/__init__.py +18 -12
  639. package/runtime/prime/egregore_runtime/access.py +608 -0
  640. package/runtime/prime/egregore_runtime/access_cli.py +190 -0
  641. package/runtime/prime/egregore_runtime/adapters/invitation.py +5 -3
  642. package/runtime/prime/egregore_runtime/adapters/qmd.py +521 -47
  643. package/runtime/prime/egregore_runtime/admin_entry.py +56 -0
  644. package/runtime/prime/egregore_runtime/artifacts.py +222 -16
  645. package/runtime/prime/egregore_runtime/contracts.py +27 -0
  646. package/runtime/prime/egregore_runtime/eligibility.py +5 -12
  647. package/runtime/prime/egregore_runtime/episode_binding.py +16 -5
  648. package/runtime/prime/egregore_runtime/github_issues.py +2 -2
  649. package/runtime/prime/egregore_runtime/handoff_lookup.py +9 -20
  650. package/runtime/prime/egregore_runtime/harness_cli.py +674 -149
  651. package/runtime/prime/egregore_runtime/held.py +2436 -0
  652. package/runtime/prime/egregore_runtime/hosted_api.py +137 -0
  653. package/runtime/prime/egregore_runtime/identity.py +196 -65
  654. package/runtime/prime/egregore_runtime/identity_transfer.py +6 -2
  655. package/runtime/prime/egregore_runtime/ingest_cli.py +2 -2
  656. package/runtime/prime/egregore_runtime/ingest_index_cli.py +4 -16
  657. package/runtime/prime/egregore_runtime/installation.py +119 -0
  658. package/runtime/prime/egregore_runtime/investigation.py +64 -41
  659. package/runtime/prime/egregore_runtime/investigation_guidance.py +16 -11
  660. package/runtime/prime/egregore_runtime/issue_cli.py +51 -32
  661. package/runtime/prime/egregore_runtime/issue_reporting.py +4 -3
  662. package/runtime/prime/egregore_runtime/issue_triage_service.py +2 -2
  663. package/runtime/prime/egregore_runtime/issues.py +12 -6
  664. package/runtime/prime/egregore_runtime/knowledge.py +331 -5
  665. package/runtime/prime/egregore_runtime/knowledge_cli.py +57 -17
  666. package/runtime/prime/egregore_runtime/lifecycle.py +88 -22
  667. package/runtime/prime/egregore_runtime/lifecycle_cli.py +21 -13
  668. package/runtime/prime/egregore_runtime/line_ids.py +10 -0
  669. package/runtime/prime/egregore_runtime/memory_push.py +349 -0
  670. package/runtime/prime/egregore_runtime/observe.py +64 -25
  671. package/runtime/prime/egregore_runtime/observers.py +19 -0
  672. package/runtime/prime/egregore_runtime/optional_modules.py +13 -0
  673. package/runtime/prime/egregore_runtime/outputs_cli.py +321 -0
  674. package/runtime/prime/egregore_runtime/policy.py +651 -24
  675. package/runtime/prime/egregore_runtime/private_result.py +2 -1
  676. package/runtime/prime/egregore_runtime/projection.py +946 -0
  677. package/runtime/prime/egregore_runtime/quest_cli.py +20 -23
  678. package/runtime/prime/egregore_runtime/question_cli.py +15 -11
  679. package/runtime/prime/egregore_runtime/questions.py +10 -10
  680. package/runtime/prime/egregore_runtime/quests.py +10 -10
  681. package/runtime/prime/egregore_runtime/record_publish.py +548 -0
  682. package/runtime/prime/egregore_runtime/record_subject.py +564 -0
  683. package/runtime/prime/egregore_runtime/research_ingest.py +2 -4
  684. package/runtime/prime/egregore_runtime/runtime.py +169 -60
  685. package/runtime/prime/egregore_runtime/sandbox.py +194 -0
  686. package/runtime/prime/egregore_runtime/scroll_cli.py +22 -32
  687. package/runtime/prime/egregore_runtime/scrolls.py +23 -15
  688. package/runtime/prime/egregore_runtime/shadow.py +262 -0
  689. package/runtime/prime/egregore_runtime/spirit_discovery.py +487 -0
  690. package/runtime/prime/egregore_runtime/spirit_discovery_cli.py +25 -0
  691. package/runtime/prime/egregore_runtime/spirit_discovery_transport.py +18 -0
  692. package/runtime/prime/egregore_runtime/status.py +101 -44
  693. package/runtime/prime/egregore_runtime/status_cli.py +13 -5
  694. package/runtime/prime/egregore_runtime/sync.py +39 -21
  695. package/runtime/prime/egregore_runtime/taste.py +934 -0
  696. package/runtime/prime/egregore_runtime/taste_cli.py +242 -0
  697. package/runtime/prime/egregore_runtime/taste_rules.py +91 -0
  698. package/runtime/prime/egregore_runtime/telemetry.py +26 -3
  699. package/runtime/prime/egregore_runtime/todo_cli.py +15 -12
  700. package/runtime/prime/egregore_runtime/todos.py +3 -2
  701. package/runtime/prime/egregore_runtime/upgrade.py +85 -58
  702. package/runtime/prime/egregore_runtime/writeback.py +330 -24
  703. package/runtime/prime/manifest.json +66 -5
  704. package/runtime/claude/bin/thread.sh +0 -9
  705. package/runtime/claude/egregore_runtime/admin_gate.py +0 -102
  706. package/runtime/claude/egregore_runtime/thread_cli.py +0 -173
  707. package/runtime/claude/egregore_runtime/threads.py +0 -521
  708. package/runtime/codex/bin/thread.sh +0 -9
  709. package/runtime/codex/egregore_runtime/admin_gate.py +0 -102
  710. package/runtime/codex/egregore_runtime/thread_cli.py +0 -173
  711. package/runtime/codex/egregore_runtime/threads.py +0 -521
  712. package/runtime/pi/bin/thread.sh +0 -9
  713. package/runtime/pi/egregore_runtime/admin_gate.py +0 -102
  714. package/runtime/pi/egregore_runtime/thread_cli.py +0 -173
  715. package/runtime/pi/egregore_runtime/threads.py +0 -521
  716. package/runtime/prime/bin/thread.sh +0 -9
  717. package/runtime/prime/egregore_runtime/admin_gate.py +0 -102
  718. package/runtime/prime/egregore_runtime/thread_cli.py +0 -173
  719. package/runtime/prime/egregore_runtime/threads.py +0 -521
@@ -0,0 +1,137 @@
1
+ """The address every hosted request goes to.
2
+
3
+ The hosted API moved to api.egregore.xyz after the October 2026 security
4
+ reset. Its old Railway origin was retired and is no longer ours to trust: a
5
+ credential sent there could reach whoever holds that address next. Every
6
+ request that carries a token or key resolves its address here, so a
7
+ configuration or environment saved before the move never reaches it.
8
+
9
+ The rule is an allowlist, not a normalizer. Only a plain web address is used:
10
+ http or https, a host of ASCII letters, digits, dots, hyphens and underscores,
11
+ an optional port and a plain path. Every transport reads such an address the
12
+ same way, so its host is the one the request reaches. A plain address on the
13
+ retired host becomes the hosted API; any other plain address is kept.
14
+ Anything else is refused, because curl, urllib and WHATWG parsers disagree
15
+ about where it goes: userinfo, backslashes, percent-encoding, curl glob
16
+ characters, a query or fragment, whitespace or invisible characters inside,
17
+ a ``URL:`` prefix, non-ASCII. A refused address raises
18
+ :class:`RefusedApiAddress` and nothing is sent.
19
+
20
+ The shell (bin/lib/config.sh), Node (packages/create-egregore/lib/
21
+ hosted-api.js) and connector resolvers follow the same table:
22
+ bin/tests/fixtures/hosted-api-url-vectors.json.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import re
28
+ import sys
29
+ from typing import Mapping
30
+ from urllib.request import Request
31
+
32
+ HOSTED_API_ORIGIN = "https://api.egregore.xyz"
33
+ RETIRED_API_ORIGIN = "https://egregore-production-55f2.up.railway.app"
34
+ REFUSED_API_ADDRESS_NOTICE = (
35
+ "The hosted services address isn't a plain web address, so nothing was sent. "
36
+ f"Correct api_url in egregore.json; the hosted address is {HOSTED_API_ORIGIN}."
37
+ )
38
+
39
+ _RETIRED_HOST = RETIRED_API_ORIGIN.split("://", 1)[1]
40
+ _WHITESPACE = " \t\n\r\f\v"
41
+ _PLAIN = re.compile(r"https?://([A-Za-z0-9._-]+)(:[0-9]{1,5})?(/[A-Za-z0-9._~/-]*)?")
42
+ # The start of a request URL: a plain origin, then a path, query or nothing.
43
+ _PLAIN_ORIGIN = re.compile(r"https?://([A-Za-z0-9._-]+)(:[0-9]{1,5})?(?=[/?#]|\Z)")
44
+ _notice_shown = False
45
+
46
+
47
+ class RefusedApiAddress(ValueError):
48
+ """The hosted address is not a plain web address, so nothing may be sent to it."""
49
+
50
+ def __init__(self) -> None:
51
+ super().__init__(REFUSED_API_ADDRESS_NOTICE)
52
+
53
+
54
+ def _refuse() -> RefusedApiAddress:
55
+ """Say why once per process on stderr, and return the error to raise."""
56
+ global _notice_shown
57
+ if not _notice_shown:
58
+ _notice_shown = True
59
+ try:
60
+ print(REFUSED_API_ADDRESS_NOTICE, file=sys.stderr)
61
+ except (OSError, ValueError):
62
+ pass
63
+ return RefusedApiAddress()
64
+
65
+
66
+ def _retired_host(host: str) -> bool:
67
+ return host.lower().rstrip(".") == _RETIRED_HOST
68
+
69
+
70
+ def retired_api_url(value: object) -> bool:
71
+ """Whether ``value`` is a plain address on the retired hosted origin."""
72
+ text = ("" if value is None else str(value)).strip(_WHITESPACE)
73
+ plain = _PLAIN.fullmatch(text)
74
+ return bool(plain) and _retired_host(plain.group(1))
75
+
76
+
77
+ def hosted_api_url(value: object, exact: bool = False) -> str:
78
+ """The address to send a request to.
79
+
80
+ A plain address on the retired origin becomes the hosted API, keeping its
81
+ path, with trailing slashes trimmed. Any other plain address keeps its text
82
+ apart from surrounding whitespace and trailing slashes; empty stays empty.
83
+ Anything else raises :class:`RefusedApiAddress`, and the caller sends
84
+ nothing.
85
+
86
+ ``exact`` is for the server's own settings, such as an OAuth redirect URI
87
+ that is compared exactly: only a plain retired address changes, and every
88
+ other value is returned byte for byte, never refused.
89
+ """
90
+ text = "" if value is None else str(value)
91
+ trimmed = text.strip(_WHITESPACE)
92
+ plain = _PLAIN.fullmatch(trimmed)
93
+ if plain and _retired_host(plain.group(1)):
94
+ return (HOSTED_API_ORIGIN + (plain.group(3) or "")).rstrip("/")
95
+ if exact:
96
+ return text
97
+ if not trimmed:
98
+ return ""
99
+ if not plain:
100
+ raise _refuse()
101
+ return trimmed.rstrip("/")
102
+
103
+
104
+ _CREDENTIAL_HEADER = re.compile(r"authorization|cookie|token|key|secret|auth", re.IGNORECASE)
105
+
106
+
107
+ def credential_request(
108
+ url: str,
109
+ *,
110
+ headers: Mapping[str, str] | None = None,
111
+ data: bytes | None = None,
112
+ method: str | None = None,
113
+ ) -> Request:
114
+ """A request whose credentials reach only a plain, current hosted address.
115
+
116
+ The URL must start with a plain origin that is not the retired one;
117
+ otherwise :class:`RefusedApiAddress` is raised and nothing is built. The
118
+ resolver already guarantees that for an address it returned; this holds it
119
+ for every URL built from one.
120
+
121
+ urllib's redirect handler copies only ``Request.headers`` to the next
122
+ request. Authorization, cookies and any token, key or secret header are
123
+ added with ``add_unredirected_header`` instead, so they stay with the
124
+ first request and cannot follow a redirect to another host. A redirected
125
+ POST loses its body (urllib refuses 307 and 308 for it), so a credential
126
+ in the body stays behind too.
127
+ """
128
+ origin = _PLAIN_ORIGIN.match(url) if isinstance(url, str) else None
129
+ if not origin or _retired_host(origin.group(1)):
130
+ raise _refuse()
131
+ request = Request(url, data=data, method=method)
132
+ for name, value in (headers or {}).items():
133
+ if _CREDENTIAL_HEADER.search(name):
134
+ request.add_unredirected_header(name, value)
135
+ else:
136
+ request.add_header(name, value)
137
+ return request
@@ -12,8 +12,9 @@ import tempfile
12
12
  import fcntl
13
13
  import hashlib
14
14
  import re
15
+ import stat
15
16
  from contextlib import contextmanager
16
- from collections.abc import Callable, Mapping
17
+ from collections.abc import Callable, Iterable, Mapping
17
18
  from dataclasses import dataclass
18
19
  from pathlib import Path
19
20
  from typing import Any
@@ -21,12 +22,16 @@ from uuid import uuid4
21
22
 
22
23
  from .contracts import (
23
24
  AccountIdentity,
25
+ EgregoreProfile,
24
26
  ActorContext,
25
27
  ActorIdentity,
26
28
  ActorKind,
27
29
  OrgMembership,
30
+ PolicyDecision,
28
31
  )
32
+ from . import held
29
33
  from .entitlements import resolve_entitlements
34
+ from .policy import check
30
35
  from .profile import initialize_org_config, load_profile, render_identity_document
31
36
 
32
37
 
@@ -48,9 +53,10 @@ class OrganizationMemberPresentation:
48
53
  aliases: tuple[str, ...]
49
54
 
50
55
 
51
- def _profile_identity(path: Path) -> dict[str, Any]:
56
+ def _profile_identity(path: Path, source: Path | None = None, data: bytes | None = None) -> dict[str, Any]:
52
57
  try:
53
- text = path.read_text(encoding="utf-8", errors="replace")[:4_000]
58
+ text = (data.decode("utf-8", "replace") if data is not None
59
+ else (source or path).read_text(encoding="utf-8", errors="replace"))[:4_000]
54
60
  except OSError:
55
61
  return {}
56
62
  title = re.search(r"(?m)^#\s+(.+?)\s*$", text)
@@ -83,6 +89,10 @@ def _profile_identity(path: Path) -> dict[str, Any]:
83
89
  def organization_member_presentations(
84
90
  root: Path,
85
91
  actor: ActorContext | None = None,
92
+ *,
93
+ decision: PolicyDecision | None,
94
+ sources: Mapping[str, bytes] | None = None,
95
+ removed: Iterable[str] | None = None,
86
96
  ) -> tuple[OrganizationMemberPresentation, ...]:
87
97
  """Load stable member names without treating aliases as extra people.
88
98
 
@@ -90,31 +100,50 @@ def organization_member_presentations(
90
100
  reconciliation. Profiles with the same Person-ID collapse into one member;
91
101
  an explicit Alias-Of title is the preferred presentation witness for
92
102
  legacy canonical profiles whose title is still a provider handle.
103
+
104
+ ``decision`` is required: a profile is read only when the one predicate
105
+ admits it for that reader, from the path it checked, so prompt context and
106
+ query terms name nobody the reader may not see. ``decision=None`` is an
107
+ explicit opt-out for a caller that applies its own holds (the exporter's
108
+ hand-over chain); leaving it out is an error, never an unchecked read.
109
+ ``sources`` (canonical path → bytes a caller already read and authorized,
110
+ as a hand-over holds them) replaces reading the profiles from disk, and
111
+ ``removed`` the working tree's removal ledger (another principal's
112
+ hand-over passes the committed one).
93
113
  """
94
114
 
95
115
  people = root / "memory" / "people"
96
- if not people.is_dir():
116
+ if sources is None and not people.is_dir():
97
117
  return ()
98
118
  # Durable removals: the committed ledger in egregore.json outranks file
99
119
  # presence — a person file resurrected by a session write or sync never
100
120
  # 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()
121
+ if removed is None:
122
+ try:
123
+ config = json.loads((root / "egregore.json").read_text(encoding="utf-8"))
124
+ removed = {
125
+ str(handle).strip().casefold()
126
+ for handle in (config.get("people_removed") or [])
127
+ if str(handle).strip()
128
+ }
129
+ except (OSError, ValueError):
130
+ removed = set()
131
+ removed = set(removed)
111
132
  grouped: dict[str, list[dict[str, Any]]] = {}
112
- for path in sorted(people.glob("*.md")):
133
+ profiles = sorted(people.glob("*.md")) if sources is None else sorted(
134
+ root / name for name in sources if name.startswith("memory/people/") and name.count("/") == 2)
135
+ for path in profiles:
113
136
  if path.name == "index.md":
114
137
  continue
115
138
  if path.stem.casefold() in removed:
116
139
  continue
117
- profile = _profile_identity(path)
140
+ source = path
141
+ if decision is not None:
142
+ verdict = check(decision, f"memory/people/{path.name}", "read", root=root / "memory")
143
+ if not verdict.allowed:
144
+ continue
145
+ source = verdict.resolved
146
+ profile = _profile_identity(path, source, None if sources is None else sources[f"memory/people/{path.name}"])
118
147
  if not profile:
119
148
  continue
120
149
  if str(profile.get("github") or "").strip().casefold() in removed:
@@ -211,7 +240,8 @@ def organization_member_presentations(
211
240
  aliases=compact_aliases,
212
241
  )
213
242
  )
214
- return tuple(sorted(members, key=lambda item: item.display_name.casefold()))
243
+ members.sort(key=lambda item: item.display_name.casefold()) # a readable profile can name held material too
244
+ return tuple(members if decision is None else held.redact(decision, members, root=root / "memory"))
215
245
 
216
246
 
217
247
  def resolve_actor_query(actor: ActorContext, query: str) -> str:
@@ -752,6 +782,102 @@ def _identity_lock(state_path: Path):
752
782
  fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
753
783
 
754
784
 
785
+ def _existing_identity_json(path: Path) -> tuple[dict[str, Any], bytes]:
786
+ """Bounded, no-symlink read for the opt-in non-migrating resolver."""
787
+ path = Path(os.path.abspath(path))
788
+ parent = os.open(path.anchor, os.O_RDONLY | os.O_DIRECTORY)
789
+ descriptor = None
790
+ try:
791
+ for part in path.parts[1:-1]:
792
+ child = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=parent)
793
+ os.close(parent)
794
+ parent = child
795
+ descriptor = os.open(path.name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=parent)
796
+ info = os.fstat(descriptor)
797
+ if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_size > 200000:
798
+ raise ValueError("existing identity is unavailable")
799
+ raw = os.read(descriptor, 200001)
800
+ if len(raw) > 200000:
801
+ raise ValueError("existing identity is unavailable")
802
+ finally:
803
+ if descriptor is not None:
804
+ os.close(descriptor)
805
+ os.close(parent)
806
+ def unique(pairs):
807
+ result = {}
808
+ for key, value in pairs:
809
+ if key in result:
810
+ raise ValueError("existing identity is invalid")
811
+ result[key] = value
812
+ return result
813
+ try:
814
+ value = json.loads(raw, object_pairs_hook=unique)
815
+ pending = [(value, 0)]
816
+ while pending:
817
+ item, depth = pending.pop()
818
+ if depth > 64:
819
+ raise ValueError("existing identity is invalid")
820
+ if isinstance(item, str):
821
+ item.encode("utf-8")
822
+ elif isinstance(item, dict):
823
+ pending.extend((part, depth + 1) for pair in item.items() for part in pair)
824
+ elif isinstance(item, list):
825
+ pending.extend((part, depth + 1) for part in item)
826
+ if not isinstance(value, dict):
827
+ raise ValueError("existing identity is invalid")
828
+ except (ValueError, UnicodeError, RecursionError):
829
+ raise ValueError("existing identity is invalid") from None
830
+ return value, raw
831
+
832
+
833
+ def _existing_identifier(value: Any) -> bool:
834
+ return (isinstance(value, str) and bool(value) and value == value.strip()
835
+ and len(value.encode("utf-8")) <= 2000
836
+ and not any(ord(character) < 33 or ord(character) == 127 for character in value))
837
+
838
+
839
+ def _existing_identity(root: Path):
840
+ """Retained IDs and config only; deliberately never read identity documents.
841
+
842
+ The profile revision has a distinct config-only namespace and cannot stand
843
+ in for load_profile's revision of config plus declared identity documents.
844
+ """
845
+ from .profile import PROFILE_SCHEMA, DEFAULT_CONTEXT_BUDGET
846
+ config, raw = _existing_identity_json(root / "egregore.json")
847
+ state, _ = _existing_identity_json(root / ".egregore-state.json")
848
+ org = config.get("org_id")
849
+ if not _existing_identifier(org) or any(not _existing_identifier(state.get(key)) for key in ("account_id", "actor_id", "membership_id")):
850
+ raise ValueError("existing identity is required")
851
+ nested = state.get("identity", {})
852
+ if not isinstance(nested, dict) or any(key in nested and nested[key] != value for key, value in {
853
+ "account_id": state["account_id"], "actor_id": state["actor_id"],
854
+ "membership_id": state["membership_id"], "org_id": org, "actor_kind": "person",
855
+ }.items()) or "org_id" in state and state["org_id"] != org:
856
+ raise ValueError("existing identity is inconsistent")
857
+ if not isinstance(state.get("provider_identities", {}), dict):
858
+ raise ValueError("existing identity is invalid")
859
+ declared = config.get("profile") or {}
860
+ if not isinstance(declared, dict):
861
+ raise ValueError("existing profile is invalid")
862
+ schema = declared.get("schema_version") or PROFILE_SCHEMA
863
+ budget = declared.get("default_context_budget", DEFAULT_CONTEXT_BUDGET)
864
+ slug, name = str(config.get("slug") or "").strip(), str(config.get("org_name") or config.get("name") or "").strip()
865
+ if not slug or not name or schema != PROFILE_SCHEMA or type(budget) is not int or budget <= 0:
866
+ raise ValueError("existing profile is invalid")
867
+ for field in ("purpose_path", "principles_path", "conventions_path"):
868
+ relative = declared.get(field)
869
+ if relative is not None and (not isinstance(relative, str) or Path(relative.replace("\\", "/")).is_absolute() or ".." in Path(relative.replace("\\", "/")).parts):
870
+ raise ValueError("existing profile is invalid")
871
+ profile = EgregoreProfile(
872
+ schema_version=schema, org_id=org, slug=slug, name=name,
873
+ revision="profile-config-only-sha256:" + hashlib.sha256(raw).hexdigest(),
874
+ purpose_path=declared.get("purpose_path"), principles_path=declared.get("principles_path"),
875
+ conventions_path=declared.get("conventions_path"),
876
+ default_policy=str(declared.get("default_policy") or "all-access-v1"), default_context_budget=budget,
877
+ )
878
+ return profile, state, config
879
+
880
+
755
881
  class LocalIdentityResolver:
756
882
  """Resolve actor context from committed org config and local user state."""
757
883
 
@@ -761,62 +887,67 @@ class LocalIdentityResolver:
761
887
  *,
762
888
  persist_migrations: bool = True,
763
889
  entitlement_row: Mapping[str, Any] | None = None,
890
+ require_existing: bool = False,
764
891
  ) -> None:
765
892
  self.root = Path(root)
766
893
  self.persist_migrations = persist_migrations
767
894
  self.entitlement_row = entitlement_row
895
+ self.require_existing = require_existing
768
896
 
769
897
  def resolve(self, *, session_id: str, harness: str) -> ActorContext:
770
- profile = load_profile(self.root / "egregore.json")
771
- state_path = self.root / ".egregore-state.json"
772
- try:
773
- state = json.loads(state_path.read_text(encoding="utf-8"))
774
- except FileNotFoundError as exc:
775
- raise ValueError(f"local identity state is missing: {state_path}") from exc
776
- if not isinstance(state, dict):
777
- raise ValueError(f"local identity state must be an object: {state_path}")
778
-
779
- config = json.loads((self.root / "egregore.json").read_text(encoding="utf-8"))
780
- id_factory = new_identity_id
781
- if (
782
- profile.org_id.startswith("legacy-org:")
783
- and (str(config.get("mode") or "local") == "connected" or config.get("api_url"))
784
- ):
785
- # A pre-migration Connected install must remain usable offline, but
786
- # it must not mint a new random actor in each detached checkout.
787
- # These explicitly legacy ids are deterministic compatibility
788
- # placeholders and are replaced only by control-plane IDs.
789
- legacy_seed = json.dumps(
790
- {
791
- "org": profile.org_id,
792
- "person_id": state.get("person_id"),
793
- "github_id": state.get("github_id"),
794
- "github_username": state.get("github_username"),
795
- "email": state.get("email"),
796
- },
797
- sort_keys=True,
798
- separators=(",", ":"),
799
- )
898
+ if self.require_existing:
899
+ profile, migrated, config = _existing_identity(self.root)
900
+ else:
901
+ profile = load_profile(self.root / "egregore.json")
902
+ state_path = self.root / ".egregore-state.json"
903
+ try:
904
+ state = json.loads(state_path.read_text(encoding="utf-8"))
905
+ except FileNotFoundError as exc:
906
+ raise ValueError(f"local identity state is missing: {state_path}") from exc
907
+ if not isinstance(state, dict):
908
+ raise ValueError(f"local identity state must be an object: {state_path}")
909
+
910
+ config = json.loads((self.root / "egregore.json").read_text(encoding="utf-8"))
911
+ id_factory = new_identity_id
912
+ if (
913
+ profile.org_id.startswith("legacy-org:")
914
+ and (str(config.get("mode") or "local") == "connected" or config.get("api_url"))
915
+ ):
916
+ # A pre-migration Connected install must remain usable offline, but
917
+ # it must not mint a new random actor in each detached checkout.
918
+ # These explicitly legacy ids are deterministic compatibility
919
+ # placeholders and are replaced only by control-plane IDs.
920
+ legacy_seed = json.dumps(
921
+ {
922
+ "org": profile.org_id,
923
+ "person_id": state.get("person_id"),
924
+ "github_id": state.get("github_id"),
925
+ "github_username": state.get("github_username"),
926
+ "email": state.get("email"),
927
+ },
928
+ sort_keys=True,
929
+ separators=(",", ":"),
930
+ )
800
931
 
801
- def id_factory(prefix: str) -> str:
802
- digest = hashlib.sha256(f"{prefix}:{legacy_seed}".encode()).hexdigest()[:24]
803
- return f"legacy-{prefix}:{digest}"
932
+ def id_factory(prefix: str) -> str:
933
+ digest = hashlib.sha256(f"{prefix}:{legacy_seed}".encode()).hexdigest()[:24]
934
+ return f"legacy-{prefix}:{digest}"
804
935
 
805
- migrated, changed = migrate_local_identity_state(
806
- state, org_id=profile.org_id, id_factory=id_factory
807
- )
808
- if changed and self.persist_migrations:
809
- # First-session races must not mint competing account/actor ids.
810
- # Re-read after taking the instance lock, then persist once.
811
- with _identity_lock(state_path):
812
- current = json.loads(state_path.read_text(encoding="utf-8"))
813
- if not isinstance(current, dict):
814
- raise ValueError(f"local identity state must be an object: {state_path}")
815
- migrated, changed = migrate_local_identity_state(
816
- current, org_id=profile.org_id, id_factory=id_factory
817
- )
818
- if changed:
819
- _atomic_json(state_path, migrated)
936
+ migrated, changed = migrate_local_identity_state(
937
+ state, org_id=profile.org_id, id_factory=id_factory
938
+ )
939
+ if changed and self.persist_migrations:
940
+ # First-session races must not mint competing account/actor ids.
941
+ # Re-read after taking the instance lock, then persist once.
942
+ with _identity_lock(state_path):
943
+ current = json.loads(state_path.read_text(encoding="utf-8"))
944
+ if not isinstance(current, dict):
945
+ raise ValueError(f"local identity state must be an object: {state_path}")
946
+ migrated, changed = migrate_local_identity_state(
947
+ current, org_id=profile.org_id, id_factory=id_factory
948
+ )
949
+ if changed:
950
+ _atomic_json(state_path, migrated)
820
951
 
821
952
  provider_aliases: dict[str, str] = {}
822
953
  for provider, record in (migrated.get("provider_identities") or {}).items():
@@ -18,7 +18,7 @@ from typing import Any, Mapping
18
18
 
19
19
  from .artifacts import parse_canonical_markdown, render_canonical_markdown, split_frontmatter
20
20
  from .contracts import ActorContext, Permission
21
- from .policy import scope_allows_path
21
+ from .policy import allows, action_for
22
22
  from .runtime import local_runtime
23
23
 
24
24
  SCHEMA = "egregore-identity-transfer/v1"
@@ -145,7 +145,7 @@ def _document(before: str, path: str, pair: Mapping[str, Any], transfer_id: str)
145
145
 
146
146
  def _authorize(runtime, source: ActorContext, path: str, permission: Permission) -> None:
147
147
  decision = runtime.authorize(source, permission, (f"memory/{path}",))
148
- if not decision.allowed or not scope_allows_path(f"memory/{path}", decision.scopes):
148
+ if not decision.allowed or not allows(decision, f"memory/{path}", action_for(decision.permission), root=runtime.canonical_root):
149
149
  raise IdentityTransferError("current actor cannot authorize ownership transfer for the canonical path")
150
150
 
151
151
 
@@ -192,6 +192,10 @@ class _RevisionStore:
192
192
  def __init__(self, store, path: str, expected: str):
193
193
  self.store, self.path, self.expected = store, path, expected
194
194
 
195
+ @property
196
+ def root(self):
197
+ return self.store.root
198
+
195
199
  def check_write(self, document):
196
200
  if document.artifact.canonical_path != self.path:
197
201
  raise IdentityTransferError("ownership transfer attempted an unplanned write")
@@ -22,7 +22,7 @@ from .ingest import (
22
22
  LocalIngestJournal,
23
23
  LocalQuarantine,
24
24
  )
25
- from .policy import scope_allows_path
25
+ from .policy import allows, action_for
26
26
  from .runtime import local_runtime, runtime_root
27
27
  from .scratch import read_and_consume
28
28
 
@@ -70,7 +70,7 @@ def _require(runtime, actor, permission: Permission, source_id: str):
70
70
  raise PermissionError(
71
71
  "; ".join(decision.reasons) or f"ingest {permission.value} denied"
72
72
  )
73
- if not scope_allows_path(_canonical_directory(source_id), decision.scopes):
73
+ if not allows(decision, _canonical_directory(source_id), action_for(decision.permission), root=runtime.canonical_root):
74
74
  raise PermissionError("ingest path is outside the actor's authorized scope")
75
75
  return decision
76
76
 
@@ -8,6 +8,7 @@ legacy JSON shape consumed by the ingest boundary filter.
8
8
  from __future__ import annotations
9
9
 
10
10
  import argparse
11
+ from dataclasses import replace
11
12
  import json
12
13
  import os
13
14
  import sys
@@ -17,9 +18,7 @@ from pathlib import Path, PurePosixPath
17
18
  from egregore_runtime.adapters.qmd import QmdAdapterError, QmdLocalRetriever
18
19
  from egregore_runtime.contracts import RetrievalMode, RetrievalRequest
19
20
  from egregore_runtime.identity import LocalIdentityResolver
20
- from egregore_runtime.observe import DefaultOrgContextCompiler
21
- from egregore_runtime.policy import all_access_dogfood_policy
22
- from egregore_runtime.telemetry import LocalTelemetrySink
21
+ from egregore_runtime.runtime import local_runtime
23
22
 
24
23
 
25
24
  def _root() -> Path:
@@ -76,19 +75,8 @@ def _query(args: argparse.Namespace) -> int:
76
75
  mode=RetrievalMode.HYBRID,
77
76
  open_sources=False,
78
77
  )
79
- policy = all_access_dogfood_policy(
80
- org_id=actor.profile.org_id,
81
- actor_id=actor.actor.actor_id,
82
- )
83
- compiler = DefaultOrgContextCompiler(
84
- authorizer=policy,
85
- retriever=retriever,
86
- telemetry=LocalTelemetrySink(
87
- state_file=root / ".egregore-state.json",
88
- instance_root=(root / "memory").resolve(),
89
- ),
90
- )
91
- context = compiler.observe(
78
+ runtime = replace(local_runtime(root, push_remote=False), retriever=retriever)
79
+ context = runtime.observe(
92
80
  actor,
93
81
  request,
94
82
  token_budget=actor.profile.default_context_budget,