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
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
# Repository selection belongs to gh and this checkout, never to inherited Git overrides.
|
|
4
|
+
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_COMMON_DIR
|
|
5
|
+
|
|
6
|
+
# The merge gate: the one door for merging a pull request into the
|
|
7
|
+
# integration branch, on every harness (`bash bin/agent.sh merge <pr>`).
|
|
8
|
+
# It reads the checks on the PR's head commit and merges only when they
|
|
9
|
+
# allow it. A red check merges only under a receipt: a Markdown file that
|
|
10
|
+
# names every red check and why it does not matter, posted on the PR as a
|
|
11
|
+
# comment headed "Merge receipt" before the merge runs.
|
|
12
|
+
#
|
|
13
|
+
# Each merge, and each refusal of red checks without a receipt, appends one
|
|
14
|
+
# line to the gate ledger, memory/sessions/gate.jsonl, through the records
|
|
15
|
+
# module (bin/lib/session_record.py gate-event). The line holds the checks,
|
|
16
|
+
# the head's CI attempts and the jobs that passed only on a re-run, flaked
|
|
17
|
+
# when an earlier attempt failed them and recovered when it cancelled them;
|
|
18
|
+
# writing it never changes the decision or the exit code.
|
|
19
|
+
#
|
|
20
|
+
# Usage:
|
|
21
|
+
# bash bin/merge-gate.sh <pr-number-or-url> # merge when every check is green
|
|
22
|
+
# bash bin/merge-gate.sh <pr-number-or-url> --receipt FILE # merge past the red checks it names
|
|
23
|
+
# bash bin/merge-gate.sh <pr-number-or-url> --local-receipt FILE # SHA-bound local CI
|
|
24
|
+
# bash bin/merge-gate.sh <pr-number-or-url> --dry-run # print the classification and decision
|
|
25
|
+
#
|
|
26
|
+
# A number names a PR in this instance's repository (github_org/repo_name);
|
|
27
|
+
# a URL may name a managed repo, which merges into its own integration
|
|
28
|
+
# branch (bin/base-branch.sh <name>). Checks: green = success; neutral =
|
|
29
|
+
# skipped, neutral; pending = queued, in progress, waiting; red = failure,
|
|
30
|
+
# cancelled, timed out, action required and any other result. A workflow
|
|
31
|
+
# run on the commit that is still going holds the merge too. A receipt
|
|
32
|
+
# names each red check by its exact name, on a line that says why it does
|
|
33
|
+
# not matter. --dry-run posts and merges nothing and exits with the code
|
|
34
|
+
# the real run would. gh runs through bin/gh-run.sh when it is present.
|
|
35
|
+
#
|
|
36
|
+
# Exit 0 = merged (with --dry-run: would merge)
|
|
37
|
+
# Exit 1 = usage, gh unavailable, or a PR that cannot merge as it stands
|
|
38
|
+
# Exit 2 = red checks without a receipt, or a receipt that misses one
|
|
39
|
+
# Exit 3 = checks or workflow runs still going, or no checks reported yet
|
|
40
|
+
# Exit 4 = the base is not the integration branch of the PR's repository,
|
|
41
|
+
# or the repository is neither this one nor a managed repo
|
|
42
|
+
|
|
43
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
44
|
+
|
|
45
|
+
die() {
|
|
46
|
+
printf 'merge-gate: %s\n' "$*" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pr="" receipt="" local_receipt="" local_base_sha="" dry_run=false number=""
|
|
51
|
+
while [ "$#" -gt 0 ]; do
|
|
52
|
+
case "$1" in
|
|
53
|
+
--receipt)
|
|
54
|
+
[ "$#" -ge 2 ] && [ -n "$2" ] || die "--receipt needs a file"
|
|
55
|
+
receipt="$2"; shift 2 ;;
|
|
56
|
+
--receipt=*)
|
|
57
|
+
receipt="${1#--receipt=}"
|
|
58
|
+
[ -n "$receipt" ] || die "--receipt needs a file"
|
|
59
|
+
shift ;;
|
|
60
|
+
--local-receipt)
|
|
61
|
+
[ "$#" -ge 2 ] && [ -n "$2" ] || die "--local-receipt needs a file"
|
|
62
|
+
local_receipt="$2"; shift 2 ;;
|
|
63
|
+
--dry-run) dry_run=true; shift ;;
|
|
64
|
+
-h|--help) awk 'NR >= 6 { if ($0 == "") exit; sub(/^# ?/, ""); print }' "$0"; exit 0 ;;
|
|
65
|
+
-*) die "unknown option: $1 (see --help)" ;;
|
|
66
|
+
*)
|
|
67
|
+
[ -z "$pr" ] || die "one pull request at a time"
|
|
68
|
+
pr="${1#\#}"; shift ;;
|
|
69
|
+
esac
|
|
70
|
+
done
|
|
71
|
+
[ -n "$pr" ] || die "usage: bash bin/merge-gate.sh <pr-number-or-url> [--receipt FILE] [--dry-run]"
|
|
72
|
+
case "$pr" in
|
|
73
|
+
*[!0-9]*)
|
|
74
|
+
[[ "$pr" =~ ^https://[^/[:space:]]+/[^/[:space:]]+/[^/[:space:]]+/pull/[0-9]+(/[^[:space:]]*)?$ ]] \
|
|
75
|
+
|| die "not a pull request number or URL: $pr" ;;
|
|
76
|
+
esac
|
|
77
|
+
if [ -n "$local_receipt" ]; then
|
|
78
|
+
[ -z "$receipt" ] || die "choose --receipt or --local-receipt, not both"
|
|
79
|
+
case "$local_receipt" in /*) ;; *) local_receipt="$PWD/$local_receipt" ;; esac
|
|
80
|
+
[ -f "$local_receipt" ] && [ -r "$local_receipt" ] || die "local receipt not found: $local_receipt"
|
|
81
|
+
fi
|
|
82
|
+
if [ -n "$receipt" ]; then
|
|
83
|
+
case "$receipt" in /*) ;; *) receipt="$PWD/$receipt" ;; esac
|
|
84
|
+
[ -f "$receipt" ] && [ -r "$receipt" ] || die "receipt not found: $receipt"
|
|
85
|
+
command -v python3 >/dev/null 2>&1 || die "python3 is required to read a receipt"
|
|
86
|
+
fi
|
|
87
|
+
command -v gh >/dev/null 2>&1 || die "the GitHub CLI is required: install gh, then run gh auth login"
|
|
88
|
+
command -v jq >/dev/null 2>&1 || die "jq is required"
|
|
89
|
+
|
|
90
|
+
# This instance's repository, as the skills name it; base-branch.sh and
|
|
91
|
+
# config-get.sh read the same egregore.json.
|
|
92
|
+
config="${CONFIG:-$SCRIPT_DIR/egregore.json}"
|
|
93
|
+
org="$(jq -r '.github_org | select(type == "string" and length > 0)' "$config" 2>/dev/null || true)"
|
|
94
|
+
core_repo=""
|
|
95
|
+
[ -z "$org" ] || core_repo="$(bash "$SCRIPT_DIR/bin/config-get.sh" repo 2>/dev/null || true)"
|
|
96
|
+
# The gate ledger lives in this checkout's memory, as bin/agent.sh finds it.
|
|
97
|
+
memory="${EGREGORE_MEMORY_DIR:-$SCRIPT_DIR/memory}"
|
|
98
|
+
|
|
99
|
+
scratch="$(mktemp -d "${TMPDIR:-/tmp}/merge-gate.XXXXXX")" || die "could not create a scratch directory"
|
|
100
|
+
trap 'rm -rf "$scratch"' EXIT
|
|
101
|
+
cd "$SCRIPT_DIR"
|
|
102
|
+
|
|
103
|
+
# gh with the checkout's token from .env when bin/gh-run.sh is there.
|
|
104
|
+
gh_cli() {
|
|
105
|
+
if [ -f "$SCRIPT_DIR/bin/gh-run.sh" ]; then
|
|
106
|
+
bash "$SCRIPT_DIR/bin/gh-run.sh" "$@"
|
|
107
|
+
else
|
|
108
|
+
gh "$@"
|
|
109
|
+
fi
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
lower() {
|
|
113
|
+
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
first_line() {
|
|
117
|
+
head -n 1 "$1" 2>/dev/null | cut -c1-200
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
plural() {
|
|
121
|
+
if [ "$1" -eq 1 ]; then printf '1 %s' "$2"; else printf '%s %ss' "$1" "$2"; fi
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# refuse CODE REASON [DETAIL...]: stop without posting or merging. A real run
|
|
125
|
+
# writes to stderr; --dry-run prints the decision and its detail on stdout.
|
|
126
|
+
# The one refusal the ledger records sets ledger_kind first; its ledger line
|
|
127
|
+
# follows the decision on the same stream.
|
|
128
|
+
ledger_kind=""
|
|
129
|
+
refuse() {
|
|
130
|
+
local code="$1" reason="$2" line
|
|
131
|
+
shift 2
|
|
132
|
+
if $dry_run; then
|
|
133
|
+
printf 'decision: refuse, %s (exit %s)\n' "$reason" "$code"
|
|
134
|
+
else
|
|
135
|
+
printf 'merge-gate: refused #%s: %s\n' "$number" "$reason" >&2
|
|
136
|
+
fi
|
|
137
|
+
for line in "$@"; do
|
|
138
|
+
[ -n "$line" ] || continue
|
|
139
|
+
if $dry_run; then printf '%s\n' "$line"; else printf '%s\n' "$line" >&2; fi
|
|
140
|
+
done
|
|
141
|
+
if [ -n "$ledger_kind" ]; then
|
|
142
|
+
if $dry_run; then ledger "$ledger_kind" false || true; else ledger "$ledger_kind" false >&2 || true; fi
|
|
143
|
+
fi
|
|
144
|
+
exit "$code"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# --- The pull request ------------------------------------------------------
|
|
148
|
+
# A number is looked up in this repository, never in whichever remote gh
|
|
149
|
+
# would pick by default.
|
|
150
|
+
view_args=(pr view "$pr")
|
|
151
|
+
case "$pr" in
|
|
152
|
+
*[!0-9]*) ;;
|
|
153
|
+
*) [ -z "$core_repo" ] || view_args+=(--repo "$core_repo") ;;
|
|
154
|
+
esac
|
|
155
|
+
gh_cli "${view_args[@]}" --json number,url,state,isDraft,mergeable,baseRefName,headRefName,headRefOid \
|
|
156
|
+
>"$scratch/pr.json" 2>"$scratch/err" \
|
|
157
|
+
|| die "could not read pull request $pr through gh: $(first_line "$scratch/err")"
|
|
158
|
+
IFS=$'\t' read -r number url state draft mergeable pr_base head_ref sha < <(
|
|
159
|
+
jq -r '[.number, .url, .state, .isDraft, (.mergeable // "UNKNOWN"), .baseRefName, .headRefName, .headRefOid]
|
|
160
|
+
| map(tostring) | @tsv' "$scratch/pr.json" 2>/dev/null
|
|
161
|
+
) || die "gh returned an unreadable pull request for $pr"
|
|
162
|
+
[[ "$number" =~ ^[0-9]+$ && "$sha" =~ ^[0-9a-f]{40}$ && "$url" == https://*/*/*/pull/"$number" ]] \
|
|
163
|
+
|| die "gh returned an unreadable pull request for $pr"
|
|
164
|
+
short="${sha:0:8}"
|
|
165
|
+
|
|
166
|
+
[ "$state" = "OPEN" ] || die "#$number is not open (state $state)"
|
|
167
|
+
|
|
168
|
+
# --- The integration branch of the PR's repository --------------------------
|
|
169
|
+
# This repository merges into its configured base, a managed repo into its
|
|
170
|
+
# own. Without github_org there is nothing to tell apart: the PR is this
|
|
171
|
+
# repository's.
|
|
172
|
+
pr_repo="${url#https://}"
|
|
173
|
+
pr_repo="${pr_repo#*/}"
|
|
174
|
+
pr_repo="${pr_repo%/pull/*}"
|
|
175
|
+
managed=""
|
|
176
|
+
if [ -z "$core_repo" ] || [ "$(lower "$pr_repo")" = "$(lower "$core_repo")" ]; then
|
|
177
|
+
base="$(bash "$SCRIPT_DIR/bin/base-branch.sh")" || die "could not read the integration branch from egregore.json"
|
|
178
|
+
else
|
|
179
|
+
if [ "$(lower "${pr_repo%%/*}")" = "$(lower "$org")" ]; then
|
|
180
|
+
managed="$(jq -r --arg repo "$(lower "${pr_repo#*/}")" '
|
|
181
|
+
first(.repos[]? | (if type == "object" then .name else . end)
|
|
182
|
+
| select(type == "string" and ascii_downcase == $repo)) // empty' "$config" 2>/dev/null || true)"
|
|
183
|
+
fi
|
|
184
|
+
[ -n "$managed" ] \
|
|
185
|
+
|| refuse 4 "it belongs to $pr_repo, which is neither this instance's repository ($core_repo) nor a managed repo, so it has no integration branch here"
|
|
186
|
+
base="$(bash "$SCRIPT_DIR/bin/base-branch.sh" "$managed")" \
|
|
187
|
+
|| die "could not read the integration branch of the managed repo $managed from egregore.json"
|
|
188
|
+
fi
|
|
189
|
+
if [ "$pr_base" != "$base" ]; then
|
|
190
|
+
if [ -n "$managed" ]; then
|
|
191
|
+
refuse 4 "its base is $pr_base, not $base, the integration branch of $pr_repo; the gate merges only into $base"
|
|
192
|
+
fi
|
|
193
|
+
refuse 4 "its base is $pr_base, not the integration branch $base; the gate merges only into $base, and the release flow owns main"
|
|
194
|
+
fi
|
|
195
|
+
[ "$draft" = "false" ] || die "#$number is a draft; mark it ready for review first"
|
|
196
|
+
[ "$mergeable" != "CONFLICTING" ] || die "#$number conflicts with $base; rebase it, then run the gate again"
|
|
197
|
+
|
|
198
|
+
# --- The checks on the head commit -----------------------------------------
|
|
199
|
+
# gh keeps the latest run of each check, so a superseded run never counts.
|
|
200
|
+
# Its exit status says whether checks failed; the JSON is what is read.
|
|
201
|
+
gh_cli pr checks "$url" --json name,state,link >"$scratch/checks.json" 2>"$scratch/err" || true
|
|
202
|
+
if ! jq -e 'type == "array"' "$scratch/checks.json" >/dev/null 2>&1; then
|
|
203
|
+
grep -q 'no checks reported' "$scratch/err" 2>/dev/null \
|
|
204
|
+
|| die "could not read the checks on $short: $(first_line "$scratch/err")"
|
|
205
|
+
printf '[]\n' >"$scratch/checks.json"
|
|
206
|
+
fi
|
|
207
|
+
jq '
|
|
208
|
+
def class:
|
|
209
|
+
if .state == "SUCCESS" then "green"
|
|
210
|
+
elif .state == "SKIPPED" or .state == "NEUTRAL" then "neutral"
|
|
211
|
+
elif .state == "QUEUED" or .state == "IN_PROGRESS" or .state == "PENDING" or .state == "WAITING"
|
|
212
|
+
or .state == "REQUESTED" or .state == "EXPECTED" then "pending"
|
|
213
|
+
else "red" end;
|
|
214
|
+
map({class: class, name: (.name // ""), state: ((.state // "unknown") | ascii_downcase), link: (.link // "")})
|
|
215
|
+
| sort_by({"red": 0, "pending": 1, "neutral": 2, "green": 3}[.class])
|
|
216
|
+
' "$scratch/checks.json" >"$scratch/classified.json" 2>/dev/null || die "could not read the checks on $short"
|
|
217
|
+
|
|
218
|
+
count() {
|
|
219
|
+
jq --arg class "$1" '[.[] | select(.class == $class)] | length' "$scratch/classified.json"
|
|
220
|
+
}
|
|
221
|
+
# One line per check: class, name, result, and the link of a red or pending one.
|
|
222
|
+
lines() {
|
|
223
|
+
jq -r --arg class "${1:-}" '.[] | select($class == "" or .class == $class)
|
|
224
|
+
| " " + ((.class + " ")[:8]) + " " + .name + " (" + .state + ")"
|
|
225
|
+
+ (if (.class == "red" or .class == "pending") and .link != "" then " " + .link else "" end)' \
|
|
226
|
+
"$scratch/classified.json"
|
|
227
|
+
}
|
|
228
|
+
# The same lines for a refusal, unless --dry-run has already listed every check.
|
|
229
|
+
detail() {
|
|
230
|
+
$dry_run || lines "$1"
|
|
231
|
+
}
|
|
232
|
+
green="$(count green)"; neutral="$(count neutral)"; pending="$(count pending)"; red="$(count red)"
|
|
233
|
+
total=$((green + neutral + pending + red))
|
|
234
|
+
|
|
235
|
+
if $dry_run; then
|
|
236
|
+
printf 'merge-gate: #%s %s into %s at %s\n' "$number" "$head_ref" "$base" "$short"
|
|
237
|
+
[ "$total" -eq 0 ] || lines
|
|
238
|
+
printf 'checks: %s green, %s neutral, %s pending, %s red\n' "$green" "$neutral" "$pending" "$red"
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
[ "$total" -gt 0 ] || [ -n "$local_receipt" ] || refuse 3 "no checks have reported on $short yet; run the gate again once CI starts"
|
|
242
|
+
if [ "$pending" -gt 0 ]; then
|
|
243
|
+
reason="$(plural "$pending" check) of $total still pending on $short"
|
|
244
|
+
[ "$red" -eq 0 ] || reason="$reason, $(plural "$red" "red check") so far"
|
|
245
|
+
refuse 3 "$reason" "$(detail pending)" "Run the gate again when they finish."
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# A workflow run still going may not have created every check yet: jobs that
|
|
249
|
+
# wait on a planner appear only once it finishes. Such a run holds the merge.
|
|
250
|
+
repo_ref="${url#https://}"
|
|
251
|
+
repo_ref="${repo_ref%/pull/*}"
|
|
252
|
+
gh_cli run list --repo "$repo_ref" --commit "$sha" --json workflowName,status,url --limit 100 \
|
|
253
|
+
>"$scratch/runs.json" 2>"$scratch/err" \
|
|
254
|
+
|| die "could not read the workflow runs on $short: $(first_line "$scratch/err")"
|
|
255
|
+
unfinished="$(jq -r '.[]? | select(.status != "completed")
|
|
256
|
+
| " pending " + (.workflowName // "workflow") + " run (" + ((.status // "unknown") | ascii_downcase) + ")"
|
|
257
|
+
+ (if (.url // "") != "" then " " + .url else "" end)' "$scratch/runs.json" 2>/dev/null)" \
|
|
258
|
+
|| die "could not read the workflow runs on $short"
|
|
259
|
+
if [ -n "$unfinished" ]; then
|
|
260
|
+
refuse 3 "a workflow run on $short is still going, so not every check has reported" "$unfinished" \
|
|
261
|
+
"Run the gate again when it finishes."
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# Explicit local evidence can replace unavailable hosted execution, never pending
|
|
265
|
+
# execution. Bind it to the current base as well as the classified PR head.
|
|
266
|
+
read_base_sha() {
|
|
267
|
+
gh_cli pr view "$url" --json baseRefOid >"$scratch/base.json" 2>"$scratch/err" || return 1
|
|
268
|
+
jq -er '.baseRefOid | select(test("^[0-9a-f]{40}$"))' "$scratch/base.json"
|
|
269
|
+
}
|
|
270
|
+
if [ -n "$local_receipt" ]; then
|
|
271
|
+
local_base_sha="$(read_base_sha)" || die "could not resolve the current base for local evidence"
|
|
272
|
+
receipt="$scratch/local-receipt.md"
|
|
273
|
+
python3 "$SCRIPT_DIR/bin/lib/local_ci_receipt.py" validate --receipt "$local_receipt" \
|
|
274
|
+
--repo "$pr_repo" --pr "$number" --head "$sha" --base "$base" --base-sha "$local_base_sha" \
|
|
275
|
+
>"$receipt" 2>"$scratch/err" || die "local evidence rejected: $(first_line "$scratch/err")"
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
# The merge names the classified commit: a push after the read is refused by GitHub.
|
|
279
|
+
merge_head() {
|
|
280
|
+
if [ -n "$local_receipt" ]; then
|
|
281
|
+
local latest_base
|
|
282
|
+
latest_base="$(read_base_sha)" || { echo "cannot recheck integration base" >"$scratch/merge.out"; return 1; }
|
|
283
|
+
[ "$latest_base" = "$local_base_sha" ] || {
|
|
284
|
+
echo "integration base changed; refresh local checks and receipt" >"$scratch/merge.out"; return 1;
|
|
285
|
+
}
|
|
286
|
+
fi
|
|
287
|
+
gh_cli pr merge "$url" --merge --match-head-commit "$sha" >"$scratch/merge.out" 2>&1
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# --- The ledger ----------------------------------------------------------------
|
|
291
|
+
# CI's attempts on the head: the highest run_attempt among its ci.yml runs and,
|
|
292
|
+
# past the first, the jobs of every attempt in one listing. A job that failed
|
|
293
|
+
# or timed out in an earlier attempt and passes in the last flaked; one that
|
|
294
|
+
# was cancelled in an earlier attempt and passes in the last recovered, for a
|
|
295
|
+
# cause the listing does not hold (a job killed by its own time limit is
|
|
296
|
+
# cancelled like one a person stopped); the aggregate Full CI result job is
|
|
297
|
+
# neither. A gh failure leaves attempts unknown and nothing flaked or
|
|
298
|
+
# recovered.
|
|
299
|
+
attempts=""
|
|
300
|
+
ci_attempts() {
|
|
301
|
+
local host="${repo_ref%%/*}" run="" id=""
|
|
302
|
+
attempts=""
|
|
303
|
+
: >"$scratch/reruns"
|
|
304
|
+
gh_cli api "repos/$pr_repo/actions/runs?head_sha=$sha&per_page=20" --hostname "$host" \
|
|
305
|
+
>"$scratch/ci-runs.json" 2>/dev/null || return 0
|
|
306
|
+
run="$(jq -r '[.workflow_runs[]? | select((.path // "" | split("@")[0]) == ".github/workflows/ci.yml")
|
|
307
|
+
| select((.id | type) == "number" and (.run_attempt | type) == "number")]
|
|
308
|
+
| max_by([.run_attempt, .id]) // empty | "\(.id) \(.run_attempt)"' "$scratch/ci-runs.json" 2>/dev/null)" \
|
|
309
|
+
|| return 0
|
|
310
|
+
id="${run% *}"
|
|
311
|
+
run="${run#* }"
|
|
312
|
+
[[ "$id" =~ ^[0-9]+$ && "$run" =~ ^[1-9][0-9]*$ ]] || return 0
|
|
313
|
+
if [ "$run" -gt 1 ]; then
|
|
314
|
+
gh_cli api "repos/$pr_repo/actions/runs/$id/jobs?filter=all&per_page=100" --paginate --hostname "$host" \
|
|
315
|
+
>"$scratch/jobs.json" 2>/dev/null || return 0
|
|
316
|
+
jq -rs --argjson last "$run" '
|
|
317
|
+
[.[] | .jobs[]? | select((.name | type) == "string" and (.run_attempt | type) == "number")
|
|
318
|
+
| select(.name | test("^Full CI result( — |$)") | not)]
|
|
319
|
+
| ([.[] | select(.run_attempt == $last and .conclusion == "success") | .name] | unique) as $passed
|
|
320
|
+
| ([.[] | select(.run_attempt < $last and (.conclusion == "failure" or .conclusion == "timed_out")) | .name]
|
|
321
|
+
| unique | map(select(. as $job | any($passed[]; . == $job)))) as $flaked
|
|
322
|
+
| ([.[] | select(.run_attempt < $last and .conclusion == "cancelled") | .name]
|
|
323
|
+
| unique | map(select(. as $job | any($passed[]; . == $job) and (any($flaked[]; . == $job) | not)))) as $recovered
|
|
324
|
+
| ($flaked[] | "flaked\t\(.)"), ($recovered[] | "recovered\t\(.)")' "$scratch/jobs.json" \
|
|
325
|
+
>"$scratch/reruns.read" 2>/dev/null || return 0
|
|
326
|
+
mv "$scratch/reruns.read" "$scratch/reruns" || return 0
|
|
327
|
+
fi
|
|
328
|
+
attempts="$run"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# ledger KIND POSTED: append the decision to the gate ledger, or with --dry-run
|
|
332
|
+
# print the line it would append. Its callers add `|| true`: a line that cannot
|
|
333
|
+
# be written is reported and never changes the decision or the exit code.
|
|
334
|
+
ledger() {
|
|
335
|
+
local kind="$1" posted="$2" fate="" job="" flaked="" recovered="" note="" status=0 reason="" by="" session="" line=""
|
|
336
|
+
local args=(gate-event --root "$SCRIPT_DIR" --memory "$memory" --kind "$kind" --pr "$number"
|
|
337
|
+
--head "$sha" --base "$base" --repo "$pr_repo" --green "$green" --neutral "$neutral" --red "$red")
|
|
338
|
+
ci_attempts || true
|
|
339
|
+
[ -z "$attempts" ] || args+=(--attempts "$attempts")
|
|
340
|
+
while IFS=$'\t' read -r fate job; do
|
|
341
|
+
case "$fate" in
|
|
342
|
+
flaked) args+=(--flaked "$job"); flaked="${flaked:+$flaked, }$job" ;;
|
|
343
|
+
recovered) args+=(--recovered "$job"); recovered="${recovered:+$recovered, }$job" ;;
|
|
344
|
+
esac
|
|
345
|
+
done <"$scratch/reruns"
|
|
346
|
+
if $posted; then args+=(--receipt); fi
|
|
347
|
+
if [ -n "$attempts" ] && [ "$attempts" -gt 1 ]; then
|
|
348
|
+
note=" · attempts $attempts, flaked: ${flaked:-none}${recovered:+; recovered: $recovered}"
|
|
349
|
+
fi
|
|
350
|
+
if $dry_run; then
|
|
351
|
+
python3 "$SCRIPT_DIR/bin/lib/session_record.py" "${args[@]}" --dry-run \
|
|
352
|
+
>"$scratch/ledger-preview.json" 2>"$scratch/ledger.err" || status=$?
|
|
353
|
+
if [ "$status" -eq 0 ]; then
|
|
354
|
+
printf 'ledger: would append %s\n' "$(cat "$scratch/ledger-preview.json")"
|
|
355
|
+
else
|
|
356
|
+
printf 'ledger: would withhold (%s)\n' "$(first_line "$scratch/ledger.err")"
|
|
357
|
+
fi
|
|
358
|
+
return 0
|
|
359
|
+
fi
|
|
360
|
+
python3 "$SCRIPT_DIR/bin/lib/session_record.py" "${args[@]}" >/dev/null 2>"$scratch/ledger.err" || status=$?
|
|
361
|
+
if [ "$status" -eq 0 ]; then
|
|
362
|
+
printf 'ledger: gate.jsonl +1%s\n' "$note"
|
|
363
|
+
else
|
|
364
|
+
reason="$(first_line "$scratch/ledger.err")" || reason=""
|
|
365
|
+
printf 'ledger: not written (%s)\n' "${reason:-exit $status}"
|
|
366
|
+
fi
|
|
367
|
+
return 0
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if [ "$red" -eq 0 ] && [ -z "$local_receipt" ]; then
|
|
371
|
+
result="all $green checks green"
|
|
372
|
+
[ "$neutral" -eq 0 ] || result="$result, $neutral neutral"
|
|
373
|
+
if $dry_run; then
|
|
374
|
+
printf 'decision: merge, %s (exit 0)\n' "$result"
|
|
375
|
+
ledger merge false || true
|
|
376
|
+
exit 0
|
|
377
|
+
fi
|
|
378
|
+
[ -z "$receipt" ] || echo "receipt: not posted, every check is green"
|
|
379
|
+
merge_head || die "gh could not merge #$number: $(first_line "$scratch/merge.out")"
|
|
380
|
+
printf 'merged %s into %s: %s\n' "$sha" "$base" "$result"
|
|
381
|
+
ledger merge false || true
|
|
382
|
+
exit 0
|
|
383
|
+
fi
|
|
384
|
+
|
|
385
|
+
if [ -z "$receipt" ]; then
|
|
386
|
+
ledger_kind=refuse
|
|
387
|
+
refuse 2 "$(plural "$red" "red check") on $short and no receipt" "$(detail red)" \
|
|
388
|
+
"Fix them, or write a receipt that names each red check and why it does not matter, then run: bash bin/agent.sh merge $number --receipt FILE"
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
# --- The receipt -------------------------------------------------------------
|
|
392
|
+
# A red check is named when one line of the receipt holds its exact name
|
|
393
|
+
# (whitespace aside, not inside a longer word or a longer check name) and at
|
|
394
|
+
# least three words besides the check names on that line: the reason.
|
|
395
|
+
if ! python3 - "$receipt" "$scratch/classified.json" "$local_receipt" >"$scratch/missing" 2>"$scratch/err" <<'PY'
|
|
396
|
+
import json
|
|
397
|
+
import re
|
|
398
|
+
import sys
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def norm(text):
|
|
402
|
+
return " ".join(text.split())
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def pattern(name):
|
|
406
|
+
before = r"(?<![^\W_])" if name[:1].isalnum() else ""
|
|
407
|
+
after = r"(?![^\W_])" if name[-1:].isalnum() else ""
|
|
408
|
+
return re.compile(before + re.escape(name) + after)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
# Structured local evidence must explicitly explain each failed job. A job
|
|
412
|
+
# named "test" appearing in a recorded command is not its waiver reason.
|
|
413
|
+
if sys.argv[3]:
|
|
414
|
+
with open(sys.argv[3], encoding="utf-8") as handle:
|
|
415
|
+
local = json.load(handle)
|
|
416
|
+
with open(sys.argv[2], encoding="utf-8") as handle:
|
|
417
|
+
checks = json.load(handle)
|
|
418
|
+
reasons = {norm(row["name"]): row["reason"] for row in local["failed_checks"]}
|
|
419
|
+
for check in checks:
|
|
420
|
+
if check["class"] == "red" and len(re.findall(r"[^\W_]+", reasons.get(norm(check["name"]), ""))) < 3:
|
|
421
|
+
print(f" missing {check['name']} ({check['state']})")
|
|
422
|
+
sys.exit(0)
|
|
423
|
+
|
|
424
|
+
with open(sys.argv[1], encoding="utf-8", errors="replace") as handle:
|
|
425
|
+
receipt = [norm(line) for line in handle.read().splitlines()]
|
|
426
|
+
with open(sys.argv[2], encoding="utf-8") as handle:
|
|
427
|
+
checks = json.load(handle)
|
|
428
|
+
# Longest names first, so a check whose name contains another's is taken whole.
|
|
429
|
+
names = sorted({norm(check["name"]) for check in checks if norm(check["name"])}, key=len, reverse=True)
|
|
430
|
+
patterns = [(name, pattern(name)) for name in names]
|
|
431
|
+
named = set()
|
|
432
|
+
for line in receipt:
|
|
433
|
+
present = set()
|
|
434
|
+
for name, exact in patterns:
|
|
435
|
+
if exact.search(line):
|
|
436
|
+
present.add(name)
|
|
437
|
+
line = exact.sub(" ", line)
|
|
438
|
+
if len(re.findall(r"[^\W_]+", line)) >= 3:
|
|
439
|
+
named |= present
|
|
440
|
+
reported = set()
|
|
441
|
+
for check in checks:
|
|
442
|
+
name = norm(check["name"])
|
|
443
|
+
if check["class"] != "red" or name in named or name in reported:
|
|
444
|
+
continue
|
|
445
|
+
reported.add(name)
|
|
446
|
+
link = f" {check['link']}" if check["link"] else ""
|
|
447
|
+
print(f" missing {check['name']} ({check['state']}){link}")
|
|
448
|
+
PY
|
|
449
|
+
then
|
|
450
|
+
die "could not read the receipt $receipt: $(first_line "$scratch/err")"
|
|
451
|
+
fi
|
|
452
|
+
if [ -s "$scratch/missing" ]; then
|
|
453
|
+
refuse 2 "the receipt does not name every red check with a reason" "$(cat "$scratch/missing")" \
|
|
454
|
+
"Name each red check exactly as listed, on a line that says why it does not matter."
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
result="$green green"
|
|
458
|
+
[ "$neutral" -eq 0 ] || result="$result, $neutral neutral"
|
|
459
|
+
result="$result, $red red under receipt"
|
|
460
|
+
[ -z "$local_receipt" ] || result="$result, verified local evidence"
|
|
461
|
+
if $dry_run; then
|
|
462
|
+
printf 'decision: merge, %s; the receipt is posted on #%s first (exit 0)\n' "$result" "$number"
|
|
463
|
+
ledger merge true || true
|
|
464
|
+
exit 0
|
|
465
|
+
fi
|
|
466
|
+
|
|
467
|
+
# The comment: the receipt as written under one "Merge receipt" heading, then
|
|
468
|
+
# the commit, each red check and the runs, linked. Backticks are Markdown.
|
|
469
|
+
# shellcheck disable=SC2016
|
|
470
|
+
{
|
|
471
|
+
printf '## Merge receipt\n\n'
|
|
472
|
+
started=false
|
|
473
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
474
|
+
if ! $started; then
|
|
475
|
+
[ -n "${line//[[:space:]]/}" ] || continue
|
|
476
|
+
started=true
|
|
477
|
+
if [[ "$line" =~ ^#+[[:space:]]*[Mm]erge[[:space:]]+[Rr]eceipt[[:space:]]*$ ]]; then continue; fi
|
|
478
|
+
fi
|
|
479
|
+
printf '%s\n' "$line"
|
|
480
|
+
done <"$receipt"
|
|
481
|
+
printf '\n---\n\n'
|
|
482
|
+
printf 'Checks on [`%s`](%s/commit/%s) into `%s`: %s green, %s neutral, %s red.\n\n' \
|
|
483
|
+
"$short" "${url%/pull/*}" "$sha" "$base" "$green" "$neutral" "$red"
|
|
484
|
+
jq -r '.[] | select(.class == "red")
|
|
485
|
+
| "- red · " + .name + " (" + .state + ")" + (if .link != "" then " · " + .link else "" end)' \
|
|
486
|
+
"$scratch/classified.json"
|
|
487
|
+
jq -r '[.[] | .link | select(test("/actions/runs/[0-9]+/job/[0-9]+")) | sub("/job/[0-9]+.*$"; "")]
|
|
488
|
+
| unique | .[] | "- run · " + .' "$scratch/classified.json"
|
|
489
|
+
printf '\nMerging on this receipt through `bin/agent.sh merge`.\n'
|
|
490
|
+
} >"$scratch/comment.md"
|
|
491
|
+
|
|
492
|
+
comment_url="$(gh_cli pr comment "$url" --body-file "$scratch/comment.md" 2>"$scratch/err")" \
|
|
493
|
+
|| die "could not post the receipt on #$number, so nothing merged: $(first_line "$scratch/err")"
|
|
494
|
+
printf 'receipt: %s\n' "${comment_url:-posted on #$number}"
|
|
495
|
+
merge_head || die "the receipt is posted, but gh could not merge #$number: $(first_line "$scratch/merge.out")"
|
|
496
|
+
printf 'merged %s into %s: %s\n' "$sha" "$base" "$result"
|
|
497
|
+
ledger merge true || true
|
|
498
|
+
exit 0
|
package/runtime/pi/bin/notify.sh
CHANGED
|
@@ -15,6 +15,8 @@ set +x
|
|
|
15
15
|
NOTIFY_CHECKOUT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
16
16
|
# shellcheck source=/dev/null
|
|
17
17
|
source "$NOTIFY_CHECKOUT/bin/lib/scratch.sh"
|
|
18
|
+
# shellcheck source=bin/lib/config.sh
|
|
19
|
+
source "$NOTIFY_CHECKOUT/bin/lib/config.sh"
|
|
18
20
|
SCRIPT_DIR="${EGREGORE_NOTIFY_PROJECT_DIR:-$NOTIFY_CHECKOUT}"
|
|
19
21
|
CONFIG="$SCRIPT_DIR/egregore.json"
|
|
20
22
|
|
|
@@ -32,7 +34,7 @@ SESSION_ID="$(sed -n '1p' "$SCRIPT_DIR/.egregore-session-id" 2>/dev/null || true
|
|
|
32
34
|
SESSION_ID="${SESSION_ID:-no-session}"
|
|
33
35
|
PROJECT_HASH="$(printf '%s' "$SCRIPT_DIR" | cksum | awk '{print $1}')"
|
|
34
36
|
STATE_DIR="${EGREGORE_NOTIFY_STATE_DIR:-$HOME/.egregore/notifications/$PROJECT_HASH}"
|
|
35
|
-
RELAY_URL="${EGREGORE_NOTIFY_RELAY_URL
|
|
37
|
+
RELAY_URL="$(_hosted_api_url "${EGREGORE_NOTIFY_RELAY_URL:-$EGREGORE_HOSTED_API_ORIGIN}")" || RELAY_URL=""
|
|
36
38
|
mkdir -p "$STATE_DIR"
|
|
37
39
|
chmod 700 "$STATE_DIR" 2>/dev/null || true
|
|
38
40
|
|
|
@@ -41,7 +43,7 @@ if [ -f "$SCRIPT_DIR/.env" ]; then
|
|
|
41
43
|
EGREGORE_API_KEY="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
42
44
|
SLACK_BOT_TOKEN="${SLACK_BOT_TOKEN:-$(grep '^SLACK_BOT_TOKEN=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
43
45
|
fi
|
|
44
|
-
API_URL="${EGREGORE_API_URL:-$(jq -r '.api_url // empty' "$CONFIG" 2>/dev/null)}"
|
|
46
|
+
API_URL="$(_hosted_api_url "${EGREGORE_API_URL:-$(jq -r '.api_url // empty' "$CONFIG" 2>/dev/null)}")" || API_URL=""
|
|
45
47
|
API_KEY="${EGREGORE_API_KEY:-}"
|
|
46
48
|
|
|
47
49
|
die() {
|
|
@@ -438,6 +440,7 @@ dispatch_local_telegram() {
|
|
|
438
440
|
group_link=""
|
|
439
441
|
;;
|
|
440
442
|
esac
|
|
443
|
+
[ -n "$RELAY_URL" ] || die "The notification relay address isn't a plain web address, so nothing was sent."
|
|
441
444
|
curl -q -sS -X POST "${RELAY_URL}/api/notify/relay" \
|
|
442
445
|
-H "Content-Type: application/json" \
|
|
443
446
|
-d "$(jq -nc \
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Decide "Request access" asks for this organisation's hosted pages.
|
|
3
|
+
#
|
|
4
|
+
# bash bin/page-access.sh requests [--page <id>] [--status pending|granted|declined|all]
|
|
5
|
+
# bash bin/page-access.sh grant <request-id>
|
|
6
|
+
# bash bin/page-access.sh decline <request-id>
|
|
7
|
+
#
|
|
8
|
+
# Someone signed in who opens a page outside its audience (publish-artifact.sh
|
|
9
|
+
# --visible-to) can ask for it. A grant adds their email to the page's
|
|
10
|
+
# audience; a decline shows them "Request declined" and stops new asks for
|
|
11
|
+
# seven days. Nothing is sent to anyone: the requester sees the outcome on
|
|
12
|
+
# the page. Connected mode only. The org key names the organisation and your
|
|
13
|
+
# own GitHub token proves who decides; both stay inside bin/api-call.sh.
|
|
14
|
+
#
|
|
15
|
+
# Exit: 0 done, 1 the request failed, 2 usage, 4 Local mode.
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
19
|
+
# shellcheck source=/dev/null
|
|
20
|
+
source "$SCRIPT_DIR/bin/lib/config.sh"
|
|
21
|
+
|
|
22
|
+
usage() {
|
|
23
|
+
echo 'Usage: bash bin/page-access.sh requests [--page <id>] [--status pending|granted|declined|all]' >&2
|
|
24
|
+
echo ' bash bin/page-access.sh grant <request-id>' >&2
|
|
25
|
+
echo ' bash bin/page-access.sh decline <request-id>' >&2
|
|
26
|
+
exit 2
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
[ "$#" -ge 1 ] || usage
|
|
30
|
+
command="$1"
|
|
31
|
+
shift
|
|
32
|
+
|
|
33
|
+
page=''
|
|
34
|
+
status=pending
|
|
35
|
+
request_id=''
|
|
36
|
+
case "$command" in
|
|
37
|
+
requests)
|
|
38
|
+
while [ "$#" -gt 0 ]; do
|
|
39
|
+
[ "$#" -ge 2 ] || usage
|
|
40
|
+
case "$1" in
|
|
41
|
+
--page) page="$2" ;;
|
|
42
|
+
--status) status="$2" ;;
|
|
43
|
+
*) usage ;;
|
|
44
|
+
esac
|
|
45
|
+
shift 2
|
|
46
|
+
done
|
|
47
|
+
[[ "$status" =~ ^(pending|granted|declined|all)$ ]] || usage
|
|
48
|
+
[ -z "$page" ] || [[ "$page" =~ ^[A-Za-z0-9_-]{1,50}$ ]] || usage
|
|
49
|
+
;;
|
|
50
|
+
grant|decline)
|
|
51
|
+
[ "$#" -eq 1 ] || usage
|
|
52
|
+
request_id="$1"
|
|
53
|
+
[[ "$request_id" =~ ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ ]] || usage
|
|
54
|
+
;;
|
|
55
|
+
*) usage ;;
|
|
56
|
+
esac
|
|
57
|
+
|
|
58
|
+
if [ "$(_detect_mode)" != connected ]; then
|
|
59
|
+
echo 'page-access: asking for a page and deciding requests needs Connected mode, where pages open by identity. In Local mode, hand over the password-protected file instead.' >&2
|
|
60
|
+
exit 4
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
mkdir -p "$SCRIPT_DIR/tmp"
|
|
64
|
+
out=$(mktemp "$SCRIPT_DIR/tmp/page-access.XXXXXX")
|
|
65
|
+
err=$(mktemp "$SCRIPT_DIR/tmp/page-access-error.XXXXXX")
|
|
66
|
+
trap 'rm -f -- "$out" "$err"' EXIT
|
|
67
|
+
|
|
68
|
+
call() {
|
|
69
|
+
if ! bash "$SCRIPT_DIR/bin/api-call.sh" "$1" "$2" --auth egregore+github \
|
|
70
|
+
--out "$out" --error-out "$err"; then
|
|
71
|
+
detail=$(jq -r '.detail // empty' "$err" 2>/dev/null || true)
|
|
72
|
+
[ -z "$detail" ] || printf 'page-access: %s\n' "$detail" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
case "$command" in
|
|
78
|
+
requests)
|
|
79
|
+
path="/api/page-access/requests?status=$status"
|
|
80
|
+
[ -z "$page" ] || path="$path&page=$page"
|
|
81
|
+
call GET "$path"
|
|
82
|
+
if [ "$(jq '.requests | length' "$out")" -eq 0 ]; then
|
|
83
|
+
echo "No $([ "$status" = all ] || printf '%s ' "$status")requests."
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
86
|
+
jq -r '.requests[] | [
|
|
87
|
+
.id,
|
|
88
|
+
.page,
|
|
89
|
+
(.requester.email + (if .requester.handle then " (@" + .requester.handle + ")" else "" end)),
|
|
90
|
+
.status,
|
|
91
|
+
.requested_at,
|
|
92
|
+
(if .decided_by then "by " + .decided_by + " " + .decided_at else "" end)
|
|
93
|
+
] | @tsv' "$out"
|
|
94
|
+
;;
|
|
95
|
+
grant)
|
|
96
|
+
call POST "/api/page-access/requests/$request_id/grant"
|
|
97
|
+
jq -r '"Granted: " + .request.requester.email + " can open " + .request.page + " when signed in."' "$out"
|
|
98
|
+
;;
|
|
99
|
+
decline)
|
|
100
|
+
call POST "/api/page-access/requests/$request_id/decline"
|
|
101
|
+
jq -r '"Declined: " + .request.requester.email + " sees \"Request declined\" on " + .request.page + " and cannot ask again for seven days."' "$out"
|
|
102
|
+
;;
|
|
103
|
+
esac
|
package/runtime/pi/bin/person.sh
CHANGED
|
@@ -29,7 +29,7 @@ authorize_identity() {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
remote_config() {
|
|
32
|
-
api_url="${EGREGORE_API_URL:-$(_config_val api_url)}"
|
|
32
|
+
api_url="$(_hosted_api_url "${EGREGORE_API_URL:-$(_config_val api_url)}")" || api_url=""
|
|
33
33
|
api_key="${EGREGORE_API_KEY:-$(_load_env_var EGREGORE_API_KEY)}"
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -49,11 +49,22 @@ sync_remote_identity() {
|
|
|
49
49
|
email,
|
|
50
50
|
emails
|
|
51
51
|
}')"
|
|
52
|
-
|
|
52
|
+
api_response="$(curl -q -s "${api_url}/api/user/ensure" \
|
|
53
53
|
-H "Authorization: Bearer ${api_key}" \
|
|
54
54
|
-H "Content-Type: application/json" \
|
|
55
55
|
-d "$api_payload" \
|
|
56
|
+
-w '\n%{http_code}' \
|
|
56
57
|
--max-time 10 2>/dev/null || true)"
|
|
58
|
+
api_code="${api_response##*$'\n'}"
|
|
59
|
+
api_result=""
|
|
60
|
+
case "$api_code" in 2??) api_result="${api_response%$'\n'*}" ;; esac
|
|
61
|
+
# This is each session's one authenticated call, so it doubles as the
|
|
62
|
+
# probe: a rejected organization key is renewed through verified GitHub
|
|
63
|
+
# membership (bin/runtime-identity.sh), never from this response.
|
|
64
|
+
if [ "$api_code" = 401 ] && [ -f "$ROOT/bin/runtime-identity.sh" ]; then
|
|
65
|
+
rejected_code="$(printf '%s' "${api_response%$'\n'*}" | jq -r '.code // empty' 2>/dev/null || true)"
|
|
66
|
+
bash "$ROOT/bin/runtime-identity.sh" recover "$rejected_code" >/dev/null 2>&1 || true
|
|
67
|
+
fi
|
|
57
68
|
api_status="$(printf '%s' "$api_result" | jq -r '.status // "failed"' 2>/dev/null || echo failed)"
|
|
58
69
|
platform_user_id="$(printf '%s' "$api_result" | jq -r '.user_id // empty' 2>/dev/null || true)"
|
|
59
70
|
if [ "$api_status" = "ok" ]; then
|