create-egregore 0.21.6 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +58 -6
- package/assets/egregore-launcher.sh +15 -2
- package/lib/claude-runtime.js +2 -0
- package/lib/codex-runtime.js +2 -0
- package/lib/setup.js +20 -3
- package/package.json +2 -2
- package/runtime/claude/.claude/context/commit-format.md +7 -4
- package/runtime/claude/.claude/context/handoff-claiming.md +32 -0
- package/runtime/claude/.claude/context/notification-consent.md +12 -9
- package/runtime/claude/.claude/hooks/boundary-check.sh +14 -331
- package/runtime/claude/.claude/hooks/memory-read-guard.sh +9 -1
- package/runtime/claude/.claude/rules/voice-bedrock.md +6 -2
- package/runtime/claude/.claude/settings.json +1 -1
- package/runtime/claude/.claude/skills/commit/SKILL.md +5 -3
- package/runtime/claude/.claude/skills/dark-mode/SKILL.md +48 -369
- package/runtime/claude/.claude/skills/records/SKILL.md +158 -0
- package/runtime/claude/.claude/skills/reflect/SKILL.md +50 -21
- package/runtime/claude/.claude/skills/review-pr/SKILL.md +4 -2
- package/runtime/claude/.claude/skills/save/SKILL.md +9 -3
- package/runtime/claude/.claude/skills/summon/SKILL.md +138 -207
- package/runtime/claude/.claude/skills/tui-design/SKILL.md +1 -1
- package/runtime/claude/.claude/skills/update/SKILL.md +1 -1
- package/runtime/claude/.claude/skills/view/SKILL.md +52 -16
- package/runtime/claude/.claude/skills/view/compose-scaffold.html +19 -13
- package/runtime/claude/CLAUDE.md +59 -95
- package/runtime/claude/bin/agent.sh +231 -7
- package/runtime/claude/bin/api-call.sh +44 -23
- package/runtime/claude/bin/artifact-register.sh +7 -1
- package/runtime/claude/bin/artifact-writeback.sh +1 -1
- package/runtime/claude/bin/artifacts.sh +66 -265
- package/runtime/claude/bin/attendant.sh +4 -1
- package/runtime/claude/bin/boundary-guard.cjs +1848 -0
- package/runtime/claude/bin/boundary-hook.sh +41 -0
- package/runtime/claude/bin/boundary.sh +15 -155
- package/runtime/claude/bin/capture-run.sh +32 -2
- package/runtime/claude/bin/codex-render-spec.mjs +24 -46
- package/runtime/claude/bin/codex-session-start.sh +19 -0
- package/runtime/claude/bin/codex-skill-render.mjs +18 -0
- package/runtime/claude/bin/config-get.sh +10 -1
- package/runtime/claude/bin/connect-refresh.sh +16 -0
- package/runtime/claude/bin/corpus_map.py +5 -0
- package/runtime/claude/bin/data/capabilities/claude.json +1480 -0
- package/runtime/claude/bin/genui.sh +19 -0
- package/runtime/claude/bin/graph-batch.sh +1 -1
- package/runtime/claude/bin/graph-op.sh +14 -0
- package/runtime/claude/bin/graph.sh +1 -1
- package/runtime/claude/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/claude/bin/handoff-run.sh +11 -6
- package/runtime/claude/bin/ingest-item.sh +1 -1
- package/runtime/claude/bin/ingest-surface.html +22 -4
- package/runtime/claude/bin/issue.sh +1 -1
- package/runtime/claude/bin/knowledge.sh +1 -1
- package/runtime/claude/bin/lib/card-delivery.sh +329 -0
- package/runtime/claude/bin/lib/config.sh +148 -3
- package/runtime/claude/bin/lib/context.sh +38 -67
- package/runtime/claude/bin/lib/genui-entry.py +36 -0
- package/runtime/claude/bin/lib/git-message.sh +25 -6
- package/runtime/claude/bin/lib/git-sync.sh +12 -4
- package/runtime/claude/bin/lib/greeting.sh +255 -28
- package/runtime/claude/bin/lib/handoff_meta.py +294 -0
- package/runtime/claude/bin/lib/health-footer.sh +83 -2
- package/runtime/claude/bin/lib/identity.sh +31 -10
- package/runtime/claude/bin/lib/instance-registry.sh +71 -0
- package/runtime/claude/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/claude/bin/lib/metrics.sh +121 -8
- package/runtime/claude/bin/lib/notices.sh +36 -0
- package/runtime/claude/bin/lib/record_outcomes.py +729 -0
- package/runtime/claude/bin/lib/reflexes.py +267 -0
- package/runtime/claude/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/claude/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/claude/bin/lib/session_record.py +3747 -0
- package/runtime/claude/bin/lib/session_trail.py +1983 -0
- package/runtime/claude/bin/lib/settings-drift.sh +8 -5
- package/runtime/claude/bin/lib/startup-restart.sh +41 -0
- package/runtime/claude/bin/lib/worktree-links.sh +92 -0
- package/runtime/claude/bin/memory-push.sh +6 -0
- package/runtime/claude/bin/merge-gate.sh +498 -0
- package/runtime/claude/bin/notify.sh +5 -2
- package/runtime/claude/bin/page-access.sh +103 -0
- package/runtime/claude/bin/person.sh +13 -2
- package/runtime/claude/bin/pi-render-spec.mjs +0 -1
- package/runtime/claude/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/claude/bin/post-update.sh +181 -0
- package/runtime/claude/bin/prime-render-spec.mjs +0 -1
- package/runtime/claude/bin/publish-artifact.sh +89 -42
- package/runtime/claude/bin/qa-inventory.sh +2 -2
- package/runtime/claude/bin/quest.sh +1 -1
- package/runtime/claude/bin/records.sh +11 -0
- package/runtime/claude/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/claude/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/claude/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/claude/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/claude/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/claude/bin/reflexes/30-cost.py +116 -0
- package/runtime/claude/bin/reflexes/40-shells.py +51 -0
- package/runtime/claude/bin/reflexes/README.md +61 -0
- package/runtime/claude/bin/reflexes.sh +7 -0
- package/runtime/claude/bin/runtime-identity.sh +140 -42
- package/runtime/claude/bin/screenshot.sh +77 -0
- package/runtime/claude/bin/scroll.sh +1 -1
- package/runtime/claude/bin/search.sh +31 -5
- package/runtime/claude/bin/session-autosave.sh +6 -3
- package/runtime/claude/bin/session-id.sh +147 -0
- package/runtime/claude/bin/session-log.sh +19 -0
- package/runtime/claude/bin/session-record.sh +26 -0
- package/runtime/claude/bin/session-start.sh +121 -151
- package/runtime/claude/bin/settings.sh +13 -8
- package/runtime/claude/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/claude/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/claude/bin/spirit-discovery.sh +4 -0
- package/runtime/claude/bin/startup-check.sh +1 -1
- package/runtime/claude/bin/telemetry.sh +11 -4
- package/runtime/claude/bin/test-baseline.sh +33 -5
- package/runtime/claude/bin/todo.sh +1 -1
- package/runtime/claude/bin/worktree-create.sh +15 -45
- package/runtime/claude/bin/worktree.sh +6 -0
- package/runtime/claude/docs/spirit-discovery.md +96 -0
- package/runtime/claude/egregore_runtime/__init__.py +18 -12
- package/runtime/claude/egregore_runtime/access.py +608 -0
- package/runtime/claude/egregore_runtime/access_cli.py +190 -0
- package/runtime/claude/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/claude/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/claude/egregore_runtime/admin_entry.py +56 -0
- package/runtime/claude/egregore_runtime/artifacts.py +222 -16
- package/runtime/claude/egregore_runtime/contracts.py +27 -0
- package/runtime/claude/egregore_runtime/eligibility.py +5 -12
- package/runtime/claude/egregore_runtime/episode_binding.py +16 -5
- package/runtime/claude/egregore_runtime/github_issues.py +2 -2
- package/runtime/claude/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/claude/egregore_runtime/harness_cli.py +674 -149
- package/runtime/claude/egregore_runtime/held.py +2436 -0
- package/runtime/claude/egregore_runtime/hosted_api.py +137 -0
- package/runtime/claude/egregore_runtime/identity.py +196 -65
- package/runtime/claude/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/claude/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/claude/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/claude/egregore_runtime/installation.py +119 -0
- package/runtime/claude/egregore_runtime/investigation.py +64 -41
- package/runtime/claude/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/claude/egregore_runtime/issue_cli.py +51 -32
- package/runtime/claude/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/claude/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/claude/egregore_runtime/issues.py +12 -6
- package/runtime/claude/egregore_runtime/knowledge.py +331 -5
- package/runtime/claude/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/claude/egregore_runtime/lifecycle.py +88 -22
- package/runtime/claude/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/claude/egregore_runtime/line_ids.py +10 -0
- package/runtime/claude/egregore_runtime/memory_push.py +349 -0
- package/runtime/claude/egregore_runtime/observe.py +64 -25
- package/runtime/claude/egregore_runtime/observers.py +19 -0
- package/runtime/claude/egregore_runtime/optional_modules.py +13 -0
- package/runtime/claude/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/claude/egregore_runtime/policy.py +651 -24
- package/runtime/claude/egregore_runtime/private_result.py +2 -1
- package/runtime/claude/egregore_runtime/projection.py +946 -0
- package/runtime/claude/egregore_runtime/quest_cli.py +20 -23
- package/runtime/claude/egregore_runtime/question_cli.py +15 -11
- package/runtime/claude/egregore_runtime/questions.py +10 -10
- package/runtime/claude/egregore_runtime/quests.py +10 -10
- package/runtime/claude/egregore_runtime/record_publish.py +548 -0
- package/runtime/claude/egregore_runtime/record_subject.py +564 -0
- package/runtime/claude/egregore_runtime/research_ingest.py +2 -4
- package/runtime/claude/egregore_runtime/runtime.py +169 -60
- package/runtime/claude/egregore_runtime/sandbox.py +194 -0
- package/runtime/claude/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/claude/egregore_runtime/scrolls.py +23 -15
- package/runtime/claude/egregore_runtime/shadow.py +262 -0
- package/runtime/claude/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/claude/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/claude/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/claude/egregore_runtime/status.py +101 -44
- package/runtime/claude/egregore_runtime/status_cli.py +13 -5
- package/runtime/claude/egregore_runtime/sync.py +39 -21
- package/runtime/claude/egregore_runtime/taste.py +934 -0
- package/runtime/claude/egregore_runtime/taste_cli.py +242 -0
- package/runtime/claude/egregore_runtime/taste_rules.py +91 -0
- package/runtime/claude/egregore_runtime/telemetry.py +26 -3
- package/runtime/claude/egregore_runtime/todo_cli.py +15 -12
- package/runtime/claude/egregore_runtime/todos.py +3 -2
- package/runtime/claude/egregore_runtime/upgrade.py +85 -58
- package/runtime/claude/egregore_runtime/writeback.py +330 -24
- package/runtime/claude/manifest.json +65 -5
- package/runtime/codex/.claude/context/handoff-claiming.md +32 -0
- package/runtime/codex/.claude/context/notification-consent.md +12 -9
- package/runtime/codex/.claude/skills/view/SKILL.md +438 -0
- package/runtime/codex/.codex/hooks.json +11 -0
- package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/records/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/codex/.codex/spec-manifest.json +13 -19
- package/runtime/codex/AGENTS.md +54 -105
- package/runtime/codex/bin/agent.sh +231 -7
- package/runtime/codex/bin/api-call.sh +44 -23
- package/runtime/codex/bin/artifact-register.sh +7 -1
- package/runtime/codex/bin/artifact-writeback.sh +1 -1
- package/runtime/codex/bin/artifacts.sh +66 -265
- package/runtime/codex/bin/attendant.sh +4 -1
- package/runtime/codex/bin/boundary-guard.cjs +1848 -0
- package/runtime/codex/bin/boundary-hook.sh +41 -0
- package/runtime/codex/bin/capture-run.sh +32 -2
- package/runtime/codex/bin/codex-render-spec.mjs +24 -46
- package/runtime/codex/bin/codex-session-start.sh +19 -0
- package/runtime/codex/bin/codex-skill-render.mjs +18 -0
- package/runtime/codex/bin/config-get.sh +10 -1
- package/runtime/codex/bin/connect-refresh.sh +16 -0
- package/runtime/codex/bin/corpus_map.py +5 -0
- package/runtime/codex/bin/data/capabilities/codex.json +1458 -0
- package/runtime/codex/bin/genui.sh +19 -0
- package/runtime/codex/bin/graph-batch.sh +1 -1
- package/runtime/codex/bin/graph-op.sh +14 -0
- package/runtime/codex/bin/graph.sh +1 -1
- package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/codex/bin/handoff-run.sh +11 -6
- package/runtime/codex/bin/ingest-item.sh +1 -1
- package/runtime/codex/bin/ingest-surface.html +22 -4
- package/runtime/codex/bin/issue.sh +1 -1
- package/runtime/codex/bin/knowledge.sh +1 -1
- package/runtime/codex/bin/lib/card-delivery.sh +329 -0
- package/runtime/codex/bin/lib/config.sh +148 -3
- package/runtime/codex/bin/lib/context.sh +38 -67
- package/runtime/codex/bin/lib/genui-entry.py +36 -0
- package/runtime/codex/bin/lib/git-message.sh +25 -6
- package/runtime/codex/bin/lib/git-sync.sh +12 -4
- package/runtime/codex/bin/lib/greeting.sh +255 -28
- package/runtime/codex/bin/lib/handoff_meta.py +294 -0
- package/runtime/codex/bin/lib/health-footer.sh +83 -2
- package/runtime/codex/bin/lib/identity.sh +31 -10
- package/runtime/codex/bin/lib/instance-registry.sh +71 -0
- package/runtime/codex/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/codex/bin/lib/metrics.sh +121 -8
- package/runtime/codex/bin/lib/notices.sh +36 -0
- package/runtime/codex/bin/lib/record_outcomes.py +729 -0
- package/runtime/codex/bin/lib/reflexes.py +267 -0
- package/runtime/codex/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/codex/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/codex/bin/lib/session_record.py +3747 -0
- package/runtime/codex/bin/lib/session_trail.py +1983 -0
- package/runtime/codex/bin/lib/settings-drift.sh +8 -5
- package/runtime/codex/bin/lib/startup-restart.sh +41 -0
- package/runtime/codex/bin/lib/worktree-links.sh +92 -0
- package/runtime/codex/bin/memory-push.sh +6 -0
- package/runtime/codex/bin/merge-gate.sh +498 -0
- package/runtime/codex/bin/notify.sh +5 -2
- package/runtime/codex/bin/page-access.sh +103 -0
- package/runtime/codex/bin/person.sh +13 -2
- package/runtime/codex/bin/pi-render-spec.mjs +0 -1
- package/runtime/codex/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/codex/bin/post-update.sh +181 -0
- package/runtime/codex/bin/prime-render-spec.mjs +0 -1
- package/runtime/codex/bin/publish-artifact.sh +89 -42
- package/runtime/codex/bin/qa-inventory.sh +2 -2
- package/runtime/codex/bin/quest.sh +1 -1
- package/runtime/codex/bin/records.sh +11 -0
- package/runtime/codex/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/codex/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/codex/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/codex/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/codex/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/codex/bin/reflexes/30-cost.py +116 -0
- package/runtime/codex/bin/reflexes/40-shells.py +51 -0
- package/runtime/codex/bin/reflexes/README.md +61 -0
- package/runtime/codex/bin/reflexes.sh +7 -0
- package/runtime/codex/bin/runtime-identity.sh +140 -42
- package/runtime/codex/bin/screenshot.sh +77 -0
- package/runtime/codex/bin/scroll.sh +1 -1
- package/runtime/codex/bin/search.sh +31 -5
- package/runtime/codex/bin/session-autosave.sh +6 -3
- package/runtime/codex/bin/session-id.sh +147 -0
- package/runtime/codex/bin/session-log.sh +19 -0
- package/runtime/codex/bin/session-record.sh +26 -0
- package/runtime/codex/bin/settings.sh +13 -8
- package/runtime/codex/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/codex/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/codex/bin/spirit-discovery.sh +4 -0
- package/runtime/codex/bin/startup-check.sh +1 -1
- package/runtime/codex/bin/telemetry.sh +11 -4
- package/runtime/codex/bin/test-baseline.sh +33 -5
- package/runtime/codex/bin/todo.sh +1 -1
- package/runtime/codex/bin/worktree-create.sh +15 -45
- package/runtime/codex/bin/worktree.sh +6 -0
- package/runtime/codex/docs/spirit-discovery.md +96 -0
- package/runtime/codex/egregore_runtime/__init__.py +18 -12
- package/runtime/codex/egregore_runtime/access.py +608 -0
- package/runtime/codex/egregore_runtime/access_cli.py +190 -0
- package/runtime/codex/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/codex/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/codex/egregore_runtime/admin_entry.py +56 -0
- package/runtime/codex/egregore_runtime/artifacts.py +222 -16
- package/runtime/codex/egregore_runtime/contracts.py +27 -0
- package/runtime/codex/egregore_runtime/eligibility.py +5 -12
- package/runtime/codex/egregore_runtime/episode_binding.py +16 -5
- package/runtime/codex/egregore_runtime/github_issues.py +2 -2
- package/runtime/codex/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/codex/egregore_runtime/harness_cli.py +674 -149
- package/runtime/codex/egregore_runtime/held.py +2436 -0
- package/runtime/codex/egregore_runtime/hosted_api.py +137 -0
- package/runtime/codex/egregore_runtime/identity.py +196 -65
- package/runtime/codex/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/codex/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/codex/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/codex/egregore_runtime/installation.py +119 -0
- package/runtime/codex/egregore_runtime/investigation.py +64 -41
- package/runtime/codex/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/codex/egregore_runtime/issue_cli.py +51 -32
- package/runtime/codex/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/codex/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/codex/egregore_runtime/issues.py +12 -6
- package/runtime/codex/egregore_runtime/knowledge.py +331 -5
- package/runtime/codex/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/codex/egregore_runtime/lifecycle.py +88 -22
- package/runtime/codex/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/codex/egregore_runtime/line_ids.py +10 -0
- package/runtime/codex/egregore_runtime/memory_push.py +349 -0
- package/runtime/codex/egregore_runtime/observe.py +64 -25
- package/runtime/codex/egregore_runtime/observers.py +19 -0
- package/runtime/codex/egregore_runtime/optional_modules.py +13 -0
- package/runtime/codex/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/codex/egregore_runtime/policy.py +651 -24
- package/runtime/codex/egregore_runtime/private_result.py +2 -1
- package/runtime/codex/egregore_runtime/projection.py +946 -0
- package/runtime/codex/egregore_runtime/quest_cli.py +20 -23
- package/runtime/codex/egregore_runtime/question_cli.py +15 -11
- package/runtime/codex/egregore_runtime/questions.py +10 -10
- package/runtime/codex/egregore_runtime/quests.py +10 -10
- package/runtime/codex/egregore_runtime/record_publish.py +548 -0
- package/runtime/codex/egregore_runtime/record_subject.py +564 -0
- package/runtime/codex/egregore_runtime/research_ingest.py +2 -4
- package/runtime/codex/egregore_runtime/runtime.py +169 -60
- package/runtime/codex/egregore_runtime/sandbox.py +194 -0
- package/runtime/codex/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/codex/egregore_runtime/scrolls.py +23 -15
- package/runtime/codex/egregore_runtime/shadow.py +262 -0
- package/runtime/codex/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/codex/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/codex/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/codex/egregore_runtime/status.py +101 -44
- package/runtime/codex/egregore_runtime/status_cli.py +13 -5
- package/runtime/codex/egregore_runtime/sync.py +39 -21
- package/runtime/codex/egregore_runtime/taste.py +934 -0
- package/runtime/codex/egregore_runtime/taste_cli.py +242 -0
- package/runtime/codex/egregore_runtime/taste_rules.py +91 -0
- package/runtime/codex/egregore_runtime/telemetry.py +26 -3
- package/runtime/codex/egregore_runtime/todo_cli.py +15 -12
- package/runtime/codex/egregore_runtime/todos.py +3 -2
- package/runtime/codex/egregore_runtime/upgrade.py +85 -58
- package/runtime/codex/egregore_runtime/writeback.py +330 -24
- package/runtime/codex/manifest.json +66 -5
- package/runtime/pi/.claude/context/handoff-claiming.md +32 -0
- package/runtime/pi/.claude/context/notification-consent.md +12 -9
- package/runtime/pi/.claude/skills/view/SKILL.md +438 -0
- package/runtime/pi/.codex/hooks.json +11 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/records/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/pi/.codex/spec-manifest.json +13 -19
- package/runtime/pi/.pi/APPEND_SYSTEM.md +53 -99
- package/runtime/pi/.pi/extensions/egregore.ts +13 -1
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +54 -105
- package/runtime/pi/bin/agent.sh +231 -7
- package/runtime/pi/bin/api-call.sh +44 -23
- package/runtime/pi/bin/artifact-register.sh +7 -1
- package/runtime/pi/bin/artifact-writeback.sh +1 -1
- package/runtime/pi/bin/artifacts.sh +66 -265
- package/runtime/pi/bin/attendant.sh +4 -1
- package/runtime/pi/bin/boundary-guard.cjs +1848 -0
- package/runtime/pi/bin/boundary-hook.sh +41 -0
- package/runtime/pi/bin/capture-run.sh +32 -2
- package/runtime/pi/bin/codex-render-spec.mjs +24 -46
- package/runtime/pi/bin/codex-session-start.sh +19 -0
- package/runtime/pi/bin/codex-skill-render.mjs +18 -0
- package/runtime/pi/bin/config-get.sh +10 -1
- package/runtime/pi/bin/connect-refresh.sh +16 -0
- package/runtime/pi/bin/corpus_map.py +5 -0
- package/runtime/pi/bin/data/capabilities/pi.json +1458 -0
- package/runtime/pi/bin/genui.sh +19 -0
- package/runtime/pi/bin/graph-batch.sh +1 -1
- package/runtime/pi/bin/graph-op.sh +14 -0
- package/runtime/pi/bin/graph.sh +1 -1
- package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/pi/bin/handoff-run.sh +11 -6
- package/runtime/pi/bin/ingest-item.sh +1 -1
- package/runtime/pi/bin/ingest-surface.html +22 -4
- package/runtime/pi/bin/issue.sh +1 -1
- package/runtime/pi/bin/knowledge.sh +1 -1
- package/runtime/pi/bin/lib/card-delivery.sh +329 -0
- package/runtime/pi/bin/lib/config.sh +148 -3
- package/runtime/pi/bin/lib/context.sh +38 -67
- package/runtime/pi/bin/lib/genui-entry.py +36 -0
- package/runtime/pi/bin/lib/git-message.sh +25 -6
- package/runtime/pi/bin/lib/git-sync.sh +12 -4
- package/runtime/pi/bin/lib/greeting.sh +255 -28
- package/runtime/pi/bin/lib/handoff_meta.py +294 -0
- package/runtime/pi/bin/lib/health-footer.sh +83 -2
- package/runtime/pi/bin/lib/identity.sh +31 -10
- package/runtime/pi/bin/lib/instance-registry.sh +71 -0
- package/runtime/pi/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/pi/bin/lib/metrics.sh +121 -8
- package/runtime/pi/bin/lib/notices.sh +36 -0
- package/runtime/pi/bin/lib/record_outcomes.py +729 -0
- package/runtime/pi/bin/lib/reflexes.py +267 -0
- package/runtime/pi/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/pi/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/pi/bin/lib/session_record.py +3747 -0
- package/runtime/pi/bin/lib/session_trail.py +1983 -0
- package/runtime/pi/bin/lib/settings-drift.sh +8 -5
- package/runtime/pi/bin/lib/startup-restart.sh +41 -0
- package/runtime/pi/bin/lib/worktree-links.sh +92 -0
- package/runtime/pi/bin/memory-push.sh +6 -0
- package/runtime/pi/bin/merge-gate.sh +498 -0
- package/runtime/pi/bin/notify.sh +5 -2
- package/runtime/pi/bin/page-access.sh +103 -0
- package/runtime/pi/bin/person.sh +13 -2
- package/runtime/pi/bin/pi-branch-policy.mjs +37 -0
- package/runtime/pi/bin/pi-observe-context.mjs +97 -4
- package/runtime/pi/bin/pi-render-spec.mjs +0 -1
- package/runtime/pi/bin/pi-session-start.sh +4 -1
- package/runtime/pi/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/pi/bin/post-update.sh +181 -0
- package/runtime/pi/bin/prime-render-spec.mjs +0 -1
- package/runtime/pi/bin/publish-artifact.sh +89 -42
- package/runtime/pi/bin/qa-inventory.sh +2 -2
- package/runtime/pi/bin/quest.sh +1 -1
- package/runtime/pi/bin/records.sh +11 -0
- package/runtime/pi/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/pi/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/pi/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/pi/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/pi/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/pi/bin/reflexes/30-cost.py +116 -0
- package/runtime/pi/bin/reflexes/40-shells.py +51 -0
- package/runtime/pi/bin/reflexes/README.md +61 -0
- package/runtime/pi/bin/reflexes.sh +7 -0
- package/runtime/pi/bin/runtime-identity.sh +140 -42
- package/runtime/pi/bin/screenshot.sh +77 -0
- package/runtime/pi/bin/scroll.sh +1 -1
- package/runtime/pi/bin/search.sh +31 -5
- package/runtime/pi/bin/session-autosave.sh +6 -3
- package/runtime/pi/bin/session-id.sh +147 -0
- package/runtime/pi/bin/session-log.sh +19 -0
- package/runtime/pi/bin/session-record.sh +26 -0
- package/runtime/pi/bin/settings.sh +13 -8
- package/runtime/pi/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/pi/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/pi/bin/spirit-discovery.sh +4 -0
- package/runtime/pi/bin/startup-check.sh +1 -1
- package/runtime/pi/bin/telemetry.sh +11 -4
- package/runtime/pi/bin/test-baseline.sh +33 -5
- package/runtime/pi/bin/todo.sh +1 -1
- package/runtime/pi/bin/worktree-create.sh +15 -45
- package/runtime/pi/bin/worktree.sh +6 -0
- package/runtime/pi/docs/spirit-discovery.md +96 -0
- package/runtime/pi/egregore_runtime/__init__.py +18 -12
- package/runtime/pi/egregore_runtime/access.py +608 -0
- package/runtime/pi/egregore_runtime/access_cli.py +190 -0
- package/runtime/pi/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/pi/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/pi/egregore_runtime/admin_entry.py +56 -0
- package/runtime/pi/egregore_runtime/artifacts.py +222 -16
- package/runtime/pi/egregore_runtime/contracts.py +27 -0
- package/runtime/pi/egregore_runtime/eligibility.py +5 -12
- package/runtime/pi/egregore_runtime/episode_binding.py +16 -5
- package/runtime/pi/egregore_runtime/github_issues.py +2 -2
- package/runtime/pi/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/pi/egregore_runtime/harness_cli.py +674 -149
- package/runtime/pi/egregore_runtime/held.py +2436 -0
- package/runtime/pi/egregore_runtime/hosted_api.py +137 -0
- package/runtime/pi/egregore_runtime/identity.py +196 -65
- package/runtime/pi/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/pi/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/pi/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/pi/egregore_runtime/installation.py +119 -0
- package/runtime/pi/egregore_runtime/investigation.py +64 -41
- package/runtime/pi/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/pi/egregore_runtime/issue_cli.py +51 -32
- package/runtime/pi/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/pi/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/pi/egregore_runtime/issues.py +12 -6
- package/runtime/pi/egregore_runtime/knowledge.py +331 -5
- package/runtime/pi/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/pi/egregore_runtime/lifecycle.py +88 -22
- package/runtime/pi/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/pi/egregore_runtime/line_ids.py +10 -0
- package/runtime/pi/egregore_runtime/memory_push.py +349 -0
- package/runtime/pi/egregore_runtime/observe.py +64 -25
- package/runtime/pi/egregore_runtime/observers.py +19 -0
- package/runtime/pi/egregore_runtime/optional_modules.py +13 -0
- package/runtime/pi/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/pi/egregore_runtime/policy.py +651 -24
- package/runtime/pi/egregore_runtime/private_result.py +2 -1
- package/runtime/pi/egregore_runtime/projection.py +946 -0
- package/runtime/pi/egregore_runtime/quest_cli.py +20 -23
- package/runtime/pi/egregore_runtime/question_cli.py +15 -11
- package/runtime/pi/egregore_runtime/questions.py +10 -10
- package/runtime/pi/egregore_runtime/quests.py +10 -10
- package/runtime/pi/egregore_runtime/record_publish.py +548 -0
- package/runtime/pi/egregore_runtime/record_subject.py +564 -0
- package/runtime/pi/egregore_runtime/research_ingest.py +2 -4
- package/runtime/pi/egregore_runtime/runtime.py +169 -60
- package/runtime/pi/egregore_runtime/sandbox.py +194 -0
- package/runtime/pi/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/pi/egregore_runtime/scrolls.py +23 -15
- package/runtime/pi/egregore_runtime/shadow.py +262 -0
- package/runtime/pi/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/pi/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/pi/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/pi/egregore_runtime/status.py +101 -44
- package/runtime/pi/egregore_runtime/status_cli.py +13 -5
- package/runtime/pi/egregore_runtime/sync.py +39 -21
- package/runtime/pi/egregore_runtime/taste.py +934 -0
- package/runtime/pi/egregore_runtime/taste_cli.py +242 -0
- package/runtime/pi/egregore_runtime/taste_rules.py +91 -0
- package/runtime/pi/egregore_runtime/telemetry.py +26 -3
- package/runtime/pi/egregore_runtime/todo_cli.py +15 -12
- package/runtime/pi/egregore_runtime/todos.py +3 -2
- package/runtime/pi/egregore_runtime/upgrade.py +85 -58
- package/runtime/pi/egregore_runtime/writeback.py +330 -24
- package/runtime/pi/manifest.json +66 -5
- package/runtime/prime/.claude/context/handoff-claiming.md +32 -0
- package/runtime/prime/.claude/context/notification-consent.md +12 -9
- package/runtime/prime/.claude/skills/view/SKILL.md +438 -0
- package/runtime/prime/.codex/hooks.json +11 -0
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/records/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +19 -13
- package/runtime/prime/.codex/spec-manifest.json +13 -19
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +53 -99
- package/runtime/prime/.prime/agent/extensions/egregore.ts +14 -1
- package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +14 -6
- package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +66 -1
- package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
- package/runtime/prime/AGENTS.md +54 -105
- package/runtime/prime/bin/agent.sh +231 -7
- package/runtime/prime/bin/api-call.sh +44 -23
- package/runtime/prime/bin/artifact-register.sh +7 -1
- package/runtime/prime/bin/artifact-writeback.sh +1 -1
- package/runtime/prime/bin/artifacts.sh +66 -265
- package/runtime/prime/bin/attendant.sh +4 -1
- package/runtime/prime/bin/boundary-guard.cjs +1848 -0
- package/runtime/prime/bin/boundary-hook.sh +41 -0
- package/runtime/prime/bin/capture-run.sh +32 -2
- package/runtime/prime/bin/codex-render-spec.mjs +24 -46
- package/runtime/prime/bin/codex-session-start.sh +19 -0
- package/runtime/prime/bin/codex-skill-render.mjs +18 -0
- package/runtime/prime/bin/config-get.sh +10 -1
- package/runtime/prime/bin/connect-refresh.sh +16 -0
- package/runtime/prime/bin/corpus_map.py +5 -0
- package/runtime/prime/bin/data/capabilities/prime.json +1458 -0
- package/runtime/prime/bin/genui.sh +19 -0
- package/runtime/prime/bin/graph-batch.sh +1 -1
- package/runtime/prime/bin/graph-op.sh +14 -0
- package/runtime/prime/bin/graph.sh +1 -1
- package/runtime/prime/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/prime/bin/handoff-run.sh +11 -6
- package/runtime/prime/bin/ingest-item.sh +1 -1
- package/runtime/prime/bin/ingest-surface.html +22 -4
- package/runtime/prime/bin/issue.sh +1 -1
- package/runtime/prime/bin/knowledge.sh +1 -1
- package/runtime/prime/bin/lib/card-delivery.sh +329 -0
- package/runtime/prime/bin/lib/config.sh +148 -3
- package/runtime/prime/bin/lib/context.sh +38 -67
- package/runtime/prime/bin/lib/genui-entry.py +36 -0
- package/runtime/prime/bin/lib/git-message.sh +25 -6
- package/runtime/prime/bin/lib/git-sync.sh +12 -4
- package/runtime/prime/bin/lib/greeting.sh +255 -28
- package/runtime/prime/bin/lib/handoff_meta.py +294 -0
- package/runtime/prime/bin/lib/health-footer.sh +83 -2
- package/runtime/prime/bin/lib/identity.sh +31 -10
- package/runtime/prime/bin/lib/instance-registry.sh +71 -0
- package/runtime/prime/bin/lib/local_ci_receipt.py +249 -0
- package/runtime/prime/bin/lib/metrics.sh +121 -8
- package/runtime/prime/bin/lib/notices.sh +36 -0
- package/runtime/prime/bin/lib/record_outcomes.py +729 -0
- package/runtime/prime/bin/lib/reflexes.py +267 -0
- package/runtime/prime/bin/lib/retrieval_scorecard.py +624 -0
- package/runtime/prime/bin/lib/session-record-prompt.txt +18 -0
- package/runtime/prime/bin/lib/session_record.py +3747 -0
- package/runtime/prime/bin/lib/session_trail.py +1983 -0
- package/runtime/prime/bin/lib/settings-drift.sh +8 -5
- package/runtime/prime/bin/lib/startup-restart.sh +41 -0
- package/runtime/prime/bin/lib/worktree-links.sh +92 -0
- package/runtime/prime/bin/memory-push.sh +6 -0
- package/runtime/prime/bin/merge-gate.sh +498 -0
- package/runtime/prime/bin/notify.sh +5 -2
- package/runtime/prime/bin/page-access.sh +103 -0
- package/runtime/prime/bin/person.sh +13 -2
- package/runtime/prime/bin/pi-branch-policy.mjs +37 -0
- package/runtime/prime/bin/pi-observe-context.mjs +97 -4
- package/runtime/prime/bin/pi-render-spec.mjs +0 -1
- package/runtime/prime/bin/pi-spirit-discovery.mjs +34 -0
- package/runtime/prime/bin/post-update.sh +181 -0
- package/runtime/prime/bin/prime-render-spec.mjs +0 -1
- package/runtime/prime/bin/prime-session-start.sh +3 -1
- package/runtime/prime/bin/publish-artifact.sh +89 -42
- package/runtime/prime/bin/qa-inventory.sh +2 -2
- package/runtime/prime/bin/quest.sh +1 -1
- package/runtime/prime/bin/records.sh +11 -0
- package/runtime/prime/bin/reflexes/05-vitals.sh +9 -0
- package/runtime/prime/bin/reflexes/10-open-items.sh +12 -0
- package/runtime/prime/bin/reflexes/15-retrieval.sh +8 -0
- package/runtime/prime/bin/reflexes/20-nightly.sh +100 -0
- package/runtime/prime/bin/reflexes/20-sandbox.sh +85 -0
- package/runtime/prime/bin/reflexes/30-cost.py +116 -0
- package/runtime/prime/bin/reflexes/40-shells.py +51 -0
- package/runtime/prime/bin/reflexes/README.md +61 -0
- package/runtime/prime/bin/reflexes.sh +7 -0
- package/runtime/prime/bin/runtime-identity.sh +140 -42
- package/runtime/prime/bin/screenshot.sh +77 -0
- package/runtime/prime/bin/scroll.sh +1 -1
- package/runtime/prime/bin/search.sh +31 -5
- package/runtime/prime/bin/session-autosave.sh +6 -3
- package/runtime/prime/bin/session-id.sh +147 -0
- package/runtime/prime/bin/session-log.sh +19 -0
- package/runtime/prime/bin/session-record.sh +26 -0
- package/runtime/prime/bin/settings.sh +13 -8
- package/runtime/prime/bin/spirit-discovery-claude.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-codex.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-pi.sh +4 -0
- package/runtime/prime/bin/spirit-discovery-prime.sh +4 -0
- package/runtime/prime/bin/spirit-discovery.sh +4 -0
- package/runtime/prime/bin/startup-check.sh +1 -1
- package/runtime/prime/bin/telemetry.sh +11 -4
- package/runtime/prime/bin/test-baseline.sh +33 -5
- package/runtime/prime/bin/todo.sh +1 -1
- package/runtime/prime/bin/worktree-create.sh +15 -45
- package/runtime/prime/bin/worktree.sh +6 -0
- package/runtime/prime/docs/spirit-discovery.md +96 -0
- package/runtime/prime/egregore_runtime/__init__.py +18 -12
- package/runtime/prime/egregore_runtime/access.py +608 -0
- package/runtime/prime/egregore_runtime/access_cli.py +190 -0
- package/runtime/prime/egregore_runtime/adapters/invitation.py +5 -3
- package/runtime/prime/egregore_runtime/adapters/qmd.py +521 -47
- package/runtime/prime/egregore_runtime/admin_entry.py +56 -0
- package/runtime/prime/egregore_runtime/artifacts.py +222 -16
- package/runtime/prime/egregore_runtime/contracts.py +27 -0
- package/runtime/prime/egregore_runtime/eligibility.py +5 -12
- package/runtime/prime/egregore_runtime/episode_binding.py +16 -5
- package/runtime/prime/egregore_runtime/github_issues.py +2 -2
- package/runtime/prime/egregore_runtime/handoff_lookup.py +9 -20
- package/runtime/prime/egregore_runtime/harness_cli.py +674 -149
- package/runtime/prime/egregore_runtime/held.py +2436 -0
- package/runtime/prime/egregore_runtime/hosted_api.py +137 -0
- package/runtime/prime/egregore_runtime/identity.py +196 -65
- package/runtime/prime/egregore_runtime/identity_transfer.py +6 -2
- package/runtime/prime/egregore_runtime/ingest_cli.py +2 -2
- package/runtime/prime/egregore_runtime/ingest_index_cli.py +4 -16
- package/runtime/prime/egregore_runtime/installation.py +119 -0
- package/runtime/prime/egregore_runtime/investigation.py +64 -41
- package/runtime/prime/egregore_runtime/investigation_guidance.py +16 -11
- package/runtime/prime/egregore_runtime/issue_cli.py +51 -32
- package/runtime/prime/egregore_runtime/issue_reporting.py +4 -3
- package/runtime/prime/egregore_runtime/issue_triage_service.py +2 -2
- package/runtime/prime/egregore_runtime/issues.py +12 -6
- package/runtime/prime/egregore_runtime/knowledge.py +331 -5
- package/runtime/prime/egregore_runtime/knowledge_cli.py +57 -17
- package/runtime/prime/egregore_runtime/lifecycle.py +88 -22
- package/runtime/prime/egregore_runtime/lifecycle_cli.py +21 -13
- package/runtime/prime/egregore_runtime/line_ids.py +10 -0
- package/runtime/prime/egregore_runtime/memory_push.py +349 -0
- package/runtime/prime/egregore_runtime/observe.py +64 -25
- package/runtime/prime/egregore_runtime/observers.py +19 -0
- package/runtime/prime/egregore_runtime/optional_modules.py +13 -0
- package/runtime/prime/egregore_runtime/outputs_cli.py +321 -0
- package/runtime/prime/egregore_runtime/policy.py +651 -24
- package/runtime/prime/egregore_runtime/private_result.py +2 -1
- package/runtime/prime/egregore_runtime/projection.py +946 -0
- package/runtime/prime/egregore_runtime/quest_cli.py +20 -23
- package/runtime/prime/egregore_runtime/question_cli.py +15 -11
- package/runtime/prime/egregore_runtime/questions.py +10 -10
- package/runtime/prime/egregore_runtime/quests.py +10 -10
- package/runtime/prime/egregore_runtime/record_publish.py +548 -0
- package/runtime/prime/egregore_runtime/record_subject.py +564 -0
- package/runtime/prime/egregore_runtime/research_ingest.py +2 -4
- package/runtime/prime/egregore_runtime/runtime.py +169 -60
- package/runtime/prime/egregore_runtime/sandbox.py +194 -0
- package/runtime/prime/egregore_runtime/scroll_cli.py +22 -32
- package/runtime/prime/egregore_runtime/scrolls.py +23 -15
- package/runtime/prime/egregore_runtime/shadow.py +262 -0
- package/runtime/prime/egregore_runtime/spirit_discovery.py +487 -0
- package/runtime/prime/egregore_runtime/spirit_discovery_cli.py +25 -0
- package/runtime/prime/egregore_runtime/spirit_discovery_transport.py +18 -0
- package/runtime/prime/egregore_runtime/status.py +101 -44
- package/runtime/prime/egregore_runtime/status_cli.py +13 -5
- package/runtime/prime/egregore_runtime/sync.py +39 -21
- package/runtime/prime/egregore_runtime/taste.py +934 -0
- package/runtime/prime/egregore_runtime/taste_cli.py +242 -0
- package/runtime/prime/egregore_runtime/taste_rules.py +91 -0
- package/runtime/prime/egregore_runtime/telemetry.py +26 -3
- package/runtime/prime/egregore_runtime/todo_cli.py +15 -12
- package/runtime/prime/egregore_runtime/todos.py +3 -2
- package/runtime/prime/egregore_runtime/upgrade.py +85 -58
- package/runtime/prime/egregore_runtime/writeback.py +330 -24
- package/runtime/prime/manifest.json +66 -5
- package/runtime/claude/bin/thread.sh +0 -9
- package/runtime/claude/egregore_runtime/admin_gate.py +0 -102
- package/runtime/claude/egregore_runtime/thread_cli.py +0 -173
- package/runtime/claude/egregore_runtime/threads.py +0 -521
- package/runtime/codex/bin/thread.sh +0 -9
- package/runtime/codex/egregore_runtime/admin_gate.py +0 -102
- package/runtime/codex/egregore_runtime/thread_cli.py +0 -173
- package/runtime/codex/egregore_runtime/threads.py +0 -521
- package/runtime/pi/bin/thread.sh +0 -9
- package/runtime/pi/egregore_runtime/admin_gate.py +0 -102
- package/runtime/pi/egregore_runtime/thread_cli.py +0 -173
- package/runtime/pi/egregore_runtime/threads.py +0 -521
- package/runtime/prime/bin/thread.sh +0 -9
- package/runtime/prime/egregore_runtime/admin_gate.py +0 -102
- package/runtime/prime/egregore_runtime/thread_cli.py +0 -173
- package/runtime/prime/egregore_runtime/threads.py +0 -521
|
@@ -28,7 +28,7 @@ from .artifacts import (
|
|
|
28
28
|
)
|
|
29
29
|
from .contracts import ActorContext, ArtifactRelationship, CanonicalArtifact, Permission, WritebackEvent, WritebackStatus
|
|
30
30
|
from .errors import AuthorizationDenied
|
|
31
|
-
from .policy import
|
|
31
|
+
from .policy import check, action_for
|
|
32
32
|
from .runtime import EgregoreRuntime
|
|
33
33
|
|
|
34
34
|
|
|
@@ -253,27 +253,34 @@ class CanonicalScrollService:
|
|
|
253
253
|
def _path(self, slug: str) -> Path:
|
|
254
254
|
return self.memory_root / "scrolls" / f"{_slug(slug)}.md"
|
|
255
255
|
|
|
256
|
-
def _authorize(self, actor: ActorContext, permission: Permission, resource: str, path: str) ->
|
|
256
|
+
def _authorize(self, actor: ActorContext, permission: Permission, resource: str, path: str) -> Path:
|
|
257
257
|
decision = self.runtime.authorize(actor, permission, (resource,))
|
|
258
258
|
if not decision.allowed:
|
|
259
259
|
raise AuthorizationDenied("; ".join(decision.reasons) or f"scroll {permission.value} denied")
|
|
260
|
-
|
|
260
|
+
verdict = check(decision, path, action_for(permission), root=self.memory_root)
|
|
261
|
+
if not verdict.allowed:
|
|
261
262
|
raise AuthorizationDenied("scroll path is outside the actor's authorized scope")
|
|
263
|
+
return verdict.resolved
|
|
262
264
|
|
|
263
|
-
def _source_path(self, value: str | Path) -> Path:
|
|
265
|
+
def _source_path(self, actor: ActorContext, value: str | Path) -> Path:
|
|
264
266
|
path = Path(value)
|
|
265
267
|
if not path.is_absolute():
|
|
266
268
|
path = self.root / path
|
|
267
269
|
resolved = path.resolve()
|
|
268
270
|
if resolved != self.root and self.root not in resolved.parents:
|
|
269
271
|
raise ScrollError("scroll source must remain inside the active Egregore workspace")
|
|
272
|
+
if resolved.is_relative_to(self.memory_root): # a source inside memory is a memory read
|
|
273
|
+
relative = resolved.relative_to(self.memory_root).as_posix()
|
|
274
|
+
verdict = check(self.runtime.authorize(actor, Permission.READ), f"memory/{relative}", root=self.memory_root)
|
|
275
|
+
# Read only the path the predicate decided: a spelling it normalizes (a trailing space) names another file.
|
|
276
|
+
if not verdict.allowed or verdict.resolved != resolved:
|
|
277
|
+
raise AuthorizationDenied("scroll source is outside the actor's read scope")
|
|
270
278
|
return resolved
|
|
271
279
|
|
|
272
280
|
def read(self, actor: ActorContext, slug: str) -> ScrollSnapshot:
|
|
273
281
|
normalized = _slug(slug)
|
|
274
282
|
canonical = self.canonical_path(normalized)
|
|
275
|
-
self._authorize(actor, Permission.READ, f"scroll:{normalized}", canonical)
|
|
276
|
-
path = self._path(normalized)
|
|
283
|
+
path = self._authorize(actor, Permission.READ, f"scroll:{normalized}", canonical)
|
|
277
284
|
if not path.is_file():
|
|
278
285
|
raise ScrollError(f"scroll does not exist: {normalized}")
|
|
279
286
|
return parse_scroll_markdown(
|
|
@@ -353,7 +360,7 @@ class CanonicalScrollService:
|
|
|
353
360
|
self._authorize(actor, Permission.WRITE, f"scroll:{normalized}", canonical)
|
|
354
361
|
if self._path(normalized).exists():
|
|
355
362
|
raise ScrollError(f"scroll already exists: {normalized}")
|
|
356
|
-
html = self._source_path(source_html)
|
|
363
|
+
html = self._source_path(actor, source_html)
|
|
357
364
|
if not html.is_file():
|
|
358
365
|
raise ScrollError("scroll HTML source does not exist")
|
|
359
366
|
if min(review_after_days, attention_ttl_days) < 1:
|
|
@@ -378,8 +385,8 @@ class CanonicalScrollService:
|
|
|
378
385
|
actual_path = event_path.resolve().relative_to(self.memory_root)
|
|
379
386
|
except ValueError as exc:
|
|
380
387
|
raise ScrollError("Scroll creation event escapes canonical memory") from exc
|
|
381
|
-
self._authorize(actor, Permission.READ, event_document.artifact.artifact_id,
|
|
382
|
-
|
|
388
|
+
event_path = self._authorize(actor, Permission.READ, event_document.artifact.artifact_id,
|
|
389
|
+
f"memory/{event_document.artifact.canonical_path}")
|
|
383
390
|
recovering = parse_canonical_markdown(
|
|
384
391
|
event_path.read_text(encoding="utf-8"),
|
|
385
392
|
canonical_path=event_document.artifact.canonical_path,
|
|
@@ -493,12 +500,13 @@ class CanonicalScrollService:
|
|
|
493
500
|
if directory.is_dir():
|
|
494
501
|
for path in sorted(directory.glob("*.md")):
|
|
495
502
|
canonical = path.relative_to(self.memory_root).as_posix()
|
|
496
|
-
|
|
503
|
+
verdict = check(decision, f"memory/{canonical}", "read", root=self.memory_root)
|
|
504
|
+
if not verdict.allowed:
|
|
497
505
|
raise AuthorizationDenied(
|
|
498
506
|
"Scroll event collection is outside the actor's authorized scope"
|
|
499
507
|
)
|
|
500
508
|
document = parse_canonical_markdown(
|
|
501
|
-
|
|
509
|
+
verdict.resolved.read_text(encoding="utf-8"),
|
|
502
510
|
canonical_path=canonical,
|
|
503
511
|
default_org_id=actor.profile.org_id,
|
|
504
512
|
)
|
|
@@ -591,7 +599,7 @@ class CanonicalScrollService:
|
|
|
591
599
|
raise ScrollError("a Scroll version requires its complete rewritten face")
|
|
592
600
|
if source_html is None:
|
|
593
601
|
raise ScrollError("a Scroll version requires its HTML source path")
|
|
594
|
-
html = self._source_path(source_html)
|
|
602
|
+
html = self._source_path(actor, source_html)
|
|
595
603
|
request_face = face_markdown
|
|
596
604
|
request_source = html.relative_to(self.root).as_posix()
|
|
597
605
|
request_digest = self._request_digest(row, request_face, request_source)
|
|
@@ -628,7 +636,7 @@ class CanonicalScrollService:
|
|
|
628
636
|
version_number = current.version
|
|
629
637
|
if kind == "version-published":
|
|
630
638
|
body = face_markdown or body
|
|
631
|
-
source_path = self._source_path(source_html or current.source_html).relative_to(self.root).as_posix()
|
|
639
|
+
source_path = self._source_path(actor, source_html or current.source_html).relative_to(self.root).as_posix()
|
|
632
640
|
version_number = current.version + 1
|
|
633
641
|
fields["version"] = version_number
|
|
634
642
|
fields["source_html"] = source_path
|
|
@@ -726,7 +734,7 @@ class CanonicalScrollService:
|
|
|
726
734
|
) -> ScrollSharePlan:
|
|
727
735
|
current = self.read(actor, slug)
|
|
728
736
|
self._authorize(actor, Permission.SHARE, f"scroll:{current.slug}", self.canonical_path(current.slug))
|
|
729
|
-
html = self._source_path(html_path or current.source_html)
|
|
737
|
+
html = self._source_path(actor, html_path or current.source_html)
|
|
730
738
|
if not html.is_file():
|
|
731
739
|
raise ScrollError("Scroll HTML source does not exist")
|
|
732
740
|
now = self.clock().astimezone(UTC)
|
|
@@ -798,7 +806,7 @@ class CanonicalScrollService:
|
|
|
798
806
|
):
|
|
799
807
|
raise ScrollError("Scroll changed after preview; create and approve a fresh SHARE plan")
|
|
800
808
|
if not recovering:
|
|
801
|
-
html = self._source_path(plan.html_path)
|
|
809
|
+
html = self._source_path(actor, plan.html_path)
|
|
802
810
|
if hashlib.sha256(html.read_bytes()).hexdigest() != plan.html_hash:
|
|
803
811
|
raise ScrollError("Scroll HTML changed after preview; create and approve a fresh SHARE plan")
|
|
804
812
|
url = self.publisher(current, html, plan.description).strip()
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""roster-v1-shadow: serve all-access and count where roster-v1 would hold.
|
|
2
|
+
|
|
3
|
+
Counts are kept by surface (the Runtime module that asked) and held class,
|
|
4
|
+
never by path, title or person, in a private per-user folder (0700, files
|
|
5
|
+
0600, never through a link). A process adds its counts once, when it exits.
|
|
6
|
+
|
|
7
|
+
No read waits for a tree to be built. ``policy.check`` holds a shadow read by
|
|
8
|
+
class 1 alone and hands the rest here (``defer``): the class roster-v1 would
|
|
9
|
+
hold comes from this user's verdict index when it matches the walk in effect.
|
|
10
|
+
Otherwise the read is bound to that walk and judged at exit by a detached
|
|
11
|
+
settler, which builds the tree and adds the count only while memory is still
|
|
12
|
+
as walked. Until it does, the read counts as ``pending``; a read nothing
|
|
13
|
+
could judge counts as ``unjudged``. The counts are complete or say they are
|
|
14
|
+
partial, never silently short.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import atexit
|
|
20
|
+
from collections import Counter
|
|
21
|
+
from functools import lru_cache
|
|
22
|
+
import fcntl
|
|
23
|
+
import hashlib
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
import stat
|
|
28
|
+
import subprocess
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
PENDING, UNJUDGED = "pending", "unjudged" # reads with a settler, and reads nothing could judge
|
|
32
|
+
|
|
33
|
+
_pending: dict[str, Counter] = {} # counts file -> "surface|class", PENDING, UNJUDGED -> reads
|
|
34
|
+
# (counts file, memory root, release, walk digest) -> (path as named, path as resolved, content sha256)
|
|
35
|
+
# -> surface -> reads
|
|
36
|
+
_deferred: dict[tuple[str, str, tuple[str, ...], str], dict[tuple[str, str, str], Counter]] = {}
|
|
37
|
+
_settlers: list[tuple[str, str, tuple[str, ...], str, dict]] = []
|
|
38
|
+
_registered: list = []
|
|
39
|
+
_LATER = object()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _file(memory_root: Path) -> Path:
|
|
43
|
+
key = hashlib.sha256(str(Path(memory_root).resolve()).encode("utf-8")).hexdigest()[:16]
|
|
44
|
+
return Path.home() / ".egregore" / "runtime" / "shadow" / f"{key}.json"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@lru_cache(maxsize=16)
|
|
48
|
+
def _place(memory_root: str, home: str) -> tuple[Path, str]:
|
|
49
|
+
"""A memory root resolved and its counts file under ``home`` ($HOME), once per process: ``defer`` runs on
|
|
50
|
+
every shadow read."""
|
|
51
|
+
return Path(memory_root).resolve(), str(_file(Path(memory_root)))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _private(path: Path, flags: int) -> int:
|
|
55
|
+
"""Open a counts file in this user's private folder, never through a link."""
|
|
56
|
+
if flags & os.O_CREAT:
|
|
57
|
+
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
58
|
+
folder = os.lstat(path.parent)
|
|
59
|
+
if not stat.S_ISDIR(folder.st_mode) or folder.st_uid != os.getuid():
|
|
60
|
+
raise PermissionError(f"{path.parent} is not this user's folder")
|
|
61
|
+
if stat.S_IMODE(folder.st_mode) != 0o700:
|
|
62
|
+
os.chmod(path.parent, 0o700)
|
|
63
|
+
descriptor = os.open(path, flags | os.O_NOFOLLOW, 0o600)
|
|
64
|
+
try:
|
|
65
|
+
if os.fstat(descriptor).st_uid != os.getuid():
|
|
66
|
+
raise PermissionError(f"{path} is not this user's file")
|
|
67
|
+
os.fchmod(descriptor, 0o600)
|
|
68
|
+
except OSError:
|
|
69
|
+
os.close(descriptor)
|
|
70
|
+
raise
|
|
71
|
+
return descriptor
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _surface() -> str:
|
|
75
|
+
frame = sys._getframe(1)
|
|
76
|
+
while frame is not None and frame.f_globals.get("__name__", "").rsplit(".", 1)[-1] in {"shadow", "policy"}:
|
|
77
|
+
frame = frame.f_back
|
|
78
|
+
return (frame.f_globals.get("__name__", "") if frame is not None else "").rsplit(".", 1)[-1] or "unknown"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _register() -> None:
|
|
82
|
+
if not _registered:
|
|
83
|
+
_registered.append(atexit.register(flush, wait=False))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def note(memory_root: Path, held_class: int) -> None:
|
|
87
|
+
_register()
|
|
88
|
+
_pending.setdefault(str(_file(memory_root)), Counter())[f"{_surface()}|{held_class}"] += 1
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _classify(tree, root: Path, relative: str, physical: str, sha: str, deep: bool):
|
|
92
|
+
"""The class roster-v1 would hold for one read, 0 for none. Not ``deep``: only from what the tree already
|
|
93
|
+
judged for these bytes (``Tree.known``), else _LATER. ``deep``: bytes it never judged are read again and judged, and None when
|
|
94
|
+
they have changed since the read or are gone."""
|
|
95
|
+
found = tree.known(relative, sha, physical)
|
|
96
|
+
if found is None and not deep:
|
|
97
|
+
return _LATER
|
|
98
|
+
if found is None:
|
|
99
|
+
try:
|
|
100
|
+
raw = (root / physical).read_bytes()
|
|
101
|
+
except OSError:
|
|
102
|
+
return None
|
|
103
|
+
if hashlib.sha256(raw).hexdigest() != sha:
|
|
104
|
+
return None
|
|
105
|
+
found = tree.classes(relative, raw, physical)
|
|
106
|
+
return next((number for number in found if number != 1), 0)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def defer(memory_root: Path, release, relative: str, physical: str, data: bytes) -> None:
|
|
110
|
+
"""A read served under roster-v1-shadow that class 1 did not hold. The class roster-v1 would hold is taken
|
|
111
|
+
from this user's verdict index when it matches the walk in effect (one walk per FRESH_SECONDS; no document
|
|
112
|
+
is read and nothing is built). Otherwise the read is judged off the read path, against that walk or not at
|
|
113
|
+
all."""
|
|
114
|
+
from . import held
|
|
115
|
+
|
|
116
|
+
_register()
|
|
117
|
+
root, counts = _place(str(memory_root), os.environ.get("HOME", ""))
|
|
118
|
+
release, surface = tuple(sorted(set(release))), _surface()
|
|
119
|
+
read = (relative, physical, hashlib.sha256(data).hexdigest())
|
|
120
|
+
try:
|
|
121
|
+
tree, walk = held.current(root, release, trust_cache=True, build=False)
|
|
122
|
+
would = _classify(tree, root, *read, deep=False) if tree is not None else _LATER
|
|
123
|
+
except Exception: # noqa: BLE001 - a read bound to no walk is counted as unjudged, never dropped
|
|
124
|
+
walk, would = "", _LATER
|
|
125
|
+
if would is _LATER:
|
|
126
|
+
_deferred.setdefault((counts, str(root), release, walk), {}).setdefault(read, Counter())[surface] += 1
|
|
127
|
+
elif would:
|
|
128
|
+
_pending.setdefault(counts, Counter())[f"{surface}|{would}"] += 1
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _reads(reads: dict) -> int:
|
|
132
|
+
return sum(sum(surfaces.values()) for surfaces in reads.values())
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _judge(root: str, release: tuple[str, ...], walk: str, reads: dict) -> Counter:
|
|
136
|
+
"""Counts for reads made against ``walk``: every one unjudged unless memory is still as walked then."""
|
|
137
|
+
from . import held
|
|
138
|
+
|
|
139
|
+
judged, memory = Counter(), Path(root)
|
|
140
|
+
try:
|
|
141
|
+
tree, now = held.current(memory, release, trust_cache=True) if walk and memory.is_dir() else (None, "")
|
|
142
|
+
if tree is None or now != walk:
|
|
143
|
+
raise LookupError("memory is no longer as walked when these reads were served")
|
|
144
|
+
for read, surfaces in reads.items():
|
|
145
|
+
would = _classify(tree, memory, *read, deep=True)
|
|
146
|
+
if would is None:
|
|
147
|
+
judged[UNJUDGED] += sum(surfaces.values())
|
|
148
|
+
elif would:
|
|
149
|
+
for surface, count in surfaces.items():
|
|
150
|
+
judged[f"{surface}|{would}"] += count
|
|
151
|
+
except Exception: # noqa: BLE001 - a read nothing judged is counted as such
|
|
152
|
+
judged = Counter({UNJUDGED: _reads(reads)})
|
|
153
|
+
return judged
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def settle(wait: bool = False) -> None:
|
|
157
|
+
"""Judge the deferred reads against the walk each was served in: now, building that tree, when ``wait``;
|
|
158
|
+
otherwise (at exit) they are marked pending and go to a settler once ``flush`` has written the mark."""
|
|
159
|
+
while _deferred:
|
|
160
|
+
(counts, root, release, walk), reads = _deferred.popitem()
|
|
161
|
+
counter = _pending.setdefault(counts, Counter())
|
|
162
|
+
if wait:
|
|
163
|
+
counter.update(_judge(root, release, walk, reads))
|
|
164
|
+
else:
|
|
165
|
+
counter[PENDING] += _reads(reads)
|
|
166
|
+
_settlers.append((counts, root, release, walk, reads))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _add(path: Path, counter: Counter) -> None:
|
|
170
|
+
try:
|
|
171
|
+
with os.fdopen(_private(path, os.O_RDWR | os.O_CREAT), "r+", encoding="utf-8") as handle:
|
|
172
|
+
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
|
|
173
|
+
handle.seek(0)
|
|
174
|
+
try:
|
|
175
|
+
saved = Counter({key: value for key, value in json.loads(handle.read() or "{}").items()
|
|
176
|
+
if isinstance(value, int)})
|
|
177
|
+
except (ValueError, AttributeError):
|
|
178
|
+
saved = Counter()
|
|
179
|
+
saved.update(counter)
|
|
180
|
+
handle.seek(0)
|
|
181
|
+
handle.truncate()
|
|
182
|
+
json.dump({key: value for key, value in sorted(saved.items()) if value}, handle)
|
|
183
|
+
except OSError:
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _payload(counts: str, root: str, release: tuple[str, ...], walk: str, reads: dict) -> bytes:
|
|
188
|
+
return json.dumps({"counts": counts, "root": root, "release": list(release), "walk": walk,
|
|
189
|
+
"reads": [[*read, dict(surfaces)] for read, surfaces in reads.items()]}).encode()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _spawn(payload: bytes) -> bool:
|
|
193
|
+
"""Start a detached settler (``settle_detached``) for one payload: paths and digests go through a pipe, so
|
|
194
|
+
nothing a member read is written to disk."""
|
|
195
|
+
package = Path(__file__).resolve().parent
|
|
196
|
+
code = ("import sys;payload=sys.stdin.buffer.read();sys.path.insert(0,sys.argv[1]);"
|
|
197
|
+
"import importlib;importlib.import_module(sys.argv[2]).settle_detached(payload)")
|
|
198
|
+
try:
|
|
199
|
+
child = subprocess.Popen([sys.executable, "-c", code, str(package.parent), __name__], stdin=subprocess.PIPE,
|
|
200
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=str(package.parent),
|
|
201
|
+
start_new_session=True, close_fds=True)
|
|
202
|
+
except (OSError, ValueError):
|
|
203
|
+
return False
|
|
204
|
+
try:
|
|
205
|
+
child.stdin.write(payload)
|
|
206
|
+
child.stdin.close()
|
|
207
|
+
except OSError:
|
|
208
|
+
return False
|
|
209
|
+
child.returncode = 0 # detached: it outlives this process, which never waits for it
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def settle_detached(payload: bytes) -> None:
|
|
214
|
+
"""The settler: judge the reads one process handed over at exit, add their counts and take them off pending."""
|
|
215
|
+
request = json.loads(payload)
|
|
216
|
+
reads = {tuple(read[:3]): Counter(read[3]) for read in request["reads"]}
|
|
217
|
+
judged = _judge(request["root"], tuple(request["release"]), request["walk"], reads)
|
|
218
|
+
judged[PENDING] -= _reads(reads)
|
|
219
|
+
_add(Path(request["counts"]), judged)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def flush(wait: bool = True) -> None:
|
|
223
|
+
"""Write this process's counts, judging every deferred read first. At exit (``wait=False``) nothing is
|
|
224
|
+
walked or built: deferred reads go to a settler, and their pending mark is written before it starts."""
|
|
225
|
+
settle(wait)
|
|
226
|
+
while _pending:
|
|
227
|
+
path, counter = _pending.popitem()
|
|
228
|
+
if +counter or -counter:
|
|
229
|
+
_add(Path(path), counter)
|
|
230
|
+
while _settlers:
|
|
231
|
+
counts, root, release, walk, reads = _settlers.pop()
|
|
232
|
+
if not _spawn(_payload(counts, root, release, walk, reads)):
|
|
233
|
+
_add(Path(counts), Counter({PENDING: -_reads(reads), UNJUDGED: _reads(reads)}))
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def tally(memory_root: Path) -> tuple[dict[tuple[str, int], int], dict[str, int]]:
|
|
237
|
+
"""This memory's disagreements by (surface, class), and the reads they lack (``pending`` with a settler,
|
|
238
|
+
``unjudged`` judged by nothing), from one reading of the counts file."""
|
|
239
|
+
flush()
|
|
240
|
+
try:
|
|
241
|
+
with os.fdopen(_private(_file(memory_root), os.O_RDONLY), encoding="utf-8") as handle:
|
|
242
|
+
fcntl.flock(handle.fileno(), fcntl.LOCK_SH)
|
|
243
|
+
saved = json.loads(handle.read())
|
|
244
|
+
except (OSError, ValueError):
|
|
245
|
+
saved = {}
|
|
246
|
+
saved = saved if isinstance(saved, dict) else {}
|
|
247
|
+
counted = {}
|
|
248
|
+
for key, value in saved.items():
|
|
249
|
+
surface, _, number = str(key).rpartition("|")
|
|
250
|
+
if surface and number.isdigit() and isinstance(value, int):
|
|
251
|
+
counted[(surface, int(number))] = value
|
|
252
|
+
return counted, {key: saved[key] for key in (PENDING, UNJUDGED) if isinstance(saved.get(key), int) and saved[key]}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def counts(memory_root: Path) -> dict[tuple[str, int], int]:
|
|
256
|
+
"""Every recorded disagreement for this memory, by (surface, class)."""
|
|
257
|
+
return tally(memory_root)[0]
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def unsettled(memory_root: Path) -> dict[str, int]:
|
|
261
|
+
"""Reads the counts do not hold yet; either one makes them partial."""
|
|
262
|
+
return tally(memory_root)[1]
|