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
@@ -1,521 +0,0 @@
1
- """Runtime-owned Thread intent, fold-in events, and projection inputs.
2
-
3
- Threads deliberately keep two substances separate: mutable human intent is
4
- canonical Markdown/Git state, while inferred state is a disposable projection.
5
- Every intent mutation is accompanied by an append-only, actor-attributed event.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- from dataclasses import dataclass, replace
11
- from datetime import UTC, datetime, timedelta
12
- import hashlib
13
- import json
14
- from pathlib import Path
15
- import re
16
- from typing import Any, Callable, Mapping, Sequence
17
- from uuid import UUID
18
-
19
- from .artifacts import (
20
- ARTIFACT_SCHEMA_VERSION,
21
- CanonicalDocument,
22
- content_digest,
23
- parse_canonical_markdown,
24
- )
25
- from .contracts import ActorContext, ArtifactRelationship, CanonicalArtifact, Permission, WritebackEvent, WritebackStatus
26
- from .errors import AuthorizationDenied
27
- from .policy import scope_allows_path
28
- from .runtime import EgregoreRuntime
29
-
30
-
31
- THREAD_SCHEMA_VERSION = "egregore-thread/v1"
32
- THREAD_EVENT_SCHEMA_VERSION = "egregore-thread-event/v1"
33
- THREAD_CONFIRMATION = "CONFIRM_THREAD_FOLD"
34
- THREAD_STATUSES = frozenset({"active", "paused", "shipped", "archived"})
35
- THREAD_EVENT_TYPES = frozenset(
36
- {
37
- "attach",
38
- "detach",
39
- "status-change",
40
- "blocker-mark",
41
- "facet-update",
42
- "glossary-trim",
43
- "pin",
44
- }
45
- )
46
- THREAD_FIELDS = frozenset(
47
- {
48
- "target",
49
- "next_steps",
50
- "membership_seed",
51
- "steward",
52
- "stewards",
53
- "status",
54
- "composes_into",
55
- "domain",
56
- "planning_horizon",
57
- "next_move",
58
- "reviewed_at",
59
- "facets",
60
- "glossary",
61
- }
62
- )
63
-
64
-
65
- class ThreadError(ValueError):
66
- """A Thread operation violates its stored-intent lifecycle."""
67
-
68
-
69
- def _slug(value: str) -> str:
70
- normalized = re.sub(r"[^a-z0-9]+", "-", value.casefold()).strip("-")[:50]
71
- if not normalized:
72
- raise ThreadError("thread slug must contain a letter or number")
73
- return normalized
74
-
75
-
76
- def _iso(value: datetime) -> str:
77
- return value.astimezone(UTC).isoformat().replace("+00:00", "Z")
78
-
79
-
80
- def _clean_strings(values: Sequence[str]) -> tuple[str, ...]:
81
- return tuple(dict.fromkeys(" ".join(value.split()) for value in values if value.strip()))
82
-
83
-
84
- def _stable_actor_id(value: str) -> bool:
85
- if value.startswith(("actor_", "person:", "service:", "agent:", "legacy-actor:")):
86
- return True
87
- try:
88
- UUID(value)
89
- except (ValueError, AttributeError, TypeError):
90
- return False
91
- return True
92
-
93
-
94
- def _as_mapping(value: Any, *, field: str) -> dict[str, Any]:
95
- if value is None:
96
- return {}
97
- if not isinstance(value, Mapping):
98
- raise ThreadError(f"{field} must be an object")
99
- return dict(value)
100
-
101
-
102
- @dataclass(frozen=True, slots=True)
103
- class ThreadSnapshot:
104
- slug: str
105
- title: str
106
- target: str
107
- status: str
108
- steward_id: str
109
- steward_ids: tuple[str, ...]
110
- next_steps: tuple[str, ...]
111
- membership_seed: Mapping[str, Any]
112
- composes_into: tuple[str, ...]
113
- domain: str | None
114
- planning_horizon: str | None
115
- next_move: str | None
116
- reviewed_at: str | None
117
- facets: Mapping[str, Any]
118
- glossary: Mapping[str, Any]
119
- document: CanonicalDocument
120
-
121
- @property
122
- def revision(self) -> str:
123
- return self.document.artifact.revision
124
-
125
- def projection_input(self) -> dict[str, Any]:
126
- """Return stored intent only; callers must add derived envelopes."""
127
-
128
- return {
129
- "slug": self.slug,
130
- "target": self.target,
131
- "status": self.status,
132
- "steward": self.steward_id,
133
- "stewards": list(self.steward_ids),
134
- "next_steps": list(self.next_steps),
135
- "membership_seed": dict(self.membership_seed),
136
- "composes_into": list(self.composes_into),
137
- "domain": self.domain,
138
- "planning_horizon": self.planning_horizon,
139
- "next_move": self.next_move,
140
- "reviewed_at": self.reviewed_at,
141
- "facets": dict(self.facets),
142
- "glossary": dict(self.glossary),
143
- "canonical_revision": self.revision,
144
- }
145
-
146
-
147
- @dataclass(frozen=True, slots=True)
148
- class ThreadFoldReceipt:
149
- thread: ThreadSnapshot
150
- event_id: str
151
- receipts: tuple[WritebackEvent, ...]
152
-
153
-
154
- def parse_thread_markdown(markdown: str, *, slug: str, org_id: str) -> ThreadSnapshot:
155
- normalized_slug = _slug(slug)
156
- document = parse_canonical_markdown(
157
- markdown,
158
- canonical_path=f"threads/{normalized_slug}.md",
159
- default_org_id=org_id,
160
- )
161
- if document.artifact.artifact_type != "thread":
162
- raise ThreadError("canonical Thread record has the wrong artifact type")
163
- fields = document.legacy_fields
164
- declared_slug = _slug(str(fields.get("slug") or normalized_slug))
165
- if declared_slug != normalized_slug:
166
- raise ThreadError("thread slug does not match its canonical path")
167
- status = document.artifact.status.casefold()
168
- if status not in THREAD_STATUSES:
169
- raise ThreadError(f"invalid thread status: {status}")
170
- steward_id = str(fields.get("steward_id") or fields.get("steward") or "").strip()
171
- if not steward_id:
172
- raise ThreadError("thread requires a stable steward actor id")
173
- if not _stable_actor_id(steward_id):
174
- raise ThreadError(
175
- "legacy steward alias is not fold authority; migrate it to a stable actor id"
176
- )
177
- raw_stewards = fields.get("steward_ids") or fields.get("stewards") or [steward_id]
178
- if isinstance(raw_stewards, str):
179
- raw_stewards = [raw_stewards]
180
- if not isinstance(raw_stewards, Sequence):
181
- raise ThreadError("thread stewards must be a list of actor ids")
182
- steward_ids = tuple(dict.fromkeys([steward_id, *(str(item) for item in raw_stewards)]))
183
- if not all(_stable_actor_id(value) for value in steward_ids):
184
- raise ThreadError(
185
- "legacy steward aliases are not fold authority; migrate them to stable actor ids"
186
- )
187
- raw_steps = fields.get("next_steps") or ()
188
- if isinstance(raw_steps, str):
189
- raw_steps = [raw_steps]
190
- raw_composes = fields.get("composes_into") or ()
191
- if isinstance(raw_composes, str):
192
- raw_composes = [raw_composes]
193
- target = str(fields.get("target") or "").strip()
194
- if not target:
195
- raise ThreadError("thread target is required")
196
- return ThreadSnapshot(
197
- slug=normalized_slug,
198
- title=document.artifact.title,
199
- target=target,
200
- status=status,
201
- steward_id=steward_id,
202
- steward_ids=steward_ids,
203
- next_steps=tuple(str(item) for item in raw_steps if str(item).strip()),
204
- membership_seed=_as_mapping(fields.get("membership_seed"), field="membership_seed"),
205
- composes_into=tuple(str(item) for item in raw_composes if str(item).strip()),
206
- domain=str(fields["domain"]) if fields.get("domain") else None,
207
- planning_horizon=str(fields["planning_horizon"]) if fields.get("planning_horizon") else None,
208
- next_move=str(fields["next_move"]) if fields.get("next_move") else None,
209
- reviewed_at=str(fields["reviewed_at"]) if fields.get("reviewed_at") else None,
210
- facets=_as_mapping(fields.get("facets"), field="facets"),
211
- glossary=_as_mapping(fields.get("glossary"), field="glossary"),
212
- document=document,
213
- )
214
-
215
-
216
- def _body(title: str, target: str, next_steps: Sequence[str]) -> str:
217
- steps = "\n".join(f"- {item}" for item in next_steps) or "- Define the next deliberate move"
218
- return f"# {title}\n\n## Target\n\n{target}\n\n## Next steps\n\n{steps}\n"
219
-
220
-
221
- def _replace_or_append_section(body: str, heading: str, content: str) -> str:
222
- """Update one level-two section without discarding richer Thread prose."""
223
-
224
- normalized = body.rstrip() + "\n"
225
- pattern = re.compile(
226
- rf"(?ms)^## {re.escape(heading)}\s*\n.*?(?=^## |\Z)"
227
- )
228
- replacement = f"## {heading}\n\n{content.rstrip()}\n\n"
229
- if pattern.search(normalized):
230
- return pattern.sub(replacement, normalized, count=1).rstrip() + "\n"
231
- return normalized.rstrip() + f"\n\n{replacement}"
232
-
233
-
234
- def _fold_body(current: ThreadSnapshot, field: str, after: Any) -> str:
235
- if field == "target":
236
- return _replace_or_append_section(current.document.body, "Target", str(after))
237
- if field == "next_steps":
238
- steps = "\n".join(f"- {item}" for item in after)
239
- return _replace_or_append_section(current.document.body, "Next steps", steps)
240
- return current.document.body
241
-
242
-
243
- class CanonicalThreadService:
244
- """Own Thread authorization, intent folds, event provenance, and reads."""
245
-
246
- def __init__(
247
- self,
248
- runtime: EgregoreRuntime,
249
- memory_root: Path,
250
- *,
251
- clock: Callable[[], datetime] | None = None,
252
- ) -> None:
253
- self.runtime = runtime
254
- self.memory_root = memory_root.resolve()
255
- self.clock = clock or (lambda: datetime.now(UTC))
256
-
257
- @staticmethod
258
- def canonical_path(slug: str) -> str:
259
- return f"memory/threads/{_slug(slug)}.md"
260
-
261
- def _path(self, slug: str) -> Path:
262
- return self.memory_root / "threads" / f"{_slug(slug)}.md"
263
-
264
- def _authorize(self, actor: ActorContext, permission: Permission, resource: str, path: str) -> None:
265
- decision = self.runtime.authorize(actor, permission, (resource,))
266
- if not decision.allowed:
267
- raise AuthorizationDenied("; ".join(decision.reasons) or f"thread {permission.value} denied")
268
- if not scope_allows_path(path, decision.scopes):
269
- raise AuthorizationDenied("thread path is outside the actor's authorized scope")
270
-
271
- def read(self, actor: ActorContext, slug: str) -> ThreadSnapshot:
272
- normalized = _slug(slug)
273
- path = self.canonical_path(normalized)
274
- self._authorize(actor, Permission.READ, f"thread:{normalized}", path)
275
- source = self._path(normalized)
276
- if not source.is_file():
277
- raise ThreadError(f"thread does not exist: {normalized}")
278
- return parse_thread_markdown(
279
- source.read_text(encoding="utf-8"), slug=normalized, org_id=actor.profile.org_id
280
- )
281
-
282
- def list(self, actor: ActorContext, *, include_archived: bool = False) -> tuple[ThreadSnapshot, ...]:
283
- decision = self.runtime.authorize(actor, Permission.READ, ("threads",))
284
- if not decision.allowed:
285
- raise AuthorizationDenied("; ".join(decision.reasons) or "thread read denied")
286
- directory = self.memory_root / "threads"
287
- if not directory.is_dir():
288
- return ()
289
- rows = []
290
- for path in sorted(directory.glob("*.md")):
291
- if path.name.startswith("_"):
292
- continue
293
- canonical = f"memory/threads/{path.name}"
294
- if not scope_allows_path(canonical, decision.scopes):
295
- continue
296
- row = parse_thread_markdown(
297
- path.read_text(encoding="utf-8"), slug=path.stem, org_id=actor.profile.org_id
298
- )
299
- if include_archived or row.status != "archived":
300
- rows.append(row)
301
- return tuple(rows)
302
-
303
- def create(
304
- self,
305
- actor: ActorContext,
306
- *,
307
- title: str,
308
- target: str,
309
- slug: str | None = None,
310
- topics: Sequence[str] = (),
311
- people: Sequence[str] = (),
312
- next_steps: Sequence[str] = (),
313
- window_days: int = 45,
314
- review_after_days: int = 7,
315
- attention_ttl_days: int = 30,
316
- supersedes: Sequence[str] = (),
317
- ) -> tuple[ThreadSnapshot, WritebackEvent]:
318
- clean_title = " ".join(title.split()).strip()
319
- clean_target = " ".join(target.split()).strip()
320
- if not clean_title or not clean_target:
321
- raise ThreadError("thread title and target are required")
322
- if min(window_days, review_after_days, attention_ttl_days) < 1:
323
- raise ThreadError("thread windows must be positive days")
324
- normalized = _slug(slug or clean_title)
325
- canonical = self.canonical_path(normalized)
326
- self._authorize(actor, Permission.WRITE, f"thread:{normalized}", canonical)
327
- if self._path(normalized).exists():
328
- raise ThreadError(f"thread already exists: {normalized}")
329
- now = self.clock().astimezone(UTC)
330
- steps = _clean_strings(next_steps)
331
- body = _body(clean_title, clean_target, steps)
332
- digest = content_digest(body)
333
- artifact = CanonicalArtifact(
334
- schema_version=ARTIFACT_SCHEMA_VERSION,
335
- artifact_id=f"thread:{normalized}",
336
- org_id=actor.profile.org_id,
337
- artifact_type="thread",
338
- title=clean_title,
339
- created_at=now,
340
- created_by=actor.actor.actor_id,
341
- status="active",
342
- canonical_path=f"threads/{normalized}.md",
343
- revision=f"sha256:{digest[:16]}",
344
- content_hash=digest,
345
- supersedes=tuple(dict.fromkeys(value for value in supersedes if value)),
346
- )
347
- document = CanonicalDocument(
348
- artifact=artifact,
349
- body=body,
350
- policy_hints={"human_intent_only": True, "projection_state": "disposable"},
351
- legacy_fields={
352
- "thread_schema": THREAD_SCHEMA_VERSION,
353
- "slug": normalized,
354
- "target": clean_target,
355
- "next_steps": list(steps),
356
- "membership_seed": {
357
- "topics": list(_clean_strings(topics)),
358
- "people": list(_clean_strings(people)),
359
- "window_days": window_days,
360
- },
361
- "steward_id": actor.actor.actor_id,
362
- "steward_ids": [actor.actor.actor_id],
363
- "steward_alias": actor.actor.display_name,
364
- "review_at": _iso(now + timedelta(days=review_after_days)),
365
- "attention_expires_at": _iso(now + timedelta(days=attention_ttl_days)),
366
- "reviewed_at": _iso(now),
367
- },
368
- )
369
- receipt = self.runtime.write_document(actor, document)
370
- return parse_thread_markdown(
371
- self._path(normalized).read_text(encoding="utf-8") if self._path(normalized).is_file() else "",
372
- slug=normalized,
373
- org_id=actor.profile.org_id,
374
- ) if receipt.status.value != "rejected" else ThreadSnapshot(
375
- normalized, clean_title, clean_target, "active", actor.actor.actor_id,
376
- (actor.actor.actor_id,), steps, document.legacy_fields["membership_seed"], (),
377
- None, None, None, _iso(now), {}, {}, document,
378
- ), receipt
379
-
380
- def fold(
381
- self,
382
- actor: ActorContext,
383
- *,
384
- slug: str,
385
- event_type: str,
386
- field: str,
387
- operation: str,
388
- payload: Any,
389
- source: str,
390
- expected_revision: str,
391
- confirmation: str,
392
- ) -> ThreadFoldReceipt:
393
- if confirmation != THREAD_CONFIRMATION:
394
- raise ThreadError("thread fold requires the exact steward confirmation")
395
- if event_type not in THREAD_EVENT_TYPES:
396
- raise ThreadError("unsupported thread event type")
397
- if field not in THREAD_FIELDS:
398
- raise ThreadError("unsupported stored-intent field")
399
- if operation not in {"replace", "append", "remove"}:
400
- raise ThreadError("thread fold operation must be replace, append, or remove")
401
- current = self.read(actor, slug)
402
- self._authorize(actor, Permission.WRITE, f"thread:{current.slug}", self.canonical_path(current.slug))
403
- if actor.actor.actor_id not in current.steward_ids:
404
- admin = self.runtime.authorize(actor, Permission.ADMINISTER, (f"thread:{current.slug}",))
405
- admin_role = bool(
406
- actor.membership
407
- and {role.casefold() for role in actor.membership.roles}
408
- & {"admin", "administrator", "owner", "founder"}
409
- )
410
- if not admin.allowed or not admin_role:
411
- raise AuthorizationDenied("only a stable steward actor id may confirm this fold")
412
- if current.revision != expected_revision:
413
- raise ThreadError("thread revision changed; re-open it before confirming the fold")
414
- intent = current.projection_input()
415
- before = intent.get(field)
416
- if operation == "replace":
417
- after = payload
418
- elif operation == "append":
419
- values = list(before or [])
420
- if payload not in values:
421
- values.append(payload)
422
- after = values
423
- else:
424
- values = list(before or [])
425
- after = [value for value in values if value != payload]
426
- if field == "status" and str(after) not in THREAD_STATUSES:
427
- raise ThreadError("invalid thread status transition")
428
- if field == "planning_horizon" and after not in {None, "now", "next", "later"}:
429
- raise ThreadError("planning horizon must be now, next, or later")
430
- if field in {"steward", "stewards"}:
431
- candidates = [str(after)] if field == "steward" else [str(item) for item in after]
432
- if not all(_stable_actor_id(value) for value in candidates):
433
- raise ThreadError("stewards must use stable actor ids, not display aliases")
434
- now = self.clock().astimezone(UTC)
435
- event_payload = {
436
- "thread": current.document.artifact.artifact_id,
437
- "thread_revision": current.revision,
438
- "occurred_at": _iso(now),
439
- "actor_id": actor.actor.actor_id,
440
- "actor_alias": actor.actor.display_name,
441
- "type": event_type,
442
- "field": field,
443
- "op": operation,
444
- "before": before,
445
- "payload": payload,
446
- "after": after,
447
- "source": source.strip(),
448
- "confirmed": True,
449
- }
450
- encoded = json.dumps(event_payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
451
- event_hash = hashlib.sha256(encoded.encode("utf-8")).hexdigest()
452
- event_id = f"thread-event:{current.slug}:{event_hash[:20]}"
453
- event_body = f"# Thread fold: {current.title}\n\n```json\n{encoded}\n```\n"
454
- event_document = CanonicalDocument(
455
- artifact=CanonicalArtifact(
456
- schema_version=ARTIFACT_SCHEMA_VERSION,
457
- artifact_id=event_id,
458
- org_id=actor.profile.org_id,
459
- artifact_type="lifecycle-event",
460
- title=f"thread fold: {current.title}",
461
- created_at=now,
462
- created_by=actor.actor.actor_id,
463
- status="accepted",
464
- canonical_path=f"threads/.events/{current.slug}/{event_hash[:20]}.md",
465
- revision=f"sha256:{content_digest(event_body)[:16]}",
466
- content_hash=content_digest(event_body),
467
- relationships=(ArtifactRelationship("transitions", current.document.artifact.artifact_id),),
468
- ),
469
- body=event_body,
470
- legacy_fields={
471
- "lifecycle_action": "thread-fold",
472
- "target_artifact_id": current.document.artifact.artifact_id,
473
- "target_path": f"memory/threads/{current.slug}.md",
474
- "expected_lifecycle_revision": current.revision,
475
- "domain_event_schema": THREAD_EVENT_SCHEMA_VERSION,
476
- "domain_event": event_payload,
477
- },
478
- )
479
- updates = dict(current.document.legacy_fields)
480
- key = "steward_id" if field == "steward" else "steward_ids" if field == "stewards" else field
481
- updates[key] = after
482
- updates["last_event_id"] = event_id
483
- updates["reviewed_at"] = _iso(now)
484
- body = _fold_body(current, field, after)
485
- digest = content_digest(body)
486
- thread_document = CanonicalDocument(
487
- artifact=replace(
488
- current.document.artifact,
489
- status=str(after) if field == "status" else current.status,
490
- revision=f"sha256:{digest[:16]}-{event_hash[:8]}",
491
- content_hash=digest,
492
- ),
493
- body=body,
494
- policy_hints=current.document.policy_hints,
495
- legacy_fields=updates,
496
- migrated_from=current.document.migrated_from,
497
- warnings=current.document.warnings,
498
- expected_revision=current.revision,
499
- )
500
- receipts = self.runtime.write_documents(actor, (event_document, thread_document))
501
- if any(row.status is WritebackStatus.REJECTED for row in receipts):
502
- raise ThreadError("Thread canonical persistence failed; partial writes may remain: " +
503
- "; ".join(w for row in receipts for w in row.warnings))
504
- updated = replace(
505
- current,
506
- target=str(after) if field == "target" else current.target,
507
- status=str(after) if field == "status" else current.status,
508
- steward_id=str(after) if field == "steward" else current.steward_id,
509
- steward_ids=tuple(str(item) for item in after) if field == "stewards" else current.steward_ids,
510
- next_steps=tuple(str(item) for item in after) if field == "next_steps" else current.next_steps,
511
- membership_seed=_as_mapping(after, field=field) if field == "membership_seed" else current.membership_seed,
512
- composes_into=tuple(str(item) for item in after) if field == "composes_into" else current.composes_into,
513
- domain=str(after) if field == "domain" and after is not None else current.domain,
514
- planning_horizon=str(after) if field == "planning_horizon" and after is not None else current.planning_horizon,
515
- next_move=str(after) if field == "next_move" and after is not None else current.next_move,
516
- reviewed_at=str(updates["reviewed_at"]),
517
- facets=_as_mapping(after, field=field) if field == "facets" else current.facets,
518
- glossary=_as_mapping(after, field=field) if field == "glossary" else current.glossary,
519
- document=thread_document,
520
- )
521
- return ThreadFoldReceipt(updated, event_id, receipts)
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
- export EGREGORE_ROOT="$ROOT"
6
- export EGREGORE_SESSION_ID="${EGREGORE_SESSION_ID:-$(cat "$ROOT/.egregore-session-id" 2>/dev/null || true)}"
7
- export PYTHONSAFEPATH=1 PYTHONPATH="$ROOT${PYTHONPATH:+:$PYTHONPATH}"
8
-
9
- exec python3 -m egregore_runtime.thread_cli "$@"
@@ -1,102 +0,0 @@
1
- """Egregore-surface admin-content protection.
2
-
3
- ``admin: true`` frontmatter gives a canonical document its documented
4
- meaning: hidden on Egregore surfaces from non-admin members, and refused for
5
- external publication without an authorized admin's separate confirmation.
6
-
7
- This is soft product-layer protection. Every organization member holds full
8
- repository access and can read raw Git files; nothing here claims per-document
9
- filesystem confidentiality, and no surface may describe it as an ACL. What it
10
- guarantees is that Egregore itself — retrieval evidence, Observe context,
11
- source opening, listings, rendering, publication — never hands admin-marked
12
- content to a non-admin member, exposing at most an opaque withheld count.
13
-
14
- Admin capability derives from stable identity: the resolved actor's
15
- ``github.username`` alias (recorded by onboarding into the identity state)
16
- matched against the organization's configured ``admins`` list. Display names,
17
- the ``legacy.person`` alias, Git configuration, OS usernames, branch names,
18
- and paths never participate. Every error path fails closed to non-admin.
19
- """
20
-
21
- from __future__ import annotations
22
-
23
- import json
24
- import re
25
- from dataclasses import dataclass, field
26
- from pathlib import Path
27
- from typing import Any
28
-
29
- _FRONTMATTER_ADMIN = re.compile(r"^\s*admin:\s*[\"']?true[\"']?\s*$", re.MULTILINE)
30
- _HEAD_BYTES = 4096
31
-
32
-
33
- def document_is_admin(text: str) -> bool:
34
- """True when the document's YAML frontmatter declares ``admin: true``."""
35
-
36
- if not text.startswith("---"):
37
- return False
38
- end = text.find("\n---", 3)
39
- head = text[3:end] if end != -1 else text[3:_HEAD_BYTES]
40
- return bool(_FRONTMATTER_ADMIN.search(head))
41
-
42
-
43
- @dataclass
44
- class AdminGate:
45
- config_path: Path
46
- memory_root: Path
47
- _flag_cache: dict[str, tuple[int, int, bool]] = field(default_factory=dict)
48
-
49
- def admin_handles(self) -> frozenset[str]:
50
- try:
51
- config: Any = json.loads(self.config_path.read_text(encoding="utf-8"))
52
- except (OSError, ValueError):
53
- return frozenset()
54
- handles = config.get("admins") if isinstance(config, dict) else None
55
- if not isinstance(handles, list):
56
- return frozenset()
57
- return frozenset(
58
- str(handle).strip().casefold()
59
- for handle in handles
60
- if str(handle).strip()
61
- )
62
-
63
- def actor_is_admin(self, actor: Any) -> bool:
64
- """Stable-identity admin check — github.username alias only."""
65
-
66
- handles = self.admin_handles()
67
- if not handles:
68
- return False
69
- try:
70
- aliases = getattr(actor.actor, "aliases", None) or {}
71
- handle = aliases.get("github.username") if isinstance(aliases, dict) else None
72
- except AttributeError:
73
- return False
74
- if not handle:
75
- return False
76
- return str(handle).strip().casefold() in handles
77
-
78
- def path_is_admin(self, canonical_path: str) -> bool:
79
- """True when the canonical document at ``memory/...`` is admin-marked.
80
-
81
- Unreadable or missing files report False — absence of evidence is not
82
- an admin marking, and the surrounding authorization already decided
83
- whether the path may be read at all.
84
- """
85
-
86
- relative = canonical_path.strip().replace("\\", "/").removeprefix("memory/")
87
- target = self.memory_root / relative
88
- try:
89
- stat = target.stat()
90
- except OSError:
91
- return False
92
- cached = self._flag_cache.get(relative)
93
- if cached and cached[0] == stat.st_mtime_ns and cached[1] == stat.st_size:
94
- return cached[2]
95
- try:
96
- with target.open("r", encoding="utf-8", errors="replace") as handle:
97
- head = handle.read(_HEAD_BYTES)
98
- except OSError:
99
- return False
100
- flag = document_is_admin(head)
101
- self._flag_cache[relative] = (stat.st_mtime_ns, stat.st_size, flag)
102
- return flag