create-egregore 0.20.2 → 0.21.0-runtime-mvp.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +1013 -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,523 @@
|
|
|
1
|
+
"""Canonical Markdown artifact envelope, compatibility reader, and local store.
|
|
2
|
+
|
|
3
|
+
Markdown remains the canonical organizational state. This module standardizes
|
|
4
|
+
only the small machine-readable envelope around that Markdown; type-specific
|
|
5
|
+
payloads and lifecycles remain owned by their rituals (for example, Scrolls
|
|
6
|
+
remain mutable living documents while handoffs are append-only captures).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import unicodedata
|
|
16
|
+
from dataclasses import dataclass, field, replace
|
|
17
|
+
from datetime import UTC, date, datetime
|
|
18
|
+
from pathlib import Path, PurePosixPath
|
|
19
|
+
from typing import Any, Mapping, Protocol, runtime_checkable
|
|
20
|
+
|
|
21
|
+
from .contracts import ArtifactRelationship, CanonicalArtifact, SourceProvenance
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
ARTIFACT_SCHEMA_VERSION = "egregore-artifact/v1"
|
|
25
|
+
_FRONTMATTER_KEY = re.compile(r"^([A-Za-z][A-Za-z0-9_-]*):(?:[ \t]*(.*))?$")
|
|
26
|
+
# Existing hosted artifact ids are URL-safe opaque tokens and may begin with
|
|
27
|
+
# ``-`` or ``_``. Keep those stable during explicit v1 migration.
|
|
28
|
+
_OPAQUE_ID = re.compile(r"^[A-Za-z0-9_-][A-Za-z0-9._:/-]{2,255}$")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ArtifactSchemaError(ValueError):
|
|
32
|
+
"""A canonical artifact envelope is invalid or cannot be migrated safely."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ArtifactConflictError(FileExistsError):
|
|
36
|
+
"""An append-only artifact path already contains a different artifact."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, slots=True)
|
|
40
|
+
class CanonicalDocument:
|
|
41
|
+
"""A canonical artifact plus its Markdown body and advisory policy hints."""
|
|
42
|
+
|
|
43
|
+
artifact: CanonicalArtifact
|
|
44
|
+
body: str
|
|
45
|
+
policy_hints: Mapping[str, Any] = field(default_factory=dict)
|
|
46
|
+
legacy_fields: Mapping[str, Any] = field(default_factory=dict)
|
|
47
|
+
migrated_from: str | None = None
|
|
48
|
+
warnings: tuple[str, ...] = ()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@runtime_checkable
|
|
52
|
+
class CanonicalArtifactStore(Protocol):
|
|
53
|
+
"""Persistence seam for canonical Markdown, independent of Git transport."""
|
|
54
|
+
|
|
55
|
+
def persist(self, document: CanonicalDocument) -> Path: ...
|
|
56
|
+
|
|
57
|
+
def read(self, canonical_path: str) -> CanonicalDocument: ...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def normalize_markdown_body(content: str) -> str:
|
|
61
|
+
"""Return a deterministic UTF-8 Markdown body with LF endings and one EOF LF."""
|
|
62
|
+
|
|
63
|
+
normalized = unicodedata.normalize("NFC", content.replace("\r\n", "\n").replace("\r", "\n"))
|
|
64
|
+
return normalized.rstrip("\n") + "\n"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def content_digest(content: str) -> str:
|
|
68
|
+
return hashlib.sha256(normalize_markdown_body(content).encode("utf-8")).hexdigest()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def canonical_relative_path(raw_path: str) -> str:
|
|
72
|
+
"""Normalize a repo-relative path and reject traversal/absolute targets."""
|
|
73
|
+
|
|
74
|
+
value = raw_path.strip().replace("\\", "/")
|
|
75
|
+
while value.startswith("./"):
|
|
76
|
+
value = value[2:]
|
|
77
|
+
value = re.sub(r"/{2,}", "/", value)
|
|
78
|
+
candidate = PurePosixPath(value)
|
|
79
|
+
if not value or candidate.is_absolute() or ".." in candidate.parts:
|
|
80
|
+
raise ArtifactSchemaError("canonical_path must be a non-empty relative path")
|
|
81
|
+
return candidate.as_posix()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def artifact_id_from_path(raw_path: str) -> str:
|
|
85
|
+
"""Match ``bin/lib/artifact-id.sh`` and the JavaScript artifact package."""
|
|
86
|
+
|
|
87
|
+
canonical = canonical_relative_path(raw_path)
|
|
88
|
+
extension = PurePosixPath(canonical).suffix.lower()
|
|
89
|
+
prefix = {".md": "m", ".html": "h"}.get(extension)
|
|
90
|
+
if prefix is None:
|
|
91
|
+
raise ArtifactSchemaError(f"unsupported canonical artifact extension: {extension or '(none)'}")
|
|
92
|
+
digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()[:12]
|
|
93
|
+
return f"{prefix}-{digest}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def canonical_artifact_id(markdown: str, canonical_path: str) -> str:
|
|
97
|
+
"""Resolve one stable artifact id for retrieval and direct source opens.
|
|
98
|
+
|
|
99
|
+
Typed/hosted artifacts keep their declared opaque id. Historical Markdown
|
|
100
|
+
without one uses the established deterministic path id. Keeping this in the
|
|
101
|
+
domain layer prevents retrieval adapters and harness opens from inventing
|
|
102
|
+
different identities for the same canonical document.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
fields, _ = split_frontmatter(markdown)
|
|
107
|
+
except ArtifactSchemaError:
|
|
108
|
+
fields = {}
|
|
109
|
+
for key in ("artifact_id", "id", "session_id", "slug"):
|
|
110
|
+
value = fields.get(key)
|
|
111
|
+
if value is None:
|
|
112
|
+
continue
|
|
113
|
+
candidate = str(value).strip()
|
|
114
|
+
if _OPAQUE_ID.fullmatch(candidate):
|
|
115
|
+
return candidate
|
|
116
|
+
relative = canonical_relative_path(canonical_path).removeprefix("memory/")
|
|
117
|
+
return artifact_id_from_path(relative)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _json_value(value: Any) -> str:
|
|
121
|
+
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _parse_scalar(raw: str) -> Any:
|
|
125
|
+
value = raw.strip()
|
|
126
|
+
if not value:
|
|
127
|
+
return None
|
|
128
|
+
try:
|
|
129
|
+
return json.loads(value)
|
|
130
|
+
except json.JSONDecodeError:
|
|
131
|
+
pass
|
|
132
|
+
if value.startswith("[") and value.endswith("]"):
|
|
133
|
+
inner = value[1:-1].strip()
|
|
134
|
+
if not inner:
|
|
135
|
+
return []
|
|
136
|
+
return [part.strip().strip("\"'") for part in inner.split(",")]
|
|
137
|
+
if len(value) >= 2 and value[0] == value[-1] == "'":
|
|
138
|
+
return value[1:-1].replace("''", "'")
|
|
139
|
+
lowered = value.lower()
|
|
140
|
+
if lowered in {"true", "false"}:
|
|
141
|
+
return lowered == "true"
|
|
142
|
+
if lowered in {"null", "~"}:
|
|
143
|
+
return None
|
|
144
|
+
# Deliberately retain plain values verbatim. In particular, a title such
|
|
145
|
+
# as ``Launch: architecture`` is one scalar, not malformed YAML.
|
|
146
|
+
return value
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def split_frontmatter(markdown: str) -> tuple[dict[str, Any], str]:
|
|
150
|
+
"""Parse the deterministic JSON-in-YAML subset used by v1 and legacy scalars.
|
|
151
|
+
|
|
152
|
+
JSON values are valid YAML and let the runtime round-trip nested provenance
|
|
153
|
+
without a YAML dependency. Unknown indented legacy fields are ignored and
|
|
154
|
+
preserved in the original file until an explicit migration/write occurs.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
normalized = markdown.replace("\r\n", "\n").replace("\r", "\n")
|
|
158
|
+
if not normalized.startswith("---\n"):
|
|
159
|
+
return {}, normalized
|
|
160
|
+
closing = normalized.find("\n---\n", 4)
|
|
161
|
+
if closing < 0:
|
|
162
|
+
raise ArtifactSchemaError("incomplete frontmatter block")
|
|
163
|
+
raw_header = normalized[4:closing]
|
|
164
|
+
result: dict[str, Any] = {}
|
|
165
|
+
for line_number, line in enumerate(raw_header.splitlines(), 2):
|
|
166
|
+
if not line.strip() or line.lstrip().startswith("#") or line[:1].isspace():
|
|
167
|
+
continue
|
|
168
|
+
match = _FRONTMATTER_KEY.match(line)
|
|
169
|
+
if match is None:
|
|
170
|
+
raise ArtifactSchemaError(f"invalid frontmatter line {line_number}")
|
|
171
|
+
key, raw = match.groups()
|
|
172
|
+
if key in result:
|
|
173
|
+
raise ArtifactSchemaError(f"duplicate frontmatter key: {key}")
|
|
174
|
+
result[key] = _parse_scalar(raw or "")
|
|
175
|
+
return result, normalized[closing + 5 :]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _as_strings(value: Any) -> tuple[str, ...]:
|
|
179
|
+
if value is None:
|
|
180
|
+
return ()
|
|
181
|
+
if isinstance(value, str):
|
|
182
|
+
return (value,) if value.strip() else ()
|
|
183
|
+
if isinstance(value, (list, tuple, set)):
|
|
184
|
+
return tuple(str(item) for item in value if str(item).strip())
|
|
185
|
+
raise ArtifactSchemaError("expected a string or list of strings")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _datetime(value: Any, *, field_name: str) -> datetime:
|
|
189
|
+
if isinstance(value, datetime):
|
|
190
|
+
parsed = value
|
|
191
|
+
elif isinstance(value, date):
|
|
192
|
+
parsed = datetime(value.year, value.month, value.day, tzinfo=UTC)
|
|
193
|
+
elif isinstance(value, str) and value.strip():
|
|
194
|
+
raw = value.strip()
|
|
195
|
+
if re.fullmatch(r"\d{4}-\d{2}-\d{2}", raw):
|
|
196
|
+
parsed = datetime.fromisoformat(raw).replace(tzinfo=UTC)
|
|
197
|
+
else:
|
|
198
|
+
try:
|
|
199
|
+
parsed = datetime.fromisoformat(raw.replace("Z", "+00:00"))
|
|
200
|
+
except ValueError as exc:
|
|
201
|
+
raise ArtifactSchemaError(f"{field_name} must be ISO-8601") from exc
|
|
202
|
+
else:
|
|
203
|
+
raise ArtifactSchemaError(f"{field_name} is required")
|
|
204
|
+
if parsed.tzinfo is None:
|
|
205
|
+
parsed = parsed.replace(tzinfo=UTC)
|
|
206
|
+
return parsed.astimezone(UTC)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _title(fields: Mapping[str, Any], body: str, canonical_path: str) -> str:
|
|
210
|
+
explicit = fields.get("title") or fields.get("topic") or fields.get("name")
|
|
211
|
+
if explicit:
|
|
212
|
+
return str(explicit).strip()
|
|
213
|
+
heading = re.search(r"^#\s+(.+?)\s*$", body, re.MULTILINE)
|
|
214
|
+
if heading:
|
|
215
|
+
return heading.group(1).strip()
|
|
216
|
+
return PurePosixPath(canonical_path).stem.replace("-", " ").strip()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _infer_type(fields: Mapping[str, Any], canonical_path: str) -> str:
|
|
220
|
+
explicit = fields.get("type") or fields.get("artifact_type")
|
|
221
|
+
if explicit:
|
|
222
|
+
return str(explicit)
|
|
223
|
+
kind = fields.get("kind") or fields.get("capture_mode")
|
|
224
|
+
if kind == "addressed":
|
|
225
|
+
return "handoff"
|
|
226
|
+
parts = set(PurePosixPath(canonical_path).parts)
|
|
227
|
+
for directory, artifact_type in (
|
|
228
|
+
("handoffs", "handoff"),
|
|
229
|
+
("decisions", "decision"),
|
|
230
|
+
("reflections", "reflection"),
|
|
231
|
+
("wraps", "reflection"),
|
|
232
|
+
("sessions", "session"),
|
|
233
|
+
("threads", "thread"),
|
|
234
|
+
("scrolls", "scroll"),
|
|
235
|
+
("quests", "quest"),
|
|
236
|
+
("questions", "question"),
|
|
237
|
+
("issues", "issue"),
|
|
238
|
+
("todos", "todo"),
|
|
239
|
+
("ingest", "ingest-document"),
|
|
240
|
+
):
|
|
241
|
+
if directory in parts:
|
|
242
|
+
return artifact_type
|
|
243
|
+
return "artifact"
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _relationships(value: Any) -> tuple[ArtifactRelationship, ...]:
|
|
247
|
+
if value is None:
|
|
248
|
+
return ()
|
|
249
|
+
if not isinstance(value, (list, tuple)):
|
|
250
|
+
raise ArtifactSchemaError("relationships must be a list")
|
|
251
|
+
rows: list[ArtifactRelationship] = []
|
|
252
|
+
for item in value:
|
|
253
|
+
if isinstance(item, str):
|
|
254
|
+
rows.append(ArtifactRelationship("related-to", item))
|
|
255
|
+
elif isinstance(item, Mapping):
|
|
256
|
+
relation = item.get("relation") or item.get("type")
|
|
257
|
+
target = item.get("target_id") or item.get("target") or item.get("id")
|
|
258
|
+
if not relation or not target:
|
|
259
|
+
raise ArtifactSchemaError("each relationship needs relation and target_id")
|
|
260
|
+
rows.append(ArtifactRelationship(str(relation), str(target)))
|
|
261
|
+
else:
|
|
262
|
+
raise ArtifactSchemaError("invalid relationship")
|
|
263
|
+
return tuple(rows)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _provenance(value: Any) -> tuple[SourceProvenance, ...]:
|
|
267
|
+
if value is None:
|
|
268
|
+
return ()
|
|
269
|
+
candidates = value if isinstance(value, (list, tuple)) else [value]
|
|
270
|
+
rows: list[SourceProvenance] = []
|
|
271
|
+
for item in candidates:
|
|
272
|
+
if not isinstance(item, Mapping):
|
|
273
|
+
continue
|
|
274
|
+
required = ("source_type", "source_id", "revision", "content_hash", "observed_at")
|
|
275
|
+
if not all(item.get(key) is not None for key in required):
|
|
276
|
+
# Historical ingest provenance was an open evidence object. It is
|
|
277
|
+
# preserved as a legacy field, but cannot masquerade as typed v1
|
|
278
|
+
# provenance without its source identity and revision.
|
|
279
|
+
continue
|
|
280
|
+
rows.append(
|
|
281
|
+
SourceProvenance(
|
|
282
|
+
source_type=str(item["source_type"]),
|
|
283
|
+
source_id=str(item["source_id"]),
|
|
284
|
+
revision=str(item["revision"]),
|
|
285
|
+
content_hash=str(item["content_hash"]),
|
|
286
|
+
observed_at=_datetime(item["observed_at"], field_name="provenance.observed_at"),
|
|
287
|
+
source_uri=str(item["source_uri"]) if item.get("source_uri") else None,
|
|
288
|
+
imported_by=str(item["imported_by"]) if item.get("imported_by") else None,
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
return tuple(rows)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def validate_artifact(artifact: CanonicalArtifact, body: str) -> CanonicalArtifact:
|
|
295
|
+
"""Validate the common v1 envelope and return its normalized form."""
|
|
296
|
+
|
|
297
|
+
if artifact.schema_version != ARTIFACT_SCHEMA_VERSION:
|
|
298
|
+
raise ArtifactSchemaError(f"unsupported artifact schema: {artifact.schema_version}")
|
|
299
|
+
canonical_path = canonical_relative_path(artifact.canonical_path)
|
|
300
|
+
if not _OPAQUE_ID.fullmatch(artifact.artifact_id):
|
|
301
|
+
raise ArtifactSchemaError("artifact_id must be an opaque non-empty identifier")
|
|
302
|
+
for name, value in (
|
|
303
|
+
("org_id", artifact.org_id),
|
|
304
|
+
("artifact_type", artifact.artifact_type),
|
|
305
|
+
("title", artifact.title),
|
|
306
|
+
("created_by", artifact.created_by),
|
|
307
|
+
("status", artifact.status),
|
|
308
|
+
("revision", artifact.revision),
|
|
309
|
+
):
|
|
310
|
+
if not value or not str(value).strip():
|
|
311
|
+
raise ArtifactSchemaError(f"{name} is required")
|
|
312
|
+
if artifact.created_at.tzinfo is None:
|
|
313
|
+
raise ArtifactSchemaError("created_at must include a timezone")
|
|
314
|
+
actual_hash = content_digest(body)
|
|
315
|
+
if artifact.content_hash not in {actual_hash, f"sha256:{actual_hash}"}:
|
|
316
|
+
raise ArtifactSchemaError("content_hash does not match the normalized Markdown body")
|
|
317
|
+
if any(not row.relation or not row.target_id for row in artifact.relationships):
|
|
318
|
+
raise ArtifactSchemaError("relationships require relation and target_id")
|
|
319
|
+
return replace(
|
|
320
|
+
artifact,
|
|
321
|
+
canonical_path=canonical_path,
|
|
322
|
+
created_at=artifact.created_at.astimezone(UTC),
|
|
323
|
+
content_hash=actual_hash,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def parse_canonical_markdown(
|
|
328
|
+
markdown: str,
|
|
329
|
+
*,
|
|
330
|
+
canonical_path: str,
|
|
331
|
+
default_org_id: str | None = None,
|
|
332
|
+
) -> CanonicalDocument:
|
|
333
|
+
"""Read v1 Markdown or migrate a legacy artifact into the common envelope."""
|
|
334
|
+
|
|
335
|
+
path = canonical_relative_path(canonical_path)
|
|
336
|
+
fields, raw_body = split_frontmatter(markdown)
|
|
337
|
+
body = normalize_markdown_body(raw_body)
|
|
338
|
+
schema = fields.get("schema_version") or fields.get("schema")
|
|
339
|
+
is_v1 = schema == ARTIFACT_SCHEMA_VERSION
|
|
340
|
+
if is_v1:
|
|
341
|
+
required = (
|
|
342
|
+
"id", "org", "type", "title", "created_at", "created_by", "status",
|
|
343
|
+
"canonical_path", "revision", "content_hash",
|
|
344
|
+
)
|
|
345
|
+
missing = [key for key in required if fields.get(key) in (None, "")]
|
|
346
|
+
if missing:
|
|
347
|
+
raise ArtifactSchemaError(f"v1 frontmatter is missing: {', '.join(missing)}")
|
|
348
|
+
declared_path = canonical_relative_path(str(fields["canonical_path"]))
|
|
349
|
+
if declared_path != path:
|
|
350
|
+
raise ArtifactSchemaError("canonical_path does not match the opened source path")
|
|
351
|
+
org_id = fields.get("org") or fields.get("org_id") or default_org_id
|
|
352
|
+
if not org_id:
|
|
353
|
+
raise ArtifactSchemaError("org is required (or supply default_org_id for legacy artifacts)")
|
|
354
|
+
artifact_id = fields.get("id") or fields.get("artifact_id") or artifact_id_from_path(path)
|
|
355
|
+
created_raw = (
|
|
356
|
+
fields.get("created_at")
|
|
357
|
+
or fields.get("date")
|
|
358
|
+
or fields.get("ingested_at")
|
|
359
|
+
or fields.get("started")
|
|
360
|
+
)
|
|
361
|
+
if created_raw is None:
|
|
362
|
+
raise ArtifactSchemaError("created_at is required and could not be migrated")
|
|
363
|
+
created_by = (
|
|
364
|
+
fields.get("created_by")
|
|
365
|
+
or fields.get("actor")
|
|
366
|
+
or fields.get("author")
|
|
367
|
+
or fields.get("from")
|
|
368
|
+
or fields.get("started_by")
|
|
369
|
+
)
|
|
370
|
+
if not created_by:
|
|
371
|
+
raise ArtifactSchemaError("created_by is required and could not be migrated")
|
|
372
|
+
|
|
373
|
+
relationships_value = fields.get("relationships")
|
|
374
|
+
if relationships_value is None:
|
|
375
|
+
legacy_related = fields.get("related") or fields.get("references")
|
|
376
|
+
relationships_value = _as_strings(legacy_related) if legacy_related else ()
|
|
377
|
+
provenance = _provenance(fields.get("source_provenance") or fields.get("provenance"))
|
|
378
|
+
if not provenance and fields.get("source_id") and fields.get("content_hash"):
|
|
379
|
+
provenance = (
|
|
380
|
+
SourceProvenance(
|
|
381
|
+
source_type=str(fields.get("source_type") or "ingest"),
|
|
382
|
+
source_id=str(fields["source_id"]),
|
|
383
|
+
revision=str(fields.get("source_revision") or fields.get("revision") or fields["content_hash"]),
|
|
384
|
+
content_hash=str(fields["content_hash"]),
|
|
385
|
+
observed_at=_datetime(created_raw, field_name="created_at"),
|
|
386
|
+
source_uri=str(fields["source_path"]) if fields.get("source_path") else None,
|
|
387
|
+
imported_by=str(created_by),
|
|
388
|
+
),
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
actual_hash = content_digest(body)
|
|
392
|
+
artifact = CanonicalArtifact(
|
|
393
|
+
schema_version=ARTIFACT_SCHEMA_VERSION,
|
|
394
|
+
artifact_id=str(artifact_id),
|
|
395
|
+
org_id=str(org_id),
|
|
396
|
+
artifact_type=_infer_type(fields, path),
|
|
397
|
+
title=_title(fields, body, path),
|
|
398
|
+
created_at=_datetime(created_raw, field_name="created_at"),
|
|
399
|
+
created_by=str(created_by),
|
|
400
|
+
status=str(fields.get("status") or ("unverified" if _infer_type(fields, path) == "ingest-document" else "active")),
|
|
401
|
+
canonical_path=path,
|
|
402
|
+
revision=str(fields.get("revision") or f"sha256:{actual_hash[:16]}"),
|
|
403
|
+
content_hash=str(fields["content_hash"]) if is_v1 else actual_hash,
|
|
404
|
+
workstream=str(fields.get("workstream") or fields.get("topic") or fields.get("project"))
|
|
405
|
+
if fields.get("workstream") or fields.get("topic") or fields.get("project")
|
|
406
|
+
else None,
|
|
407
|
+
visibility=_as_strings(fields.get("visibility")),
|
|
408
|
+
relationships=_relationships(relationships_value),
|
|
409
|
+
supersedes=_as_strings(fields.get("supersedes")),
|
|
410
|
+
provenance=provenance,
|
|
411
|
+
)
|
|
412
|
+
artifact = validate_artifact(artifact, body)
|
|
413
|
+
|
|
414
|
+
common_keys = {
|
|
415
|
+
"schema_version", "schema", "id", "artifact_id", "org", "org_id", "type",
|
|
416
|
+
"artifact_type", "title", "created_at", "created_by", "status", "revision",
|
|
417
|
+
"content_hash", "workstream", "visibility", "relationships", "supersedes",
|
|
418
|
+
"source_provenance", "policy_hints", "canonical_path",
|
|
419
|
+
}
|
|
420
|
+
policy_hints = fields.get("policy_hints") if isinstance(fields.get("policy_hints"), Mapping) else {}
|
|
421
|
+
policy_hints = dict(policy_hints)
|
|
422
|
+
for key in ("admin", "boundaries"):
|
|
423
|
+
if key in fields:
|
|
424
|
+
policy_hints[key] = fields[key]
|
|
425
|
+
warnings = () if is_v1 else ("legacy frontmatter migrated in memory; source file was not rewritten",)
|
|
426
|
+
return CanonicalDocument(
|
|
427
|
+
artifact=artifact,
|
|
428
|
+
body=body,
|
|
429
|
+
policy_hints=policy_hints,
|
|
430
|
+
legacy_fields={key: value for key, value in fields.items() if key not in common_keys},
|
|
431
|
+
migrated_from=None if is_v1 else str(schema or "unversioned"),
|
|
432
|
+
warnings=warnings,
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def render_canonical_markdown(document: CanonicalDocument) -> str:
|
|
437
|
+
"""Serialize a canonical document as deterministic, parseable Markdown."""
|
|
438
|
+
|
|
439
|
+
artifact = validate_artifact(document.artifact, document.body)
|
|
440
|
+
fields: list[tuple[str, Any]] = [
|
|
441
|
+
("schema_version", artifact.schema_version),
|
|
442
|
+
("id", artifact.artifact_id),
|
|
443
|
+
("org", artifact.org_id),
|
|
444
|
+
("type", artifact.artifact_type),
|
|
445
|
+
("title", artifact.title),
|
|
446
|
+
("created_at", artifact.created_at.isoformat().replace("+00:00", "Z")),
|
|
447
|
+
("created_by", artifact.created_by),
|
|
448
|
+
("workstream", artifact.workstream),
|
|
449
|
+
("status", artifact.status),
|
|
450
|
+
("relationships", [row.to_dict() for row in artifact.relationships]),
|
|
451
|
+
("supersedes", list(artifact.supersedes)),
|
|
452
|
+
("source_provenance", [row.to_dict() for row in artifact.provenance]),
|
|
453
|
+
("visibility", list(artifact.visibility)),
|
|
454
|
+
("policy_hints", dict(document.policy_hints)),
|
|
455
|
+
("canonical_path", artifact.canonical_path),
|
|
456
|
+
("revision", artifact.revision),
|
|
457
|
+
("content_hash", artifact.content_hash),
|
|
458
|
+
]
|
|
459
|
+
common_names = {key for key, _ in fields}
|
|
460
|
+
fields.extend(
|
|
461
|
+
(key, value)
|
|
462
|
+
for key, value in document.legacy_fields.items()
|
|
463
|
+
if key not in common_names and value is not None
|
|
464
|
+
)
|
|
465
|
+
header = ["---", *(f"{key}: {_json_value(value)}" for key, value in fields if value not in (None, (), [], {})), "---"]
|
|
466
|
+
return "\n".join(header) + "\n" + normalize_markdown_body(document.body)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
class LocalMarkdownArtifactStore:
|
|
470
|
+
"""Atomic filesystem store with explicit append-only vs living lifecycles."""
|
|
471
|
+
|
|
472
|
+
def __init__(
|
|
473
|
+
self,
|
|
474
|
+
root: Path,
|
|
475
|
+
*,
|
|
476
|
+
mutable_types: frozenset[str] = frozenset(
|
|
477
|
+
{"scroll", "thread", "question", "quest", "todo", "issue"}
|
|
478
|
+
),
|
|
479
|
+
):
|
|
480
|
+
self.root = root.resolve()
|
|
481
|
+
self.mutable_types = mutable_types
|
|
482
|
+
|
|
483
|
+
def _target(self, canonical_path: str) -> Path:
|
|
484
|
+
relative = canonical_relative_path(canonical_path)
|
|
485
|
+
target = (self.root / relative).resolve()
|
|
486
|
+
if target != self.root and self.root not in target.parents:
|
|
487
|
+
raise ArtifactSchemaError("canonical path escapes the artifact store")
|
|
488
|
+
return target
|
|
489
|
+
|
|
490
|
+
def persist(self, document: CanonicalDocument) -> Path:
|
|
491
|
+
artifact = validate_artifact(document.artifact, document.body)
|
|
492
|
+
target = self._target(artifact.canonical_path)
|
|
493
|
+
if target.exists():
|
|
494
|
+
# Legacy sources may not yet declare organization identity. The
|
|
495
|
+
# authorized incoming document supplies the active organization
|
|
496
|
+
# for this one-time typed migration; ordinary reads remain strict.
|
|
497
|
+
existing = parse_canonical_markdown(
|
|
498
|
+
target.read_text(encoding="utf-8"),
|
|
499
|
+
canonical_path=artifact.canonical_path,
|
|
500
|
+
default_org_id=artifact.org_id,
|
|
501
|
+
)
|
|
502
|
+
if existing.artifact.artifact_id != artifact.artifact_id:
|
|
503
|
+
raise ArtifactConflictError("artifact path belongs to a different stable id")
|
|
504
|
+
if artifact.artifact_type not in self.mutable_types:
|
|
505
|
+
if existing.artifact.content_hash == artifact.content_hash:
|
|
506
|
+
# An explicit writeback of a legacy document is its safe,
|
|
507
|
+
# typed migration point. Preserve ritual-specific fields.
|
|
508
|
+
if document.migrated_from is None:
|
|
509
|
+
return target
|
|
510
|
+
else:
|
|
511
|
+
raise ArtifactConflictError(f"{artifact.artifact_type} artifacts are append-only")
|
|
512
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
513
|
+
temporary = target.with_name(f".{target.name}.{os.getpid()}.tmp")
|
|
514
|
+
temporary.write_text(render_canonical_markdown(replace(document, artifact=artifact)), encoding="utf-8")
|
|
515
|
+
temporary.replace(target)
|
|
516
|
+
return target
|
|
517
|
+
|
|
518
|
+
def read(self, canonical_path: str) -> CanonicalDocument:
|
|
519
|
+
target = self._target(canonical_path)
|
|
520
|
+
return parse_canonical_markdown(
|
|
521
|
+
target.read_text(encoding="utf-8"),
|
|
522
|
+
canonical_path=canonical_relative_path(canonical_path),
|
|
523
|
+
)
|