create-egregore 0.20.2 → 0.21.0-runtime-mvp.1
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.
- package/assets/egregore-launcher.js +1011 -16
- package/bin/cli.js +11 -0
- package/lib/claude-runtime.js +192 -0
- package/lib/local.js +38 -31
- package/lib/pi-runtime.js +1 -0
- package/lib/prime-runtime.js +1 -0
- package/lib/runtime-identity.js +146 -0
- package/lib/setup.js +116 -17
- package/package.json +10 -6
- package/runtime/claude/.claude/context/commit-format.md +110 -0
- package/runtime/claude/.claude/context/git-language.md +46 -0
- package/runtime/claude/.claude/context/notification-consent.md +66 -0
- package/runtime/claude/.claude/context/pr-format.md +67 -0
- package/runtime/claude/.claude/context/telemetry.md +133 -0
- package/runtime/claude/.claude/hooks/boundary-check.sh +333 -0
- package/runtime/claude/.claude/hooks/branch-guard.sh +231 -0
- package/runtime/claude/.claude/hooks/card-context.sh +38 -0
- package/runtime/claude/.claude/hooks/emissary-detect.sh +53 -0
- package/runtime/claude/.claude/hooks/memory-refresh.sh +67 -0
- package/runtime/claude/.claude/hooks/onboarding-guard.sh +86 -0
- package/runtime/claude/.claude/hooks/retrieval-context.sh +43 -0
- package/runtime/claude/.claude/rules/voice-bedrock.md +73 -0
- package/runtime/claude/.claude/settings.json +131 -0
- package/runtime/claude/.claude/skills/activity/SKILL.md +68 -0
- package/runtime/claude/.claude/skills/announce/SKILL.md +54 -0
- package/runtime/claude/.claude/skills/archive/SKILL.md +71 -0
- package/runtime/claude/.claude/skills/branch/SKILL.md +147 -0
- package/runtime/claude/.claude/skills/checkup/SKILL.md +159 -0
- package/runtime/claude/.claude/skills/commit/SKILL.md +103 -0
- package/runtime/claude/.claude/skills/contribute/SKILL.md +343 -0
- package/runtime/claude/.claude/skills/create-skill/SKILL.md +135 -0
- package/runtime/claude/.claude/skills/dark-mode/SKILL.md +410 -0
- package/runtime/claude/.claude/skills/dashboard/SKILL.md +43 -0
- package/runtime/claude/.claude/skills/deep-reflect/REPORT.md +140 -0
- package/runtime/claude/.claude/skills/deep-reflect/RESEARCH.md +132 -0
- package/runtime/claude/.claude/skills/deep-reflect/ROLES.md +104 -0
- package/runtime/claude/.claude/skills/deep-reflect/SKILL.md +170 -0
- package/runtime/claude/.claude/skills/delete-user/SKILL.md +150 -0
- package/runtime/claude/.claude/skills/emissary/SKILL.md +220 -0
- package/runtime/claude/.claude/skills/handoff/SKILL.md +124 -0
- package/runtime/claude/.claude/skills/harvest/AUDIT.md +546 -0
- package/runtime/claude/.claude/skills/harvest/FORMAT.md +436 -0
- package/runtime/claude/.claude/skills/harvest/PROCESS.md +395 -0
- package/runtime/claude/.claude/skills/harvest/QUESTION_PALETTE.md +76 -0
- package/runtime/claude/.claude/skills/harvest/SKILL.md +100 -0
- package/runtime/claude/.claude/skills/ingest/SKILL.md +112 -0
- package/runtime/claude/.claude/skills/ingest-corpus/SKILL.md +187 -0
- package/runtime/claude/.claude/skills/ingest-notion/SKILL.md +214 -0
- package/runtime/claude/.claude/skills/ingest-user-interview/SKILL.md +65 -0
- package/runtime/claude/.claude/skills/ingest-user-interview/references/analysis-contract.md +125 -0
- package/runtime/claude/.claude/skills/invite/SKILL.md +111 -0
- package/runtime/claude/.claude/skills/issue/SKILL.md +104 -0
- package/runtime/claude/.claude/skills/me/SKILL.md +81 -0
- package/runtime/claude/.claude/skills/meeting/SKILL.md +76 -0
- package/runtime/claude/.claude/skills/meeting/references/analysis-contract.md +102 -0
- package/runtime/claude/.claude/skills/note/SKILL.md +71 -0
- package/runtime/claude/.claude/skills/notion-connect/SKILL.md +124 -0
- package/runtime/claude/.claude/skills/onboarding/SKILL.md +159 -0
- package/runtime/claude/.claude/skills/pr/SKILL.md +85 -0
- package/runtime/claude/.claude/skills/pull/SKILL.md +79 -0
- package/runtime/claude/.claude/skills/push/SKILL.md +47 -0
- package/runtime/claude/.claude/skills/quest/SKILL.md +121 -0
- package/runtime/claude/.claude/skills/reflect/SKILL.md +69 -0
- package/runtime/claude/.claude/skills/review-pr/SKILL.md +276 -0
- package/runtime/claude/.claude/skills/save/SKILL.md +92 -0
- package/runtime/claude/.claude/skills/slack-connect/SKILL.md +148 -0
- package/runtime/claude/.claude/skills/summon/SKILL.md +240 -0
- package/runtime/claude/.claude/skills/sync-repos/SKILL.md +82 -0
- package/runtime/claude/.claude/skills/teams-connect/SKILL.md +216 -0
- package/runtime/claude/.claude/skills/telegram-connect/SKILL.md +110 -0
- package/runtime/claude/.claude/skills/telemetry/SKILL.md +117 -0
- package/runtime/claude/.claude/skills/test/SKILL.md +87 -0
- package/runtime/claude/.claude/skills/the-spiral/SKILL.md +492 -0
- package/runtime/claude/.claude/skills/tui-design/SKILL.md +188 -0
- package/runtime/claude/.claude/skills/tutorial/SKILL.md +100 -0
- package/runtime/claude/.claude/skills/update/SKILL.md +265 -0
- package/runtime/claude/.claude/skills/view/SKILL.md +393 -0
- package/runtime/claude/.claude/skills/view/compose-scaffold.html +221 -0
- package/runtime/claude/.claude/skills/visual-explain/SKILL.md +132 -0
- package/runtime/claude/.claude/skills/wrap/SKILL.md +98 -0
- package/runtime/claude/CLAUDE.md +447 -0
- package/runtime/claude/bin/activity-action.sh +92 -0
- package/runtime/claude/bin/activity-data.sh +17 -0
- package/runtime/claude/bin/agent.sh +783 -0
- package/runtime/claude/bin/artifact-register.sh +164 -0
- package/runtime/claude/bin/artifact-writeback.sh +53 -0
- package/runtime/claude/bin/artifacts.sh +288 -0
- package/runtime/claude/bin/attendant.sh +707 -0
- package/runtime/claude/bin/autosave.sh +112 -0
- package/runtime/claude/bin/boundary.sh +253 -0
- package/runtime/claude/bin/build-sealed-dataroom.mjs +722 -0
- package/runtime/claude/bin/capture-reconcile.sh +69 -0
- package/runtime/claude/bin/capture-run.sh +357 -0
- package/runtime/claude/bin/codex-install-skills.sh +78 -0
- package/runtime/claude/bin/codex-render-spec.mjs +462 -0
- package/runtime/claude/bin/codex-session-start.sh +275 -0
- package/runtime/claude/bin/codex-skill-render.mjs +349 -0
- package/runtime/claude/bin/codex-sync-skills.sh +237 -0
- package/runtime/claude/bin/connect-refresh.sh +33 -0
- package/runtime/claude/bin/contribute-guard.sh +79 -0
- package/runtime/claude/bin/corpus_map.py +806 -0
- package/runtime/claude/bin/corpus_survey.py +304 -0
- package/runtime/claude/bin/dashboard-data.sh +18 -0
- package/runtime/claude/bin/github-auth.sh +121 -0
- package/runtime/claude/bin/graph-batch.sh +86 -0
- package/runtime/claude/bin/graph-op.sh +1106 -0
- package/runtime/claude/bin/graph-projection.sh +54 -0
- package/runtime/claude/bin/graph-wal.sh +242 -0
- package/runtime/claude/bin/graph.sh +267 -0
- package/runtime/claude/bin/handoff-lifecycle-job.sh +146 -0
- package/runtime/claude/bin/handoff-lifecycle.sh +280 -0
- package/runtime/claude/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/claude/bin/handoff-preview.sh +169 -0
- package/runtime/claude/bin/handoff-run.sh +692 -0
- package/runtime/claude/bin/handoff-save-egregore.sh +224 -0
- package/runtime/claude/bin/index-handoff.sh +322 -0
- package/runtime/claude/bin/ingest-graph.sh +86 -0
- package/runtime/claude/bin/ingest-item.sh +10 -0
- package/runtime/claude/bin/ingest-surface.html +495 -0
- package/runtime/claude/bin/ingest.py +1011 -0
- package/runtime/claude/bin/ingest.sh +308 -0
- package/runtime/claude/bin/ingest_graph.py +856 -0
- package/runtime/claude/bin/ingest_surface.py +544 -0
- package/runtime/claude/bin/invite.sh +6 -0
- package/runtime/claude/bin/issue.sh +10 -0
- package/runtime/claude/bin/knowledge.sh +10 -0
- package/runtime/claude/bin/lib/artifact-id.sh +46 -0
- package/runtime/claude/bin/lib/config.sh +136 -0
- package/runtime/claude/bin/lib/context.sh +642 -0
- package/runtime/claude/bin/lib/dashboard-artifact.sh +242 -0
- package/runtime/claude/bin/lib/ensure-develop.sh +183 -0
- package/runtime/claude/bin/lib/git-message.sh +37 -0
- package/runtime/claude/bin/lib/git-sync.sh +494 -0
- package/runtime/claude/bin/lib/greeting.sh +548 -0
- package/runtime/claude/bin/lib/handoff-meta.sh +97 -0
- package/runtime/claude/bin/lib/hash.sh +13 -0
- package/runtime/claude/bin/lib/identity.sh +213 -0
- package/runtime/claude/bin/lib/metrics.sh +374 -0
- package/runtime/claude/bin/lib/noncode.sh +31 -0
- package/runtime/claude/bin/lib/permissions.sh +106 -0
- package/runtime/claude/bin/lib/time.sh +56 -0
- package/runtime/claude/bin/lib/worktree-links.sh +85 -0
- package/runtime/claude/bin/node-run.sh +42 -0
- package/runtime/claude/bin/notification.sh +6 -0
- package/runtime/claude/bin/notify.sh +636 -0
- package/runtime/claude/bin/observe-context.sh +14 -0
- package/runtime/claude/bin/onboarding.sh +6 -0
- package/runtime/claude/bin/person.py +634 -0
- package/runtime/claude/bin/person.sh +481 -0
- package/runtime/claude/bin/pi-render-spec.mjs +140 -0
- package/runtime/claude/bin/post-update.sh +37 -0
- package/runtime/claude/bin/prime-render-spec.mjs +147 -0
- package/runtime/claude/bin/publish-artifact.sh +324 -0
- package/runtime/claude/bin/publish-references.sh +144 -0
- package/runtime/claude/bin/quest.sh +9 -0
- package/runtime/claude/bin/question.sh +8 -0
- package/runtime/claude/bin/repo-state.sh +96 -0
- package/runtime/claude/bin/research-ingest.sh +7 -0
- package/runtime/claude/bin/restore-owned-skills.sh +67 -0
- package/runtime/claude/bin/retrieval_eval.py +385 -0
- package/runtime/claude/bin/runtime-identity.sh +70 -0
- package/runtime/claude/bin/runtime-upgrade.sh +43 -0
- package/runtime/claude/bin/scroll.sh +9 -0
- package/runtime/claude/bin/search.sh +318 -0
- package/runtime/claude/bin/session-autosave.sh +220 -0
- package/runtime/claude/bin/session-log.sh +199 -0
- package/runtime/claude/bin/session-start.sh +696 -0
- package/runtime/claude/bin/settings.sh +532 -0
- package/runtime/claude/bin/startup-check.sh +136 -0
- package/runtime/claude/bin/statusline.sh +41 -0
- package/runtime/claude/bin/telemetry.sh +666 -0
- package/runtime/claude/bin/test-changes.sh +344 -0
- package/runtime/claude/bin/thread.sh +9 -0
- package/runtime/claude/bin/todo.sh +9 -0
- package/runtime/claude/bin/view-render.sh +126 -0
- package/runtime/claude/bin/worktree-create.sh +145 -0
- package/runtime/claude/bin/worktree-remove.sh +50 -0
- package/runtime/claude/bin/worktree.sh +198 -0
- package/runtime/claude/egregore_runtime/__init__.py +228 -0
- package/runtime/claude/egregore_runtime/adapters/__init__.py +5 -0
- package/runtime/claude/egregore_runtime/adapters/invitation.py +237 -0
- package/runtime/claude/egregore_runtime/adapters/notification.py +59 -0
- package/runtime/claude/egregore_runtime/adapters/qmd.py +1701 -0
- package/runtime/claude/egregore_runtime/adapters/qmd_cli.py +210 -0
- package/runtime/claude/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
- package/runtime/claude/egregore_runtime/admin_gate.py +102 -0
- package/runtime/claude/egregore_runtime/artifacts.py +523 -0
- package/runtime/claude/egregore_runtime/contracts.py +499 -0
- package/runtime/claude/egregore_runtime/entitlements.py +85 -0
- package/runtime/claude/egregore_runtime/errors.py +10 -0
- package/runtime/claude/egregore_runtime/graph.py +356 -0
- package/runtime/claude/egregore_runtime/handoff_lookup.py +196 -0
- package/runtime/claude/egregore_runtime/harness_cli.py +1221 -0
- package/runtime/claude/egregore_runtime/identity.py +734 -0
- package/runtime/claude/egregore_runtime/identity_cli.py +91 -0
- package/runtime/claude/egregore_runtime/ingest.py +575 -0
- package/runtime/claude/egregore_runtime/ingest_cli.py +254 -0
- package/runtime/claude/egregore_runtime/ingest_index_cli.py +147 -0
- package/runtime/claude/egregore_runtime/invitation_cli.py +51 -0
- package/runtime/claude/egregore_runtime/invitations.py +116 -0
- package/runtime/claude/egregore_runtime/issue_cli.py +305 -0
- package/runtime/claude/egregore_runtime/issues.py +359 -0
- package/runtime/claude/egregore_runtime/knowledge.py +376 -0
- package/runtime/claude/egregore_runtime/knowledge_cli.py +173 -0
- package/runtime/claude/egregore_runtime/lifecycle.py +888 -0
- package/runtime/claude/egregore_runtime/lifecycle_cli.py +269 -0
- package/runtime/claude/egregore_runtime/notification_cli.py +125 -0
- package/runtime/claude/egregore_runtime/notifications.py +230 -0
- package/runtime/claude/egregore_runtime/observe.py +342 -0
- package/runtime/claude/egregore_runtime/onboarding.py +290 -0
- package/runtime/claude/egregore_runtime/onboarding_cli.py +110 -0
- package/runtime/claude/egregore_runtime/policy.py +144 -0
- package/runtime/claude/egregore_runtime/profile.py +193 -0
- package/runtime/claude/egregore_runtime/quest_cli.py +216 -0
- package/runtime/claude/egregore_runtime/question_cli.py +180 -0
- package/runtime/claude/egregore_runtime/questions.py +304 -0
- package/runtime/claude/egregore_runtime/quests.py +419 -0
- package/runtime/claude/egregore_runtime/readiness.py +379 -0
- package/runtime/claude/egregore_runtime/research_ingest.py +256 -0
- package/runtime/claude/egregore_runtime/research_ingest_cli.py +44 -0
- package/runtime/claude/egregore_runtime/runtime.py +464 -0
- package/runtime/claude/egregore_runtime/scroll_cli.py +185 -0
- package/runtime/claude/egregore_runtime/scrolls.py +726 -0
- package/runtime/claude/egregore_runtime/status.py +412 -0
- package/runtime/claude/egregore_runtime/status_cli.py +174 -0
- package/runtime/claude/egregore_runtime/sync.py +218 -0
- package/runtime/claude/egregore_runtime/teamsync.py +139 -0
- package/runtime/claude/egregore_runtime/telemetry.py +640 -0
- package/runtime/claude/egregore_runtime/thread_cli.py +173 -0
- package/runtime/claude/egregore_runtime/threads.py +517 -0
- package/runtime/claude/egregore_runtime/todo_cli.py +142 -0
- package/runtime/claude/egregore_runtime/todos.py +244 -0
- package/runtime/claude/egregore_runtime/upgrade.py +770 -0
- package/runtime/claude/egregore_runtime/upgrade_worker.py +53 -0
- package/runtime/claude/egregore_runtime/writeback.py +391 -0
- package/runtime/claude/egregore_runtime/writeback_cli.py +114 -0
- package/runtime/claude/manifest.json +284 -0
- package/runtime/codex/.codex/hooks/search-hint.js +21 -52
- package/runtime/codex/.codex/hooks.json +3 -3
- package/runtime/codex/.codex/skills/activity/SKILL.md +23 -66
- package/runtime/codex/.codex/skills/announce/SKILL.md +27 -50
- package/runtime/codex/.codex/skills/archive/SKILL.md +21 -36
- package/runtime/codex/.codex/skills/checkup/SKILL.md +77 -36
- package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/dashboard/SKILL.md +17 -47
- package/runtime/codex/.codex/skills/handoff/SKILL.md +84 -133
- package/runtime/codex/.codex/skills/harvest/SKILL.md +32 -117
- package/runtime/codex/.codex/skills/ingest/SKILL.md +47 -25
- package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +18 -36
- package/runtime/codex/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
- package/runtime/codex/.codex/skills/invite/SKILL.md +21 -35
- package/runtime/codex/.codex/skills/issue/SKILL.md +60 -41
- package/runtime/codex/.codex/skills/me/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/meeting/SKILL.md +16 -36
- package/runtime/codex/.codex/skills/meeting/references/analysis-contract.md +48 -0
- package/runtime/codex/.codex/skills/note/SKILL.md +30 -25
- package/runtime/codex/.codex/skills/onboarding/SKILL.md +36 -41
- package/runtime/codex/.codex/skills/pull/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/quest/SKILL.md +27 -66
- package/runtime/codex/.codex/skills/reflect/SKILL.md +22 -36
- package/runtime/codex/.codex/skills/save/SKILL.md +2 -2
- package/runtime/codex/.codex/skills/telemetry/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/test/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/tutorial/SKILL.md +40 -41
- package/runtime/codex/.codex/skills/view/SKILL.md +115 -111
- package/runtime/codex/.codex/skills/wrap/SKILL.md +45 -41
- package/runtime/codex/.codex/spec-manifest.json +9 -9
- package/runtime/codex/AGENTS.md +62 -58
- package/runtime/codex/bin/activity-action.sh +92 -0
- package/runtime/codex/bin/activity-data.sh +12 -233
- package/runtime/codex/bin/agent.sh +84 -100
- package/runtime/codex/bin/artifact-register.sh +164 -0
- package/runtime/codex/bin/artifact-writeback.sh +53 -0
- package/runtime/codex/bin/artifacts.sh +288 -0
- package/runtime/codex/bin/build-sealed-dataroom.mjs +722 -0
- package/runtime/codex/bin/capture-run.sh +18 -2
- package/runtime/codex/bin/codex-render-spec.mjs +41 -16
- package/runtime/codex/bin/codex-session-start.sh +14 -4
- package/runtime/codex/bin/codex-skill-render.mjs +9 -7
- package/runtime/codex/bin/corpus_map.py +806 -0
- package/runtime/codex/bin/corpus_survey.py +304 -0
- package/runtime/codex/bin/dashboard-data.sh +11 -171
- package/runtime/codex/bin/github-auth.sh +121 -0
- package/runtime/codex/bin/graph-batch.sh +7 -0
- package/runtime/codex/bin/graph-op.sh +4 -0
- package/runtime/codex/bin/graph-projection.sh +54 -0
- package/runtime/codex/bin/graph-wal.sh +13 -0
- package/runtime/codex/bin/graph.sh +44 -0
- package/runtime/codex/bin/handoff-lifecycle-job.sh +146 -0
- package/runtime/codex/bin/handoff-lifecycle.sh +280 -0
- package/runtime/codex/bin/handoff-preview.sh +169 -0
- package/runtime/codex/bin/handoff-run.sh +42 -26
- package/runtime/codex/bin/ingest-graph.sh +6 -0
- package/runtime/codex/bin/ingest-item.sh +10 -0
- package/runtime/codex/bin/ingest.sh +59 -30
- package/runtime/codex/bin/invite.sh +6 -0
- package/runtime/codex/bin/issue.sh +10 -0
- package/runtime/codex/bin/knowledge.sh +10 -0
- package/runtime/codex/bin/lib/config.sh +20 -0
- package/runtime/codex/bin/lib/context.sh +46 -25
- package/runtime/codex/bin/lib/git-sync.sh +70 -6
- package/runtime/codex/bin/lib/greeting.sh +34 -5
- package/runtime/codex/bin/lib/handoff-meta.sh +97 -0
- package/runtime/codex/bin/lib/worktree-links.sh +6 -0
- package/runtime/codex/bin/node-run.sh +42 -0
- package/runtime/codex/bin/notification.sh +6 -0
- package/runtime/codex/bin/observe-context.sh +14 -0
- package/runtime/codex/bin/onboarding.sh +6 -0
- package/runtime/codex/bin/person.py +4 -0
- package/runtime/codex/bin/person.sh +43 -10
- package/runtime/codex/bin/pi-render-spec.mjs +140 -0
- package/runtime/codex/bin/post-update.sh +37 -0
- package/runtime/codex/bin/prime-render-spec.mjs +147 -0
- package/runtime/codex/bin/publish-artifact.sh +55 -2
- package/runtime/codex/bin/quest.sh +9 -0
- package/runtime/codex/bin/question.sh +8 -0
- package/runtime/codex/bin/research-ingest.sh +7 -0
- package/runtime/codex/bin/restore-owned-skills.sh +67 -0
- package/runtime/codex/bin/retrieval_eval.py +385 -0
- package/runtime/codex/bin/runtime-identity.sh +70 -0
- package/runtime/codex/bin/runtime-upgrade.sh +43 -0
- package/runtime/codex/bin/scroll.sh +9 -0
- package/runtime/codex/bin/search.sh +231 -195
- package/runtime/codex/bin/settings.sh +174 -5
- package/runtime/codex/bin/startup-check.sh +136 -0
- package/runtime/codex/bin/telemetry.sh +522 -247
- package/runtime/codex/bin/test-changes.sh +344 -0
- package/runtime/codex/bin/thread.sh +9 -0
- package/runtime/codex/bin/todo.sh +9 -0
- package/runtime/codex/bin/view-render.sh +126 -0
- package/runtime/codex/bin/worktree-create.sh +19 -5
- package/runtime/codex/egregore_runtime/__init__.py +228 -0
- package/runtime/codex/egregore_runtime/adapters/__init__.py +5 -0
- package/runtime/codex/egregore_runtime/adapters/invitation.py +237 -0
- package/runtime/codex/egregore_runtime/adapters/notification.py +59 -0
- package/runtime/codex/egregore_runtime/adapters/qmd.py +1701 -0
- package/runtime/codex/egregore_runtime/adapters/qmd_cli.py +210 -0
- package/runtime/codex/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
- package/runtime/codex/egregore_runtime/admin_gate.py +102 -0
- package/runtime/codex/egregore_runtime/artifacts.py +523 -0
- package/runtime/codex/egregore_runtime/contracts.py +499 -0
- package/runtime/codex/egregore_runtime/entitlements.py +85 -0
- package/runtime/codex/egregore_runtime/errors.py +10 -0
- package/runtime/codex/egregore_runtime/graph.py +356 -0
- package/runtime/codex/egregore_runtime/handoff_lookup.py +196 -0
- package/runtime/codex/egregore_runtime/harness_cli.py +1221 -0
- package/runtime/codex/egregore_runtime/identity.py +734 -0
- package/runtime/codex/egregore_runtime/identity_cli.py +91 -0
- package/runtime/codex/egregore_runtime/ingest.py +575 -0
- package/runtime/codex/egregore_runtime/ingest_cli.py +254 -0
- package/runtime/codex/egregore_runtime/ingest_index_cli.py +147 -0
- package/runtime/codex/egregore_runtime/invitation_cli.py +51 -0
- package/runtime/codex/egregore_runtime/invitations.py +116 -0
- package/runtime/codex/egregore_runtime/issue_cli.py +305 -0
- package/runtime/codex/egregore_runtime/issues.py +359 -0
- package/runtime/codex/egregore_runtime/knowledge.py +376 -0
- package/runtime/codex/egregore_runtime/knowledge_cli.py +173 -0
- package/runtime/codex/egregore_runtime/lifecycle.py +888 -0
- package/runtime/codex/egregore_runtime/lifecycle_cli.py +269 -0
- package/runtime/codex/egregore_runtime/notification_cli.py +125 -0
- package/runtime/codex/egregore_runtime/notifications.py +230 -0
- package/runtime/codex/egregore_runtime/observe.py +342 -0
- package/runtime/codex/egregore_runtime/onboarding.py +290 -0
- package/runtime/codex/egregore_runtime/onboarding_cli.py +110 -0
- package/runtime/codex/egregore_runtime/policy.py +144 -0
- package/runtime/codex/egregore_runtime/profile.py +193 -0
- package/runtime/codex/egregore_runtime/quest_cli.py +216 -0
- package/runtime/codex/egregore_runtime/question_cli.py +180 -0
- package/runtime/codex/egregore_runtime/questions.py +304 -0
- package/runtime/codex/egregore_runtime/quests.py +419 -0
- package/runtime/codex/egregore_runtime/readiness.py +379 -0
- package/runtime/codex/egregore_runtime/research_ingest.py +256 -0
- package/runtime/codex/egregore_runtime/research_ingest_cli.py +44 -0
- package/runtime/codex/egregore_runtime/runtime.py +464 -0
- package/runtime/codex/egregore_runtime/scroll_cli.py +185 -0
- package/runtime/codex/egregore_runtime/scrolls.py +726 -0
- package/runtime/codex/egregore_runtime/status.py +412 -0
- package/runtime/codex/egregore_runtime/status_cli.py +174 -0
- package/runtime/codex/egregore_runtime/sync.py +218 -0
- package/runtime/codex/egregore_runtime/teamsync.py +139 -0
- package/runtime/codex/egregore_runtime/telemetry.py +640 -0
- package/runtime/codex/egregore_runtime/thread_cli.py +173 -0
- package/runtime/codex/egregore_runtime/threads.py +517 -0
- package/runtime/codex/egregore_runtime/todo_cli.py +142 -0
- package/runtime/codex/egregore_runtime/todos.py +244 -0
- package/runtime/codex/egregore_runtime/upgrade.py +770 -0
- package/runtime/codex/egregore_runtime/upgrade_worker.py +53 -0
- package/runtime/codex/egregore_runtime/writeback.py +391 -0
- package/runtime/codex/egregore_runtime/writeback_cli.py +114 -0
- package/runtime/codex/manifest.json +98 -1
- package/runtime/pi/.codex/hooks/search-hint.js +21 -52
- package/runtime/pi/.codex/hooks.json +3 -3
- package/runtime/pi/.codex/skills/activity/SKILL.md +23 -66
- package/runtime/pi/.codex/skills/announce/SKILL.md +27 -50
- package/runtime/pi/.codex/skills/archive/SKILL.md +21 -36
- package/runtime/pi/.codex/skills/checkup/SKILL.md +77 -36
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/dashboard/SKILL.md +17 -47
- package/runtime/pi/.codex/skills/handoff/SKILL.md +84 -133
- package/runtime/pi/.codex/skills/harvest/SKILL.md +32 -117
- package/runtime/pi/.codex/skills/ingest/SKILL.md +47 -25
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +18 -36
- package/runtime/pi/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
- package/runtime/pi/.codex/skills/invite/SKILL.md +21 -35
- package/runtime/pi/.codex/skills/issue/SKILL.md +60 -41
- package/runtime/pi/.codex/skills/me/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/meeting/SKILL.md +16 -36
- package/runtime/pi/.codex/skills/meeting/references/analysis-contract.md +48 -0
- package/runtime/pi/.codex/skills/note/SKILL.md +30 -25
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +36 -41
- package/runtime/pi/.codex/skills/pull/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/quest/SKILL.md +27 -66
- package/runtime/pi/.codex/skills/reflect/SKILL.md +22 -36
- package/runtime/pi/.codex/skills/save/SKILL.md +2 -2
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/test/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +40 -41
- package/runtime/pi/.codex/skills/view/SKILL.md +115 -111
- package/runtime/pi/.codex/skills/wrap/SKILL.md +45 -41
- package/runtime/pi/.codex/spec-manifest.json +9 -9
- package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -48
- package/runtime/pi/.pi/extensions/egregore.ts +7 -0
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +62 -58
- package/runtime/pi/bin/activity-action.sh +92 -0
- package/runtime/pi/bin/activity-data.sh +12 -233
- package/runtime/pi/bin/agent.sh +84 -100
- package/runtime/pi/bin/artifact-register.sh +164 -0
- package/runtime/pi/bin/artifact-writeback.sh +53 -0
- package/runtime/pi/bin/artifacts.sh +288 -0
- package/runtime/pi/bin/build-sealed-dataroom.mjs +722 -0
- package/runtime/pi/bin/capture-run.sh +18 -2
- package/runtime/pi/bin/codex-render-spec.mjs +41 -16
- package/runtime/pi/bin/codex-session-start.sh +14 -4
- package/runtime/pi/bin/codex-skill-render.mjs +9 -7
- package/runtime/pi/bin/corpus_map.py +806 -0
- package/runtime/pi/bin/corpus_survey.py +304 -0
- package/runtime/pi/bin/dashboard-data.sh +11 -171
- package/runtime/pi/bin/github-auth.sh +121 -0
- package/runtime/pi/bin/graph-batch.sh +7 -0
- package/runtime/pi/bin/graph-op.sh +4 -0
- package/runtime/pi/bin/graph-projection.sh +54 -0
- package/runtime/pi/bin/graph-wal.sh +13 -0
- package/runtime/pi/bin/graph.sh +44 -0
- package/runtime/pi/bin/handoff-lifecycle-job.sh +146 -0
- package/runtime/pi/bin/handoff-lifecycle.sh +280 -0
- package/runtime/pi/bin/handoff-preview.sh +169 -0
- package/runtime/pi/bin/handoff-run.sh +42 -26
- package/runtime/pi/bin/ingest-graph.sh +6 -0
- package/runtime/pi/bin/ingest-item.sh +10 -0
- package/runtime/pi/bin/ingest.sh +59 -30
- package/runtime/pi/bin/invite.sh +6 -0
- package/runtime/pi/bin/issue.sh +10 -0
- package/runtime/pi/bin/knowledge.sh +10 -0
- package/runtime/pi/bin/lib/config.sh +20 -0
- package/runtime/pi/bin/lib/context.sh +46 -25
- package/runtime/pi/bin/lib/git-sync.sh +70 -6
- package/runtime/pi/bin/lib/greeting.sh +34 -5
- package/runtime/pi/bin/lib/handoff-meta.sh +97 -0
- package/runtime/pi/bin/lib/worktree-links.sh +6 -0
- package/runtime/pi/bin/node-run.sh +42 -0
- package/runtime/pi/bin/notification.sh +6 -0
- package/runtime/pi/bin/observe-context.sh +14 -0
- package/runtime/pi/bin/onboarding.sh +6 -0
- package/runtime/pi/bin/person.py +4 -0
- package/runtime/pi/bin/person.sh +43 -10
- package/runtime/pi/bin/pi-observe-context.mjs +93 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +1 -1
- package/runtime/pi/bin/pi-render-spec.mjs +1 -1
- package/runtime/pi/bin/post-update.sh +37 -0
- package/runtime/pi/bin/prime-render-spec.mjs +147 -0
- package/runtime/pi/bin/publish-artifact.sh +55 -2
- package/runtime/pi/bin/quest.sh +9 -0
- package/runtime/pi/bin/question.sh +8 -0
- package/runtime/pi/bin/research-ingest.sh +7 -0
- package/runtime/pi/bin/restore-owned-skills.sh +67 -0
- package/runtime/pi/bin/retrieval_eval.py +385 -0
- package/runtime/pi/bin/runtime-identity.sh +70 -0
- package/runtime/pi/bin/runtime-upgrade.sh +43 -0
- package/runtime/pi/bin/scroll.sh +9 -0
- package/runtime/pi/bin/search.sh +231 -195
- package/runtime/pi/bin/settings.sh +174 -5
- package/runtime/pi/bin/startup-check.sh +136 -0
- package/runtime/pi/bin/telemetry.sh +522 -247
- package/runtime/pi/bin/test-changes.sh +344 -0
- package/runtime/pi/bin/thread.sh +9 -0
- package/runtime/pi/bin/todo.sh +9 -0
- package/runtime/pi/bin/view-render.sh +126 -0
- package/runtime/pi/bin/worktree-create.sh +19 -5
- package/runtime/pi/egregore_runtime/__init__.py +228 -0
- package/runtime/pi/egregore_runtime/adapters/__init__.py +5 -0
- package/runtime/pi/egregore_runtime/adapters/invitation.py +237 -0
- package/runtime/pi/egregore_runtime/adapters/notification.py +59 -0
- package/runtime/pi/egregore_runtime/adapters/qmd.py +1701 -0
- package/runtime/pi/egregore_runtime/adapters/qmd_cli.py +210 -0
- package/runtime/pi/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
- package/runtime/pi/egregore_runtime/admin_gate.py +102 -0
- package/runtime/pi/egregore_runtime/artifacts.py +523 -0
- package/runtime/pi/egregore_runtime/contracts.py +499 -0
- package/runtime/pi/egregore_runtime/entitlements.py +85 -0
- package/runtime/pi/egregore_runtime/errors.py +10 -0
- package/runtime/pi/egregore_runtime/graph.py +356 -0
- package/runtime/pi/egregore_runtime/handoff_lookup.py +196 -0
- package/runtime/pi/egregore_runtime/harness_cli.py +1221 -0
- package/runtime/pi/egregore_runtime/identity.py +734 -0
- package/runtime/pi/egregore_runtime/identity_cli.py +91 -0
- package/runtime/pi/egregore_runtime/ingest.py +575 -0
- package/runtime/pi/egregore_runtime/ingest_cli.py +254 -0
- package/runtime/pi/egregore_runtime/ingest_index_cli.py +147 -0
- package/runtime/pi/egregore_runtime/invitation_cli.py +51 -0
- package/runtime/pi/egregore_runtime/invitations.py +116 -0
- package/runtime/pi/egregore_runtime/issue_cli.py +305 -0
- package/runtime/pi/egregore_runtime/issues.py +359 -0
- package/runtime/pi/egregore_runtime/knowledge.py +376 -0
- package/runtime/pi/egregore_runtime/knowledge_cli.py +173 -0
- package/runtime/pi/egregore_runtime/lifecycle.py +888 -0
- package/runtime/pi/egregore_runtime/lifecycle_cli.py +269 -0
- package/runtime/pi/egregore_runtime/notification_cli.py +125 -0
- package/runtime/pi/egregore_runtime/notifications.py +230 -0
- package/runtime/pi/egregore_runtime/observe.py +342 -0
- package/runtime/pi/egregore_runtime/onboarding.py +290 -0
- package/runtime/pi/egregore_runtime/onboarding_cli.py +110 -0
- package/runtime/pi/egregore_runtime/policy.py +144 -0
- package/runtime/pi/egregore_runtime/profile.py +193 -0
- package/runtime/pi/egregore_runtime/quest_cli.py +216 -0
- package/runtime/pi/egregore_runtime/question_cli.py +180 -0
- package/runtime/pi/egregore_runtime/questions.py +304 -0
- package/runtime/pi/egregore_runtime/quests.py +419 -0
- package/runtime/pi/egregore_runtime/readiness.py +379 -0
- package/runtime/pi/egregore_runtime/research_ingest.py +256 -0
- package/runtime/pi/egregore_runtime/research_ingest_cli.py +44 -0
- package/runtime/pi/egregore_runtime/runtime.py +464 -0
- package/runtime/pi/egregore_runtime/scroll_cli.py +185 -0
- package/runtime/pi/egregore_runtime/scrolls.py +726 -0
- package/runtime/pi/egregore_runtime/status.py +412 -0
- package/runtime/pi/egregore_runtime/status_cli.py +174 -0
- package/runtime/pi/egregore_runtime/sync.py +218 -0
- package/runtime/pi/egregore_runtime/teamsync.py +139 -0
- package/runtime/pi/egregore_runtime/telemetry.py +640 -0
- package/runtime/pi/egregore_runtime/thread_cli.py +173 -0
- package/runtime/pi/egregore_runtime/threads.py +517 -0
- package/runtime/pi/egregore_runtime/todo_cli.py +142 -0
- package/runtime/pi/egregore_runtime/todos.py +244 -0
- package/runtime/pi/egregore_runtime/upgrade.py +770 -0
- package/runtime/pi/egregore_runtime/upgrade_worker.py +53 -0
- package/runtime/pi/egregore_runtime/writeback.py +391 -0
- package/runtime/pi/egregore_runtime/writeback_cli.py +114 -0
- package/runtime/pi/manifest.json +98 -1
- package/runtime/prime/.codex/hooks/search-hint.js +21 -52
- package/runtime/prime/.codex/hooks.json +3 -3
- package/runtime/prime/.codex/skills/activity/SKILL.md +23 -66
- package/runtime/prime/.codex/skills/announce/SKILL.md +27 -50
- package/runtime/prime/.codex/skills/archive/SKILL.md +21 -36
- package/runtime/prime/.codex/skills/checkup/SKILL.md +77 -36
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/dashboard/SKILL.md +17 -47
- package/runtime/prime/.codex/skills/handoff/SKILL.md +84 -133
- package/runtime/prime/.codex/skills/harvest/SKILL.md +32 -117
- package/runtime/prime/.codex/skills/ingest/SKILL.md +47 -25
- package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +18 -36
- package/runtime/prime/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
- package/runtime/prime/.codex/skills/invite/SKILL.md +21 -35
- package/runtime/prime/.codex/skills/issue/SKILL.md +60 -41
- package/runtime/prime/.codex/skills/me/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/meeting/SKILL.md +16 -36
- package/runtime/prime/.codex/skills/meeting/references/analysis-contract.md +48 -0
- package/runtime/prime/.codex/skills/note/SKILL.md +30 -25
- package/runtime/prime/.codex/skills/onboarding/SKILL.md +36 -41
- package/runtime/prime/.codex/skills/pull/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/quest/SKILL.md +27 -66
- package/runtime/prime/.codex/skills/reflect/SKILL.md +22 -36
- package/runtime/prime/.codex/skills/save/SKILL.md +2 -2
- package/runtime/prime/.codex/skills/telemetry/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/test/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/tutorial/SKILL.md +40 -41
- package/runtime/prime/.codex/skills/view/SKILL.md +115 -111
- package/runtime/prime/.codex/skills/wrap/SKILL.md +45 -41
- package/runtime/prime/.codex/spec-manifest.json +9 -9
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +57 -48
- package/runtime/prime/.prime/agent/extensions/egregore.ts +7 -0
- package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
- package/runtime/prime/AGENTS.md +62 -58
- package/runtime/prime/bin/activity-action.sh +92 -0
- package/runtime/prime/bin/activity-data.sh +12 -233
- package/runtime/prime/bin/agent.sh +84 -100
- package/runtime/prime/bin/artifact-register.sh +164 -0
- package/runtime/prime/bin/artifact-writeback.sh +53 -0
- package/runtime/prime/bin/artifacts.sh +288 -0
- package/runtime/prime/bin/build-sealed-dataroom.mjs +722 -0
- package/runtime/prime/bin/capture-run.sh +18 -2
- package/runtime/prime/bin/codex-render-spec.mjs +41 -16
- package/runtime/prime/bin/codex-session-start.sh +14 -4
- package/runtime/prime/bin/codex-skill-render.mjs +9 -7
- package/runtime/prime/bin/corpus_map.py +806 -0
- package/runtime/prime/bin/corpus_survey.py +304 -0
- package/runtime/prime/bin/dashboard-data.sh +11 -171
- package/runtime/prime/bin/github-auth.sh +121 -0
- package/runtime/prime/bin/graph-batch.sh +7 -0
- package/runtime/prime/bin/graph-op.sh +4 -0
- package/runtime/prime/bin/graph-projection.sh +54 -0
- package/runtime/prime/bin/graph-wal.sh +13 -0
- package/runtime/prime/bin/graph.sh +44 -0
- package/runtime/prime/bin/handoff-lifecycle-job.sh +146 -0
- package/runtime/prime/bin/handoff-lifecycle.sh +280 -0
- package/runtime/prime/bin/handoff-preview.sh +169 -0
- package/runtime/prime/bin/handoff-run.sh +42 -26
- package/runtime/prime/bin/ingest-graph.sh +6 -0
- package/runtime/prime/bin/ingest-item.sh +10 -0
- package/runtime/prime/bin/ingest.sh +59 -30
- package/runtime/prime/bin/invite.sh +6 -0
- package/runtime/prime/bin/issue.sh +10 -0
- package/runtime/prime/bin/knowledge.sh +10 -0
- package/runtime/prime/bin/lib/config.sh +20 -0
- package/runtime/prime/bin/lib/context.sh +46 -25
- package/runtime/prime/bin/lib/git-sync.sh +70 -6
- package/runtime/prime/bin/lib/greeting.sh +34 -5
- package/runtime/prime/bin/lib/handoff-meta.sh +97 -0
- package/runtime/prime/bin/lib/worktree-links.sh +6 -0
- package/runtime/prime/bin/node-run.sh +42 -0
- package/runtime/prime/bin/notification.sh +6 -0
- package/runtime/prime/bin/observe-context.sh +14 -0
- package/runtime/prime/bin/onboarding.sh +6 -0
- package/runtime/prime/bin/person.py +4 -0
- package/runtime/prime/bin/person.sh +43 -10
- package/runtime/prime/bin/pi-observe-context.mjs +93 -0
- package/runtime/prime/bin/pi-product-workflows.mjs +1 -1
- package/runtime/prime/bin/pi-render-spec.mjs +140 -0
- package/runtime/prime/bin/post-update.sh +37 -0
- package/runtime/prime/bin/prime-render-spec.mjs +1 -1
- package/runtime/prime/bin/publish-artifact.sh +55 -2
- package/runtime/prime/bin/quest.sh +9 -0
- package/runtime/prime/bin/question.sh +8 -0
- package/runtime/prime/bin/research-ingest.sh +7 -0
- package/runtime/prime/bin/restore-owned-skills.sh +67 -0
- package/runtime/prime/bin/retrieval_eval.py +385 -0
- package/runtime/prime/bin/runtime-identity.sh +70 -0
- package/runtime/prime/bin/runtime-upgrade.sh +43 -0
- package/runtime/prime/bin/scroll.sh +9 -0
- package/runtime/prime/bin/search.sh +231 -195
- package/runtime/prime/bin/settings.sh +174 -5
- package/runtime/prime/bin/startup-check.sh +136 -0
- package/runtime/prime/bin/telemetry.sh +522 -247
- package/runtime/prime/bin/test-changes.sh +344 -0
- package/runtime/prime/bin/thread.sh +9 -0
- package/runtime/prime/bin/todo.sh +9 -0
- package/runtime/prime/bin/view-render.sh +126 -0
- package/runtime/prime/bin/worktree-create.sh +19 -5
- package/runtime/prime/egregore_runtime/__init__.py +228 -0
- package/runtime/prime/egregore_runtime/adapters/__init__.py +5 -0
- package/runtime/prime/egregore_runtime/adapters/invitation.py +237 -0
- package/runtime/prime/egregore_runtime/adapters/notification.py +59 -0
- package/runtime/prime/egregore_runtime/adapters/qmd.py +1701 -0
- package/runtime/prime/egregore_runtime/adapters/qmd_cli.py +210 -0
- package/runtime/prime/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
- package/runtime/prime/egregore_runtime/admin_gate.py +102 -0
- package/runtime/prime/egregore_runtime/artifacts.py +523 -0
- package/runtime/prime/egregore_runtime/contracts.py +499 -0
- package/runtime/prime/egregore_runtime/entitlements.py +85 -0
- package/runtime/prime/egregore_runtime/errors.py +10 -0
- package/runtime/prime/egregore_runtime/graph.py +356 -0
- package/runtime/prime/egregore_runtime/handoff_lookup.py +196 -0
- package/runtime/prime/egregore_runtime/harness_cli.py +1221 -0
- package/runtime/prime/egregore_runtime/identity.py +734 -0
- package/runtime/prime/egregore_runtime/identity_cli.py +91 -0
- package/runtime/prime/egregore_runtime/ingest.py +575 -0
- package/runtime/prime/egregore_runtime/ingest_cli.py +254 -0
- package/runtime/prime/egregore_runtime/ingest_index_cli.py +147 -0
- package/runtime/prime/egregore_runtime/invitation_cli.py +51 -0
- package/runtime/prime/egregore_runtime/invitations.py +116 -0
- package/runtime/prime/egregore_runtime/issue_cli.py +305 -0
- package/runtime/prime/egregore_runtime/issues.py +359 -0
- package/runtime/prime/egregore_runtime/knowledge.py +376 -0
- package/runtime/prime/egregore_runtime/knowledge_cli.py +173 -0
- package/runtime/prime/egregore_runtime/lifecycle.py +888 -0
- package/runtime/prime/egregore_runtime/lifecycle_cli.py +269 -0
- package/runtime/prime/egregore_runtime/notification_cli.py +125 -0
- package/runtime/prime/egregore_runtime/notifications.py +230 -0
- package/runtime/prime/egregore_runtime/observe.py +342 -0
- package/runtime/prime/egregore_runtime/onboarding.py +290 -0
- package/runtime/prime/egregore_runtime/onboarding_cli.py +110 -0
- package/runtime/prime/egregore_runtime/policy.py +144 -0
- package/runtime/prime/egregore_runtime/profile.py +193 -0
- package/runtime/prime/egregore_runtime/quest_cli.py +216 -0
- package/runtime/prime/egregore_runtime/question_cli.py +180 -0
- package/runtime/prime/egregore_runtime/questions.py +304 -0
- package/runtime/prime/egregore_runtime/quests.py +419 -0
- package/runtime/prime/egregore_runtime/readiness.py +379 -0
- package/runtime/prime/egregore_runtime/research_ingest.py +256 -0
- package/runtime/prime/egregore_runtime/research_ingest_cli.py +44 -0
- package/runtime/prime/egregore_runtime/runtime.py +464 -0
- package/runtime/prime/egregore_runtime/scroll_cli.py +185 -0
- package/runtime/prime/egregore_runtime/scrolls.py +726 -0
- package/runtime/prime/egregore_runtime/status.py +412 -0
- package/runtime/prime/egregore_runtime/status_cli.py +174 -0
- package/runtime/prime/egregore_runtime/sync.py +218 -0
- package/runtime/prime/egregore_runtime/teamsync.py +139 -0
- package/runtime/prime/egregore_runtime/telemetry.py +640 -0
- package/runtime/prime/egregore_runtime/thread_cli.py +173 -0
- package/runtime/prime/egregore_runtime/threads.py +517 -0
- package/runtime/prime/egregore_runtime/todo_cli.py +142 -0
- package/runtime/prime/egregore_runtime/todos.py +244 -0
- package/runtime/prime/egregore_runtime/upgrade.py +770 -0
- package/runtime/prime/egregore_runtime/upgrade_worker.py +53 -0
- package/runtime/prime/egregore_runtime/writeback.py +391 -0
- package/runtime/prime/egregore_runtime/writeback_cli.py +114 -0
- package/runtime/prime/manifest.json +98 -1
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
"""Transactional Runtime upgrade engine — staged, durable, per instance.
|
|
2
|
+
|
|
3
|
+
The engine prepares a candidate Runtime for exactly one registered Egregore
|
|
4
|
+
instance while the currently active Runtime keeps serving. Preparation is a
|
|
5
|
+
sequence of durable stages; every transition is an atomic write to an
|
|
6
|
+
instance-scoped state file, so an interrupted worker resumes completed work
|
|
7
|
+
instead of restarting it. Activation is a separate, explicit, atomic step
|
|
8
|
+
that only succeeds when every gate passes — BM25 alone never activates.
|
|
9
|
+
|
|
10
|
+
All state lives under the instance's own runtime root, keyed by the same
|
|
11
|
+
stable instance hash the QMD adapter uses. Nothing here reads or writes
|
|
12
|
+
another instance's files, processes, indexes, or configuration.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import time
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Mapping
|
|
25
|
+
|
|
26
|
+
from .adapters.qmd import QMD_VERSION, RERANKING_ENABLED, QmdLocalRetriever
|
|
27
|
+
|
|
28
|
+
SCHEMA_VERSION = "egregore-runtime-upgrade/v1"
|
|
29
|
+
|
|
30
|
+
# Stage keys in preparation order. Labels are the user-facing truth shown by
|
|
31
|
+
# Settings; keep them in plain team language.
|
|
32
|
+
STAGES: tuple[tuple[str, str], ...] = (
|
|
33
|
+
("sync", "Team memory synchronized"),
|
|
34
|
+
("candidate", "Runtime candidate recorded"),
|
|
35
|
+
("install", "Runtime candidate installed"),
|
|
36
|
+
("node", "Node runtime ready"),
|
|
37
|
+
("qmd", "QMD installed"),
|
|
38
|
+
("bm25", "Keyword index built"),
|
|
39
|
+
("embed", "Meaning index generated"),
|
|
40
|
+
("delta", "Late team changes embedded"),
|
|
41
|
+
("verify", "Retrieval, identity, and boundary verified"),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# The harness bundle applied to the instance at activation. All four bundles
|
|
45
|
+
# must be present in the candidate package; the Claude bundle carries the
|
|
46
|
+
# shared core (bin/, egregore_runtime/, .claude/) this instance runs on.
|
|
47
|
+
BUNDLE_TREES = ("claude", "codex", "pi", "prime")
|
|
48
|
+
# Activation applies every harness adapter tree, not only Claude's: the
|
|
49
|
+
# Codex black-box found a live instance whose .codex adapters were still the
|
|
50
|
+
# pre-cutover generation, sending Codex sessions through a broken global QMD
|
|
51
|
+
# and graph-era instructions while Claude ran the new Runtime. Shared paths
|
|
52
|
+
# (bin/, egregore_runtime/) are byte-identical across trees; the first tree
|
|
53
|
+
# wins and duplicates are skipped.
|
|
54
|
+
APPLY_TREES = ("claude", "codex", "pi", "prime")
|
|
55
|
+
APPLY_TREE = APPLY_TREES[0]
|
|
56
|
+
|
|
57
|
+
STATUS_PENDING = "pending"
|
|
58
|
+
STATUS_PREPARING = "preparing"
|
|
59
|
+
STATUS_FAILED = "failed"
|
|
60
|
+
STATUS_READY = "ready-to-activate"
|
|
61
|
+
STATUS_ACTIVE = "active"
|
|
62
|
+
STATUS_CANCELLED = "cancelled"
|
|
63
|
+
STATUS_ROLLED_BACK = "rolled-back"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class UpgradeError(RuntimeError):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _now() -> int:
|
|
71
|
+
return int(time.time())
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class UpgradeStore:
|
|
76
|
+
"""Durable, instance-scoped upgrade state with atomic writes."""
|
|
77
|
+
|
|
78
|
+
root: Path
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def state_path(self) -> Path:
|
|
82
|
+
return self.root / "state.json"
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def activation_path(self) -> Path:
|
|
86
|
+
return self.root / "active.json"
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def cancel_path(self) -> Path:
|
|
90
|
+
return self.root / "cancel"
|
|
91
|
+
|
|
92
|
+
def load(self) -> dict[str, Any] | None:
|
|
93
|
+
try:
|
|
94
|
+
value = json.loads(self.state_path.read_text(encoding="utf-8"))
|
|
95
|
+
except (OSError, ValueError):
|
|
96
|
+
return None
|
|
97
|
+
return value if isinstance(value, dict) else None
|
|
98
|
+
|
|
99
|
+
def write(self, state: Mapping[str, Any]) -> None:
|
|
100
|
+
self.root.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
101
|
+
os.chmod(self.root, 0o700)
|
|
102
|
+
payload = dict(state)
|
|
103
|
+
payload["updated_at"] = _now()
|
|
104
|
+
temporary = self.state_path.with_suffix(f".tmp.{os.getpid()}")
|
|
105
|
+
temporary.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
106
|
+
os.chmod(temporary, 0o600)
|
|
107
|
+
temporary.replace(self.state_path)
|
|
108
|
+
|
|
109
|
+
def load_activation(self) -> dict[str, Any] | None:
|
|
110
|
+
try:
|
|
111
|
+
value = json.loads(self.activation_path.read_text(encoding="utf-8"))
|
|
112
|
+
except (OSError, ValueError):
|
|
113
|
+
return None
|
|
114
|
+
return value if isinstance(value, dict) else None
|
|
115
|
+
|
|
116
|
+
def write_activation(self, record: Mapping[str, Any]) -> None:
|
|
117
|
+
self.root.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
118
|
+
temporary = self.activation_path.with_suffix(f".tmp.{os.getpid()}")
|
|
119
|
+
temporary.write_text(json.dumps(dict(record), indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
120
|
+
os.chmod(temporary, 0o600)
|
|
121
|
+
temporary.replace(self.activation_path)
|
|
122
|
+
|
|
123
|
+
def cancel_requested(self) -> bool:
|
|
124
|
+
return self.cancel_path.exists()
|
|
125
|
+
|
|
126
|
+
def request_cancel(self) -> None:
|
|
127
|
+
self.root.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
128
|
+
self.cancel_path.write_text(str(_now()) + "\n", encoding="utf-8")
|
|
129
|
+
|
|
130
|
+
def clear_cancel(self) -> None:
|
|
131
|
+
self.cancel_path.unlink(missing_ok=True)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def installation_root(repository_root: Path) -> Path:
|
|
135
|
+
"""The main installation directory a checkout belongs to.
|
|
136
|
+
|
|
137
|
+
A linked git worktree (``.git`` is a file) belongs to its main checkout;
|
|
138
|
+
upgrade state is per installation, so every worktree of one install
|
|
139
|
+
shares it. A standalone clone is its own installation.
|
|
140
|
+
"""
|
|
141
|
+
root = repository_root.resolve()
|
|
142
|
+
git_marker = root / ".git"
|
|
143
|
+
if git_marker.is_file():
|
|
144
|
+
try:
|
|
145
|
+
content = git_marker.read_text(encoding="utf-8")
|
|
146
|
+
for line in content.splitlines():
|
|
147
|
+
if line.startswith("gitdir:"):
|
|
148
|
+
git_dir = Path(line.split(":", 1)[1].strip())
|
|
149
|
+
if not git_dir.is_absolute():
|
|
150
|
+
git_dir = (root / git_dir).resolve()
|
|
151
|
+
# <main>/.git/worktrees/<name> → <main>
|
|
152
|
+
if git_dir.parent.name == "worktrees" and git_dir.parent.parent.name == ".git":
|
|
153
|
+
return git_dir.parent.parent.parent.resolve()
|
|
154
|
+
except OSError:
|
|
155
|
+
pass
|
|
156
|
+
return root
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def instance_key(repository_root: Path) -> str:
|
|
160
|
+
"""Durable upgrade-state key: stable org identity × installation identity.
|
|
161
|
+
|
|
162
|
+
Identity is never inferred from a path alone: the stable organization ID
|
|
163
|
+
from egregore.json is required and leads the key, so two organizations
|
|
164
|
+
that share, symlink, or otherwise resolve to the same memory repository
|
|
165
|
+
can never share activation, worker, lock, or rollback state. The
|
|
166
|
+
installation path is composed in explicitly so two installs of the same
|
|
167
|
+
organization stay independent, while worktrees of one install share it.
|
|
168
|
+
A checkout without a stable org_id fails closed.
|
|
169
|
+
"""
|
|
170
|
+
root = repository_root.resolve()
|
|
171
|
+
try:
|
|
172
|
+
config = json.loads((root / "egregore.json").read_text(encoding="utf-8"))
|
|
173
|
+
except (OSError, ValueError) as exc:
|
|
174
|
+
raise UpgradeError(f"cannot read egregore.json for instance identity: {exc}") from exc
|
|
175
|
+
org_id = str(config.get("org_id") or "").strip()
|
|
176
|
+
if not org_id:
|
|
177
|
+
raise UpgradeError(
|
|
178
|
+
"this instance has no stable org_id; upgrade state fails closed "
|
|
179
|
+
"without a stable organization identity"
|
|
180
|
+
)
|
|
181
|
+
install = installation_root(root)
|
|
182
|
+
import hashlib
|
|
183
|
+
|
|
184
|
+
return hashlib.sha256(f"{org_id}|{install}".encode("utf-8")).hexdigest()[:16]
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def upgrade_root(repository_root: Path) -> Path:
|
|
188
|
+
"""Instance-scoped upgrade state dir, keyed by composed stable identity."""
|
|
189
|
+
override = os.environ.get("EGREGORE_UPGRADE_ROOT")
|
|
190
|
+
if override:
|
|
191
|
+
return Path(override).expanduser() / instance_key(repository_root)
|
|
192
|
+
return Path.home() / ".egregore" / "runtime" / "upgrade" / instance_key(repository_root)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def build_cli_context(repository_root: Path):
|
|
196
|
+
"""Store/engine/retriever wiring for the CLI — keeps retrieval adapter
|
|
197
|
+
imports out of harness_cli, which must stay behind Runtime contracts."""
|
|
198
|
+
from .runtime import local_runtime
|
|
199
|
+
|
|
200
|
+
retriever = QmdLocalRetriever(repository_root=repository_root)
|
|
201
|
+
store = UpgradeStore(root=upgrade_root(repository_root))
|
|
202
|
+
engine = UpgradeEngine(
|
|
203
|
+
repository_root=repository_root,
|
|
204
|
+
store=store,
|
|
205
|
+
runtime=local_runtime(repository_root, push_remote=False),
|
|
206
|
+
retriever=retriever,
|
|
207
|
+
)
|
|
208
|
+
return store, engine, retriever
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _pid_alive(pid: int) -> bool:
|
|
212
|
+
try:
|
|
213
|
+
os.kill(pid, 0)
|
|
214
|
+
return True
|
|
215
|
+
except (OSError, ValueError, TypeError):
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def new_state(
|
|
220
|
+
*,
|
|
221
|
+
instance_path: Path,
|
|
222
|
+
org_id: str,
|
|
223
|
+
instance_hash: str,
|
|
224
|
+
candidate_version: str,
|
|
225
|
+
active_version: str,
|
|
226
|
+
candidate_source: str = "",
|
|
227
|
+
) -> dict[str, Any]:
|
|
228
|
+
return {
|
|
229
|
+
"schema_version": SCHEMA_VERSION,
|
|
230
|
+
"instance_path": str(instance_path),
|
|
231
|
+
"org_id": org_id,
|
|
232
|
+
"instance_hash": instance_hash,
|
|
233
|
+
"candidate_version": candidate_version,
|
|
234
|
+
"candidate_source": candidate_source,
|
|
235
|
+
"candidate_digest": None,
|
|
236
|
+
"installed_path": None,
|
|
237
|
+
"active_version": active_version,
|
|
238
|
+
"rollback_version": active_version,
|
|
239
|
+
"status": STATUS_PENDING,
|
|
240
|
+
"stages": [
|
|
241
|
+
{"key": key, "label": label, "state": "pending", "detail": ""}
|
|
242
|
+
for key, label in STAGES
|
|
243
|
+
],
|
|
244
|
+
"source_revision": None,
|
|
245
|
+
"semantic_revision": None,
|
|
246
|
+
"progress": {},
|
|
247
|
+
"gates": {},
|
|
248
|
+
"failure": None,
|
|
249
|
+
"worker": None,
|
|
250
|
+
"started_at": _now(),
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def stage_index(state: Mapping[str, Any], key: str) -> int:
|
|
255
|
+
for index, stage in enumerate(state.get("stages", ())):
|
|
256
|
+
if stage.get("key") == key:
|
|
257
|
+
return index
|
|
258
|
+
raise UpgradeError(f"unknown upgrade stage: {key}")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def describe(state: Mapping[str, Any] | None, *, worker_alive: bool | None = None) -> dict[str, Any]:
|
|
262
|
+
"""Status projection for adapters (Settings, session start, readiness)."""
|
|
263
|
+
if state is None:
|
|
264
|
+
return {"schema_version": SCHEMA_VERSION, "status": "none"}
|
|
265
|
+
projected = dict(state)
|
|
266
|
+
worker = state.get("worker") or {}
|
|
267
|
+
pid = worker.get("pid")
|
|
268
|
+
alive = worker_alive if worker_alive is not None else (_pid_alive(pid) if pid else False)
|
|
269
|
+
projected["worker_alive"] = bool(alive)
|
|
270
|
+
# A preparing status with a dead worker is not "active work" — it is a
|
|
271
|
+
# resumable interruption and must be presented as such.
|
|
272
|
+
if state.get("status") == STATUS_PREPARING and not alive:
|
|
273
|
+
projected["status"] = "interrupted"
|
|
274
|
+
return projected
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@dataclass
|
|
278
|
+
class UpgradeEngine:
|
|
279
|
+
"""Runs preparation stages against one instance. Resumable by design."""
|
|
280
|
+
|
|
281
|
+
repository_root: Path
|
|
282
|
+
store: UpgradeStore
|
|
283
|
+
runtime: Any # EgregoreRuntime; typed loosely to keep import edges thin
|
|
284
|
+
retriever: QmdLocalRetriever
|
|
285
|
+
poll_seconds: float = 1.0
|
|
286
|
+
log: Any = None # callable(str) for worker logs; never user-facing
|
|
287
|
+
|
|
288
|
+
def _mark(self, state: dict[str, Any], key: str, stage_state: str, detail: str = "") -> None:
|
|
289
|
+
index = stage_index(state, key)
|
|
290
|
+
stage = state["stages"][index]
|
|
291
|
+
stage["state"] = stage_state
|
|
292
|
+
stage["detail"] = detail
|
|
293
|
+
if stage_state == "running" and "started_at" not in stage:
|
|
294
|
+
stage["started_at"] = _now()
|
|
295
|
+
if stage_state in ("done", "failed"):
|
|
296
|
+
stage["finished_at"] = _now()
|
|
297
|
+
self.store.write(state)
|
|
298
|
+
|
|
299
|
+
def _fail(self, state: dict[str, Any], key: str, message: str) -> None:
|
|
300
|
+
self._mark(state, key, "failed", message[:300])
|
|
301
|
+
state["status"] = STATUS_FAILED
|
|
302
|
+
state["failure"] = {"stage": key, "message": message[:300]}
|
|
303
|
+
self.store.write(state)
|
|
304
|
+
|
|
305
|
+
def _cancelled(self, state: dict[str, Any]) -> bool:
|
|
306
|
+
if not self.store.cancel_requested():
|
|
307
|
+
return False
|
|
308
|
+
state["status"] = STATUS_CANCELLED
|
|
309
|
+
self.store.write(state)
|
|
310
|
+
return True
|
|
311
|
+
|
|
312
|
+
def _stage_done(self, state: dict[str, Any], key: str) -> bool:
|
|
313
|
+
return state["stages"][stage_index(state, key)].get("state") == "done"
|
|
314
|
+
|
|
315
|
+
# -- stages ----------------------------------------------------------
|
|
316
|
+
|
|
317
|
+
def _run_sync(self, state: dict[str, Any]) -> bool:
|
|
318
|
+
receipt = self.runtime.synchronize()
|
|
319
|
+
current = bool(getattr(receipt, "canonical_current", False))
|
|
320
|
+
if not current:
|
|
321
|
+
failures = tuple(getattr(receipt, "failures", ()) or ())
|
|
322
|
+
self._fail(state, "sync", "; ".join(failures) or "team memory could not be synchronized")
|
|
323
|
+
return False
|
|
324
|
+
self._mark(state, "sync", "done", "canonical memory current")
|
|
325
|
+
return True
|
|
326
|
+
|
|
327
|
+
def _run_candidate(self, state: dict[str, Any]) -> bool:
|
|
328
|
+
revision = self.retriever._source_revision()
|
|
329
|
+
state["source_revision"] = revision
|
|
330
|
+
self._mark(state, "candidate", "done", revision)
|
|
331
|
+
return True
|
|
332
|
+
|
|
333
|
+
@staticmethod
|
|
334
|
+
def _sha256(path: Path) -> str:
|
|
335
|
+
import hashlib
|
|
336
|
+
|
|
337
|
+
digest = hashlib.sha256()
|
|
338
|
+
with path.open("rb") as handle:
|
|
339
|
+
for block in iter(lambda: handle.read(1 << 20), b""):
|
|
340
|
+
digest.update(block)
|
|
341
|
+
return digest.hexdigest()
|
|
342
|
+
|
|
343
|
+
def _run_install(self, state: dict[str, Any]) -> bool:
|
|
344
|
+
import shutil
|
|
345
|
+
import tarfile
|
|
346
|
+
|
|
347
|
+
source = Path(state.get("candidate_source") or "")
|
|
348
|
+
if not source.is_file():
|
|
349
|
+
self._fail(state, "install", "no candidate package tarball was staged for this upgrade")
|
|
350
|
+
return False
|
|
351
|
+
state["candidate_digest"] = self._sha256(source)
|
|
352
|
+
self.store.write(state)
|
|
353
|
+
|
|
354
|
+
target = self.store.root / "candidate"
|
|
355
|
+
if target.exists():
|
|
356
|
+
shutil.rmtree(target)
|
|
357
|
+
target.mkdir(parents=True, mode=0o700)
|
|
358
|
+
try:
|
|
359
|
+
with tarfile.open(source, "r:gz") as archive:
|
|
360
|
+
for member in archive.getmembers():
|
|
361
|
+
member_path = (target / member.name).resolve()
|
|
362
|
+
if not str(member_path).startswith(str(target.resolve())):
|
|
363
|
+
raise UpgradeError(f"unsafe path in candidate archive: {member.name}")
|
|
364
|
+
archive.extractall(target)
|
|
365
|
+
except Exception as exc:
|
|
366
|
+
self._fail(state, "install", f"candidate extraction failed: {exc}")
|
|
367
|
+
return False
|
|
368
|
+
|
|
369
|
+
package_root = target / "package"
|
|
370
|
+
try:
|
|
371
|
+
manifest = json.loads((package_root / "package.json").read_text(encoding="utf-8"))
|
|
372
|
+
except (OSError, ValueError) as exc:
|
|
373
|
+
self._fail(state, "install", f"candidate package.json unreadable: {exc}")
|
|
374
|
+
return False
|
|
375
|
+
if str(manifest.get("version")) != state["candidate_version"]:
|
|
376
|
+
self._fail(
|
|
377
|
+
state,
|
|
378
|
+
"install",
|
|
379
|
+
f"candidate package is {manifest.get('version')}, expected {state['candidate_version']}",
|
|
380
|
+
)
|
|
381
|
+
return False
|
|
382
|
+
scripts = manifest.get("scripts") or {}
|
|
383
|
+
for lifecycle in ("preinstall", "install", "postinstall"):
|
|
384
|
+
if lifecycle in scripts:
|
|
385
|
+
self._fail(state, "install", f"candidate declares a {lifecycle} lifecycle script; refused")
|
|
386
|
+
return False
|
|
387
|
+
missing = [tree for tree in BUNDLE_TREES if not (package_root / "runtime" / tree).is_dir()]
|
|
388
|
+
if missing:
|
|
389
|
+
self._fail(state, "install", "candidate is missing packaged bundles: " + ", ".join(missing))
|
|
390
|
+
return False
|
|
391
|
+
state["installed_path"] = str(package_root)
|
|
392
|
+
self._mark(
|
|
393
|
+
state,
|
|
394
|
+
"install",
|
|
395
|
+
"done",
|
|
396
|
+
f"{manifest.get('version')} · sha256 {state['candidate_digest'][:16]} · all four bundles present",
|
|
397
|
+
)
|
|
398
|
+
return True
|
|
399
|
+
|
|
400
|
+
def _run_node(self, state: dict[str, Any]) -> bool:
|
|
401
|
+
node = self.retriever.environment.get("EGREGORE_QMD_NODE_BIN")
|
|
402
|
+
if not node:
|
|
403
|
+
self._fail(state, "node", "no usable Node runtime was found for this instance")
|
|
404
|
+
return False
|
|
405
|
+
probe = subprocess.run(
|
|
406
|
+
(node, "--version"), capture_output=True, text=True, timeout=15, check=False
|
|
407
|
+
)
|
|
408
|
+
if probe.returncode != 0:
|
|
409
|
+
self._fail(state, "node", "the selected Node runtime failed its version probe")
|
|
410
|
+
return False
|
|
411
|
+
self._mark(state, "node", "done", f"{probe.stdout.strip()} at instance-selected path")
|
|
412
|
+
return True
|
|
413
|
+
|
|
414
|
+
def _run_qmd(self, state: dict[str, Any]) -> bool:
|
|
415
|
+
# An explicitly pinned QMD (constructor command or EGREGORE_QMD_BIN)
|
|
416
|
+
# is the instance's chosen binary — verify it instead of installing
|
|
417
|
+
# over it. Otherwise ensure the instance-owned managed install.
|
|
418
|
+
if self.retriever._configured_command or self.retriever.environment.get("EGREGORE_QMD_BIN"):
|
|
419
|
+
try:
|
|
420
|
+
self.retriever._ensure_version()
|
|
421
|
+
except Exception as exc:
|
|
422
|
+
self._fail(state, "qmd", f"pinned QMD failed verification: {exc}")
|
|
423
|
+
return False
|
|
424
|
+
self._mark(state, "qmd", "done", f"QMD {QMD_VERSION} (pinned by configuration)")
|
|
425
|
+
return True
|
|
426
|
+
binary = self.retriever._managed_binary()
|
|
427
|
+
if not (binary.is_file() and os.access(binary, os.X_OK)):
|
|
428
|
+
try:
|
|
429
|
+
binary = self.retriever.install_managed()
|
|
430
|
+
except Exception as exc:
|
|
431
|
+
self._fail(state, "qmd", f"QMD {QMD_VERSION} install failed: {exc}")
|
|
432
|
+
return False
|
|
433
|
+
self._mark(state, "qmd", "done", f"QMD {QMD_VERSION} (instance-owned)")
|
|
434
|
+
return True
|
|
435
|
+
|
|
436
|
+
def _run_bm25(self, state: dict[str, Any]) -> bool:
|
|
437
|
+
try:
|
|
438
|
+
health = self.retriever.update()
|
|
439
|
+
except Exception as exc:
|
|
440
|
+
self._fail(state, "bm25", f"keyword index build failed: {exc}")
|
|
441
|
+
return False
|
|
442
|
+
if not health.bm25_ready:
|
|
443
|
+
self._fail(state, "bm25", "keyword index did not become ready")
|
|
444
|
+
return False
|
|
445
|
+
self._record_progress(state)
|
|
446
|
+
self._mark(state, "bm25", "done", "keyword search current for the candidate revision")
|
|
447
|
+
return True
|
|
448
|
+
|
|
449
|
+
def _record_progress(self, state: dict[str, Any]) -> None:
|
|
450
|
+
completed = self.retriever._run(("status",), timeout=30)
|
|
451
|
+
counts = self.retriever._status_counts(completed.stdout if completed.returncode == 0 else "")
|
|
452
|
+
state["progress"] = {
|
|
453
|
+
"documents": counts["documents"],
|
|
454
|
+
"vectors": counts["vectors"],
|
|
455
|
+
"pending": counts["pending"],
|
|
456
|
+
"counted_at": _now(),
|
|
457
|
+
}
|
|
458
|
+
self.store.write(state)
|
|
459
|
+
|
|
460
|
+
def _run_embed(self, state: dict[str, Any], key: str = "embed") -> bool:
|
|
461
|
+
self._mark(state, key, "running", "embedding team memory")
|
|
462
|
+
# One `qmd embed` invocation proves nothing by itself: qmd embeds in
|
|
463
|
+
# bounded batches, a concurrent session's embedder can hold the embed
|
|
464
|
+
# lock (our pass prints "Skipping" and exits 0 while theirs runs), and
|
|
465
|
+
# freshly written vectors become countable slightly after the process
|
|
466
|
+
# exits. Drain instead: keep invoking embed and re-reading the real
|
|
467
|
+
# count for as long as pending falls; fail only after a full settle
|
|
468
|
+
# window passes with no progress from anyone.
|
|
469
|
+
try:
|
|
470
|
+
settle = float(
|
|
471
|
+
self.retriever.environment.get(
|
|
472
|
+
"EGREGORE_UPGRADE_EMBED_SETTLE_SECONDS", "20"
|
|
473
|
+
)
|
|
474
|
+
)
|
|
475
|
+
except (AttributeError, TypeError, ValueError):
|
|
476
|
+
settle = 20.0
|
|
477
|
+
previous: int | None = None
|
|
478
|
+
deadline = time.monotonic() + settle
|
|
479
|
+
while True:
|
|
480
|
+
if self._cancelled(state):
|
|
481
|
+
return False
|
|
482
|
+
try:
|
|
483
|
+
self.retriever.embed_foreground()
|
|
484
|
+
except Exception as exc:
|
|
485
|
+
self._fail(state, key, f"meaning-index build failed: {exc}")
|
|
486
|
+
return False
|
|
487
|
+
self._record_progress(state)
|
|
488
|
+
pending = int(state["progress"].get("pending", 0) or 0)
|
|
489
|
+
if not pending:
|
|
490
|
+
break
|
|
491
|
+
if previous is None or pending < previous:
|
|
492
|
+
previous = pending
|
|
493
|
+
deadline = time.monotonic() + settle
|
|
494
|
+
vectors = state["progress"].get("vectors", 0)
|
|
495
|
+
self._mark(
|
|
496
|
+
state, key, "running", f"{vectors} chunks embedded · {pending} pending"
|
|
497
|
+
)
|
|
498
|
+
elif time.monotonic() >= deadline:
|
|
499
|
+
self._fail(state, key, f"{pending} documents still awaiting embeddings after the build")
|
|
500
|
+
return False
|
|
501
|
+
if settle > 0:
|
|
502
|
+
time.sleep(min(2.0, settle))
|
|
503
|
+
self._mark(state, key, "done", f"{state['progress'].get('vectors', 0)} chunks embedded")
|
|
504
|
+
return True
|
|
505
|
+
|
|
506
|
+
def _run_delta(self, state: dict[str, Any]) -> bool:
|
|
507
|
+
# Canonical memory may have moved while the initial build ran. Converge:
|
|
508
|
+
# re-sync, re-index, embed the remaining delta — bounded, resumable.
|
|
509
|
+
for _ in range(5):
|
|
510
|
+
if self._cancelled(state):
|
|
511
|
+
return False
|
|
512
|
+
self.retriever._source_revision_cache = None
|
|
513
|
+
if self.retriever._semantic_state_current():
|
|
514
|
+
state["semantic_revision"] = self.retriever._semantic_source_revision()
|
|
515
|
+
state["source_revision"] = self.retriever._source_revision()
|
|
516
|
+
self._mark(state, "delta", "done", "meaning index matches the candidate revision")
|
|
517
|
+
return True
|
|
518
|
+
receipt = self.runtime.synchronize()
|
|
519
|
+
if not bool(getattr(receipt, "canonical_current", False)):
|
|
520
|
+
self._fail(state, "delta", "team memory changed and could not be re-synchronized")
|
|
521
|
+
return False
|
|
522
|
+
# Bring the keyword index to the new revision before embedding the
|
|
523
|
+
# delta — a no-op when synchronize already did it.
|
|
524
|
+
try:
|
|
525
|
+
self.retriever.update()
|
|
526
|
+
except Exception as exc:
|
|
527
|
+
self._fail(state, "delta", f"keyword index refresh failed: {exc}")
|
|
528
|
+
return False
|
|
529
|
+
if not self._run_embed(state, key="delta"):
|
|
530
|
+
return False
|
|
531
|
+
# _run_embed marked delta done; loop re-checks currency in case
|
|
532
|
+
# memory moved again during that embed.
|
|
533
|
+
self._mark(state, "delta", "running", "verifying the delta landed")
|
|
534
|
+
self._fail(state, "delta", "team memory kept changing during preparation; retry when quieter")
|
|
535
|
+
return False
|
|
536
|
+
|
|
537
|
+
def _run_verify(self, state: dict[str, Any]) -> bool:
|
|
538
|
+
from .readiness import build_report, session_boundary_state
|
|
539
|
+
from .contracts import Permission
|
|
540
|
+
|
|
541
|
+
health = self.retriever.health()
|
|
542
|
+
gates: dict[str, bool | str] = {}
|
|
543
|
+
gates["bm25_current"] = bool(health.bm25_ready and health.index_source_revision == health.source_revision)
|
|
544
|
+
gates["semantic_complete"] = bool(health.semantic_ready)
|
|
545
|
+
gates["semantic_matches_source"] = bool(
|
|
546
|
+
health.semantic_source_revision is not None
|
|
547
|
+
and health.semantic_source_revision == health.source_revision
|
|
548
|
+
)
|
|
549
|
+
gates["reranker_disabled"] = not RERANKING_ENABLED
|
|
550
|
+
boundary = session_boundary_state(self.repository_root)
|
|
551
|
+
authorization: Any
|
|
552
|
+
try:
|
|
553
|
+
actor = self.runtime.resolve_actor(
|
|
554
|
+
session_id=os.environ.get("EGREGORE_SESSION_ID", "upgrade-verify"),
|
|
555
|
+
harness="upgrade",
|
|
556
|
+
)
|
|
557
|
+
authorization = self.runtime.authorize(actor, Permission.READ)
|
|
558
|
+
gates["identity_resolves"] = True
|
|
559
|
+
gates["authorization_allows"] = bool(authorization.allowed)
|
|
560
|
+
except Exception as exc:
|
|
561
|
+
authorization = exc
|
|
562
|
+
gates["identity_resolves"] = False
|
|
563
|
+
gates["authorization_allows"] = False
|
|
564
|
+
report = build_report(
|
|
565
|
+
root=self.repository_root,
|
|
566
|
+
health=health,
|
|
567
|
+
boundary=boundary,
|
|
568
|
+
authorization=authorization if not isinstance(authorization, bool) else None,
|
|
569
|
+
)
|
|
570
|
+
stages = {item.key: item.stage for item in report.items}
|
|
571
|
+
gates["boundary_ready"] = stages.get("boundary") == "ready"
|
|
572
|
+
gates["hybrid_probe"] = stages.get("search") == "ready" and gates["semantic_complete"]
|
|
573
|
+
# Retrieval in the upgraded Runtime is QMD-only by construction: the
|
|
574
|
+
# engine never consults graph projections and records that contract.
|
|
575
|
+
gates["graph_retrieval"] = "not-used-by-this-runtime"
|
|
576
|
+
state["gates"] = gates
|
|
577
|
+
state["semantic_revision"] = health.semantic_source_revision
|
|
578
|
+
failed = [name for name, value in gates.items() if value is False]
|
|
579
|
+
if failed:
|
|
580
|
+
self._fail(state, "verify", "gates failed: " + ", ".join(failed))
|
|
581
|
+
return False
|
|
582
|
+
self._mark(state, "verify", "done", "all activation gates passed")
|
|
583
|
+
return True
|
|
584
|
+
|
|
585
|
+
# -- driver ----------------------------------------------------------
|
|
586
|
+
|
|
587
|
+
def prepare(self) -> dict[str, Any]:
|
|
588
|
+
state = self.store.load()
|
|
589
|
+
if state is None:
|
|
590
|
+
raise UpgradeError("no upgrade has been initialized for this instance")
|
|
591
|
+
if state.get("status") == STATUS_ACTIVE:
|
|
592
|
+
return state
|
|
593
|
+
self.store.clear_cancel()
|
|
594
|
+
state["status"] = STATUS_PREPARING
|
|
595
|
+
state["worker"] = {"pid": os.getpid(), "started_at": _now()}
|
|
596
|
+
self.store.write(state)
|
|
597
|
+
|
|
598
|
+
runners = {
|
|
599
|
+
"sync": self._run_sync,
|
|
600
|
+
"candidate": self._run_candidate,
|
|
601
|
+
"install": self._run_install,
|
|
602
|
+
"node": self._run_node,
|
|
603
|
+
"qmd": self._run_qmd,
|
|
604
|
+
"bm25": self._run_bm25,
|
|
605
|
+
"embed": self._run_embed,
|
|
606
|
+
"delta": self._run_delta,
|
|
607
|
+
"verify": self._run_verify,
|
|
608
|
+
}
|
|
609
|
+
for key, _label in STAGES:
|
|
610
|
+
if self._cancelled(state):
|
|
611
|
+
return state
|
|
612
|
+
if self._stage_done(state, key):
|
|
613
|
+
continue
|
|
614
|
+
self._mark(state, key, "running")
|
|
615
|
+
if not runners[key](state):
|
|
616
|
+
return state
|
|
617
|
+
state["status"] = STATUS_READY
|
|
618
|
+
self.store.write(state)
|
|
619
|
+
return state
|
|
620
|
+
|
|
621
|
+
def _apply_candidate_bytes(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
622
|
+
"""Copy the installed candidate bundle into the instance, backing up
|
|
623
|
+
every replaced file so rollback restores exact previous bytes."""
|
|
624
|
+
import shutil
|
|
625
|
+
import tarfile
|
|
626
|
+
|
|
627
|
+
package_root = Path(state.get("installed_path") or "")
|
|
628
|
+
if not (package_root / "runtime" / APPLY_TREES[0]).is_dir():
|
|
629
|
+
raise UpgradeError("candidate bundle tree is missing; re-run preparation")
|
|
630
|
+
instance = Path(state["instance_path"])
|
|
631
|
+
replaced: list[str] = []
|
|
632
|
+
added: list[str] = []
|
|
633
|
+
selected: dict[str, Path] = {}
|
|
634
|
+
for tree_name in APPLY_TREES:
|
|
635
|
+
tree = package_root / "runtime" / tree_name
|
|
636
|
+
if not tree.is_dir():
|
|
637
|
+
continue
|
|
638
|
+
for source_file in tree.rglob("*"):
|
|
639
|
+
if not source_file.is_file():
|
|
640
|
+
continue
|
|
641
|
+
relative = str(source_file.relative_to(tree))
|
|
642
|
+
selected.setdefault(relative, source_file)
|
|
643
|
+
backup_path = self.store.root / "rollback-files.tar.gz"
|
|
644
|
+
with tarfile.open(backup_path, "w:gz") as backup:
|
|
645
|
+
for relative in sorted(selected):
|
|
646
|
+
destination = instance / relative
|
|
647
|
+
if destination.exists():
|
|
648
|
+
backup.add(destination, arcname=relative)
|
|
649
|
+
replaced.append(relative)
|
|
650
|
+
else:
|
|
651
|
+
added.append(relative)
|
|
652
|
+
for relative in sorted(selected):
|
|
653
|
+
destination = instance / relative
|
|
654
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
655
|
+
shutil.copy2(selected[relative], destination)
|
|
656
|
+
manifest = {"replaced": replaced, "added": added, "trees": list(APPLY_TREES)}
|
|
657
|
+
(self.store.root / "rollback-manifest.json").write_text(
|
|
658
|
+
json.dumps(manifest, indent=2) + "\n", encoding="utf-8"
|
|
659
|
+
)
|
|
660
|
+
return {"applied_files": len(selected), "replaced": len(replaced), "added": len(added)}
|
|
661
|
+
|
|
662
|
+
def activate(self) -> dict[str, Any]:
|
|
663
|
+
state = self.store.load()
|
|
664
|
+
if state is None or state.get("status") != STATUS_READY:
|
|
665
|
+
raise UpgradeError(
|
|
666
|
+
"the candidate Runtime is not ready to activate; every gate must pass first"
|
|
667
|
+
)
|
|
668
|
+
gates = state.get("gates", {})
|
|
669
|
+
if any(value is False for value in gates.values()):
|
|
670
|
+
raise UpgradeError("activation refused: a recorded gate is failing")
|
|
671
|
+
# The recorded tarball digest must still match the bytes on disk —
|
|
672
|
+
# a swapped or corrupted candidate never activates.
|
|
673
|
+
source = Path(state.get("candidate_source") or "")
|
|
674
|
+
if not source.is_file() or self._sha256(source) != state.get("candidate_digest"):
|
|
675
|
+
raise UpgradeError("candidate package digest no longer matches; re-run preparation")
|
|
676
|
+
applied = self._apply_candidate_bytes(state)
|
|
677
|
+
record = {
|
|
678
|
+
"schema_version": SCHEMA_VERSION,
|
|
679
|
+
"active_version": state["candidate_version"],
|
|
680
|
+
"rollback_version": state["rollback_version"],
|
|
681
|
+
"source_revision": state["source_revision"],
|
|
682
|
+
"semantic_revision": state["semantic_revision"],
|
|
683
|
+
"candidate_digest": state["candidate_digest"],
|
|
684
|
+
"installed_path": state["installed_path"],
|
|
685
|
+
"applied": applied,
|
|
686
|
+
"activated_at": _now(),
|
|
687
|
+
"retrieval": "runtime-qmd",
|
|
688
|
+
"graph_retrieval": "not-used-by-this-runtime",
|
|
689
|
+
}
|
|
690
|
+
# Atomic: the activation record replaces in one rename; the state file
|
|
691
|
+
# then records active status. A crash between the two leaves a valid
|
|
692
|
+
# activation record and a resumable state — never partial retrieval.
|
|
693
|
+
self.store.write_activation(record)
|
|
694
|
+
state["status"] = STATUS_ACTIVE
|
|
695
|
+
state["active_version"] = state["candidate_version"]
|
|
696
|
+
self.store.write(state)
|
|
697
|
+
return state
|
|
698
|
+
|
|
699
|
+
def rollback(self) -> dict[str, Any]:
|
|
700
|
+
import tarfile
|
|
701
|
+
|
|
702
|
+
state = self.store.load()
|
|
703
|
+
if state is None:
|
|
704
|
+
raise UpgradeError("no upgrade state exists for this instance")
|
|
705
|
+
activation = self.store.load_activation() or {}
|
|
706
|
+
target = activation.get("rollback_version") or state.get("rollback_version")
|
|
707
|
+
if not target:
|
|
708
|
+
raise UpgradeError("no rollback version is recorded for this instance")
|
|
709
|
+
# Restore exact previous bytes: replaced files come back from the
|
|
710
|
+
# backup archive, files the candidate added are removed.
|
|
711
|
+
backup_path = self.store.root / "rollback-files.tar.gz"
|
|
712
|
+
manifest_path = self.store.root / "rollback-manifest.json"
|
|
713
|
+
if activation.get("applied") and backup_path.is_file() and manifest_path.is_file():
|
|
714
|
+
instance = Path(state["instance_path"])
|
|
715
|
+
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
716
|
+
with tarfile.open(backup_path, "r:gz") as backup:
|
|
717
|
+
for member in backup.getmembers():
|
|
718
|
+
member_path = (instance / member.name).resolve()
|
|
719
|
+
if not str(member_path).startswith(str(instance.resolve())):
|
|
720
|
+
raise UpgradeError(f"unsafe path in rollback archive: {member.name}")
|
|
721
|
+
backup.extractall(instance)
|
|
722
|
+
for relative in manifest.get("added", ()): # candidate-only files
|
|
723
|
+
candidate_file = (instance / relative).resolve()
|
|
724
|
+
if str(candidate_file).startswith(str(instance.resolve())):
|
|
725
|
+
candidate_file.unlink(missing_ok=True)
|
|
726
|
+
record = {
|
|
727
|
+
"schema_version": SCHEMA_VERSION,
|
|
728
|
+
"active_version": target,
|
|
729
|
+
"rollback_version": target,
|
|
730
|
+
"rolled_back_from": state.get("candidate_version"),
|
|
731
|
+
"rolled_back_at": _now(),
|
|
732
|
+
"retrieval": "previous-runtime",
|
|
733
|
+
}
|
|
734
|
+
self.store.write_activation(record)
|
|
735
|
+
state["status"] = STATUS_ROLLED_BACK
|
|
736
|
+
state["active_version"] = target
|
|
737
|
+
self.store.write(state)
|
|
738
|
+
return state
|
|
739
|
+
|
|
740
|
+
def cancel(self) -> dict[str, Any]:
|
|
741
|
+
state = self.store.load()
|
|
742
|
+
if state is None:
|
|
743
|
+
raise UpgradeError("no upgrade state exists for this instance")
|
|
744
|
+
self.store.request_cancel()
|
|
745
|
+
worker = state.get("worker") or {}
|
|
746
|
+
pid = worker.get("pid")
|
|
747
|
+
if state.get("status") == STATUS_PREPARING and not (pid and _pid_alive(pid)):
|
|
748
|
+
# No live worker will observe the flag — record the cancel now.
|
|
749
|
+
state["status"] = STATUS_CANCELLED
|
|
750
|
+
self.store.write(state)
|
|
751
|
+
return self.store.load() or state
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
__all__ = [
|
|
755
|
+
"SCHEMA_VERSION",
|
|
756
|
+
"STAGES",
|
|
757
|
+
"STATUS_ACTIVE",
|
|
758
|
+
"STATUS_CANCELLED",
|
|
759
|
+
"STATUS_FAILED",
|
|
760
|
+
"STATUS_PENDING",
|
|
761
|
+
"STATUS_PREPARING",
|
|
762
|
+
"STATUS_READY",
|
|
763
|
+
"STATUS_ROLLED_BACK",
|
|
764
|
+
"UpgradeEngine",
|
|
765
|
+
"UpgradeError",
|
|
766
|
+
"UpgradeStore",
|
|
767
|
+
"describe",
|
|
768
|
+
"new_state",
|
|
769
|
+
"upgrade_root",
|
|
770
|
+
]
|