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,548 @@
1
+ """Read-only facts about a host-selected outgoing canonical Git history.
2
+
3
+ This module neither authorizes publication nor proves remote freshness. It reads
4
+ the complete selected ancestry, newly reachable objects and changed tree paths.
5
+ Manual Git and transports which do not call it remain outside this prerequisite.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from contextlib import contextmanager
11
+ from contextvars import ContextVar
12
+ import hashlib
13
+ import json
14
+ import os
15
+ from pathlib import Path
16
+ import re
17
+ import selectors
18
+ import signal
19
+ import stat
20
+ import subprocess
21
+ import time
22
+ import unicodedata
23
+
24
+ from .record_subject import classify_record
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class HistoryLimits:
29
+ commits: int = 256
30
+ objects: int = 4096
31
+ object_bytes: int = 256 * 1024
32
+ total_bytes: int = 16 * 1024 * 1024
33
+ tree_entries: int = 8192
34
+ seconds: float = 20
35
+
36
+
37
+ DEFAULT_LIMITS = HistoryLimits()
38
+ OID = re.compile(r"(?:[a-f0-9]{40}|[a-f0-9]{64})\Z")
39
+ _CONTAINED_GIT = ContextVar("egregore_host_contained_git", default=False)
40
+
41
+
42
+ @contextmanager
43
+ def contained_git_children():
44
+ """Fixed detached host owns the outer process group, including Git helpers.
45
+
46
+ Private native host workers opt in directly; no environment or public CLI
47
+ input selects this. Generic managed/history callers keep detached children.
48
+ The host must terminate its whole outer group even after normal completion.
49
+ """
50
+ if os.getpgrp() != os.getpid():
51
+ raise ValueError("contained Git requires a detached process-group owner")
52
+ token = _CONTAINED_GIT.set(True)
53
+ try:
54
+ yield
55
+ finally:
56
+ _CONTAINED_GIT.reset(token)
57
+
58
+
59
+ class _Unknown(Exception):
60
+ pass
61
+
62
+
63
+ def _digest(value):
64
+ return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
65
+
66
+
67
+ def _ref(value, *, head=False):
68
+ if head and value == "HEAD":
69
+ return True
70
+ return (isinstance(value, str) and len(value) <= 240
71
+ and value.startswith(("refs/heads/", "refs/remotes/"))
72
+ and not re.search(r"[\x00-\x20\x7f~^:?*\[\\]", value)
73
+ and ".." not in value and "@{" not in value
74
+ and all(part and not part.startswith(".") and not part.endswith((".", ".lock")) for part in value.split("/")))
75
+
76
+
77
+ def _regular(path, limit=65536):
78
+ info = path.lstat()
79
+ if not stat.S_ISREG(info.st_mode) or info.st_size > limit:
80
+ raise _Unknown("unsupported_repository")
81
+ with path.open("rb") as handle:
82
+ value = handle.read(limit + 1)
83
+ if len(value) > limit:
84
+ raise _Unknown("unsupported_repository")
85
+ return value
86
+
87
+
88
+ def _repository(repository, deadline):
89
+ def remaining():
90
+ if time.monotonic() >= deadline:
91
+ raise _Unknown("time_limit")
92
+ remaining()
93
+ root = Path(repository)
94
+ if not root.is_absolute() or root != root.resolve() or not root.is_dir() or any(ord(c) < 32 for c in str(root)):
95
+ raise _Unknown("unsupported_repository")
96
+ dot = root / ".git"
97
+ git_dir = dot if dot.is_dir() else root
98
+ if dot.exists() and (dot.is_symlink() or not dot.is_dir()):
99
+ raise _Unknown("unsupported_repository") # Linked worktrees need a separate host binding.
100
+ config = _regular(git_dir / "config")
101
+ _regular(git_dir / "HEAD")
102
+ if (git_dir / "commondir").exists() or re.search(rb"(?im)^\s*\[\s*include(?:if)?\b|\b(?:partialclone|promisor)\s*=", config):
103
+ raise _Unknown("unsupported_repository")
104
+ for name in ("shallow", "info/grafts", "objects/info/alternates", "objects/info/http-alternates"):
105
+ target = git_dir / name
106
+ for parent in target.parents:
107
+ if parent == git_dir:
108
+ break
109
+ if parent.is_symlink():
110
+ raise _Unknown("unsupported_repository")
111
+ if target.exists() or target.is_symlink():
112
+ if _regular(target).strip():
113
+ raise _Unknown("incomplete_repository")
114
+ objects = git_dir / "objects"
115
+ if not objects.is_dir() or objects.is_symlink():
116
+ raise _Unknown("unsupported_repository")
117
+ # Git may consult pack indexes and commit graphs without naming each file
118
+ # in its output. Do not allow those metadata paths to lead to another repo.
119
+ metadata_entries = 0
120
+ def metadata_tree(directory, depth=0):
121
+ nonlocal metadata_entries
122
+ with os.scandir(directory) as stream:
123
+ for entry in stream:
124
+ remaining()
125
+ metadata_entries += 1
126
+ if metadata_entries > 512 or entry.is_symlink():
127
+ raise _Unknown("unsupported_repository")
128
+ if entry.is_dir(follow_symlinks=False):
129
+ if depth >= 2:
130
+ raise _Unknown("unsupported_repository")
131
+ metadata_tree(Path(entry.path), depth + 1)
132
+ elif not entry.is_file(follow_symlinks=False):
133
+ raise _Unknown("unsupported_repository")
134
+ with os.scandir(objects) as stream:
135
+ for index, entry in enumerate(stream):
136
+ remaining()
137
+ if index >= 258 or entry.is_symlink() or not entry.is_dir(follow_symlinks=False):
138
+ raise _Unknown("unsupported_repository")
139
+ if entry.name in {"info", "pack"}:
140
+ metadata_tree(Path(entry.path))
141
+ elif not re.fullmatch(r"[a-f0-9]{2}", entry.name):
142
+ raise _Unknown("unsupported_repository")
143
+ return root, git_dir
144
+
145
+
146
+ class _Git:
147
+ def __init__(self, root, deadline):
148
+ self.root, self.deadline = root, deadline
149
+ self.env = {key: value for key, value in os.environ.items() if not key.startswith("GIT_")}
150
+ self.env.update(GIT_CONFIG_NOSYSTEM="1", GIT_CONFIG_GLOBAL=os.devnull, GIT_TERMINAL_PROMPT="0",
151
+ GIT_OPTIONAL_LOCKS="0", GIT_NO_LAZY_FETCH="1", LC_ALL="C")
152
+
153
+ def start(self, args, *, stdin=False):
154
+ self.remaining()
155
+ contained = _CONTAINED_GIT.get()
156
+ process = subprocess.Popen(["git", "--no-replace-objects", "-c", "protocol.allow=never",
157
+ "-c", "core.fsmonitor=false", "-c", "core.hooksPath=" + os.devnull,
158
+ "-C", str(self.root), *args],
159
+ stdin=subprocess.PIPE if stdin else subprocess.DEVNULL,
160
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
161
+ env=self.env, start_new_session=not contained, bufsize=0)
162
+ process._egregore_contained = contained
163
+ return process
164
+
165
+ def remaining(self):
166
+ remaining = self.deadline - time.monotonic()
167
+ if remaining <= 0:
168
+ raise _Unknown("time_limit")
169
+ return remaining
170
+
171
+ @staticmethod
172
+ def close(process):
173
+ if process.poll() is None:
174
+ try:
175
+ if getattr(process, "_egregore_contained", False):
176
+ process.kill()
177
+ else:
178
+ os.killpg(process.pid, signal.SIGKILL)
179
+ except ProcessLookupError:
180
+ pass
181
+ except PermissionError:
182
+ # Some hosts prohibit process-group signals while permitting
183
+ # termination of this exact child. These plumbing commands do
184
+ # not launch filters, hooks, pagers or transport helpers.
185
+ process.kill()
186
+ process.wait()
187
+ if process.stdin:
188
+ process.stdin.close()
189
+ process.stdout.close()
190
+
191
+ def read(self, process, count):
192
+ with selectors.DefaultSelector() as selector:
193
+ selector.register(process.stdout, selectors.EVENT_READ)
194
+ if not selector.select(self.remaining()):
195
+ raise _Unknown("time_limit")
196
+ return os.read(process.stdout.fileno(), count)
197
+
198
+ def run(self, *args, limit=262144, allowed=(0,)):
199
+ process = self.start(args)
200
+ output = bytearray()
201
+ try:
202
+ while True:
203
+ chunk = self.read(process, min(65536, limit + 1 - len(output)))
204
+ if not chunk:
205
+ break
206
+ output.extend(chunk)
207
+ if len(output) > limit:
208
+ raise _Unknown("inventory_limit")
209
+ code = process.wait(timeout=self.remaining())
210
+ if code not in allowed:
211
+ raise _Unknown("git_evidence_unavailable")
212
+ return bytes(output), code
213
+ finally:
214
+ self.close(process)
215
+
216
+
217
+ class _Objects:
218
+ """One size-aware cat-file protocol process; never buffer an unlimited body."""
219
+ def __init__(self, git, git_dir, limits, counts):
220
+ self.git, self.git_dir, self.limits, self.counts = git, git_dir, limits, counts
221
+ self.process = git.start(["cat-file", "--batch"], stdin=True)
222
+ self.buffer, self.cache = bytearray(), {}
223
+
224
+ def exact(self, size):
225
+ while len(self.buffer) < size:
226
+ chunk = self.git.read(self.process, min(65536, size - len(self.buffer)))
227
+ if not chunk:
228
+ raise _Unknown("object_unavailable")
229
+ self.buffer.extend(chunk)
230
+ value = bytes(self.buffer[:size]); del self.buffer[:size]
231
+ return value
232
+
233
+ def get(self, oid):
234
+ self.git.remaining()
235
+ if oid in self.cache:
236
+ return self.cache[oid]
237
+ if len(self.cache) >= self.limits.objects:
238
+ raise _Unknown("object_limit")
239
+ loose = self.git_dir / "objects" / oid[:2] / oid[2:]
240
+ if loose.is_symlink():
241
+ raise _Unknown("unsupported_repository")
242
+ self.process.stdin.write(oid.encode() + b"\n")
243
+ header = bytearray()
244
+ while not header.endswith(b"\n"):
245
+ if len(header) >= 128:
246
+ raise _Unknown("object_unavailable")
247
+ header.extend(self.exact(1))
248
+ fields = header.split()
249
+ if len(fields) != 3 or fields[0].decode("ascii") != oid or fields[1] not in (b"commit", b"tree", b"blob"):
250
+ raise _Unknown("object_unavailable")
251
+ size = int(fields[2])
252
+ if size < 0 or size > self.limits.object_bytes:
253
+ raise _Unknown("object_byte_limit")
254
+ if self.counts["bytes"] + size > self.limits.total_bytes:
255
+ raise _Unknown("total_byte_limit")
256
+ value = self.exact(size)
257
+ if self.exact(1) != b"\n":
258
+ raise _Unknown("object_unavailable")
259
+ algorithm = hashlib.sha1 if len(oid) == 40 else hashlib.sha256
260
+ if algorithm(fields[1] + b" " + str(size).encode() + b"\0" + value).hexdigest() != oid:
261
+ raise _Unknown("object_corrupt")
262
+ self.counts["objects"] += 1; self.counts["bytes"] += size
263
+ result = fields[1].decode(), value
264
+ self.cache[oid] = result
265
+ return result
266
+
267
+ def close(self):
268
+ self.git.close(self.process)
269
+
270
+
271
+ def _tree(value, oid_bytes):
272
+ offset, names, entries = 0, set(), {}
273
+ while offset < len(value):
274
+ end = value.find(b"\0", offset)
275
+ if end < 0 or end + 1 + oid_bytes > len(value):
276
+ raise _Unknown("malformed_tree")
277
+ prefix = value[offset:end]
278
+ mode, sep, raw_name = prefix.partition(b" ")
279
+ if not sep or mode not in (b"40000", b"100644", b"100755"):
280
+ raise _Unknown("unsupported_tree_entry")
281
+ name = raw_name.decode("utf-8")
282
+ folded = unicodedata.normalize("NFC", name).casefold()
283
+ if (not name or name in (".", "..") or folded == ".git" or "/" in name or "\\" in name
284
+ or len(raw_name) > 255 or any(ord(c) < 32 or ord(c) == 127 for c in name)
285
+ or name != unicodedata.normalize("NFC", name) or folded in names):
286
+ raise _Unknown("ambiguous_tree_path")
287
+ entries[name] = (mode.decode(), value[end + 1:end + 1 + oid_bytes].hex())
288
+ names.add(folded); offset = end + 1 + oid_bytes
289
+ return entries
290
+
291
+
292
+ def _commit(value, oid_length):
293
+ header, sep, _ = value.partition(b"\n\n")
294
+ if not sep or b"\0" in header:
295
+ raise _Unknown("malformed_commit")
296
+ lines = header.splitlines()
297
+ trees = [line[5:] for line in lines if line.startswith(b"tree ")]
298
+ parents = [line[7:] for line in lines if line.startswith(b"parent ")]
299
+ if len(trees) != 1 or not lines or lines[0] != b"tree " + trees[0]:
300
+ raise _Unknown("malformed_commit")
301
+ refs = [oid.decode("ascii") for oid in trees + parents]
302
+ if any(not OID.fullmatch(oid) or len(oid) != oid_length for oid in refs):
303
+ raise _Unknown("malformed_commit")
304
+ return refs[0], refs[1:]
305
+
306
+
307
+ def _subject(value):
308
+ # Native canonical records start with frontmatter. Arbitrary prose or body
309
+ # code examples are not a native record marker and are never promoted here.
310
+ if not value.startswith((b"---", b"\xef\xbb\xbf---")):
311
+ return None
312
+ text = value.decode("utf-8").replace("\r\n", "\n").replace("\r", "\n").removeprefix("\ufeff")
313
+ lines = text.splitlines()
314
+ if not lines or lines[0].strip() != "---":
315
+ return None
316
+ fields, closed, malformed, marked, block_indent = {}, False, False, False, None
317
+ container = False
318
+ for line in lines[1:]:
319
+ if line == "---":
320
+ closed = True; break
321
+ indent = len(line) - len(line.lstrip(" "))
322
+ if block_indent is not None:
323
+ parent_indent, content_indent = block_indent
324
+ if not line.strip():
325
+ continue
326
+ if content_indent is None and indent > parent_indent:
327
+ content_indent = indent
328
+ block_indent = (parent_indent, content_indent)
329
+ if content_indent is not None and indent >= content_indent:
330
+ continue # An explicit YAML block scalar is prose, not fields.
331
+ block_indent = None
332
+ if not line.strip() or line.lstrip().startswith("#"):
333
+ continue
334
+ key, sep, raw = line.partition(":")
335
+ if sep and classify_record({key: raw.strip().strip("\"'")}) != "human-session":
336
+ marked = True
337
+ if indent or line.startswith("-"):
338
+ # Common YAML lists and nested values are valid human frontmatter.
339
+ # A marker with malformed indentation still remains native-marked.
340
+ if not container or "\t" in line[:indent + 1]:
341
+ malformed = True
342
+ continue
343
+ if not sep or key in fields or not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_-]*|\"[^\"\n]+\"|'[^'\n]+'", key):
344
+ malformed = True
345
+ if sep:
346
+ scalar_text = raw.strip()
347
+ container = not scalar_text or scalar_text.startswith(("[", "{"))
348
+ if re.fullmatch(r"[|>][1-9]?[+-]?(?:\s+#.*)?|[|>][+-][1-9](?:\s+#.*)?", scalar_text):
349
+ explicit = re.search(r"[1-9]", scalar_text.split("#", 1)[0])
350
+ block_indent = (indent, indent + int(explicit[0]) if explicit else None)
351
+ try:
352
+ scalar = json.loads(scalar_text)
353
+ except ValueError:
354
+ scalar = scalar_text
355
+ fields[key] = scalar
356
+ subject = classify_record(fields)
357
+ if marked and subject == "human-session":
358
+ subject = "unsupported-native"
359
+ if subject != "human-session":
360
+ return subject if closed and not malformed else "unsupported-native"
361
+ if not closed or malformed:
362
+ raise _Unknown("ambiguous_record_header")
363
+ return None
364
+
365
+
366
+ def inspect_outgoing_history(repository, *, base_oid, head_oid, base_ref, head_ref, limits=DEFAULT_LIMITS):
367
+ """Inspect exact local selections. A returned fact is never a SHARE grant.
368
+
369
+ Every newly reachable ancestor blob remains inspected even if the tip later
370
+ replaces, renames or deletes its path. Changed paths in every outgoing
371
+ commit are also inspected on both sides, including blobs reused from base.
372
+ Unchanged base subtrees are excluded. Local ref equality is checked twice;
373
+ a transport must still obtain fresh destination and source-CAS admission.
374
+ An explicitly absent base (both base fields None) scans all head history;
375
+ it does not prove a remote branch is absent.
376
+ """
377
+ initial = base_oid is None and base_ref is None
378
+ object_base = base_oid is not None and base_ref is None
379
+ binding = {"repository_fingerprint": None, "base_oid": base_oid if isinstance(base_oid, str) and OID.fullmatch(base_oid) else None,
380
+ "head_oid": head_oid if isinstance(head_oid, str) and OID.fullmatch(head_oid) else None,
381
+ "base_ref": base_ref if _ref(base_ref) else None, "head_ref": head_ref if _ref(head_ref, head=True) else None,
382
+ "object_format": None, "range_kind": "initial-history" if initial else "outgoing-range",
383
+ "base_witness": "absent-selection" if initial else "object-only" if object_base else "local-ref"}
384
+ counts = {"commits": 0, "objects": 0, "bytes": 0, "tree_entries": 0, "path_checks": 0, "native_blobs": 0}
385
+ native, reasons, objects, classified = [], [], None, set()
386
+ try:
387
+ if (not isinstance(limits, HistoryLimits) or any(type(getattr(limits, key)) is not int or not 1 <= getattr(limits, key) <= getattr(DEFAULT_LIMITS, key)
388
+ for key in ("commits", "objects", "object_bytes", "total_bytes", "tree_entries"))
389
+ or type(limits.seconds) not in (float, int) or not 0 < limits.seconds <= DEFAULT_LIMITS.seconds):
390
+ raise _Unknown("invalid_limits")
391
+ required = ("head_oid", "head_ref") if initial else ("base_oid", "head_oid", "head_ref") if object_base else ("base_oid", "head_oid", "base_ref", "head_ref")
392
+ if any(binding[key] is None for key in required) or not initial and len(base_oid) != len(head_oid):
393
+ raise _Unknown("invalid_selection")
394
+ deadline = time.monotonic() + limits.seconds
395
+ root, git_dir = _repository(repository, deadline)
396
+ binding["repository_fingerprint"] = _digest({"repository": str(root), "git_dir": str(git_dir)})
397
+ git = _Git(root, deadline)
398
+ def check_refs():
399
+ selections = [(head_ref, head_oid)] if initial or object_base else [(base_ref, base_oid), (head_ref, head_oid)]
400
+ for ref, expected in selections:
401
+ # Follow only bounded, validated symbolic-ref spellings before
402
+ # asking Git to resolve them. HEAD must not hide a ref symlink.
403
+ target, seen = ref, set()
404
+ while True:
405
+ if target in seen or len(seen) >= 8:
406
+ raise _Unknown("ambiguous_range")
407
+ seen.add(target)
408
+ parts = ("HEAD",) if target == "HEAD" else tuple(target.split("/"))
409
+ selected = git_dir
410
+ for part in parts:
411
+ selected /= part
412
+ if selected.is_symlink():
413
+ raise _Unknown("unsupported_repository")
414
+ if not selected.exists():
415
+ break # Git may find this exact ref in packed-refs.
416
+ value = _regular(selected, 1024).strip()
417
+ if not value.startswith(b"ref: "):
418
+ break
419
+ target = value[5:].decode("ascii")
420
+ if not _ref(target):
421
+ raise _Unknown("ambiguous_range")
422
+ if (git_dir / "packed-refs").is_symlink():
423
+ raise _Unknown("unsupported_repository")
424
+ raw, _ = git.run("rev-parse", "--verify", ref, limit=256)
425
+ if raw.strip().decode("ascii") != expected:
426
+ raise _Unknown("stale_ref_binding")
427
+ check_refs()
428
+ fmt, _ = git.run("rev-parse", "--show-object-format", limit=32)
429
+ binding["object_format"] = fmt.strip().decode("ascii")
430
+ if binding["object_format"] != ("sha1" if len(head_oid) == 40 else "sha256"):
431
+ raise _Unknown("invalid_selection")
432
+ objects = _Objects(git, git_dir, limits, counts)
433
+ if any(objects.get(oid)[0] != "commit" for oid in ([head_oid] if initial else [base_oid, head_oid])):
434
+ raise _Unknown("invalid_selection")
435
+ if not initial:
436
+ _, code = git.run("merge-base", "--is-ancestor", base_oid, head_oid, allowed=(0, 1), limit=0)
437
+ if code:
438
+ raise _Unknown("non_ancestor_base")
439
+ selected_range = [head_oid] if initial else [head_oid, "^" + base_oid]
440
+ raw, _ = git.run("rev-list", f"--max-count={limits.commits + 1}", *selected_range, "--",
441
+ limit=(limits.commits + 1) * (len(head_oid) + 1))
442
+ commits = raw.decode("ascii").splitlines()
443
+ if len(commits) > limits.commits:
444
+ raise _Unknown("commit_limit")
445
+ if any(not OID.fullmatch(oid) or len(oid) != len(head_oid) for oid in commits) or len(set(commits)) != len(commits):
446
+ raise _Unknown("ambiguous_range")
447
+ counts["commits"] = len(commits)
448
+ try:
449
+ raw, _ = git.run("rev-list", "--objects", "--no-object-names", *selected_range, "--",
450
+ limit=(limits.objects + 1) * (len(head_oid) + 1))
451
+ except _Unknown as exc:
452
+ if str(exc) == "inventory_limit":
453
+ raise _Unknown("object_limit") from exc
454
+ raise
455
+ outgoing = raw.decode("ascii").splitlines()
456
+ if len(outgoing) > limits.objects:
457
+ raise _Unknown("object_limit")
458
+ if len(set(outgoing)) != len(outgoing) or any(not OID.fullmatch(oid) or len(oid) != len(head_oid) for oid in outgoing) or not set(commits) <= set(outgoing):
459
+ raise _Unknown("ambiguous_range")
460
+ trees, commit_trees = {}, {}
461
+
462
+ def inspect_blob(oid):
463
+ if oid in classified:
464
+ return
465
+ kind, value = objects.get(oid)
466
+ if kind != "blob":
467
+ raise _Unknown("malformed_tree")
468
+ classified.add(oid)
469
+ subject = _subject(value)
470
+ if subject:
471
+ counts["native_blobs"] += 1
472
+ if len(native) < 32:
473
+ native.append({"oid": oid, "subject_kind": subject})
474
+
475
+ def tree(oid):
476
+ if oid is None:
477
+ return {}
478
+ if oid not in trees:
479
+ kind, value = objects.get(oid)
480
+ if kind != "tree":
481
+ raise _Unknown("malformed_tree")
482
+ entries = _tree(value, len(oid) // 2)
483
+ counts["tree_entries"] += len(entries)
484
+ if counts["tree_entries"] > limits.tree_entries:
485
+ raise _Unknown("tree_entry_limit")
486
+ trees[oid] = entries
487
+ return trees[oid]
488
+
489
+ def commit(oid):
490
+ if oid not in commit_trees:
491
+ kind, value = objects.get(oid)
492
+ if kind != "commit":
493
+ raise _Unknown("malformed_commit")
494
+ commit_trees[oid] = _commit(value, len(oid))
495
+ return commit_trees[oid]
496
+
497
+ for oid in outgoing:
498
+ kind, value = objects.get(oid)
499
+ if kind == "tree":
500
+ tree(oid)
501
+ elif kind == "blob":
502
+ inspect_blob(oid)
503
+ elif kind == "commit":
504
+ commit(oid)
505
+ # Object exclusion alone misses a new path which reuses a base blob.
506
+ # Compare each selected commit with its first parent, but enumerate ALL
507
+ # selected ancestry above. Side-branch ancestors therefore still count.
508
+ # This is intentionally conservative about removed/renamed preimages.
509
+ for oid in commits:
510
+ after, parents = commit(oid)
511
+ before = commit(parents[0])[0] if parents else None
512
+ stack = [(before, after, "")]
513
+ while stack:
514
+ git.remaining()
515
+ old_oid, new_oid, prefix = stack.pop()
516
+ if old_oid == new_oid:
517
+ continue
518
+ old, new = tree(old_oid), tree(new_oid)
519
+ for name in sorted(old.keys() | new.keys()):
520
+ counts["path_checks"] += 1
521
+ if counts["path_checks"] > limits.tree_entries:
522
+ raise _Unknown("path_check_limit")
523
+ left, right = old.get(name), new.get(name)
524
+ if left == right:
525
+ continue
526
+ path = prefix + name
527
+ if len(path.encode()) > 4096 or path.count("/") >= 256:
528
+ raise _Unknown("ambiguous_tree_path")
529
+ for entry in (left, right):
530
+ if entry and entry[0] != "40000":
531
+ inspect_blob(entry[1])
532
+ left_tree = left[1] if left and left[0] == "40000" else None
533
+ right_tree = right[1] if right and right[0] == "40000" else None
534
+ if left_tree != right_tree:
535
+ stack.append((left_tree, right_tree, path + "/"))
536
+ check_refs()
537
+ except _Unknown as exc:
538
+ reasons.append(str(exc))
539
+ except (OSError, ValueError, UnicodeError, TypeError, OverflowError, RecursionError, subprocess.TimeoutExpired):
540
+ reasons.append("git_evidence_unavailable")
541
+ finally:
542
+ if objects:
543
+ objects.close()
544
+ result = {"schema": "egregore.native-outgoing-history/v1", "status": "unknown" if reasons else "present" if native else "none",
545
+ "complete": not reasons, "binding": binding, "counts": counts, "native_blobs": sorted(native, key=lambda row: row["oid"]),
546
+ "examples_truncated": counts["native_blobs"] > len(native), "reasons": reasons,
547
+ "remote_freshness": "not_checked", "publication_authority": False}
548
+ return {**result, "digest": _digest(result)}